]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
drm/atmel-hlcdc: Simplify the HLCDC layer logic
authorBoris Brezillon <boris.brezillon@free-electrons.com>
Mon, 6 Feb 2017 17:57:19 +0000 (18:57 +0100)
committerBoris Brezillon <boris.brezillon@free-electrons.com>
Tue, 28 Feb 2017 10:57:56 +0000 (11:57 +0100)
An HLCDC layers in Atmel's nomenclature is either a DRM plane or a 'Post
Processing Layer' which can be used to output the results of the HLCDC
composition in a memory buffer.

atmel_hlcdc_layer.c was designed to be generic enough to be re-usable in
both cases, but we're not exposing the post-processing layer yet, and
even if we were, I'm not sure the code would provide the necessary tools
to manipulate this kind of layer.

Moreover, the code in atmel_hlcdc_{plane,layer}.c was designed before the
atomic modesetting API, and was trying solve the
check-setting/commit-if-ok/rollback-otherwise problem, which is now
entirely solved by the existing core infrastructure.

And finally, the code in atmel_hlcdc_layer.c is over-complicated compared
to what we really need. This rework is a good excuse to simplify it. Note
that this rework solves an existing resource leak (leading to a -EBUSY
error) which I failed to clearly identify.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Eric Anholt <eric@anholt.net>
Tested-by: Nicolas Ferre <nicolas.ferre@microchip.com>
drivers/gpu/drm/atmel-hlcdc/Makefile
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c [deleted file]
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h [deleted file]
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c

index 10ae426e60bd2cb0f36264f29c584d189558a386..bb5f8507a8cee8d893beb23117ad01da98b2c010 100644 (file)
@@ -1,6 +1,5 @@
 atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
                atmel_hlcdc_dc.o \
-               atmel_hlcdc_layer.o \
                atmel_hlcdc_output.o \
                atmel_hlcdc_plane.o
 
index fabeeea0e8999de3bdf7d0539d2870ab6f317432..6b50fb706c0e6e0e77db14690adcda64f115168f 100644 (file)
@@ -466,8 +466,8 @@ static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
 
 int atmel_hlcdc_crtc_create(struct drm_device *dev)
 {
+       struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
        struct atmel_hlcdc_dc *dc = dev->dev_private;
-       struct atmel_hlcdc_planes *planes = dc->planes;
        struct atmel_hlcdc_crtc *crtc;
        int ret;
        int i;
@@ -478,20 +478,41 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
 
        crtc->dc = dc;
 
-       ret = drm_crtc_init_with_planes(dev, &crtc->base,
-                               &planes->primary->base,
-                               planes->cursor ? &planes->cursor->base : NULL,
-                               &atmel_hlcdc_crtc_funcs, NULL);
+       for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+               if (!dc->layers[i])
+                       continue;
+
+               switch (dc->layers[i]->desc->type) {
+               case ATMEL_HLCDC_BASE_LAYER:
+                       primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
+                       break;
+
+               case ATMEL_HLCDC_CURSOR_LAYER:
+                       cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
+                                       &cursor->base, &atmel_hlcdc_crtc_funcs,
+                                       NULL);
        if (ret < 0)
                goto fail;
 
        crtc->id = drm_crtc_index(&crtc->base);
 
-       if (planes->cursor)
-               planes->cursor->base.possible_crtcs = 1 << crtc->id;
+       for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+               struct atmel_hlcdc_plane *overlay;
 
-       for (i = 0; i < planes->noverlays; i++)
-               planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
+               if (dc->layers[i] &&
+                   dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
+                       overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
+                       overlay->base.possible_crtcs = 1 << crtc->id;
+               }
+       }
 
        drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
        drm_crtc_vblank_reset(&crtc->base);
index fd1a2d0c870dc04cb3eca8fb13171312f1df3e87..970bd87d7ccec26a8699eb5fb2f289c4b4cb2d69 100644 (file)
@@ -36,7 +36,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = {
                .regs_offset = 0x40,
                .id = 0,
                .type = ATMEL_HLCDC_BASE_LAYER,
-               .nconfigs = 5,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .xstride = { 2 },
                        .default_color = 3,
@@ -65,7 +65,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
                .regs_offset = 0x40,
                .id = 0,
                .type = ATMEL_HLCDC_BASE_LAYER,
-               .nconfigs = 5,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .xstride = { 2 },
                        .default_color = 3,
@@ -80,7 +80,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
                .regs_offset = 0x100,
                .id = 1,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 10,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -98,7 +98,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
                .regs_offset = 0x280,
                .id = 2,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 17,
+               .cfgs_offset = 0x4c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -109,6 +109,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
                        .chroma_key = 10,
                        .chroma_key_mask = 11,
                        .general_config = 12,
+                       .scaler_config = 13,
                        .csc = 14,
                },
        },
@@ -118,9 +119,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
                .regs_offset = 0x340,
                .id = 3,
                .type = ATMEL_HLCDC_CURSOR_LAYER,
-               .nconfigs = 10,
                .max_width = 128,
                .max_height = 128,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -153,7 +154,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                .regs_offset = 0x40,
                .id = 0,
                .type = ATMEL_HLCDC_BASE_LAYER,
-               .nconfigs = 7,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .xstride = { 2 },
                        .default_color = 3,
@@ -168,7 +169,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                .regs_offset = 0x140,
                .id = 1,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 10,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -186,7 +187,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                .regs_offset = 0x240,
                .id = 2,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 10,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -204,7 +205,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                .regs_offset = 0x340,
                .id = 3,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 42,
+               .cfgs_offset = 0x4c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -215,6 +216,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                        .chroma_key = 10,
                        .chroma_key_mask = 11,
                        .general_config = 12,
+                       .scaler_config = 13,
+                       .phicoeffs = {
+                               .x = 17,
+                               .y = 33,
+                       },
                        .csc = 14,
                },
        },
@@ -224,9 +230,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                .regs_offset = 0x440,
                .id = 4,
                .type = ATMEL_HLCDC_CURSOR_LAYER,
-               .nconfigs = 10,
                .max_width = 128,
                .max_height = 128,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -236,6 +242,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
                        .chroma_key = 7,
                        .chroma_key_mask = 8,
                        .general_config = 9,
+                       .scaler_config = 13,
                },
        },
 };
@@ -260,7 +267,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
                .regs_offset = 0x40,
                .id = 0,
                .type = ATMEL_HLCDC_BASE_LAYER,
-               .nconfigs = 7,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .xstride = { 2 },
                        .default_color = 3,
@@ -275,7 +282,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
                .regs_offset = 0x140,
                .id = 1,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 10,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -293,7 +300,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
                .regs_offset = 0x240,
                .id = 2,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 10,
+               .cfgs_offset = 0x2c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -311,7 +318,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
                .regs_offset = 0x340,
                .id = 3,
                .type = ATMEL_HLCDC_OVERLAY_LAYER,
-               .nconfigs = 42,
+               .cfgs_offset = 0x4c,
                .layout = {
                        .pos = 2,
                        .size = 3,
@@ -322,6 +329,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
                        .chroma_key = 10,
                        .chroma_key_mask = 11,
                        .general_config = 12,
+                       .scaler_config = 13,
+                       .phicoeffs = {
+                               .x = 17,
+                               .y = 33,
+                       },
                        .csc = 14,
                },
        },
@@ -392,6 +404,17 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
        return MODE_OK;
 }
 
+static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
+{
+       if (!layer)
+               return;
+
+       if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER ||
+           layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
+           layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER)
+               atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer));
+}
+
 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
 {
        struct drm_device *dev = data;
@@ -410,12 +433,8 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
                atmel_hlcdc_crtc_irq(dc->crtc);
 
        for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
-               struct atmel_hlcdc_layer *layer = dc->layers[i];
-
-               if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
-                       continue;
-
-               atmel_hlcdc_layer_irq(layer);
+               if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
+                       atmel_hlcdc_layer_irq(dc->layers[i]);
        }
 
        return IRQ_HANDLED;
@@ -537,9 +556,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
 {
        struct atmel_hlcdc_dc *dc = dev->dev_private;
-       struct atmel_hlcdc_planes *planes;
        int ret;
-       int i;
 
        drm_mode_config_init(dev);
 
@@ -549,25 +566,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
                return ret;
        }
 
-       planes = atmel_hlcdc_create_planes(dev);
-       if (IS_ERR(planes)) {
-               dev_err(dev->dev, "failed to create planes\n");
-               return PTR_ERR(planes);
+       ret = atmel_hlcdc_create_planes(dev);
+       if (ret) {
+               dev_err(dev->dev, "failed to create planes: %d\n", ret);
+               return ret;
        }
 
-       dc->planes = planes;
-
-       dc->layers[planes->primary->layer.desc->id] =
-                                               &planes->primary->layer;
-
-       if (planes->cursor)
-               dc->layers[planes->cursor->layer.desc->id] =
-                                                       &planes->cursor->layer;
-
-       for (i = 0; i < planes->noverlays; i++)
-               dc->layers[planes->overlays[i]->layer.desc->id] =
-                                               &planes->overlays[i]->layer;
-
        ret = atmel_hlcdc_crtc_create(dev);
        if (ret) {
                dev_err(dev->dev, "failed to create crtc\n");
index 7a47f8c094d021d75a3ec86263145eb8326d7f22..da7f25a59be58445a9f3b79c72b716960dcc4175 100644 (file)
@@ -23,7 +23,9 @@
 #define DRM_ATMEL_HLCDC_H
 
 #include <linux/clk.h>
+#include <linux/dmapool.h>
 #include <linux/irqdomain.h>
+#include <linux/mfd/atmel-hlcdc.h>
 #include <linux/pwm.h>
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 
-#include "atmel_hlcdc_layer.h"
+#define ATMEL_HLCDC_LAYER_CHER                 0x0
+#define ATMEL_HLCDC_LAYER_CHDR                 0x4
+#define ATMEL_HLCDC_LAYER_CHSR                 0x8
+#define ATMEL_HLCDC_LAYER_EN                   BIT(0)
+#define ATMEL_HLCDC_LAYER_UPDATE               BIT(1)
+#define ATMEL_HLCDC_LAYER_A2Q                  BIT(2)
+#define ATMEL_HLCDC_LAYER_RST                  BIT(8)
 
-#define ATMEL_HLCDC_MAX_LAYERS         5
+#define ATMEL_HLCDC_LAYER_IER                  0xc
+#define ATMEL_HLCDC_LAYER_IDR                  0x10
+#define ATMEL_HLCDC_LAYER_IMR                  0x14
+#define ATMEL_HLCDC_LAYER_ISR                  0x18
+#define ATMEL_HLCDC_LAYER_DFETCH               BIT(0)
+#define ATMEL_HLCDC_LAYER_LFETCH               BIT(1)
+#define ATMEL_HLCDC_LAYER_DMA_IRQ(p)           BIT(2 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_DSCR_IRQ(p)          BIT(3 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_ADD_IRQ(p)           BIT(4 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_DONE_IRQ(p)          BIT(5 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_OVR_IRQ(p)           BIT(6 + (8 * (p)))
+
+#define ATMEL_HLCDC_LAYER_PLANE_HEAD(p)                (((p) * 0x10) + 0x1c)
+#define ATMEL_HLCDC_LAYER_PLANE_ADDR(p)                (((p) * 0x10) + 0x20)
+#define ATMEL_HLCDC_LAYER_PLANE_CTRL(p)                (((p) * 0x10) + 0x24)
+#define ATMEL_HLCDC_LAYER_PLANE_NEXT(p)                (((p) * 0x10) + 0x28)
+
+#define ATMEL_HLCDC_LAYER_DMA_CFG              0
+#define ATMEL_HLCDC_LAYER_DMA_SIF              BIT(0)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK                GENMASK(5, 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE      (0 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4       (1 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8       (2 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16      (3 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_DLBO             BIT(8)
+#define ATMEL_HLCDC_LAYER_DMA_ROTDIS           BIT(12)
+#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS          BIT(13)
+
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG           1
+#define ATMEL_HLCDC_LAYER_RGB                  (0 << 0)
+#define ATMEL_HLCDC_LAYER_CLUT                 (1 << 0)
+#define ATMEL_HLCDC_LAYER_YUV                  (2 << 0)
+#define ATMEL_HLCDC_RGB_MODE(m)                        \
+       (ATMEL_HLCDC_LAYER_RGB | (((m) & 0xf) << 4))
+#define ATMEL_HLCDC_CLUT_MODE(m)               \
+       (ATMEL_HLCDC_LAYER_CLUT | (((m) & 0x3) << 8))
+#define ATMEL_HLCDC_YUV_MODE(m)                        \
+       (ATMEL_HLCDC_LAYER_YUV | (((m) & 0xf) << 12))
+#define ATMEL_HLCDC_YUV422ROT                  BIT(16)
+#define ATMEL_HLCDC_YUV422SWP                  BIT(17)
+#define ATMEL_HLCDC_DSCALEOPT                  BIT(20)
+
+#define ATMEL_HLCDC_XRGB4444_MODE              ATMEL_HLCDC_RGB_MODE(0)
+#define ATMEL_HLCDC_ARGB4444_MODE              ATMEL_HLCDC_RGB_MODE(1)
+#define ATMEL_HLCDC_RGBA4444_MODE              ATMEL_HLCDC_RGB_MODE(2)
+#define ATMEL_HLCDC_RGB565_MODE                        ATMEL_HLCDC_RGB_MODE(3)
+#define ATMEL_HLCDC_ARGB1555_MODE              ATMEL_HLCDC_RGB_MODE(4)
+#define ATMEL_HLCDC_XRGB8888_MODE              ATMEL_HLCDC_RGB_MODE(9)
+#define ATMEL_HLCDC_RGB888_MODE                        ATMEL_HLCDC_RGB_MODE(10)
+#define ATMEL_HLCDC_ARGB8888_MODE              ATMEL_HLCDC_RGB_MODE(12)
+#define ATMEL_HLCDC_RGBA8888_MODE              ATMEL_HLCDC_RGB_MODE(13)
+
+#define ATMEL_HLCDC_AYUV_MODE                  ATMEL_HLCDC_YUV_MODE(0)
+#define ATMEL_HLCDC_YUYV_MODE                  ATMEL_HLCDC_YUV_MODE(1)
+#define ATMEL_HLCDC_UYVY_MODE                  ATMEL_HLCDC_YUV_MODE(2)
+#define ATMEL_HLCDC_YVYU_MODE                  ATMEL_HLCDC_YUV_MODE(3)
+#define ATMEL_HLCDC_VYUY_MODE                  ATMEL_HLCDC_YUV_MODE(4)
+#define ATMEL_HLCDC_NV61_MODE                  ATMEL_HLCDC_YUV_MODE(5)
+#define ATMEL_HLCDC_YUV422_MODE                        ATMEL_HLCDC_YUV_MODE(6)
+#define ATMEL_HLCDC_NV21_MODE                  ATMEL_HLCDC_YUV_MODE(7)
+#define ATMEL_HLCDC_YUV420_MODE                        ATMEL_HLCDC_YUV_MODE(8)
+
+#define ATMEL_HLCDC_LAYER_POS(x, y)            ((x) | ((y) << 16))
+#define ATMEL_HLCDC_LAYER_SIZE(w, h)           (((w) - 1) | (((h) - 1) << 16))
+
+#define ATMEL_HLCDC_LAYER_CRKEY                        BIT(0)
+#define ATMEL_HLCDC_LAYER_INV                  BIT(1)
+#define ATMEL_HLCDC_LAYER_ITER2BL              BIT(2)
+#define ATMEL_HLCDC_LAYER_ITER                 BIT(3)
+#define ATMEL_HLCDC_LAYER_REVALPHA             BIT(4)
+#define ATMEL_HLCDC_LAYER_GAEN                 BIT(5)
+#define ATMEL_HLCDC_LAYER_LAEN                 BIT(6)
+#define ATMEL_HLCDC_LAYER_OVR                  BIT(7)
+#define ATMEL_HLCDC_LAYER_DMA                  BIT(8)
+#define ATMEL_HLCDC_LAYER_REP                  BIT(9)
+#define ATMEL_HLCDC_LAYER_DSTKEY               BIT(10)
+#define ATMEL_HLCDC_LAYER_DISCEN               BIT(11)
+#define ATMEL_HLCDC_LAYER_GA_SHIFT             16
+#define ATMEL_HLCDC_LAYER_GA_MASK              \
+       GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
+#define ATMEL_HLCDC_LAYER_GA(x)                        \
+       ((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
+
+#define ATMEL_HLCDC_LAYER_DISC_POS(x, y)       ((x) | ((y) << 16))
+#define ATMEL_HLCDC_LAYER_DISC_SIZE(w, h)      (((w) - 1) | (((h) - 1) << 16))
+
+#define ATMEL_HLCDC_LAYER_SCALER_FACTORS(x, y) ((x) | ((y) << 16))
+#define ATMEL_HLCDC_LAYER_SCALER_ENABLE                BIT(31)
+
+#define ATMEL_HLCDC_LAYER_MAX_PLANES           3
+
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED  BIT(0)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED    BIT(1)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE      BIT(2)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN   BIT(3)
+
+#define ATMEL_HLCDC_MAX_LAYERS                 6
 
 /**
- * Atmel HLCDC Display Controller description structure.
+ * Atmel HLCDC Layer registers layout structure
  *
- * This structure describe the HLCDC IP capabilities and depends on the
- * HLCDC IP version (or Atmel SoC family).
+ * Each HLCDC layer has its own register organization and a given register
+ * can be placed differently on 2 different layers depending on its
+ * capabilities.
+ * This structure stores common registers layout for a given layer and is
+ * used by HLCDC layer code to choose the appropriate register to write to
+ * or to read from.
  *
- * @min_width: minimum width supported by the Display Controller
- * @min_height: minimum height supported by the Display Controller
- * @max_width: maximum width supported by the Display Controller
- * @max_height: maximum height supported by the Display Controller
- * @max_spw: maximum vertical/horizontal pulse width
- * @max_vpw: maximum vertical back/front porch width
- * @max_hpw: maximum horizontal back/front porch width
- * @conflicting_output_formats: true if RGBXXX output formats conflict with
- *                             each other.
- * @layers: a layer description table describing available layers
- * @nlayers: layer description table size
+ * For all fields, a value of zero means "unsupported".
+ *
+ * See Atmel's datasheet for a detailled description of these registers.
+ *
+ * @xstride: xstride registers
+ * @pstride: pstride registers
+ * @pos: position register
+ * @size: displayed size register
+ * @memsize: memory size register
+ * @default_color: default color register
+ * @chroma_key: chroma key register
+ * @chroma_key_mask: chroma key mask register
+ * @general_config: general layer config register
+ * @sacler_config: scaler factors register
+ * @phicoeffs: X/Y PHI coefficient registers
+ * @disc_pos: discard area position register
+ * @disc_size: discard area size register
+ * @csc: color space conversion register
  */
-struct atmel_hlcdc_dc_desc {
-       int min_width;
-       int min_height;
+struct atmel_hlcdc_layer_cfg_layout {
+       int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
+       int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
+       int pos;
+       int size;
+       int memsize;
+       int default_color;
+       int chroma_key;
+       int chroma_key_mask;
+       int general_config;
+       int scaler_config;
+       struct {
+               int x;
+               int y;
+       } phicoeffs;
+       int disc_pos;
+       int disc_size;
+       int csc;
+};
+
+/**
+ * Atmel HLCDC DMA descriptor structure
+ *
+ * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
+ *
+ * The structure fields must remain in this specific order, because they're
+ * used by the HLCDC DMA engine, which expect them in this order.
+ * HLCDC DMA descriptors must be aligned on 64 bits.
+ *
+ * @addr: buffer DMA address
+ * @ctrl: DMA transfer options
+ * @next: next DMA descriptor to fetch
+ * @self: descriptor DMA address
+ */
+struct atmel_hlcdc_dma_channel_dscr {
+       dma_addr_t addr;
+       u32 ctrl;
+       dma_addr_t next;
+       dma_addr_t self;
+} __aligned(sizeof(u64));
+
+/**
+ * Atmel HLCDC layer types
+ */
+enum atmel_hlcdc_layer_type {
+       ATMEL_HLCDC_NO_LAYER,
+       ATMEL_HLCDC_BASE_LAYER,
+       ATMEL_HLCDC_OVERLAY_LAYER,
+       ATMEL_HLCDC_CURSOR_LAYER,
+       ATMEL_HLCDC_PP_LAYER,
+};
+
+/**
+ * Atmel HLCDC Supported formats structure
+ *
+ * This structure list all the formats supported by a given layer.
+ *
+ * @nformats: number of supported formats
+ * @formats: supported formats
+ */
+struct atmel_hlcdc_formats {
+       int nformats;
+       u32 *formats;
+};
+
+/**
+ * Atmel HLCDC Layer description structure
+ *
+ * This structure describes the capabilities provided by a given layer.
+ *
+ * @name: layer name
+ * @type: layer type
+ * @id: layer id
+ * @regs_offset: offset of the layer registers from the HLCDC registers base
+ * @cfgs_offset: CFGX registers offset from the layer registers base
+ * @formats: supported formats
+ * @layout: config registers layout
+ * @max_width: maximum width supported by this layer (0 means unlimited)
+ * @max_height: maximum height supported by this layer (0 means unlimited)
+ */
+struct atmel_hlcdc_layer_desc {
+       const char *name;
+       enum atmel_hlcdc_layer_type type;
+       int id;
+       int regs_offset;
+       int cfgs_offset;
+       struct atmel_hlcdc_formats *formats;
+       struct atmel_hlcdc_layer_cfg_layout layout;
        int max_width;
        int max_height;
-       int max_spw;
-       int max_vpw;
-       int max_hpw;
-       bool conflicting_output_formats;
-       const struct atmel_hlcdc_layer_desc *layers;
-       int nlayers;
 };
 
 /**
- * Atmel HLCDC Plane properties.
+ * Atmel HLCDC Layer.
  *
- * This structure stores plane property definitions.
+ * A layer can be a DRM plane of a post processing layer used to render
+ * HLCDC composition into memory.
  *
- * @alpha: alpha blending (or transparency) property
- * @rotation: rotation property
+ * @desc: layer description
+ * @regmap: pointer to the HLCDC regmap
  */
-struct atmel_hlcdc_plane_properties {
-       struct drm_property *alpha;
+struct atmel_hlcdc_layer {
+       const struct atmel_hlcdc_layer_desc *desc;
+       struct regmap *regmap;
 };
 
 /**
@@ -89,7 +285,6 @@ struct atmel_hlcdc_plane_properties {
  * @base: base DRM plane structure
  * @layer: HLCDC layer structure
  * @properties: pointer to the property definitions structure
- * @rotation: current rotation status
  */
 struct atmel_hlcdc_plane {
        struct drm_plane base;
@@ -104,47 +299,73 @@ drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
 }
 
 static inline struct atmel_hlcdc_plane *
-atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
+atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *layer)
 {
-       return container_of(l, struct atmel_hlcdc_plane, layer);
+       return container_of(layer, struct atmel_hlcdc_plane, layer);
 }
 
 /**
- * Atmel HLCDC Planes.
+ * Atmel HLCDC Display Controller description structure.
  *
- * This structure stores the instantiated HLCDC Planes and can be accessed by
- * the HLCDC Display Controller or the HLCDC CRTC.
+ * This structure describes the HLCDC IP capabilities and depends on the
+ * HLCDC IP version (or Atmel SoC family).
  *
- * @primary: primary plane
- * @cursor: hardware cursor plane
- * @overlays: overlay plane table
- * @noverlays: number of overlay planes
+ * @min_width: minimum width supported by the Display Controller
+ * @min_height: minimum height supported by the Display Controller
+ * @max_width: maximum width supported by the Display Controller
+ * @max_height: maximum height supported by the Display Controller
+ * @max_spw: maximum vertical/horizontal pulse width
+ * @max_vpw: maximum vertical back/front porch width
+ * @max_hpw: maximum horizontal back/front porch width
+ * @conflicting_output_formats: true if RGBXXX output formats conflict with
+ *                             each other.
+ * @layers: a layer description table describing available layers
+ * @nlayers: layer description table size
  */
-struct atmel_hlcdc_planes {
-       struct atmel_hlcdc_plane *primary;
-       struct atmel_hlcdc_plane *cursor;
-       struct atmel_hlcdc_plane **overlays;
-       int noverlays;
+struct atmel_hlcdc_dc_desc {
+       int min_width;
+       int min_height;
+       int max_width;
+       int max_height;
+       int max_spw;
+       int max_vpw;
+       int max_hpw;
+       bool conflicting_output_formats;
+       const struct atmel_hlcdc_layer_desc *layers;
+       int nlayers;
+};
+
+/**
+ * Atmel HLCDC Plane properties.
+ *
+ * This structure stores plane property definitions.
+ *
+ * @alpha: alpha blending (or transparency) property
+ * @rotation: rotation property
+ */
+struct atmel_hlcdc_plane_properties {
+       struct drm_property *alpha;
 };
 
 /**
  * Atmel HLCDC Display Controller.
  *
  * @desc: HLCDC Display Controller description
+ * @dscrpool: DMA coherent pool used to allocate DMA descriptors
  * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
  * @fbdev: framebuffer device attached to the Display Controller
  * @crtc: CRTC provided by the display controller
  * @planes: instantiated planes
- * @layers: active HLCDC layer
+ * @layers: active HLCDC layers
  * @wq: display controller workqueue
  * @commit: used for async commit handling
  */
 struct atmel_hlcdc_dc {
        const struct atmel_hlcdc_dc_desc *desc;
+       struct dma_pool *dscrpool;
        struct atmel_hlcdc *hlcdc;
        struct drm_fbdev_cma *fbdev;
        struct drm_crtc *crtc;
-       struct atmel_hlcdc_planes *planes;
        struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
        struct workqueue_struct *wq;
        struct {
@@ -156,11 +377,51 @@ struct atmel_hlcdc_dc {
 extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
 extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
 
+static inline void atmel_hlcdc_layer_write_reg(struct atmel_hlcdc_layer *layer,
+                                              unsigned int reg, u32 val)
+{
+       regmap_write(layer->regmap, layer->desc->regs_offset + reg, val);
+}
+
+static inline u32 atmel_hlcdc_layer_read_reg(struct atmel_hlcdc_layer *layer,
+                                            unsigned int reg)
+{
+       u32 val;
+
+       regmap_read(layer->regmap, layer->desc->regs_offset + reg, &val);
+
+       return val;
+}
+
+static inline void atmel_hlcdc_layer_write_cfg(struct atmel_hlcdc_layer *layer,
+                                              unsigned int cfgid, u32 val)
+{
+       atmel_hlcdc_layer_write_reg(layer,
+                                   layer->desc->cfgs_offset +
+                                   (cfgid * sizeof(u32)), val);
+}
+
+static inline u32 atmel_hlcdc_layer_read_cfg(struct atmel_hlcdc_layer *layer,
+                                            unsigned int cfgid)
+{
+       return atmel_hlcdc_layer_read_reg(layer,
+                                         layer->desc->cfgs_offset +
+                                         (cfgid * sizeof(u32)));
+}
+
+static inline void atmel_hlcdc_layer_init(struct atmel_hlcdc_layer *layer,
+                               const struct atmel_hlcdc_layer_desc *desc,
+                               struct regmap *regmap)
+{
+       layer->desc = desc;
+       layer->regmap = regmap;
+}
+
 int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
                              struct drm_display_mode *mode);
 
-struct atmel_hlcdc_planes *
-atmel_hlcdc_create_planes(struct drm_device *dev);
+int atmel_hlcdc_create_planes(struct drm_device *dev);
+void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane);
 
 int atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state);
 int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state);
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
deleted file mode 100644 (file)
index 63dfdbf..0000000
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * Copyright (C) 2014 Free Electrons
- * Copyright (C) 2014 Atmel
- *
- * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-
-#include "atmel_hlcdc_dc.h"
-
-static void
-atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
-{
-       struct atmel_hlcdc_layer_fb_flip *flip = val;
-
-       if (flip->fb)
-               drm_framebuffer_unreference(flip->fb);
-       kfree(flip);
-}
-
-static void
-atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
-{
-       if (flip->fb)
-               drm_framebuffer_unreference(flip->fb);
-       kfree(flip->task);
-       kfree(flip);
-}
-
-static void
-atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
-                                       struct atmel_hlcdc_layer_fb_flip *flip)
-{
-       int i;
-
-       if (!flip)
-               return;
-
-       for (i = 0; i < layer->max_planes; i++) {
-               if (!flip->dscrs[i])
-                       break;
-
-               flip->dscrs[i]->status = 0;
-               flip->dscrs[i] = NULL;
-       }
-
-       drm_flip_work_queue_task(&layer->gc, flip->task);
-       drm_flip_work_commit(&layer->gc, layer->wq);
-}
-
-static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
-                                          int id)
-{
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct atmel_hlcdc_layer_update_slot *slot;
-
-       if (id < 0 || id > 1)
-               return;
-
-       slot = &upd->slots[id];
-       bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
-       memset(slot->configs, 0,
-              sizeof(*slot->configs) * layer->desc->nconfigs);
-
-       if (slot->fb_flip) {
-               atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
-               slot->fb_flip = NULL;
-       }
-}
-
-static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct regmap *regmap = layer->hlcdc->regmap;
-       struct atmel_hlcdc_layer_update_slot *slot;
-       struct atmel_hlcdc_layer_fb_flip *fb_flip;
-       struct atmel_hlcdc_dma_channel_dscr *dscr;
-       unsigned int cfg;
-       u32 action = 0;
-       int i = 0;
-
-       if (upd->pending < 0 || upd->pending > 1)
-               return;
-
-       slot = &upd->slots[upd->pending];
-
-       for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
-               regmap_write(regmap,
-                            desc->regs_offset +
-                            ATMEL_HLCDC_LAYER_CFG(layer, cfg),
-                            slot->configs[cfg]);
-               action |= ATMEL_HLCDC_LAYER_UPDATE;
-       }
-
-       fb_flip = slot->fb_flip;
-
-       if (!fb_flip->fb)
-               goto apply;
-
-       if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
-               for (i = 0; i < fb_flip->ngems; i++) {
-                       dscr = fb_flip->dscrs[i];
-                       dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
-                                    ATMEL_HLCDC_LAYER_DMA_IRQ |
-                                    ATMEL_HLCDC_LAYER_ADD_IRQ |
-                                    ATMEL_HLCDC_LAYER_DONE_IRQ;
-
-                       regmap_write(regmap,
-                                    desc->regs_offset +
-                                    ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
-                                    dscr->addr);
-                       regmap_write(regmap,
-                                    desc->regs_offset +
-                                    ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
-                                    dscr->ctrl);
-                       regmap_write(regmap,
-                                    desc->regs_offset +
-                                    ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
-                                    dscr->next);
-               }
-
-               action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
-               dma->status = ATMEL_HLCDC_LAYER_ENABLED;
-       } else {
-               for (i = 0; i < fb_flip->ngems; i++) {
-                       dscr =  fb_flip->dscrs[i];
-                       dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
-                                    ATMEL_HLCDC_LAYER_DMA_IRQ |
-                                    ATMEL_HLCDC_LAYER_DSCR_IRQ |
-                                    ATMEL_HLCDC_LAYER_DONE_IRQ;
-
-                       regmap_write(regmap,
-                                    desc->regs_offset +
-                                    ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
-                                    dscr->next);
-               }
-
-               action |= ATMEL_HLCDC_LAYER_A2Q;
-       }
-
-       /* Release unneeded descriptors */
-       for (i = fb_flip->ngems; i < layer->max_planes; i++) {
-               fb_flip->dscrs[i]->status = 0;
-               fb_flip->dscrs[i] = NULL;
-       }
-
-       dma->queue = fb_flip;
-       slot->fb_flip = NULL;
-
-apply:
-       if (action)
-               regmap_write(regmap,
-                            desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
-                            action);
-
-       atmel_hlcdc_layer_update_reset(layer, upd->pending);
-
-       upd->pending = -1;
-}
-
-void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-       struct regmap *regmap = layer->hlcdc->regmap;
-       struct atmel_hlcdc_layer_fb_flip *flip;
-       unsigned long flags;
-       unsigned int isr, imr;
-       unsigned int status;
-       unsigned int plane_status;
-       u32 flip_status;
-
-       int i;
-
-       regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
-       regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
-       status = imr & isr;
-       if (!status)
-               return;
-
-       spin_lock_irqsave(&layer->lock, flags);
-
-       flip = dma->queue ? dma->queue : dma->cur;
-
-       if (!flip) {
-               spin_unlock_irqrestore(&layer->lock, flags);
-               return;
-       }
-
-       /*
-        * Set LOADED and DONE flags: they'll be cleared if at least one
-        * memory plane is not LOADED or DONE.
-        */
-       flip_status = ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED |
-                     ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
-       for (i = 0; i < flip->ngems; i++) {
-               plane_status = (status >> (8 * i));
-
-               if (plane_status &
-                   (ATMEL_HLCDC_LAYER_ADD_IRQ |
-                    ATMEL_HLCDC_LAYER_DSCR_IRQ) &
-                   ~flip->dscrs[i]->ctrl) {
-                       flip->dscrs[i]->status |=
-                                       ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
-                       flip->dscrs[i]->ctrl |=
-                                       ATMEL_HLCDC_LAYER_ADD_IRQ |
-                                       ATMEL_HLCDC_LAYER_DSCR_IRQ;
-               }
-
-               if (plane_status &
-                   ATMEL_HLCDC_LAYER_DONE_IRQ &
-                   ~flip->dscrs[i]->ctrl) {
-                       flip->dscrs[i]->status |=
-                                       ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
-                       flip->dscrs[i]->ctrl |=
-                                       ATMEL_HLCDC_LAYER_DONE_IRQ;
-               }
-
-               if (plane_status & ATMEL_HLCDC_LAYER_OVR_IRQ)
-                       flip->dscrs[i]->status |=
-                                       ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
-
-               /*
-                * Clear LOADED and DONE flags if the memory plane is either
-                * not LOADED or not DONE.
-                */
-               if (!(flip->dscrs[i]->status &
-                     ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED))
-                       flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
-
-               if (!(flip->dscrs[i]->status &
-                     ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE))
-                       flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
-
-               /*
-                * An overrun on one memory plane impact the whole framebuffer
-                * transfer, hence we set the OVERRUN flag as soon as there's
-                * one memory plane reporting such an overrun.
-                */
-               flip_status |= flip->dscrs[i]->status &
-                              ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
-       }
-
-       /* Get changed bits */
-       flip_status ^= flip->status;
-       flip->status |= flip_status;
-
-       if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
-               atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
-               dma->cur = dma->queue;
-               dma->queue = NULL;
-       }
-
-       if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
-               atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
-               dma->cur = NULL;
-       }
-
-       if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN) {
-               regmap_write(regmap,
-                            desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
-                            ATMEL_HLCDC_LAYER_RST);
-               if (dma->queue)
-                       atmel_hlcdc_layer_fb_flip_release_queue(layer,
-                                                               dma->queue);
-
-               if (dma->cur)
-                       atmel_hlcdc_layer_fb_flip_release_queue(layer,
-                                                               dma->cur);
-
-               dma->cur = NULL;
-               dma->queue = NULL;
-       }
-
-       if (!dma->queue) {
-               atmel_hlcdc_layer_update_apply(layer);
-
-               if (!dma->cur)
-                       dma->status = ATMEL_HLCDC_LAYER_DISABLED;
-       }
-
-       spin_unlock_irqrestore(&layer->lock, flags);
-}
-
-void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct regmap *regmap = layer->hlcdc->regmap;
-       const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-       unsigned long flags;
-       unsigned int isr;
-
-       spin_lock_irqsave(&layer->lock, flags);
-
-       /* Disable the layer */
-       regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
-                    ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
-                    ATMEL_HLCDC_LAYER_UPDATE);
-
-       /* Clear all pending interrupts */
-       regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
-
-       /* Discard current and queued framebuffer transfers. */
-       if (dma->cur) {
-               atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
-               dma->cur = NULL;
-       }
-
-       if (dma->queue) {
-               atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->queue);
-               dma->queue = NULL;
-       }
-
-       /*
-        * Then discard the pending update request (if any) to prevent
-        * DMA irq handler from restarting the DMA channel after it has
-        * been disabled.
-        */
-       if (upd->pending >= 0) {
-               atmel_hlcdc_layer_update_reset(layer, upd->pending);
-               upd->pending = -1;
-       }
-
-       dma->status = ATMEL_HLCDC_LAYER_DISABLED;
-
-       spin_unlock_irqrestore(&layer->lock, flags);
-}
-
-int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct regmap *regmap = layer->hlcdc->regmap;
-       struct atmel_hlcdc_layer_fb_flip *fb_flip;
-       struct atmel_hlcdc_layer_update_slot *slot;
-       unsigned long flags;
-       int i, j = 0;
-
-       fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
-       if (!fb_flip)
-               return -ENOMEM;
-
-       fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
-       if (!fb_flip->task) {
-               kfree(fb_flip);
-               return -ENOMEM;
-       }
-
-       spin_lock_irqsave(&layer->lock, flags);
-
-       upd->next = upd->pending ? 0 : 1;
-
-       slot = &upd->slots[upd->next];
-
-       for (i = 0; i < layer->max_planes * 4; i++) {
-               if (!dma->dscrs[i].status) {
-                       fb_flip->dscrs[j++] = &dma->dscrs[i];
-                       dma->dscrs[i].status =
-                               ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
-                       if (j == layer->max_planes)
-                               break;
-               }
-       }
-
-       if (j < layer->max_planes) {
-               for (i = 0; i < j; i++)
-                       fb_flip->dscrs[i]->status = 0;
-       }
-
-       if (j < layer->max_planes) {
-               spin_unlock_irqrestore(&layer->lock, flags);
-               atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
-               return -EBUSY;
-       }
-
-       slot->fb_flip = fb_flip;
-
-       if (upd->pending >= 0) {
-               memcpy(slot->configs,
-                      upd->slots[upd->pending].configs,
-                      layer->desc->nconfigs * sizeof(u32));
-               memcpy(slot->updated_configs,
-                      upd->slots[upd->pending].updated_configs,
-                      DIV_ROUND_UP(layer->desc->nconfigs,
-                                   BITS_PER_BYTE * sizeof(unsigned long)) *
-                      sizeof(unsigned long));
-               slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
-               if (upd->slots[upd->pending].fb_flip->fb) {
-                       slot->fb_flip->fb =
-                               upd->slots[upd->pending].fb_flip->fb;
-                       slot->fb_flip->ngems =
-                               upd->slots[upd->pending].fb_flip->ngems;
-                       drm_framebuffer_reference(slot->fb_flip->fb);
-               }
-       } else {
-               regmap_bulk_read(regmap,
-                                layer->desc->regs_offset +
-                                ATMEL_HLCDC_LAYER_CFG(layer, 0),
-                                upd->slots[upd->next].configs,
-                                layer->desc->nconfigs);
-       }
-
-       spin_unlock_irqrestore(&layer->lock, flags);
-
-       return 0;
-}
-
-void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-
-       atmel_hlcdc_layer_update_reset(layer, upd->next);
-       upd->next = -1;
-}
-
-void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
-                                    struct drm_framebuffer *fb,
-                                    unsigned int *offsets)
-{
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct atmel_hlcdc_layer_fb_flip *fb_flip;
-       struct atmel_hlcdc_layer_update_slot *slot;
-       struct atmel_hlcdc_dma_channel_dscr *dscr;
-       struct drm_framebuffer *old_fb;
-       int nplanes = 0;
-       int i;
-
-       if (upd->next < 0 || upd->next > 1)
-               return;
-
-       if (fb)
-               nplanes = fb->format->num_planes;
-
-       if (nplanes > layer->max_planes)
-               return;
-
-       slot = &upd->slots[upd->next];
-
-       fb_flip = slot->fb_flip;
-       old_fb = slot->fb_flip->fb;
-
-       for (i = 0; i < nplanes; i++) {
-               struct drm_gem_cma_object *gem;
-
-               dscr = slot->fb_flip->dscrs[i];
-               gem = drm_fb_cma_get_gem_obj(fb, i);
-               dscr->addr = gem->paddr + offsets[i];
-       }
-
-       fb_flip->ngems = nplanes;
-       fb_flip->fb = fb;
-
-       if (fb)
-               drm_framebuffer_reference(fb);
-
-       if (old_fb)
-               drm_framebuffer_unreference(old_fb);
-}
-
-void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
-                                 u32 mask, u32 val)
-{
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct atmel_hlcdc_layer_update_slot *slot;
-
-       if (upd->next < 0 || upd->next > 1)
-               return;
-
-       if (cfg >= layer->desc->nconfigs)
-               return;
-
-       slot = &upd->slots[upd->next];
-       slot->configs[cfg] &= ~mask;
-       slot->configs[cfg] |= (val & mask);
-       set_bit(cfg, slot->updated_configs);
-}
-
-void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       struct atmel_hlcdc_layer_update_slot *slot;
-       unsigned long flags;
-
-       if (upd->next < 0  || upd->next > 1)
-               return;
-
-       slot = &upd->slots[upd->next];
-
-       spin_lock_irqsave(&layer->lock, flags);
-
-       /*
-        * Release pending update request and replace it by the new one.
-        */
-       if (upd->pending >= 0)
-               atmel_hlcdc_layer_update_reset(layer, upd->pending);
-
-       upd->pending = upd->next;
-       upd->next = -1;
-
-       if (!dma->queue)
-               atmel_hlcdc_layer_update_apply(layer);
-
-       spin_unlock_irqrestore(&layer->lock, flags);
-
-
-       upd->next = -1;
-}
-
-static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
-                                     struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       dma_addr_t dma_addr;
-       int i;
-
-       dma->dscrs = dma_alloc_coherent(dev->dev,
-                                       layer->max_planes * 4 *
-                                       sizeof(*dma->dscrs),
-                                       &dma_addr, GFP_KERNEL);
-       if (!dma->dscrs)
-               return -ENOMEM;
-
-       for (i = 0; i < layer->max_planes * 4; i++) {
-               struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
-
-               dscr->next = dma_addr + (i * sizeof(*dscr));
-       }
-
-       return 0;
-}
-
-static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
-                                         struct atmel_hlcdc_layer *layer)
-{
-       struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-       int i;
-
-       for (i = 0; i < layer->max_planes * 4; i++) {
-               struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
-
-               dscr->status = 0;
-       }
-
-       dma_free_coherent(dev->dev, layer->max_planes * 4 *
-                         sizeof(*dma->dscrs), dma->dscrs,
-                         dma->dscrs[0].next);
-}
-
-static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
-                               struct atmel_hlcdc_layer *layer,
-                               const struct atmel_hlcdc_layer_desc *desc)
-{
-       struct atmel_hlcdc_layer_update *upd = &layer->update;
-       int updated_size;
-       void *buffer;
-       int i;
-
-       updated_size = DIV_ROUND_UP(desc->nconfigs,
-                                   BITS_PER_BYTE *
-                                   sizeof(unsigned long));
-
-       buffer = devm_kzalloc(dev->dev,
-                             ((desc->nconfigs * sizeof(u32)) +
-                               (updated_size * sizeof(unsigned long))) * 2,
-                             GFP_KERNEL);
-       if (!buffer)
-               return -ENOMEM;
-
-       for (i = 0; i < 2; i++) {
-               upd->slots[i].updated_configs = buffer;
-               buffer += updated_size * sizeof(unsigned long);
-               upd->slots[i].configs = buffer;
-               buffer += desc->nconfigs * sizeof(u32);
-       }
-
-       upd->pending = -1;
-       upd->next = -1;
-
-       return 0;
-}
-
-int atmel_hlcdc_layer_init(struct drm_device *dev,
-                          struct atmel_hlcdc_layer *layer,
-                          const struct atmel_hlcdc_layer_desc *desc)
-{
-       struct atmel_hlcdc_dc *dc = dev->dev_private;
-       struct regmap *regmap = dc->hlcdc->regmap;
-       unsigned int tmp;
-       int ret;
-       int i;
-
-       layer->hlcdc = dc->hlcdc;
-       layer->wq = dc->wq;
-       layer->desc = desc;
-
-       regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
-                    ATMEL_HLCDC_LAYER_RST);
-       for (i = 0; i < desc->formats->nformats; i++) {
-               int nplanes = drm_format_num_planes(desc->formats->formats[i]);
-
-               if (nplanes > layer->max_planes)
-                       layer->max_planes = nplanes;
-       }
-
-       spin_lock_init(&layer->lock);
-       drm_flip_work_init(&layer->gc, desc->name,
-                          atmel_hlcdc_layer_fb_flip_release);
-       ret = atmel_hlcdc_layer_dma_init(dev, layer);
-       if (ret)
-               return ret;
-
-       ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
-       if (ret)
-               return ret;
-
-       /* Flush Status Register */
-       regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
-                    0xffffffff);
-       regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
-                   &tmp);
-
-       tmp = 0;
-       for (i = 0; i < layer->max_planes; i++)
-               tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ |
-                       ATMEL_HLCDC_LAYER_DSCR_IRQ |
-                       ATMEL_HLCDC_LAYER_ADD_IRQ |
-                       ATMEL_HLCDC_LAYER_DONE_IRQ |
-                       ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i);
-
-       regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp);
-
-       return 0;
-}
-
-void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
-                              struct atmel_hlcdc_layer *layer)
-{
-       const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-       struct regmap *regmap = layer->hlcdc->regmap;
-
-       regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
-                    0xffffffff);
-       regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
-                    ATMEL_HLCDC_LAYER_RST);
-
-       atmel_hlcdc_layer_dma_cleanup(dev, layer);
-       drm_flip_work_cleanup(&layer->gc);
-}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
deleted file mode 100644 (file)
index 9beabc9..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2014 Free Electrons
- * Copyright (C) 2014 Atmel
- *
- * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DRM_ATMEL_HLCDC_LAYER_H
-#define DRM_ATMEL_HLCDC_LAYER_H
-
-#include <linux/mfd/atmel-hlcdc.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_flip_work.h>
-#include <drm/drmP.h>
-
-#define ATMEL_HLCDC_LAYER_CHER                 0x0
-#define ATMEL_HLCDC_LAYER_CHDR                 0x4
-#define ATMEL_HLCDC_LAYER_CHSR                 0x8
-#define ATMEL_HLCDC_LAYER_DMA_CHAN             BIT(0)
-#define ATMEL_HLCDC_LAYER_UPDATE               BIT(1)
-#define ATMEL_HLCDC_LAYER_A2Q                  BIT(2)
-#define ATMEL_HLCDC_LAYER_RST                  BIT(8)
-
-#define ATMEL_HLCDC_LAYER_IER                  0xc
-#define ATMEL_HLCDC_LAYER_IDR                  0x10
-#define ATMEL_HLCDC_LAYER_IMR                  0x14
-#define ATMEL_HLCDC_LAYER_ISR                  0x18
-#define ATMEL_HLCDC_LAYER_DFETCH               BIT(0)
-#define ATMEL_HLCDC_LAYER_LFETCH               BIT(1)
-#define ATMEL_HLCDC_LAYER_DMA_IRQ              BIT(2)
-#define ATMEL_HLCDC_LAYER_DSCR_IRQ             BIT(3)
-#define ATMEL_HLCDC_LAYER_ADD_IRQ              BIT(4)
-#define ATMEL_HLCDC_LAYER_DONE_IRQ             BIT(5)
-#define ATMEL_HLCDC_LAYER_OVR_IRQ              BIT(6)
-
-#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)                (((n) * 0x10) + 0x1c)
-#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)                (((n) * 0x10) + 0x20)
-#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)                (((n) * 0x10) + 0x24)
-#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)                (((n) * 0x10) + 0x28)
-#define ATMEL_HLCDC_LAYER_CFG(p, c)            (((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
-
-#define ATMEL_HLCDC_LAYER_DMA_CFG_ID           0
-#define ATMEL_HLCDC_LAYER_DMA_CFG(p)           ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
-#define ATMEL_HLCDC_LAYER_DMA_SIF              BIT(0)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK                GENMASK(5, 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE      (0 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4       (1 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8       (2 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16      (3 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_DLBO             BIT(8)
-#define ATMEL_HLCDC_LAYER_DMA_ROTDIS           BIT(12)
-#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS          BIT(13)
-
-#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID                1
-#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)                ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
-#define ATMEL_HLCDC_LAYER_RGB                  (0 << 0)
-#define ATMEL_HLCDC_LAYER_CLUT                 (1 << 0)
-#define ATMEL_HLCDC_LAYER_YUV                  (2 << 0)
-#define ATMEL_HLCDC_RGB_MODE(m)                        (((m) & 0xf) << 4)
-#define ATMEL_HLCDC_CLUT_MODE(m)               (((m) & 0x3) << 8)
-#define ATMEL_HLCDC_YUV_MODE(m)                        (((m) & 0xf) << 12)
-#define ATMEL_HLCDC_YUV422ROT                  BIT(16)
-#define ATMEL_HLCDC_YUV422SWP                  BIT(17)
-#define ATMEL_HLCDC_DSCALEOPT                  BIT(20)
-
-#define ATMEL_HLCDC_XRGB4444_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
-#define ATMEL_HLCDC_ARGB4444_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
-#define ATMEL_HLCDC_RGBA4444_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
-#define ATMEL_HLCDC_RGB565_MODE                        (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
-#define ATMEL_HLCDC_ARGB1555_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
-#define ATMEL_HLCDC_XRGB8888_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
-#define ATMEL_HLCDC_RGB888_MODE                        (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
-#define ATMEL_HLCDC_ARGB8888_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
-#define ATMEL_HLCDC_RGBA8888_MODE              (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
-
-#define ATMEL_HLCDC_AYUV_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
-#define ATMEL_HLCDC_YUYV_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
-#define ATMEL_HLCDC_UYVY_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
-#define ATMEL_HLCDC_YVYU_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
-#define ATMEL_HLCDC_VYUY_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
-#define ATMEL_HLCDC_NV61_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
-#define ATMEL_HLCDC_YUV422_MODE                        (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
-#define ATMEL_HLCDC_NV21_MODE                  (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
-#define ATMEL_HLCDC_YUV420_MODE                        (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
-
-#define ATMEL_HLCDC_LAYER_POS_CFG(p)           ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
-#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)          ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
-#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)       ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
-#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)       ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
-#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)       ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
-#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)     ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
-#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)         ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
-#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)    ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
-
-#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)       ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
-#define ATMEL_HLCDC_LAYER_CRKEY                        BIT(0)
-#define ATMEL_HLCDC_LAYER_INV                  BIT(1)
-#define ATMEL_HLCDC_LAYER_ITER2BL              BIT(2)
-#define ATMEL_HLCDC_LAYER_ITER                 BIT(3)
-#define ATMEL_HLCDC_LAYER_REVALPHA             BIT(4)
-#define ATMEL_HLCDC_LAYER_GAEN                 BIT(5)
-#define ATMEL_HLCDC_LAYER_LAEN                 BIT(6)
-#define ATMEL_HLCDC_LAYER_OVR                  BIT(7)
-#define ATMEL_HLCDC_LAYER_DMA                  BIT(8)
-#define ATMEL_HLCDC_LAYER_REP                  BIT(9)
-#define ATMEL_HLCDC_LAYER_DSTKEY               BIT(10)
-#define ATMEL_HLCDC_LAYER_DISCEN               BIT(11)
-#define ATMEL_HLCDC_LAYER_GA_SHIFT             16
-#define ATMEL_HLCDC_LAYER_GA_MASK              GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
-#define ATMEL_HLCDC_LAYER_GA(x)                        ((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
-
-#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)                ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
-
-#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)      ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
-
-#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)     ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
-
-#define ATMEL_HLCDC_MAX_PLANES                 3
-
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED  BIT(0)
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED    BIT(1)
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE      BIT(2)
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN   BIT(3)
-
-/**
- * Atmel HLCDC Layer registers layout structure
- *
- * Each HLCDC layer has its own register organization and a given register
- * can be placed differently on 2 different layers depending on its
- * capabilities.
- * This structure stores common registers layout for a given layer and is
- * used by HLCDC layer code to choose the appropriate register to write to
- * or to read from.
- *
- * For all fields, a value of zero means "unsupported".
- *
- * See Atmel's datasheet for a detailled description of these registers.
- *
- * @xstride: xstride registers
- * @pstride: pstride registers
- * @pos: position register
- * @size: displayed size register
- * @memsize: memory size register
- * @default_color: default color register
- * @chroma_key: chroma key register
- * @chroma_key_mask: chroma key mask register
- * @general_config: general layer config register
- * @disc_pos: discard area position register
- * @disc_size: discard area size register
- * @csc: color space conversion register
- */
-struct atmel_hlcdc_layer_cfg_layout {
-       int xstride[ATMEL_HLCDC_MAX_PLANES];
-       int pstride[ATMEL_HLCDC_MAX_PLANES];
-       int pos;
-       int size;
-       int memsize;
-       int default_color;
-       int chroma_key;
-       int chroma_key_mask;
-       int general_config;
-       int disc_pos;
-       int disc_size;
-       int csc;
-};
-
-/**
- * Atmel HLCDC framebuffer flip structure
- *
- * This structure is allocated when someone asked for a layer update (most
- * likely a DRM plane update, either primary, overlay or cursor plane) and
- * released when the layer do not need to reference the framebuffer object
- * anymore (i.e. the layer was disabled or updated).
- *
- * @dscrs: DMA descriptors
- * @fb: the referenced framebuffer object
- * @ngems: number of GEM objects referenced by the fb element
- * @status: fb flip operation status
- */
-struct atmel_hlcdc_layer_fb_flip {
-       struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
-       struct drm_flip_task *task;
-       struct drm_framebuffer *fb;
-       int ngems;
-       u32 status;
-};
-
-/**
- * Atmel HLCDC DMA descriptor structure
- *
- * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
- *
- * The structure fields must remain in this specific order, because they're
- * used by the HLCDC DMA engine, which expect them in this order.
- * HLCDC DMA descriptors must be aligned on 64 bits.
- *
- * @addr: buffer DMA address
- * @ctrl: DMA transfer options
- * @next: next DMA descriptor to fetch
- * @gem_flip: the attached gem_flip operation
- */
-struct atmel_hlcdc_dma_channel_dscr {
-       dma_addr_t addr;
-       u32 ctrl;
-       dma_addr_t next;
-       u32 status;
-} __aligned(sizeof(u64));
-
-/**
- * Atmel HLCDC layer types
- */
-enum atmel_hlcdc_layer_type {
-       ATMEL_HLCDC_BASE_LAYER,
-       ATMEL_HLCDC_OVERLAY_LAYER,
-       ATMEL_HLCDC_CURSOR_LAYER,
-       ATMEL_HLCDC_PP_LAYER,
-};
-
-/**
- * Atmel HLCDC Supported formats structure
- *
- * This structure list all the formats supported by a given layer.
- *
- * @nformats: number of supported formats
- * @formats: supported formats
- */
-struct atmel_hlcdc_formats {
-       int nformats;
-       uint32_t *formats;
-};
-
-/**
- * Atmel HLCDC Layer description structure
- *
- * This structure describe the capabilities provided by a given layer.
- *
- * @name: layer name
- * @type: layer type
- * @id: layer id
- * @regs_offset: offset of the layer registers from the HLCDC registers base
- * @nconfigs: number of config registers provided by this layer
- * @formats: supported formats
- * @layout: config registers layout
- * @max_width: maximum width supported by this layer (0 means unlimited)
- * @max_height: maximum height supported by this layer (0 means unlimited)
- */
-struct atmel_hlcdc_layer_desc {
-       const char *name;
-       enum atmel_hlcdc_layer_type type;
-       int id;
-       int regs_offset;
-       int nconfigs;
-       struct atmel_hlcdc_formats *formats;
-       struct atmel_hlcdc_layer_cfg_layout layout;
-       int max_width;
-       int max_height;
-};
-
-/**
- * Atmel HLCDC Layer Update Slot structure
- *
- * This structure stores layer update requests to be applied on next frame.
- * This is the base structure behind the atomic layer update infrastructure.
- *
- * Atomic layer update provides a way to update all layer's parameters
- * simultaneously. This is needed to avoid incompatible sequential updates
- * like this one:
- * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
- *    (2 planes/buffers)
- * 2) the format update is applied but the DMA channel for the second
- *    plane/buffer is not enabled
- * 3) enable the DMA channel for the second plane
- *
- * @fb_flip: fb_flip object
- * @updated_configs: bitmask used to record modified configs
- * @configs: new config values
- */
-struct atmel_hlcdc_layer_update_slot {
-       struct atmel_hlcdc_layer_fb_flip *fb_flip;
-       unsigned long *updated_configs;
-       u32 *configs;
-};
-
-/**
- * Atmel HLCDC Layer Update structure
- *
- * This structure provides a way to queue layer update requests.
- *
- * At a given time there is at most:
- *  - one pending update request, which means the update request has been
- *    committed (or validated) and is waiting for the DMA channel(s) to be
- *    available
- *  - one request being prepared, which means someone started a layer update
- *    but has not committed it yet. There cannot be more than one started
- *    request, because the update lock is taken when starting a layer update
- *    and release when committing or rolling back the request.
- *
- * @slots: update slots. One is used for pending request and the other one
- *        for started update request
- * @pending: the pending slot index or -1 if no request is pending
- * @next: the started update slot index or -1 no update has been started
- */
-struct atmel_hlcdc_layer_update {
-       struct atmel_hlcdc_layer_update_slot slots[2];
-       int pending;
-       int next;
-};
-
-enum atmel_hlcdc_layer_dma_channel_status {
-       ATMEL_HLCDC_LAYER_DISABLED,
-       ATMEL_HLCDC_LAYER_ENABLED,
-       ATMEL_HLCDC_LAYER_DISABLING,
-};
-
-/**
- * Atmel HLCDC Layer DMA channel structure
- *
- * This structure stores information on the DMA channel associated to a
- * given layer.
- *
- * @status: DMA channel status
- * @cur: current framebuffer
- * @queue: next framebuffer
- * @dscrs: allocated DMA descriptors
- */
-struct atmel_hlcdc_layer_dma_channel {
-       enum atmel_hlcdc_layer_dma_channel_status status;
-       struct atmel_hlcdc_layer_fb_flip *cur;
-       struct atmel_hlcdc_layer_fb_flip *queue;
-       struct atmel_hlcdc_dma_channel_dscr *dscrs;
-};
-
-/**
- * Atmel HLCDC Layer structure
- *
- * This structure stores information on the layer instance.
- *
- * @desc: layer description
- * @max_planes: maximum planes/buffers that can be associated with this layer.
- *            This depends on the supported formats.
- * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
- * @dma: dma channel
- * @gc: fb flip garbage collector
- * @update: update handler
- * @lock: layer lock
- */
-struct atmel_hlcdc_layer {
-       const struct atmel_hlcdc_layer_desc *desc;
-       int max_planes;
-       struct atmel_hlcdc *hlcdc;
-       struct workqueue_struct *wq;
-       struct drm_flip_work gc;
-       struct atmel_hlcdc_layer_dma_channel dma;
-       struct atmel_hlcdc_layer_update update;
-       spinlock_t lock;
-};
-
-void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
-
-int atmel_hlcdc_layer_init(struct drm_device *dev,
-                          struct atmel_hlcdc_layer *layer,
-                          const struct atmel_hlcdc_layer_desc *desc);
-
-void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
-                              struct atmel_hlcdc_layer *layer);
-
-void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
-
-int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
-
-void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
-                                 u32 mask, u32 val);
-
-void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
-                                    struct drm_framebuffer *fb,
-                                    unsigned int *offsets);
-
-void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
-                                          void (*finished)(void *data),
-                                          void *finished_data);
-
-void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
-
-void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
-
-#endif /* DRM_ATMEL_HLCDC_LAYER_H */
index 886ed5d8e3046430a5bf66bb28ae7b4ae234776f..29cc10d053ebc3cbd2c60c37d91bb1e5f5f83934 100644 (file)
  * @src_w: buffer width
  * @src_h: buffer height
  * @alpha: alpha blending of the plane
+ * @disc_x: x discard position
+ * @disc_y: y discard position
+ * @disc_w: discard width
+ * @disc_h: discard height
  * @bpp: bytes per pixel deduced from pixel_format
  * @offsets: offsets to apply to the GEM buffers
  * @xstride: value to add to the pixel pointer between each line
  * @pstride: value to add to the pixel pointer between each pixel
  * @nplanes: number of planes (deduced from pixel_format)
- * @prepared: plane update has been prepared
+ * @dscrs: DMA descriptors
  */
 struct atmel_hlcdc_plane_state {
        struct drm_plane_state base;
@@ -52,8 +56,6 @@ struct atmel_hlcdc_plane_state {
 
        u8 alpha;
 
-       bool disc_updated;
-
        int disc_x;
        int disc_y;
        int disc_w;
@@ -62,12 +64,14 @@ struct atmel_hlcdc_plane_state {
        int ahb_id;
 
        /* These fields are private and should not be touched */
-       int bpp[ATMEL_HLCDC_MAX_PLANES];
-       unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
-       int xstride[ATMEL_HLCDC_MAX_PLANES];
-       int pstride[ATMEL_HLCDC_MAX_PLANES];
+       int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
+       unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
+       int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
+       int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
        int nplanes;
-       bool prepared;
+
+       /* DMA descriptors. */
+       struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
 };
 
 static inline struct atmel_hlcdc_plane_state *
@@ -259,125 +263,145 @@ static u32 heo_upscaling_ycoef[] = {
        0x00205907,
 };
 
+#define ATMEL_HLCDC_XPHIDEF    4
+#define ATMEL_HLCDC_YPHIDEF    4
+
+static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
+                                                 u32 dstsize,
+                                                 u32 phidef)
+{
+       u32 factor, max_memsize;
+
+       factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
+       max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
+
+       if (max_memsize > srcsize - 1)
+               factor--;
+
+       return factor;
+}
+
 static void
-atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
-                                     struct atmel_hlcdc_plane_state *state)
+atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
+                                     const u32 *coeff_tab, int size,
+                                     unsigned int cfg_offs)
 {
-       const struct atmel_hlcdc_layer_cfg_layout *layout =
-                                               &plane->layer.desc->layout;
-
-       if (layout->size)
-               atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                            layout->size,
-                                            0xffffffff,
-                                            (state->crtc_w - 1) |
-                                            ((state->crtc_h - 1) << 16));
-
-       if (layout->memsize)
-               atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                            layout->memsize,
-                                            0xffffffff,
-                                            (state->src_w - 1) |
-                                            ((state->src_h - 1) << 16));
-
-       if (layout->pos)
-               atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                            layout->pos,
-                                            0xffffffff,
-                                            state->crtc_x |
-                                            (state->crtc_y  << 16));
-
-       /* TODO: rework the rescaling part */
-       if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) {
-               u32 factor_reg = 0;
-
-               if (state->crtc_w != state->src_w) {
-                       int i;
-                       u32 factor;
-                       u32 *coeff_tab = heo_upscaling_xcoef;
-                       u32 max_memsize;
-
-                       if (state->crtc_w < state->src_w)
-                               coeff_tab = heo_downscaling_xcoef;
-                       for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
-                               atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                                            17 + i,
-                                                            0xffffffff,
-                                                            coeff_tab[i]);
-                       factor = ((8 * 256 * state->src_w) - (256 * 4)) /
-                                state->crtc_w;
-                       factor++;
-                       max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
-                                     2048;
-                       if (max_memsize > state->src_w)
-                               factor--;
-                       factor_reg |= factor | 0x80000000;
-               }
+       int i;
 
-               if (state->crtc_h != state->src_h) {
-                       int i;
-                       u32 factor;
-                       u32 *coeff_tab = heo_upscaling_ycoef;
-                       u32 max_memsize;
-
-                       if (state->crtc_h < state->src_h)
-                               coeff_tab = heo_downscaling_ycoef;
-                       for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
-                               atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                                            33 + i,
-                                                            0xffffffff,
-                                                            coeff_tab[i]);
-                       factor = ((8 * 256 * state->src_h) - (256 * 4)) /
-                                state->crtc_h;
-                       factor++;
-                       max_memsize = ((factor * state->crtc_h) + (256 * 4)) /
-                                     2048;
-                       if (max_memsize > state->src_h)
-                               factor--;
-                       factor_reg |= (factor << 16) | 0x80000000;
-               }
+       for (i = 0; i < size; i++)
+               atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
+                                           coeff_tab[i]);
+}
+
+void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
+                                   struct atmel_hlcdc_plane_state *state)
+{
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+       u32 xfactor, yfactor;
+
+       if (!desc->layout.scaler_config)
+               return;
 
-               atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
-                                            factor_reg);
+       if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
+               atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                           desc->layout.scaler_config, 0);
+               return;
+       }
+
+       if (desc->layout.phicoeffs.x) {
+               xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
+                                                       state->crtc_w,
+                                                       ATMEL_HLCDC_XPHIDEF);
+
+               yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
+                                                       state->crtc_h,
+                                                       ATMEL_HLCDC_YPHIDEF);
+
+               atmel_hlcdc_plane_scaler_set_phicoeff(plane,
+                               state->crtc_w < state->src_w ?
+                               heo_downscaling_xcoef :
+                               heo_upscaling_xcoef,
+                               ARRAY_SIZE(heo_upscaling_xcoef),
+                               desc->layout.phicoeffs.x);
+
+               atmel_hlcdc_plane_scaler_set_phicoeff(plane,
+                               state->crtc_h < state->src_h ?
+                               heo_downscaling_ycoef :
+                               heo_upscaling_ycoef,
+                               ARRAY_SIZE(heo_upscaling_ycoef),
+                               desc->layout.phicoeffs.y);
        } else {
-               atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 0);
+               xfactor = (1024 * state->src_w) / state->crtc_w;
+               yfactor = (1024 * state->src_h) / state->crtc_h;
        }
+
+       atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
+                                   ATMEL_HLCDC_LAYER_SCALER_ENABLE |
+                                   ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
+                                                                    yfactor));
+}
+
+static void
+atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
+                                     struct atmel_hlcdc_plane_state *state)
+{
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+
+       if (desc->layout.size)
+               atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
+                                       ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
+                                                              state->crtc_h));
+
+       if (desc->layout.memsize)
+               atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                       desc->layout.memsize,
+                                       ATMEL_HLCDC_LAYER_SIZE(state->src_w,
+                                                              state->src_h));
+
+       if (desc->layout.pos)
+               atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
+                                       ATMEL_HLCDC_LAYER_POS(state->crtc_x,
+                                                             state->crtc_y));
+
+       atmel_hlcdc_plane_setup_scaler(plane, state);
 }
 
 static void
 atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
                                        struct atmel_hlcdc_plane_state *state)
 {
-       const struct atmel_hlcdc_layer_cfg_layout *layout =
-                                               &plane->layer.desc->layout;
-       unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
+       unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+       u32 format = state->base.fb->format->format;
+
+       /*
+        * Rotation optimization is not working on RGB888 (rotation is still
+        * working but without any optimization).
+        */
+       if (format == DRM_FORMAT_RGB888)
+               cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
+
+       atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
+                                   cfg);
+
+       cfg = ATMEL_HLCDC_LAYER_DMA;
 
        if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
                cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
                       ATMEL_HLCDC_LAYER_ITER;
 
-               if (atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format))
+               if (atmel_hlcdc_format_embeds_alpha(format))
                        cfg |= ATMEL_HLCDC_LAYER_LAEN;
                else
                        cfg |= ATMEL_HLCDC_LAYER_GAEN |
                               ATMEL_HLCDC_LAYER_GA(state->alpha);
        }
 
-       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                    ATMEL_HLCDC_LAYER_DMA_CFG_ID,
-                                    ATMEL_HLCDC_LAYER_DMA_BLEN_MASK |
-                                    ATMEL_HLCDC_LAYER_DMA_SIF,
-                                    ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 |
-                                    state->ahb_id);
-
-       atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
-                                    ATMEL_HLCDC_LAYER_ITER2BL |
-                                    ATMEL_HLCDC_LAYER_ITER |
-                                    ATMEL_HLCDC_LAYER_GAEN |
-                                    ATMEL_HLCDC_LAYER_GA_MASK |
-                                    ATMEL_HLCDC_LAYER_LAEN |
-                                    ATMEL_HLCDC_LAYER_OVR |
-                                    ATMEL_HLCDC_LAYER_DMA, cfg);
+       if (state->disc_h && state->disc_w)
+               cfg |= ATMEL_HLCDC_LAYER_DISCEN;
+
+       atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
+                                   cfg);
 }
 
 static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
@@ -396,50 +420,50 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
            drm_rotation_90_or_270(state->base.rotation))
                cfg |= ATMEL_HLCDC_YUV422ROT;
 
-       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                    ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
-                                    0xffffffff,
-                                    cfg);
-
-       /*
-        * Rotation optimization is not working on RGB888 (rotation is still
-        * working but without any optimization).
-        */
-       if (state->base.fb->format->format == DRM_FORMAT_RGB888)
-               cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
-       else
-               cfg = 0;
-
-       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                    ATMEL_HLCDC_LAYER_DMA_CFG_ID,
-                                    ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
+       atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                   ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
 }
 
 static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
                                        struct atmel_hlcdc_plane_state *state)
 {
-       struct atmel_hlcdc_layer *layer = &plane->layer;
-       const struct atmel_hlcdc_layer_cfg_layout *layout =
-                                                       &layer->desc->layout;
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+       struct drm_framebuffer *fb = state->base.fb;
+       u32 sr;
        int i;
 
-       atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
-                                       state->offsets);
+       sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 
        for (i = 0; i < state->nplanes; i++) {
-               if (layout->xstride[i]) {
-                       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                               layout->xstride[i],
-                                               0xffffffff,
-                                               state->xstride[i]);
+               struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
+
+               state->dscrs[i]->addr = gem->paddr + state->offsets[i];
+
+               atmel_hlcdc_layer_write_reg(&plane->layer,
+                                           ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
+                                           state->dscrs[i]->self);
+
+               if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
+                       atmel_hlcdc_layer_write_reg(&plane->layer,
+                                       ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
+                                       state->dscrs[i]->addr);
+                       atmel_hlcdc_layer_write_reg(&plane->layer,
+                                       ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
+                                       state->dscrs[i]->ctrl);
+                       atmel_hlcdc_layer_write_reg(&plane->layer,
+                                       ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
+                                       state->dscrs[i]->self);
                }
 
-               if (layout->pstride[i]) {
-                       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                               layout->pstride[i],
-                                               0xffffffff,
-                                               state->pstride[i]);
-               }
+               if (desc->layout.xstride[i])
+                       atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                                   desc->layout.xstride[i],
+                                                   state->xstride[i]);
+
+               if (desc->layout.pstride[i])
+                       atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                                   desc->layout.pstride[i],
+                                                   state->pstride[i]);
        }
 }
 
@@ -528,18 +552,10 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
                disc_w = ovl_state->crtc_w;
        }
 
-       if (disc_x == primary_state->disc_x &&
-           disc_y == primary_state->disc_y &&
-           disc_w == primary_state->disc_w &&
-           disc_h == primary_state->disc_h)
-               return 0;
-
-
        primary_state->disc_x = disc_x;
        primary_state->disc_y = disc_y;
        primary_state->disc_w = disc_w;
        primary_state->disc_h = disc_h;
-       primary_state->disc_updated = true;
 
        return 0;
 }
@@ -548,32 +564,19 @@ static void
 atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
                                   struct atmel_hlcdc_plane_state *state)
 {
-       const struct atmel_hlcdc_layer_cfg_layout *layout =
-                                               &plane->layer.desc->layout;
-       int disc_surface = 0;
-
-       if (!state->disc_updated)
-               return;
-
-       disc_surface = state->disc_h * state->disc_w;
-
-       atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
-                               ATMEL_HLCDC_LAYER_DISCEN,
-                               disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
+       const struct atmel_hlcdc_layer_cfg_layout *layout;
 
-       if (!disc_surface)
+       layout = &plane->layer.desc->layout;
+       if (!layout->disc_pos || !layout->disc_size)
                return;
 
-       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                    layout->disc_pos,
-                                    0xffffffff,
-                                    state->disc_x | (state->disc_y << 16));
+       atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
+                               ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
+                                                          state->disc_y));
 
-       atmel_hlcdc_layer_update_cfg(&plane->layer,
-                                    layout->disc_size,
-                                    0xffffffff,
-                                    (state->disc_w - 1) |
-                                    ((state->disc_h - 1) << 16));
+       atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
+                               ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
+                                                           state->disc_h));
 }
 
 static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
@@ -582,8 +585,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
        struct atmel_hlcdc_plane_state *state =
                                drm_plane_state_to_atmel_hlcdc_plane_state(s);
-       const struct atmel_hlcdc_layer_cfg_layout *layout =
-                                               &plane->layer.desc->layout;
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
        struct drm_framebuffer *fb = state->base.fb;
        const struct drm_display_mode *mode;
        struct drm_crtc_state *crtc_state;
@@ -622,7 +624,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
        state->src_h >>= 16;
 
        state->nplanes = fb->format->num_planes;
-       if (state->nplanes > ATMEL_HLCDC_MAX_PLANES)
+       if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
                return -EINVAL;
 
        /*
@@ -726,21 +728,19 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
        state->crtc_w = patched_crtc_w;
        state->crtc_h = patched_crtc_h;
 
-       if (!layout->size &&
+       if (!desc->layout.size &&
            (mode->hdisplay != state->crtc_w ||
             mode->vdisplay != state->crtc_h))
                return -EINVAL;
 
-       if (plane->layer.desc->max_height &&
-           state->crtc_h > plane->layer.desc->max_height)
+       if (desc->max_height && state->crtc_h > desc->max_height)
                return -EINVAL;
 
-       if (plane->layer.desc->max_width &&
-           state->crtc_w > plane->layer.desc->max_width)
+       if (desc->max_width && state->crtc_w > desc->max_width)
                return -EINVAL;
 
        if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
-           (!layout->memsize ||
+           (!desc->layout.memsize ||
             atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format)))
                return -EINVAL;
 
@@ -754,68 +754,13 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
        return 0;
 }
 
-static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
-                                       struct drm_plane_state *new_state)
-{
-       /*
-        * FIXME: we should avoid this const -> non-const cast but it's
-        * currently the only solution we have to modify the ->prepared
-        * state and rollback the update request.
-        * Ideally, we should rework the code to attach all the resources
-        * to atmel_hlcdc_plane_state (including the DMA desc allocation),
-        * but this require a complete rework of the atmel_hlcdc_layer
-        * code.
-        */
-       struct drm_plane_state *s = (struct drm_plane_state *)new_state;
-       struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
-       struct atmel_hlcdc_plane_state *state =
-                       drm_plane_state_to_atmel_hlcdc_plane_state(s);
-       int ret;
-
-       if (!new_state->fb)
-               return 0;
-
-       ret = atmel_hlcdc_layer_update_start(&plane->layer);
-       if (!ret)
-               state->prepared = true;
-
-       return ret;
-}
-
-static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
-                                        struct drm_plane_state *old_state)
-{
-       /*
-        * FIXME: we should avoid this const -> non-const cast but it's
-        * currently the only solution we have to modify the ->prepared
-        * state and rollback the update request.
-        * Ideally, we should rework the code to attach all the resources
-        * to atmel_hlcdc_plane_state (including the DMA desc allocation),
-        * but this require a complete rework of the atmel_hlcdc_layer
-        * code.
-        */
-       struct drm_plane_state *s = (struct drm_plane_state *)old_state;
-       struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
-       struct atmel_hlcdc_plane_state *state =
-                       drm_plane_state_to_atmel_hlcdc_plane_state(s);
-
-       /*
-        * The Request has already been applied or cancelled, nothing to do
-        * here.
-        */
-       if (!state->prepared)
-               return;
-
-       atmel_hlcdc_layer_update_rollback(&plane->layer);
-       state->prepared = false;
-}
-
 static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
                                            struct drm_plane_state *old_s)
 {
        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
        struct atmel_hlcdc_plane_state *state =
                        drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
+       u32 sr;
 
        if (!p->state->crtc || !p->state->fb)
                return;
@@ -826,7 +771,18 @@ static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
        atmel_hlcdc_plane_update_buffers(plane, state);
        atmel_hlcdc_plane_update_disc_area(plane, state);
 
-       atmel_hlcdc_layer_update_commit(&plane->layer);
+       /* Enable the overrun interrupts. */
+       atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
+                                   ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
+                                   ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
+                                   ATMEL_HLCDC_LAYER_OVR_IRQ(2));
+
+       /* Apply the new config at the next SOF event. */
+       sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
+       atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
+                       ATMEL_HLCDC_LAYER_UPDATE |
+                       (sr & ATMEL_HLCDC_LAYER_EN ?
+                        ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
 }
 
 static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
@@ -834,7 +790,18 @@ static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
 {
        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 
-       atmel_hlcdc_layer_disable(&plane->layer);
+       /* Disable interrupts */
+       atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
+                                   0xffffffff);
+
+       /* Disable the layer */
+       atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
+                                   ATMEL_HLCDC_LAYER_RST |
+                                   ATMEL_HLCDC_LAYER_A2Q |
+                                   ATMEL_HLCDC_LAYER_UPDATE);
+
+       /* Clear all pending interrupts */
+       atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 }
 
 static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
@@ -844,10 +811,7 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
        if (plane->base.fb)
                drm_framebuffer_unreference(plane->base.fb);
 
-       atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
-
        drm_plane_cleanup(p);
-       devm_kfree(p->dev->dev, plane);
 }
 
 static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
@@ -887,24 +851,15 @@ static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
 }
 
 static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
-                                            const struct atmel_hlcdc_layer_desc *desc,
-                                            struct atmel_hlcdc_plane_properties *props)
+                               struct atmel_hlcdc_plane_properties *props)
 {
-       struct regmap *regmap = plane->layer.hlcdc->regmap;
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 
        if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
-           desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
+           desc->type == ATMEL_HLCDC_CURSOR_LAYER)
                drm_object_attach_property(&plane->base.base,
                                           props->alpha, 255);
 
-               /* Set default alpha value */
-               regmap_update_bits(regmap,
-                               desc->regs_offset +
-                               ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
-                               ATMEL_HLCDC_LAYER_GA_MASK,
-                               ATMEL_HLCDC_LAYER_GA_MASK);
-       }
-
        if (desc->layout.xstride && desc->layout.pstride) {
                int ret;
 
@@ -923,31 +878,78 @@ static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
                 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
                 * userspace modify these factors (using a BLOB property ?).
                 */
-               regmap_write(regmap,
-                            desc->regs_offset +
-                            ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
-                            0x4c900091);
-               regmap_write(regmap,
-                            desc->regs_offset +
-                            ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
-                            0x7a5f5090);
-               regmap_write(regmap,
-                            desc->regs_offset +
-                            ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
-                            0x40040890);
+               atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                           desc->layout.csc,
+                                           0x4c900091);
+               atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                           desc->layout.csc + 1,
+                                           0x7a5f5090);
+               atmel_hlcdc_layer_write_cfg(&plane->layer,
+                                           desc->layout.csc + 2,
+                                           0x40040890);
        }
 
        return 0;
 }
 
+void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
+{
+       const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+       u32 isr;
+
+       isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
+
+       /*
+        * There's not much we can do in case of overrun except informing
+        * the user. However, we are in interrupt context here, hence the
+        * use of dev_dbg().
+        */
+       if (isr &
+           (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
+            ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
+               dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
+                       desc->name);
+}
+
 static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
-       .prepare_fb = atmel_hlcdc_plane_prepare_fb,
-       .cleanup_fb = atmel_hlcdc_plane_cleanup_fb,
        .atomic_check = atmel_hlcdc_plane_atomic_check,
        .atomic_update = atmel_hlcdc_plane_atomic_update,
        .atomic_disable = atmel_hlcdc_plane_atomic_disable,
 };
 
+static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
+                                        struct atmel_hlcdc_plane_state *state)
+{
+       struct atmel_hlcdc_dc *dc = p->dev->dev_private;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
+               struct atmel_hlcdc_dma_channel_dscr *dscr;
+               dma_addr_t dscr_dma;
+
+               dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
+               if (!dscr)
+                       goto err;
+
+               dscr->addr = 0;
+               dscr->next = dscr_dma;
+               dscr->self = dscr_dma;
+               dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
+
+               state->dscrs[i] = dscr;
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--) {
+               dma_pool_free(dc->dscrpool, state->dscrs[i],
+                             state->dscrs[i]->self);
+       }
+
+       return -ENOMEM;
+}
+
 static void atmel_hlcdc_plane_reset(struct drm_plane *p)
 {
        struct atmel_hlcdc_plane_state *state;
@@ -964,6 +966,13 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
 
        state = kzalloc(sizeof(*state), GFP_KERNEL);
        if (state) {
+               if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
+                       kfree(state);
+                       dev_err(p->dev->dev,
+                               "Failed to allocate initial plane state\n");
+                       return;
+               }
+
                state->alpha = 255;
                p->state = &state->base;
                p->state->plane = p;
@@ -981,8 +990,10 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
        if (!copy)
                return NULL;
 
-       copy->disc_updated = false;
-       copy->prepared = false;
+       if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
+               kfree(copy);
+               return NULL;
+       }
 
        if (copy->base.fb)
                drm_framebuffer_reference(copy->base.fb);
@@ -990,11 +1001,18 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
        return &copy->base;
 }
 
-static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane,
+static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
                                                   struct drm_plane_state *s)
 {
        struct atmel_hlcdc_plane_state *state =
                        drm_plane_state_to_atmel_hlcdc_plane_state(s);
+       struct atmel_hlcdc_dc *dc = p->dev->dev_private;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
+               dma_pool_free(dc->dscrpool, state->dscrs[i],
+                             state->dscrs[i]->self);
+       }
 
        if (s->fb)
                drm_framebuffer_unreference(s->fb);
@@ -1014,22 +1032,21 @@ static struct drm_plane_funcs layer_plane_funcs = {
        .atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
 };
 
-static struct atmel_hlcdc_plane *
-atmel_hlcdc_plane_create(struct drm_device *dev,
-                        const struct atmel_hlcdc_layer_desc *desc,
-                        struct atmel_hlcdc_plane_properties *props)
+static int atmel_hlcdc_plane_create(struct drm_device *dev,
+                                   const struct atmel_hlcdc_layer_desc *desc,
+                                   struct atmel_hlcdc_plane_properties *props)
 {
+       struct atmel_hlcdc_dc *dc = dev->dev_private;
        struct atmel_hlcdc_plane *plane;
        enum drm_plane_type type;
        int ret;
 
        plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
        if (!plane)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
-       ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
-       if (ret)
-               return ERR_PTR(ret);
+       atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
+       plane->properties = props;
 
        if (desc->type == ATMEL_HLCDC_BASE_LAYER)
                type = DRM_PLANE_TYPE_PRIMARY;
@@ -1043,17 +1060,19 @@ atmel_hlcdc_plane_create(struct drm_device *dev,
                                       desc->formats->formats,
                                       desc->formats->nformats, type, NULL);
        if (ret)
-               return ERR_PTR(ret);
+               return ret;
 
        drm_plane_helper_add(&plane->base,
                             &atmel_hlcdc_layer_plane_helper_funcs);
 
        /* Set default property values*/
-       ret = atmel_hlcdc_plane_init_properties(plane, desc, props);
+       ret = atmel_hlcdc_plane_init_properties(plane, props);
        if (ret)
-               return ERR_PTR(ret);
+               return ret;
+
+       dc->layers[desc->id] = &plane->layer;
 
-       return plane;
+       return 0;
 }
 
 static struct atmel_hlcdc_plane_properties *
@@ -1072,72 +1091,34 @@ atmel_hlcdc_plane_create_properties(struct drm_device *dev)
        return props;
 }
 
-struct atmel_hlcdc_planes *
-atmel_hlcdc_create_planes(struct drm_device *dev)
+int atmel_hlcdc_create_planes(struct drm_device *dev)
 {
        struct atmel_hlcdc_dc *dc = dev->dev_private;
        struct atmel_hlcdc_plane_properties *props;
-       struct atmel_hlcdc_planes *planes;
        const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
        int nlayers = dc->desc->nlayers;
-       int i;
-
-       planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
-       if (!planes)
-               return ERR_PTR(-ENOMEM);
-
-       for (i = 0; i < nlayers; i++) {
-               if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
-                       planes->noverlays++;
-       }
-
-       if (planes->noverlays) {
-               planes->overlays = devm_kzalloc(dev->dev,
-                                               planes->noverlays *
-                                               sizeof(*planes->overlays),
-                                               GFP_KERNEL);
-               if (!planes->overlays)
-                       return ERR_PTR(-ENOMEM);
-       }
+       int i, ret;
 
        props = atmel_hlcdc_plane_create_properties(dev);
        if (IS_ERR(props))
-               return ERR_CAST(props);
+               return PTR_ERR(props);
 
-       planes->noverlays = 0;
-       for (i = 0; i < nlayers; i++) {
-               struct atmel_hlcdc_plane *plane;
+       dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
+                               sizeof(struct atmel_hlcdc_dma_channel_dscr),
+                               sizeof(u64), 0);
+       if (!dc->dscrpool)
+               return -ENOMEM;
 
-               if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
+       for (i = 0; i < nlayers; i++) {
+               if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
+                   descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
+                   descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
                        continue;
 
-               plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
-               if (IS_ERR(plane))
-                       return ERR_CAST(plane);
-
-               plane->properties = props;
-
-               switch (descs[i].type) {
-               case ATMEL_HLCDC_BASE_LAYER:
-                       if (planes->primary)
-                               return ERR_PTR(-EINVAL);
-                       planes->primary = plane;
-                       break;
-
-               case ATMEL_HLCDC_OVERLAY_LAYER:
-                       planes->overlays[planes->noverlays++] = plane;
-                       break;
-
-               case ATMEL_HLCDC_CURSOR_LAYER:
-                       if (planes->cursor)
-                               return ERR_PTR(-EINVAL);
-                       planes->cursor = plane;
-                       break;
-
-               default:
-                       break;
-               }
+               ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
+               if (ret)
+                       return ret;
        }
 
-       return planes;
+       return 0;
 }