Le 06/02/2017 à 19:27, Boris Brezillon a écrit :
> 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 in 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.brezil...@free-electrons.com>

I have little knowledge for a review, but here is my:

Tested-by: Nicolas Ferre <nicolas.fe...@microchip.com>

on sama5d4 Xplained with PDA 7" screen and based on drm-misc.

> ---
> Hi Daniel,
> 
> You might not remember, but this is something you asked me to do a while
> ago, and it's finally there.
> This patch reworks the Atmel HLCDC plane logic to get rid of all the
> complexity in atmel_hlcdc_layer.c and this includes getting rid of
> drm_flip_work, which you were trying to kill IIRC.
> 
> Regards,
> 
> Boris
> ---
>  drivers/gpu/drm/atmel-hlcdc/Makefile            |   1 -
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c  |  32 +-
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c    |  81 +--
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h    |  62 +--
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c | 666 
> ------------------------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h | 334 ++++--------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 618 +++++++++++-----------
>  7 files changed, 522 insertions(+), 1272 deletions(-)
>  delete mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
> 
> diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile 
> b/drivers/gpu/drm/atmel-hlcdc/Makefile
> index 10ae426e60bd..bb5f8507a8ce 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/Makefile
> +++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
> @@ -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
>  
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c 
> b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> index 9b17a66cf0e1..cdf8aa2b7a8d 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> @@ -445,8 +445,8 @@ static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs 
> = {
>  
>  int atmel_hlcdc_crtc_create(struct drm_device *dev)
>  {
> +     struct drm_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;
> @@ -457,20 +457,32 @@ 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++) {
> +             switch (dc->layers[i].type) {
> +             case ATMEL_HLCDC_BASE_LAYER:
> +                     primary = dc->layers[i].plane;
> +                     break;
> +
> +             case ATMEL_HLCDC_CURSOR_LAYER:
> +                     cursor = dc->layers[i].plane;
> +                     break;
> +
> +             default:
> +                     break;
> +             }
> +     }
> +
> +     ret = drm_crtc_init_with_planes(dev, &crtc->base, primary, cursor,
> +                                     &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 < planes->noverlays; i++)
> -             planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
> +     for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> +             if (dc->layers[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
> +                     dc->layers[i].plane->possible_crtcs = 1 << crtc->id;
> +     }
>  
>       drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
>       drm_crtc_vblank_reset(&crtc->base);
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c 
> b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> index 0bf32d6ac39b..5e7ba6de1777 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> @@ -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,14 @@ 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->type == ATMEL_HLCDC_BASE_LAYER ||
> +         layer->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> +         layer->type == ATMEL_HLCDC_CURSOR_LAYER)
> +             atmel_hlcdc_plane_irq(layer->plane);
> +}
> +
>  static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
>  {
>       struct drm_device *dev = data;
> @@ -410,12 +430,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 +553,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 +563,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");
> @@ -703,7 +704,7 @@ static int atmel_hlcdc_dc_irq_postinstall(struct 
> drm_device *dev)
>  
>       /* Enable interrupts on activated layers */
>       for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> -             if (dc->layers[i])
> +             if (dc->layers[i].type != ATMEL_HLCDC_NO_LAYER)
>                       cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
>       }
>  
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h 
> b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> index 7a47f8c094d0..67b80c3a2666 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> @@ -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>
> @@ -38,7 +40,7 @@
>  
>  #include "atmel_hlcdc_layer.h"
>  
> -#define ATMEL_HLCDC_MAX_LAYERS               5
> +#define ATMEL_HLCDC_MAX_LAYERS               6
>  
>  /**
>   * Atmel HLCDC Display Controller description structure.
> @@ -84,47 +86,19 @@ struct atmel_hlcdc_plane_properties {
>  };
>  
>  /**
> - * Atmel HLCDC Plane.
> + * Atmel HLCDC Layer.
>   *
> - * @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;
> -     struct atmel_hlcdc_layer layer;
> -     struct atmel_hlcdc_plane_properties *properties;
> -};
> -
> -static inline struct atmel_hlcdc_plane *
> -drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
> -{
> -     return container_of(p, struct atmel_hlcdc_plane, base);
> -}
> -
> -static inline struct atmel_hlcdc_plane *
> -atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
> -{
> -     return container_of(l, struct atmel_hlcdc_plane, layer);
> -}
> -
> -/**
> - * Atmel HLCDC Planes.
> - *
> - * This structure stores the instantiated HLCDC Planes and can be accessed by
> - * the HLCDC Display Controller or the HLCDC CRTC.
> + * A layer can be a DRM plane of a post processing layer used to render
> + * HLCDC composition into memory.
>   *
> - * @primary: primary plane
> - * @cursor: hardware cursor plane
> - * @overlays: overlay plane table
> - * @noverlays: number of overlay planes
> + * @type: layer type
> + * @plane: pointer to the DRM plane exposed by this layer
>   */
> -struct atmel_hlcdc_planes {
> -     struct atmel_hlcdc_plane *primary;
> -     struct atmel_hlcdc_plane *cursor;
> -     struct atmel_hlcdc_plane **overlays;
> -     int noverlays;
> +struct atmel_hlcdc_layer {
> +     enum atmel_hlcdc_layer_type type;
> +     union {
> +             struct drm_plane *plane;
> +     };
>  };
>  
>  /**
> @@ -135,18 +109,18 @@ struct atmel_hlcdc_planes {
>   * @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 atmel_hlcdc_layer layers[ATMEL_HLCDC_MAX_LAYERS];
>       struct {
>               wait_queue_head_t wait;
>               bool pending;
> @@ -159,8 +133,8 @@ extern struct atmel_hlcdc_formats 
> atmel_hlcdc_plane_rgb_and_yuv_formats;
>  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 drm_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
> index 377e43cea9dd..000000000000
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
> +++ /dev/null
> @@ -1,666 +0,0 @@
> -/*
> - * Copyright (C) 2014 Free Electrons
> - * Copyright (C) 2014 Atmel
> - *
> - * Author: Boris BREZILLON <boris.brezil...@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 = drm_format_num_planes(fb->pixel_format);
> -
> -     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
> index 9beabc940bce..fd766827f651 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> @@ -20,40 +20,39 @@
>  #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_CHER(l)            ((l)->regs_offset + 0x0)
> +#define ATMEL_HLCDC_LAYER_CHDR(l)            ((l)->regs_offset + 0x4)
> +#define ATMEL_HLCDC_LAYER_CHSR(l)            ((l)->regs_offset + 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_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_IER(l)             ((l)->regs_offset + 0xc)
> +#define ATMEL_HLCDC_LAYER_IDR(l)             ((l)->regs_offset + 0x10)
> +#define ATMEL_HLCDC_LAYER_IMR(l)             ((l)->regs_offset + 0x14)
> +#define ATMEL_HLCDC_LAYER_ISR(l)             ((l)->regs_offset + 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_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(l, p)   \
> +     ((l)->regs_offset + ((p) * 0x10) + 0x1c)
> +#define ATMEL_HLCDC_LAYER_PLANE_ADDR(l, p)   \
> +     ((l)->regs_offset + ((p) * 0x10) + 0x20)
> +#define ATMEL_HLCDC_LAYER_PLANE_CTRL(l, p)   \
> +     ((l)->regs_offset + ((p) * 0x10) + 0x24)
> +#define ATMEL_HLCDC_LAYER_PLANE_NEXT(l, p)   \
> +     ((l)->regs_offset + ((p) * 0x10) + 0x28)
> +
> +#define ATMEL_HLCDC_LAYER_CFG(l, c)          \
> +     ((l)->regs_offset + (l)->cfgs_offset + ((c) * 4))
> +
> +#define ATMEL_HLCDC_LAYER_DMA_CFG(l)         ATMEL_HLCDC_LAYER_CFG(l, 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)
> @@ -64,48 +63,65 @@
>  #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_FORMAT_CFG(p)              
> ATMEL_HLCDC_LAYER_CFG(p, 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)                      (((m) & 0xf) << 4)
> -#define ATMEL_HLCDC_CLUT_MODE(m)             (((m) & 0x3) << 8)
> -#define ATMEL_HLCDC_YUV_MODE(m)                      (((m) & 0xf) << 12)
> +#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_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_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_CFG(l)         \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.pos)
> +#define ATMEL_HLCDC_LAYER_POS(x, y)          ((x) | ((y) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_SIZE_CFG(l)                \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.size)
> +#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(l)     \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.memsize)
> +#define ATMEL_HLCDC_LAYER_SIZE(w, h)         (((w) - 1) | (((h) - 1) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(l, p)  \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.xstride[p])
> +
> +#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(l, p)  \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.pstride[p])
> +
> +#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(l)   \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.default_color)
> +#define ATMEL_HLCDC_LAYER_CRKEY_CFG(l)               \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.chroma_key)
> +#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(l)  \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.chroma_key_mask)
> +
> +#define ATMEL_HLCDC_LAYER_GENERAL_CFG(l)     \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.general_config)
>  #define ATMEL_HLCDC_LAYER_CRKEY                      BIT(0)
>  #define ATMEL_HLCDC_LAYER_INV                        BIT(1)
>  #define ATMEL_HLCDC_LAYER_ITER2BL            BIT(2)
> @@ -119,14 +135,32 @@
>  #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_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_CSC_CFG(l, o)              \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->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_POS_CFG(l)    \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.disc_pos)
> +#define ATMEL_HLCDC_LAYER_DISC_POS(x, y)     ((x) | ((y) << 16))
>  
> -#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)   ATMEL_HLCDC_LAYER_CFG(p, 
> (p)->desc->layout.disc_size)
> +#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(l)   \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.disc_size)
> +#define ATMEL_HLCDC_LAYER_DISC_SIZE(w, h)    (((w) - 1) | (((h) - 1) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_SCALER_CFG(l)              \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.scaler_config)
> +#define ATMEL_HLCDC_LAYER_SCALER_FACTORS(x, y)       ((x) | ((y) << 16))
> +#define ATMEL_HLCDC_LAYER_SCALER_ENABLE              BIT(31)
> +
> +#define ATMEL_HLCDC_LAYER_XPHICOEFF_CFG(l, i)        \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.phicoeffs.x + ((i) * 4))
> +
> +#define ATMEL_HLCDC_LAYER_YPHICOEFF_CFG(l, i)        \
> +     ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.phicoeffs.y + ((i) * 4))
>  
>  #define ATMEL_HLCDC_MAX_PLANES                       3
>  
> @@ -158,6 +192,8 @@
>   * @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
> @@ -172,33 +208,17 @@ struct atmel_hlcdc_layer_cfg_layout {
>       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 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.
> @@ -210,19 +230,20 @@ struct atmel_hlcdc_layer_fb_flip {
>   * @addr: buffer DMA address
>   * @ctrl: DMA transfer options
>   * @next: next DMA descriptor to fetch
> - * @gem_flip: the attached gem_flip operation
> + * @self: descriptor DMA address
>   */
>  struct atmel_hlcdc_dma_channel_dscr {
>       dma_addr_t addr;
>       u32 ctrl;
>       dma_addr_t next;
> -     u32 status;
> +     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,
> @@ -251,7 +272,7 @@ struct atmel_hlcdc_formats {
>   * @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
> + * @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)
> @@ -262,138 +283,11 @@ struct atmel_hlcdc_layer_desc {
>       enum atmel_hlcdc_layer_type type;
>       int id;
>       int regs_offset;
> -     int nconfigs;
> +     int cfgs_offset;
>       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 */
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c 
> b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 246ed1e33d8a..4fcd91f3d124 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> @@ -37,7 +37,6 @@
>   * @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
>   */
>  struct atmel_hlcdc_plane_state {
>       struct drm_plane_state base;
> @@ -67,7 +66,9 @@ struct atmel_hlcdc_plane_state {
>       int xstride[ATMEL_HLCDC_MAX_PLANES];
>       int pstride[ATMEL_HLCDC_MAX_PLANES];
>       int nplanes;
> -     bool prepared;
> +
> +     /* DMA descriptors. */
> +     struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
>  };
>  
>  static inline struct atmel_hlcdc_plane_state *
> @@ -76,6 +77,27 @@ drm_plane_state_to_atmel_hlcdc_plane_state(struct 
> drm_plane_state *s)
>       return container_of(s, struct atmel_hlcdc_plane_state, base);
>  }
>  
> +/**
> + * Atmel HLCDC Plane.
> + *
> + * @base: base DRM plane structure
> + * @desc: HLCDC layer desc structure
> + * @properties: pointer to the property definitions structure
> + * @regmap: HLCDC regmap
> + */
> +struct atmel_hlcdc_plane {
> +     struct drm_plane base;
> +     const struct atmel_hlcdc_layer_desc *desc;
> +     struct atmel_hlcdc_plane_properties *properties;
> +     struct regmap *regmap;
> +};
> +
> +static inline struct atmel_hlcdc_plane *
> +drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
> +{
> +     return container_of(p, struct atmel_hlcdc_plane, base);
> +}
> +
>  #define SUBPIXEL_MASK                        0xffff
>  
>  static uint32_t rgb_formats[] = {
> @@ -259,130 +281,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 reg_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;
> -             }
> +     struct regmap *regmap = plane->regmap;
> +     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++)
> +             regmap_write(regmap, reg_offs + (i * 4), coeff_tab[i]);
> +}
>  
> -             atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
> -                                          factor_reg);
> +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->desc;
> +     struct regmap *regmap = plane->regmap;
> +     u32 xfactor, yfactor;
> +
> +     if (!desc->layout.scaler_config)
> +             return;
> +
> +     if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_SCALER_CFG(desc), 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),
> +                             ATMEL_HLCDC_LAYER_XPHICOEFF_CFG(desc, 0));
> +
> +             atmel_hlcdc_plane_scaler_set_phicoeff(plane,
> +                             state->crtc_h < state->src_h ?
> +                             heo_downscaling_ycoef :
> +                             heo_upscaling_ycoef,
> +                             ARRAY_SIZE(heo_upscaling_ycoef),
> +                             ATMEL_HLCDC_LAYER_YPHICOEFF_CFG(desc, 0));
>       } 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;
>       }
> +
> +     regmap_write(regmap,
> +                  ATMEL_HLCDC_LAYER_SCALER_CFG(desc),
> +                  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->desc;
> +     struct regmap *regmap = plane->regmap;
> +
> +     if (desc->layout.size)
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_SIZE_CFG(desc),
> +                          ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
> +                                                 state->crtc_h));
> +
> +     if (desc->layout.memsize)
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_MEMSIZE_CFG(desc),
> +                          ATMEL_HLCDC_LAYER_SIZE(state->src_w,
> +                                                 state->src_h));
> +
> +     if (desc->layout.pos)
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_POS_CFG(desc),
> +                          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;
> +     struct regmap *regmap = plane->regmap;
>  
>       if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
> +             u32 format = state->base.fb->pixel_format;
> +
>               cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
>                      ATMEL_HLCDC_LAYER_ITER;
>  
> -             if 
> (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_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);
> +     regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_DMA_CFG(plane->desc),
> +                        ATMEL_HLCDC_LAYER_DMA_BLEN_MASK |
> +                        ATMEL_HLCDC_LAYER_DMA_SIF,
> +                        ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id);
> +
> +     regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_GENERAL_CFG(plane->desc),
> +                        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);
>  }
>  
>  static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
>                                       struct atmel_hlcdc_plane_state *state)
>  {
> +     struct regmap *regmap = plane->regmap;
>       u32 cfg;
>       int ret;
>  
> @@ -396,10 +433,7 @@ 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);
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_FORMAT_CFG(plane->desc), cfg);
>  
>       /*
>        * Rotation optimization is not working on RGB888 (rotation is still
> @@ -410,36 +444,51 @@ static void atmel_hlcdc_plane_update_format(struct 
> atmel_hlcdc_plane *plane,
>       else
>               cfg = 0;
>  
> -     atmel_hlcdc_layer_update_cfg(&plane->layer,
> -                                  ATMEL_HLCDC_LAYER_DMA_CFG_ID,
> -                                  ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
> +     regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_DMA_CFG(plane->desc),
> +                        ATMEL_HLCDC_LAYER_DMA_ROTDIS, 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->desc;
> +     struct drm_framebuffer *fb = state->base.fb;
> +     struct regmap *regmap = plane->regmap;
> +     u32 sr;
>       int i;
>  
> -     atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
> -                                     state->offsets);
> +     regmap_read(regmap, ATMEL_HLCDC_LAYER_CHSR(desc), &sr);
>  
>       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);
>  
> -             if (layout->pstride[i]) {
> -                     atmel_hlcdc_layer_update_cfg(&plane->layer,
> -                                             layout->pstride[i],
> -                                             0xffffffff,
> -                                             state->pstride[i]);
> +             state->dscrs[i]->addr = gem->paddr + state->offsets[i];
> +
> +             regmap_write(regmap,
> +                          ATMEL_HLCDC_LAYER_PLANE_HEAD(desc, i),
> +                          state->dscrs[i]->self);
> +
> +             if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
> +                     regmap_write(regmap,
> +                                  ATMEL_HLCDC_LAYER_PLANE_ADDR(desc, i),
> +                                  state->dscrs[i]->addr);
> +                     regmap_write(regmap,
> +                                  ATMEL_HLCDC_LAYER_PLANE_CTRL(desc, i),
> +                                  state->dscrs[i]->ctrl);
> +                     regmap_write(regmap,
> +                                  ATMEL_HLCDC_LAYER_PLANE_NEXT(desc, i),
> +                                  state->dscrs[i]->self);
>               }
> +
> +             if (desc->layout.xstride[i])
> +                     regmap_write(regmap,
> +                                  ATMEL_HLCDC_LAYER_XSTRIDE_CFG(desc, i),
> +                                  state->xstride[i]);
> +
> +             if (desc->layout.pstride[i])
> +                     regmap_write(regmap,
> +                                  ATMEL_HLCDC_LAYER_PSTRIDE_CFG(desc, i),
> +                                  state->pstride[i]);
>       }
>  }
>  
> @@ -489,7 +538,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state 
> *c_state)
>       struct drm_plane *ovl;
>  
>       primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
> -     layout = &primary->layer.desc->layout;
> +     layout = &primary->desc->layout;
>       if (!layout->disc_pos || !layout->disc_size)
>               return 0;
>  
> @@ -548,8 +597,7 @@ 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;
> +     struct regmap *regmap = plane->regmap;
>       int disc_surface = 0;
>  
>       if (!state->disc_updated)
> @@ -557,23 +605,19 @@ atmel_hlcdc_plane_update_disc_area(struct 
> atmel_hlcdc_plane *plane,
>  
>       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);
> +     regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_GENERAL_CFG(plane->desc),
> +                        ATMEL_HLCDC_LAYER_DISCEN,
> +                        disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
>  
>       if (!disc_surface)
>               return;
>  
> -     atmel_hlcdc_layer_update_cfg(&plane->layer,
> -                                  layout->disc_pos,
> -                                  0xffffffff,
> -                                  state->disc_x | (state->disc_y << 16));
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_DISC_POS_CFG(plane->desc),
> +                  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));
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(plane->desc),
> +                  ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
> +                                              state->disc_h));
>  }
>  
>  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
> @@ -582,8 +626,6 @@ 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;
>       struct drm_framebuffer *fb = state->base.fb;
>       const struct drm_display_mode *mode;
>       struct drm_crtc_state *crtc_state;
> @@ -726,21 +768,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 (!plane->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 (plane->desc->max_height && state->crtc_h > plane->desc->max_height)
>               return -EINVAL;
>  
> -     if (plane->layer.desc->max_width &&
> -         state->crtc_w > plane->layer.desc->max_width)
> +     if (plane->desc->max_width && state->crtc_w > plane->desc->max_width)
>               return -EINVAL;
>  
>       if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
> -         (!layout->memsize ||
> +         (!plane->desc->layout.memsize ||
>            atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)))
>               return -EINVAL;
>  
> @@ -754,65 +794,14 @@ 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;
> -
> -     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);
> +     struct regmap *regmap = plane->regmap;
> +     u32 sr;
>  
>       if (!p->state->crtc || !p->state->fb)
>               return;
> @@ -823,15 +812,37 @@ 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. */
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_IER(plane->desc),
> +                  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. */
> +     regmap_read(regmap, ATMEL_HLCDC_LAYER_CHSR(plane->desc), &sr);
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_CHER(plane->desc),
> +                  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,
>                                            struct drm_plane_state *old_state)
>  {
>       struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> +     struct regmap *regmap = plane->regmap;
> +     unsigned int isr;
> +
> +     /* Disable interrupts */
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_IDR(plane->desc), 0xffffffff);
> +
> +     /* Disable the layer */
> +     regmap_write(regmap, ATMEL_HLCDC_LAYER_CHDR(plane->desc),
> +                  ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
> +                  ATMEL_HLCDC_LAYER_UPDATE);
>  
> -     atmel_hlcdc_layer_disable(&plane->layer);
> +     /* Clear all pending interrupts */
> +     regmap_read(regmap, ATMEL_HLCDC_LAYER_ISR(plane->desc), &isr);
>  }
>  
>  static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
> @@ -841,10 +852,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,
> @@ -884,25 +892,23 @@ 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;
> +     struct regmap *regmap = plane->regmap;
>  
> -     if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> -         desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
> +     if (plane->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> +         plane->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);
> +                                ATMEL_HLCDC_LAYER_GENERAL_CFG(plane->desc),
> +                                ATMEL_HLCDC_LAYER_GA_MASK,
> +                                ATMEL_HLCDC_LAYER_GA_MASK);
>       }
>  
> -     if (desc->layout.xstride && desc->layout.pstride) {
> +     if (plane->desc->layout.xstride && plane->desc->layout.pstride) {
>               int ret;
>  
>               ret = drm_plane_create_rotation_property(&plane->base,
> @@ -915,36 +921,81 @@ static int atmel_hlcdc_plane_init_properties(struct 
> atmel_hlcdc_plane *plane,
>                       return ret;
>       }
>  
> -     if (desc->layout.csc) {
> +     if (plane->desc->layout.csc) {
>               /*
>                * 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),
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_CSC_CFG(plane->desc, 0),
>                            0x4c900091);
> -             regmap_write(regmap,
> -                          desc->regs_offset +
> -                          ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_CSC_CFG(plane->desc, 1),
>                            0x7a5f5090);
> -             regmap_write(regmap,
> -                          desc->regs_offset +
> -                          ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
> +             regmap_write(regmap, ATMEL_HLCDC_LAYER_CSC_CFG(plane->desc, 2),
>                            0x40040890);
>       }
>  
>       return 0;
>  }
>  
> +void atmel_hlcdc_plane_irq(struct drm_plane *p)
> +{
> +     struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> +     struct regmap *regmap = plane->regmap;
> +     u32 isr;
> +
> +     regmap_read(regmap, ATMEL_HLCDC_LAYER_ISR(plane->desc), &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(p->dev->dev, "overrun on plane %s\n",
> +                     plane->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;
> @@ -961,6 +1012,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;
> @@ -979,7 +1037,13 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct 
> drm_plane *p)
>               return NULL;
>  
>       copy->disc_updated = false;
> -     copy->prepared = false;
> +     copy->nplanes = 0;
> +     memset(copy->dscrs, 0, sizeof(copy->dscrs));
> +
> +     if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
> +             kfree(copy);
> +             return NULL;
> +     }
>  
>       if (copy->base.fb)
>               drm_framebuffer_reference(copy->base.fb);
> @@ -987,11 +1051,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);
> @@ -1011,22 +1082,22 @@ 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);
> +     plane->regmap = dc->hlcdc->regmap;
> +     plane->desc = desc;
> +     plane->properties = props;
>  
>       if (desc->type == ATMEL_HLCDC_BASE_LAYER)
>               type = DRM_PLANE_TYPE_PRIMARY;
> @@ -1040,17 +1111,20 @@ 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].type = desc->type;
> +     dc->layers[desc->id].plane = &plane->base;
>  
> -     return plane;
> +     return 0;
>  }
>  
>  static struct atmel_hlcdc_plane_properties *
> @@ -1069,72 +1143,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;
>  }
> 


-- 
Nicolas Ferre
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to