diff mbox

drm/atmel-hlcdc: Simplify the HLCDC layer logic

Message ID 1486405627-25714-1-git-send-email-boris.brezillon@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Boris BREZILLON Feb. 6, 2017, 6:27 p.m. UTC
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.brezillon@free-electrons.com>
---
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

Comments

Daniel Vetter Feb. 7, 2017, 7:21 a.m. UTC | #1
On Mon, Feb 06, 2017 at 07:27:07PM +0100, Boris Brezillon wrote:
> 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.brezillon@free-electrons.com>
> ---
> 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.

[snip]

> @@ -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;

So I'm not 100%, but it looks like you have a 1:1 relationship between
atmel_hlcdc_plane and atmel_hlcdc_layer. I't go one step further even and
merge these two structures together (or embed one into the other). Only
reason against that would be if eventually you need to dynamically assign
layers to planes (e.g. 2 layers for nv12 or something like that), which
would mean you need to keep the pointer (and for dynamic assignement, move
it into the plane_state).

I didn't check the details of your patch really, but in general removing
abstractions when we don't need them is a good idea.

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Nicolas Ferre Feb. 28, 2017, 10:56 a.m. UTC | #2
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.brezillon@free-electrons.com>

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

Tested-by: Nicolas Ferre <nicolas.ferre@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.brezillon@free-electrons.com>
> - *
> - * This program is free software; you can redistribute it and/or modify it
> - * under the terms of the GNU General Public License version 2 as published by
> - * the Free Software Foundation.
> - *
> - * This program is distributed in the hope that it will be useful, but WITHOUT
> - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> - * more details.
> - *
> - * You should have received a copy of the GNU General Public License along with
> - * this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -#include <linux/dma-mapping.h>
> -#include <linux/interrupt.h>
> -
> -#include "atmel_hlcdc_dc.h"
> -
> -static void
> -atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
> -{
> -	struct atmel_hlcdc_layer_fb_flip *flip = val;
> -
> -	if (flip->fb)
> -		drm_framebuffer_unreference(flip->fb);
> -	kfree(flip);
> -}
> -
> -static void
> -atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
> -{
> -	if (flip->fb)
> -		drm_framebuffer_unreference(flip->fb);
> -	kfree(flip->task);
> -	kfree(flip);
> -}
> -
> -static void
> -atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
> -					struct atmel_hlcdc_layer_fb_flip *flip)
> -{
> -	int i;
> -
> -	if (!flip)
> -		return;
> -
> -	for (i = 0; i < layer->max_planes; i++) {
> -		if (!flip->dscrs[i])
> -			break;
> -
> -		flip->dscrs[i]->status = 0;
> -		flip->dscrs[i] = NULL;
> -	}
> -
> -	drm_flip_work_queue_task(&layer->gc, flip->task);
> -	drm_flip_work_commit(&layer->gc, layer->wq);
> -}
> -
> -static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
> -					   int id)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -
> -	if (id < 0 || id > 1)
> -		return;
> -
> -	slot = &upd->slots[id];
> -	bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
> -	memset(slot->configs, 0,
> -	       sizeof(*slot->configs) * layer->desc->nconfigs);
> -
> -	if (slot->fb_flip) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
> -		slot->fb_flip = NULL;
> -	}
> -}
> -
> -static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	struct atmel_hlcdc_dma_channel_dscr *dscr;
> -	unsigned int cfg;
> -	u32 action = 0;
> -	int i = 0;
> -
> -	if (upd->pending < 0 || upd->pending > 1)
> -		return;
> -
> -	slot = &upd->slots[upd->pending];
> -
> -	for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CFG(layer, cfg),
> -			     slot->configs[cfg]);
> -		action |= ATMEL_HLCDC_LAYER_UPDATE;
> -	}
> -
> -	fb_flip = slot->fb_flip;
> -
> -	if (!fb_flip->fb)
> -		goto apply;
> -
> -	if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
> -		for (i = 0; i < fb_flip->ngems; i++) {
> -			dscr = fb_flip->dscrs[i];
> -			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
> -				     ATMEL_HLCDC_LAYER_DMA_IRQ |
> -				     ATMEL_HLCDC_LAYER_ADD_IRQ |
> -				     ATMEL_HLCDC_LAYER_DONE_IRQ;
> -
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
> -				     dscr->addr);
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
> -				     dscr->ctrl);
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
> -				     dscr->next);
> -		}
> -
> -		action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
> -		dma->status = ATMEL_HLCDC_LAYER_ENABLED;
> -	} else {
> -		for (i = 0; i < fb_flip->ngems; i++) {
> -			dscr =  fb_flip->dscrs[i];
> -			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
> -				     ATMEL_HLCDC_LAYER_DMA_IRQ |
> -				     ATMEL_HLCDC_LAYER_DSCR_IRQ |
> -				     ATMEL_HLCDC_LAYER_DONE_IRQ;
> -
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
> -				     dscr->next);
> -		}
> -
> -		action |= ATMEL_HLCDC_LAYER_A2Q;
> -	}
> -
> -	/* Release unneeded descriptors */
> -	for (i = fb_flip->ngems; i < layer->max_planes; i++) {
> -		fb_flip->dscrs[i]->status = 0;
> -		fb_flip->dscrs[i] = NULL;
> -	}
> -
> -	dma->queue = fb_flip;
> -	slot->fb_flip = NULL;
> -
> -apply:
> -	if (action)
> -		regmap_write(regmap,
> -			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
> -			     action);
> -
> -	atmel_hlcdc_layer_update_reset(layer, upd->pending);
> -
> -	upd->pending = -1;
> -}
> -
> -void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	struct atmel_hlcdc_layer_fb_flip *flip;
> -	unsigned long flags;
> -	unsigned int isr, imr;
> -	unsigned int status;
> -	unsigned int plane_status;
> -	u32 flip_status;
> -
> -	int i;
> -
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
> -	status = imr & isr;
> -	if (!status)
> -		return;
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	flip = dma->queue ? dma->queue : dma->cur;
> -
> -	if (!flip) {
> -		spin_unlock_irqrestore(&layer->lock, flags);
> -		return;
> -	}
> -
> -	/*
> -	 * Set LOADED and DONE flags: they'll be cleared if at least one
> -	 * memory plane is not LOADED or DONE.
> -	 */
> -	flip_status = ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED |
> -		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
> -	for (i = 0; i < flip->ngems; i++) {
> -		plane_status = (status >> (8 * i));
> -
> -		if (plane_status &
> -		    (ATMEL_HLCDC_LAYER_ADD_IRQ |
> -		     ATMEL_HLCDC_LAYER_DSCR_IRQ) &
> -		    ~flip->dscrs[i]->ctrl) {
> -			flip->dscrs[i]->status |=
> -					ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
> -			flip->dscrs[i]->ctrl |=
> -					ATMEL_HLCDC_LAYER_ADD_IRQ |
> -					ATMEL_HLCDC_LAYER_DSCR_IRQ;
> -		}
> -
> -		if (plane_status &
> -		    ATMEL_HLCDC_LAYER_DONE_IRQ &
> -		    ~flip->dscrs[i]->ctrl) {
> -			flip->dscrs[i]->status |=
> -					ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
> -			flip->dscrs[i]->ctrl |=
> -					ATMEL_HLCDC_LAYER_DONE_IRQ;
> -		}
> -
> -		if (plane_status & ATMEL_HLCDC_LAYER_OVR_IRQ)
> -			flip->dscrs[i]->status |=
> -					ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
> -
> -		/*
> -		 * Clear LOADED and DONE flags if the memory plane is either
> -		 * not LOADED or not DONE.
> -		 */
> -		if (!(flip->dscrs[i]->status &
> -		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED))
> -			flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
> -
> -		if (!(flip->dscrs[i]->status &
> -		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE))
> -			flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
> -
> -		/*
> -		 * An overrun on one memory plane impact the whole framebuffer
> -		 * transfer, hence we set the OVERRUN flag as soon as there's
> -		 * one memory plane reporting such an overrun.
> -		 */
> -		flip_status |= flip->dscrs[i]->status &
> -			       ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
> -	}
> -
> -	/* Get changed bits */
> -	flip_status ^= flip->status;
> -	flip->status |= flip_status;
> -
> -	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
> -		dma->cur = dma->queue;
> -		dma->queue = NULL;
> -	}
> -
> -	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
> -		dma->cur = NULL;
> -	}
> -
> -	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN) {
> -		regmap_write(regmap,
> -			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
> -			     ATMEL_HLCDC_LAYER_RST);
> -		if (dma->queue)
> -			atmel_hlcdc_layer_fb_flip_release_queue(layer,
> -								dma->queue);
> -
> -		if (dma->cur)
> -			atmel_hlcdc_layer_fb_flip_release_queue(layer,
> -								dma->cur);
> -
> -		dma->cur = NULL;
> -		dma->queue = NULL;
> -	}
> -
> -	if (!dma->queue) {
> -		atmel_hlcdc_layer_update_apply(layer);
> -
> -		if (!dma->cur)
> -			dma->status = ATMEL_HLCDC_LAYER_DISABLED;
> -	}
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -}
> -
> -void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	unsigned long flags;
> -	unsigned int isr;
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	/* Disable the layer */
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
> -		     ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
> -		     ATMEL_HLCDC_LAYER_UPDATE);
> -
> -	/* Clear all pending interrupts */
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
> -
> -	/* Discard current and queued framebuffer transfers. */
> -	if (dma->cur) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
> -		dma->cur = NULL;
> -	}
> -
> -	if (dma->queue) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->queue);
> -		dma->queue = NULL;
> -	}
> -
> -	/*
> -	 * Then discard the pending update request (if any) to prevent
> -	 * DMA irq handler from restarting the DMA channel after it has
> -	 * been disabled.
> -	 */
> -	if (upd->pending >= 0) {
> -		atmel_hlcdc_layer_update_reset(layer, upd->pending);
> -		upd->pending = -1;
> -	}
> -
> -	dma->status = ATMEL_HLCDC_LAYER_DISABLED;
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -}
> -
> -int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	unsigned long flags;
> -	int i, j = 0;
> -
> -	fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
> -	if (!fb_flip)
> -		return -ENOMEM;
> -
> -	fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
> -	if (!fb_flip->task) {
> -		kfree(fb_flip);
> -		return -ENOMEM;
> -	}
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	upd->next = upd->pending ? 0 : 1;
> -
> -	slot = &upd->slots[upd->next];
> -
> -	for (i = 0; i < layer->max_planes * 4; i++) {
> -		if (!dma->dscrs[i].status) {
> -			fb_flip->dscrs[j++] = &dma->dscrs[i];
> -			dma->dscrs[i].status =
> -				ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
> -			if (j == layer->max_planes)
> -				break;
> -		}
> -	}
> -
> -	if (j < layer->max_planes) {
> -		for (i = 0; i < j; i++)
> -			fb_flip->dscrs[i]->status = 0;
> -	}
> -
> -	if (j < layer->max_planes) {
> -		spin_unlock_irqrestore(&layer->lock, flags);
> -		atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
> -		return -EBUSY;
> -	}
> -
> -	slot->fb_flip = fb_flip;
> -
> -	if (upd->pending >= 0) {
> -		memcpy(slot->configs,
> -		       upd->slots[upd->pending].configs,
> -		       layer->desc->nconfigs * sizeof(u32));
> -		memcpy(slot->updated_configs,
> -		       upd->slots[upd->pending].updated_configs,
> -		       DIV_ROUND_UP(layer->desc->nconfigs,
> -				    BITS_PER_BYTE * sizeof(unsigned long)) *
> -		       sizeof(unsigned long));
> -		slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
> -		if (upd->slots[upd->pending].fb_flip->fb) {
> -			slot->fb_flip->fb =
> -				upd->slots[upd->pending].fb_flip->fb;
> -			slot->fb_flip->ngems =
> -				upd->slots[upd->pending].fb_flip->ngems;
> -			drm_framebuffer_reference(slot->fb_flip->fb);
> -		}
> -	} else {
> -		regmap_bulk_read(regmap,
> -				 layer->desc->regs_offset +
> -				 ATMEL_HLCDC_LAYER_CFG(layer, 0),
> -				 upd->slots[upd->next].configs,
> -				 layer->desc->nconfigs);
> -	}
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -
> -	return 0;
> -}
> -
> -void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -
> -	atmel_hlcdc_layer_update_reset(layer, upd->next);
> -	upd->next = -1;
> -}
> -
> -void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
> -				     struct drm_framebuffer *fb,
> -				     unsigned int *offsets)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	struct atmel_hlcdc_dma_channel_dscr *dscr;
> -	struct drm_framebuffer *old_fb;
> -	int nplanes = 0;
> -	int i;
> -
> -	if (upd->next < 0 || upd->next > 1)
> -		return;
> -
> -	if (fb)
> -		nplanes = 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;
>  }
>
diff mbox

Patch

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.brezillon@free-electrons.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-
-#include "atmel_hlcdc_dc.h"
-
-static void
-atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
-{
-	struct atmel_hlcdc_layer_fb_flip *flip = val;
-
-	if (flip->fb)
-		drm_framebuffer_unreference(flip->fb);
-	kfree(flip);
-}
-
-static void
-atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
-{
-	if (flip->fb)
-		drm_framebuffer_unreference(flip->fb);
-	kfree(flip->task);
-	kfree(flip);
-}
-
-static void
-atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
-					struct atmel_hlcdc_layer_fb_flip *flip)
-{
-	int i;
-
-	if (!flip)
-		return;
-
-	for (i = 0; i < layer->max_planes; i++) {
-		if (!flip->dscrs[i])
-			break;
-
-		flip->dscrs[i]->status = 0;
-		flip->dscrs[i] = NULL;
-	}
-
-	drm_flip_work_queue_task(&layer->gc, flip->task);
-	drm_flip_work_commit(&layer->gc, layer->wq);
-}
-
-static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
-					   int id)
-{
-	struct atmel_hlcdc_layer_update *upd = &layer->update;
-	struct atmel_hlcdc_layer_update_slot *slot;
-
-	if (id < 0 || id > 1)
-		return;
-
-	slot = &upd->slots[id];
-	bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
-	memset(slot->configs, 0,
-	       sizeof(*slot->configs) * layer->desc->nconfigs);
-
-	if (slot->fb_flip) {
-		atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
-		slot->fb_flip = NULL;
-	}
-}
-
-static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
-{
-	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-	struct atmel_hlcdc_layer_update *upd = &layer->update;
-	struct regmap *regmap = layer->hlcdc->regmap;
-	struct atmel_hlcdc_layer_update_slot *slot;
-	struct atmel_hlcdc_layer_fb_flip *fb_flip;
-	struct atmel_hlcdc_dma_channel_dscr *dscr;
-	unsigned int cfg;
-	u32 action = 0;
-	int i = 0;
-
-	if (upd->pending < 0 || upd->pending > 1)
-		return;
-
-	slot = &upd->slots[upd->pending];
-
-	for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
-		regmap_write(regmap,
-			     desc->regs_offset +
-			     ATMEL_HLCDC_LAYER_CFG(layer, cfg),
-			     slot->configs[cfg]);
-		action |= ATMEL_HLCDC_LAYER_UPDATE;
-	}
-
-	fb_flip = slot->fb_flip;
-
-	if (!fb_flip->fb)
-		goto apply;
-
-	if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
-		for (i = 0; i < fb_flip->ngems; i++) {
-			dscr = fb_flip->dscrs[i];
-			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
-				     ATMEL_HLCDC_LAYER_DMA_IRQ |
-				     ATMEL_HLCDC_LAYER_ADD_IRQ |
-				     ATMEL_HLCDC_LAYER_DONE_IRQ;
-
-			regmap_write(regmap,
-				     desc->regs_offset +
-				     ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
-				     dscr->addr);
-			regmap_write(regmap,
-				     desc->regs_offset +
-				     ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
-				     dscr->ctrl);
-			regmap_write(regmap,
-				     desc->regs_offset +
-				     ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
-				     dscr->next);
-		}
-
-		action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
-		dma->status = ATMEL_HLCDC_LAYER_ENABLED;
-	} else {
-		for (i = 0; i < fb_flip->ngems; i++) {
-			dscr =  fb_flip->dscrs[i];
-			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
-				     ATMEL_HLCDC_LAYER_DMA_IRQ |
-				     ATMEL_HLCDC_LAYER_DSCR_IRQ |
-				     ATMEL_HLCDC_LAYER_DONE_IRQ;
-
-			regmap_write(regmap,
-				     desc->regs_offset +
-				     ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
-				     dscr->next);
-		}
-
-		action |= ATMEL_HLCDC_LAYER_A2Q;
-	}
-
-	/* Release unneeded descriptors */
-	for (i = fb_flip->ngems; i < layer->max_planes; i++) {
-		fb_flip->dscrs[i]->status = 0;
-		fb_flip->dscrs[i] = NULL;
-	}
-
-	dma->queue = fb_flip;
-	slot->fb_flip = NULL;
-
-apply:
-	if (action)
-		regmap_write(regmap,
-			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
-			     action);
-
-	atmel_hlcdc_layer_update_reset(layer, upd->pending);
-
-	upd->pending = -1;
-}
-
-void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
-{
-	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-	struct regmap *regmap = layer->hlcdc->regmap;
-	struct atmel_hlcdc_layer_fb_flip *flip;
-	unsigned long flags;
-	unsigned int isr, imr;
-	unsigned int status;
-	unsigned int plane_status;
-	u32 flip_status;
-
-	int i;
-
-	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
-	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
-	status = imr & isr;
-	if (!status)
-		return;
-
-	spin_lock_irqsave(&layer->lock, flags);
-
-	flip = dma->queue ? dma->queue : dma->cur;
-
-	if (!flip) {
-		spin_unlock_irqrestore(&layer->lock, flags);
-		return;
-	}
-
-	/*
-	 * Set LOADED and DONE flags: they'll be cleared if at least one
-	 * memory plane is not LOADED or DONE.
-	 */
-	flip_status = ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED |
-		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
-	for (i = 0; i < flip->ngems; i++) {
-		plane_status = (status >> (8 * i));
-
-		if (plane_status &
-		    (ATMEL_HLCDC_LAYER_ADD_IRQ |
-		     ATMEL_HLCDC_LAYER_DSCR_IRQ) &
-		    ~flip->dscrs[i]->ctrl) {
-			flip->dscrs[i]->status |=
-					ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
-			flip->dscrs[i]->ctrl |=
-					ATMEL_HLCDC_LAYER_ADD_IRQ |
-					ATMEL_HLCDC_LAYER_DSCR_IRQ;
-		}
-
-		if (plane_status &
-		    ATMEL_HLCDC_LAYER_DONE_IRQ &
-		    ~flip->dscrs[i]->ctrl) {
-			flip->dscrs[i]->status |=
-					ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
-			flip->dscrs[i]->ctrl |=
-					ATMEL_HLCDC_LAYER_DONE_IRQ;
-		}
-
-		if (plane_status & ATMEL_HLCDC_LAYER_OVR_IRQ)
-			flip->dscrs[i]->status |=
-					ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
-
-		/*
-		 * Clear LOADED and DONE flags if the memory plane is either
-		 * not LOADED or not DONE.
-		 */
-		if (!(flip->dscrs[i]->status &
-		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED))
-			flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
-
-		if (!(flip->dscrs[i]->status &
-		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE))
-			flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
-
-		/*
-		 * An overrun on one memory plane impact the whole framebuffer
-		 * transfer, hence we set the OVERRUN flag as soon as there's
-		 * one memory plane reporting such an overrun.
-		 */
-		flip_status |= flip->dscrs[i]->status &
-			       ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
-	}
-
-	/* Get changed bits */
-	flip_status ^= flip->status;
-	flip->status |= flip_status;
-
-	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
-		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
-		dma->cur = dma->queue;
-		dma->queue = NULL;
-	}
-
-	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
-		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
-		dma->cur = NULL;
-	}
-
-	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN) {
-		regmap_write(regmap,
-			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
-			     ATMEL_HLCDC_LAYER_RST);
-		if (dma->queue)
-			atmel_hlcdc_layer_fb_flip_release_queue(layer,
-								dma->queue);
-
-		if (dma->cur)
-			atmel_hlcdc_layer_fb_flip_release_queue(layer,
-								dma->cur);
-
-		dma->cur = NULL;
-		dma->queue = NULL;
-	}
-
-	if (!dma->queue) {
-		atmel_hlcdc_layer_update_apply(layer);
-
-		if (!dma->cur)
-			dma->status = ATMEL_HLCDC_LAYER_DISABLED;
-	}
-
-	spin_unlock_irqrestore(&layer->lock, flags);
-}
-
-void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
-{
-	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-	struct atmel_hlcdc_layer_update *upd = &layer->update;
-	struct regmap *regmap = layer->hlcdc->regmap;
-	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
-	unsigned long flags;
-	unsigned int isr;
-
-	spin_lock_irqsave(&layer->lock, flags);
-
-	/* Disable the layer */
-	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
-		     ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
-		     ATMEL_HLCDC_LAYER_UPDATE);
-
-	/* Clear all pending interrupts */
-	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
-
-	/* Discard current and queued framebuffer transfers. */
-	if (dma->cur) {
-		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
-		dma->cur = NULL;
-	}
-
-	if (dma->queue) {
-		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->queue);
-		dma->queue = NULL;
-	}
-
-	/*
-	 * Then discard the pending update request (if any) to prevent
-	 * DMA irq handler from restarting the DMA channel after it has
-	 * been disabled.
-	 */
-	if (upd->pending >= 0) {
-		atmel_hlcdc_layer_update_reset(layer, upd->pending);
-		upd->pending = -1;
-	}
-
-	dma->status = ATMEL_HLCDC_LAYER_DISABLED;
-
-	spin_unlock_irqrestore(&layer->lock, flags);
-}
-
-int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
-{
-	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
-	struct atmel_hlcdc_layer_update *upd = &layer->update;
-	struct regmap *regmap = layer->hlcdc->regmap;
-	struct atmel_hlcdc_layer_fb_flip *fb_flip;
-	struct atmel_hlcdc_layer_update_slot *slot;
-	unsigned long flags;
-	int i, j = 0;
-
-	fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
-	if (!fb_flip)
-		return -ENOMEM;
-
-	fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
-	if (!fb_flip->task) {
-		kfree(fb_flip);
-		return -ENOMEM;
-	}
-
-	spin_lock_irqsave(&layer->lock, flags);
-
-	upd->next = upd->pending ? 0 : 1;
-
-	slot = &upd->slots[upd->next];
-
-	for (i = 0; i < layer->max_planes * 4; i++) {
-		if (!dma->dscrs[i].status) {
-			fb_flip->dscrs[j++] = &dma->dscrs[i];
-			dma->dscrs[i].status =
-				ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
-			if (j == layer->max_planes)
-				break;
-		}
-	}
-
-	if (j < layer->max_planes) {
-		for (i = 0; i < j; i++)
-			fb_flip->dscrs[i]->status = 0;
-	}
-
-	if (j < layer->max_planes) {
-		spin_unlock_irqrestore(&layer->lock, flags);
-		atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
-		return -EBUSY;
-	}
-
-	slot->fb_flip = fb_flip;
-
-	if (upd->pending >= 0) {
-		memcpy(slot->configs,
-		       upd->slots[upd->pending].configs,
-		       layer->desc->nconfigs * sizeof(u32));
-		memcpy(slot->updated_configs,
-		       upd->slots[upd->pending].updated_configs,
-		       DIV_ROUND_UP(layer->desc->nconfigs,
-				    BITS_PER_BYTE * sizeof(unsigned long)) *
-		       sizeof(unsigned long));
-		slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
-		if (upd->slots[upd->pending].fb_flip->fb) {
-			slot->fb_flip->fb =
-				upd->slots[upd->pending].fb_flip->fb;
-			slot->fb_flip->ngems =
-				upd->slots[upd->pending].fb_flip->ngems;
-			drm_framebuffer_reference(slot->fb_flip->fb);
-		}
-	} else {
-		regmap_bulk_read(regmap,
-				 layer->desc->regs_offset +
-				 ATMEL_HLCDC_LAYER_CFG(layer, 0),
-				 upd->slots[upd->next].configs,
-				 layer->desc->nconfigs);
-	}
-
-	spin_unlock_irqrestore(&layer->lock, flags);
-
-	return 0;
-}
-
-void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
-{
-	struct atmel_hlcdc_layer_update *upd = &layer->update;
-
-	atmel_hlcdc_layer_update_reset(layer, upd->next);
-	upd->next = -1;
-}
-
-void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
-				     struct drm_framebuffer *fb,
-				     unsigned int *offsets)
-{
-	struct atmel_hlcdc_layer_update *upd = &layer->update;
-	struct atmel_hlcdc_layer_fb_flip *fb_flip;
-	struct atmel_hlcdc_layer_update_slot *slot;
-	struct atmel_hlcdc_dma_channel_dscr *dscr;
-	struct drm_framebuffer *old_fb;
-	int nplanes = 0;
-	int i;
-
-	if (upd->next < 0 || upd->next > 1)
-		return;
-
-	if (fb)
-		nplanes = 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;
 }