diff mbox

[v2] drm/atmel-hlcdc: Simplify the HLCDC layer logic

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

Commit Message

Boris BREZILLON Feb. 10, 2017, 6:07 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,

I intentionally dropped your ack, since inheriting from atmel_hlcdc_layer
is implying a lot of changes.

Regards,

Boris

Changes in v2:
- make atmel_hlcdc_plane inherit from atmel_hlcdc_layer
- provide read/write_reg/cfg() helpers to access layer regs
- move all layer related definitions into atmel_hlcdc_dc.h and remove
  atmel_hlcdc_layer.h
- fix a bug in atmel_hlcdc_plane_atomic_duplicate_state()
---
 drivers/gpu/drm/atmel-hlcdc/Makefile            |   1 -
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c  |  39 +-
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c    |  82 +--
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h    | 364 +++++++++++--
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c | 666 ------------------------
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h | 399 --------------
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 637 +++++++++++-----------
 7 files changed, 695 insertions(+), 1493 deletions(-)
 delete mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 delete mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h

Comments

Daniel Vetter Feb. 14, 2017, 9:33 p.m. UTC | #1
On Fri, Feb 10, 2017 at 07:07:45PM +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,
> 
> I intentionally dropped your ack, since inheriting from atmel_hlcdc_layer
> is implying a lot of changes.

Well I acked the idea, that still kinda holds. But if you want to
kickstart the drm-misc driver ack economy, Eric has 1-2 vc4 patches that
still need an ack, you could trade r-bs :-)

Cheers, Daniel

> 
> Regards,
> 
> Boris
> 
> Changes in v2:
> - make atmel_hlcdc_plane inherit from atmel_hlcdc_layer
> - provide read/write_reg/cfg() helpers to access layer regs
> - move all layer related definitions into atmel_hlcdc_dc.h and remove
>   atmel_hlcdc_layer.h
> - fix a bug in atmel_hlcdc_plane_atomic_duplicate_state()
> ---
>  drivers/gpu/drm/atmel-hlcdc/Makefile            |   1 -
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c  |  39 +-
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c    |  82 +--
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h    | 364 +++++++++++--
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c | 666 ------------------------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h | 399 --------------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 637 +++++++++++-----------
>  7 files changed, 695 insertions(+), 1493 deletions(-)
>  delete mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
>  delete mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> 
> 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..2fcec0a72567 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 atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
> -	struct atmel_hlcdc_planes *planes = dc->planes;
>  	struct atmel_hlcdc_crtc *crtc;
>  	int ret;
>  	int i;
> @@ -457,20 +457,41 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
>  
>  	crtc->dc = dc;
>  
> -	ret = drm_crtc_init_with_planes(dev, &crtc->base,
> -				&planes->primary->base,
> -				planes->cursor ? &planes->cursor->base : NULL,
> -				&atmel_hlcdc_crtc_funcs, NULL);
> +	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> +		if (!dc->layers[i])
> +			continue;
> +
> +		switch (dc->layers[i]->desc->type) {
> +		case ATMEL_HLCDC_BASE_LAYER:
> +			primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
> +			break;
> +
> +		case ATMEL_HLCDC_CURSOR_LAYER:
> +			cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
> +			break;
> +
> +		default:
> +			break;
> +		}
> +	}
> +
> +	ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
> +					&cursor->base, &atmel_hlcdc_crtc_funcs,
> +					NULL);
>  	if (ret < 0)
>  		goto fail;
>  
>  	crtc->id = drm_crtc_index(&crtc->base);
>  
> -	if (planes->cursor)
> -		planes->cursor->base.possible_crtcs = 1 << crtc->id;
> +	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> +		struct atmel_hlcdc_plane *overlay;
>  
> -	for (i = 0; i < planes->noverlays; i++)
> -		planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
> +		if (dc->layers[i] &&
> +		    dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
> +			overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
> +			overlay->base.possible_crtcs = 1 << crtc->id;
> +		}
> +	}
>  
>  	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
>  	drm_crtc_vblank_reset(&crtc->base);
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> index 0bf32d6ac39b..8eb1d7471c63 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,17 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
>  	return MODE_OK;
>  }
>  
> +static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
> +{
> +	if (!layer)
> +		return;
> +
> +	if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER ||
> +	    layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> +	    layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER)
> +		atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer));
> +}
> +
>  static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
>  {
>  	struct drm_device *dev = data;
> @@ -410,12 +433,8 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
>  		atmel_hlcdc_crtc_irq(dc->crtc);
>  
>  	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> -		struct atmel_hlcdc_layer *layer = dc->layers[i];
> -
> -		if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
> -			continue;
> -
> -		atmel_hlcdc_layer_irq(layer);
> +		if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
> +			atmel_hlcdc_layer_irq(dc->layers[i]);
>  	}
>  
>  	return IRQ_HANDLED;
> @@ -537,9 +556,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
>  static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
>  {
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
> -	struct atmel_hlcdc_planes *planes;
>  	int ret;
> -	int i;
>  
>  	drm_mode_config_init(dev);
>  
> @@ -549,25 +566,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
>  		return ret;
>  	}
>  
> -	planes = atmel_hlcdc_create_planes(dev);
> -	if (IS_ERR(planes)) {
> -		dev_err(dev->dev, "failed to create planes\n");
> -		return PTR_ERR(planes);
> +	ret = atmel_hlcdc_create_planes(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "failed to create planes: %d\n", ret);
> +		return ret;
>  	}
>  
> -	dc->planes = planes;
> -
> -	dc->layers[planes->primary->layer.desc->id] =
> -						&planes->primary->layer;
> -
> -	if (planes->cursor)
> -		dc->layers[planes->cursor->layer.desc->id] =
> -							&planes->cursor->layer;
> -
> -	for (i = 0; i < planes->noverlays; i++)
> -		dc->layers[planes->overlays[i]->layer.desc->id] =
> -						&planes->overlays[i]->layer;
> -
>  	ret = atmel_hlcdc_crtc_create(dev);
>  	if (ret) {
>  		dev_err(dev->dev, "failed to create crtc\n");
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> index 7a47f8c094d0..d84c9a56bbb5 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>
> @@ -36,51 +38,245 @@
>  #include <drm/drm_plane_helper.h>
>  #include <drm/drmP.h>
>  
> -#include "atmel_hlcdc_layer.h"
> +#define ATMEL_HLCDC_LAYER_CHER			0x0
> +#define ATMEL_HLCDC_LAYER_CHDR			0x4
> +#define ATMEL_HLCDC_LAYER_CHSR			0x8
> +#define ATMEL_HLCDC_LAYER_EN			BIT(0)
> +#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
> +#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
> +#define ATMEL_HLCDC_LAYER_RST			BIT(8)
>  
> -#define ATMEL_HLCDC_MAX_LAYERS		5
> +#define ATMEL_HLCDC_LAYER_IER			0xc
> +#define ATMEL_HLCDC_LAYER_IDR			0x10
> +#define ATMEL_HLCDC_LAYER_IMR			0x14
> +#define ATMEL_HLCDC_LAYER_ISR			0x18
> +#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
> +#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
> +#define ATMEL_HLCDC_LAYER_DMA_IRQ(p)		BIT(2 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_DSCR_IRQ(p)		BIT(3 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_ADD_IRQ(p)		BIT(4 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_DONE_IRQ(p)		BIT(5 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_OVR_IRQ(p)		BIT(6 + (8 * (p)))
> +
> +#define ATMEL_HLCDC_LAYER_PLANE_HEAD(p)		(((p) * 0x10) + 0x1c)
> +#define ATMEL_HLCDC_LAYER_PLANE_ADDR(p)		(((p) * 0x10) + 0x20)
> +#define ATMEL_HLCDC_LAYER_PLANE_CTRL(p)		(((p) * 0x10) + 0x24)
> +#define ATMEL_HLCDC_LAYER_PLANE_NEXT(p)		(((p) * 0x10) + 0x28)
> +
> +#define ATMEL_HLCDC_LAYER_DMA_CFG		0
> +#define ATMEL_HLCDC_LAYER_DMA_SIF		BIT(0)
> +#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK		GENMASK(5, 4)
> +#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE	(0 << 4)
> +#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4	(1 << 4)
> +#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8	(2 << 4)
> +#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16	(3 << 4)
> +#define ATMEL_HLCDC_LAYER_DMA_DLBO		BIT(8)
> +#define ATMEL_HLCDC_LAYER_DMA_ROTDIS		BIT(12)
> +#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS		BIT(13)
> +
> +#define ATMEL_HLCDC_LAYER_FORMAT_CFG		1
> +#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
> +#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
> +#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
> +#define ATMEL_HLCDC_RGB_MODE(m)			\
> +	(ATMEL_HLCDC_LAYER_RGB | (((m) & 0xf) << 4))
> +#define ATMEL_HLCDC_CLUT_MODE(m)		\
> +	(ATMEL_HLCDC_LAYER_CLUT | (((m) & 0x3) << 8))
> +#define ATMEL_HLCDC_YUV_MODE(m)			\
> +	(ATMEL_HLCDC_LAYER_YUV | (((m) & 0xf) << 12))
> +#define ATMEL_HLCDC_YUV422ROT			BIT(16)
> +#define ATMEL_HLCDC_YUV422SWP			BIT(17)
> +#define ATMEL_HLCDC_DSCALEOPT			BIT(20)
> +
> +#define ATMEL_HLCDC_XRGB4444_MODE		ATMEL_HLCDC_RGB_MODE(0)
> +#define ATMEL_HLCDC_ARGB4444_MODE		ATMEL_HLCDC_RGB_MODE(1)
> +#define ATMEL_HLCDC_RGBA4444_MODE		ATMEL_HLCDC_RGB_MODE(2)
> +#define ATMEL_HLCDC_RGB565_MODE			ATMEL_HLCDC_RGB_MODE(3)
> +#define ATMEL_HLCDC_ARGB1555_MODE		ATMEL_HLCDC_RGB_MODE(4)
> +#define ATMEL_HLCDC_XRGB8888_MODE		ATMEL_HLCDC_RGB_MODE(9)
> +#define ATMEL_HLCDC_RGB888_MODE			ATMEL_HLCDC_RGB_MODE(10)
> +#define ATMEL_HLCDC_ARGB8888_MODE		ATMEL_HLCDC_RGB_MODE(12)
> +#define ATMEL_HLCDC_RGBA8888_MODE		ATMEL_HLCDC_RGB_MODE(13)
> +
> +#define ATMEL_HLCDC_AYUV_MODE			ATMEL_HLCDC_YUV_MODE(0)
> +#define ATMEL_HLCDC_YUYV_MODE			ATMEL_HLCDC_YUV_MODE(1)
> +#define ATMEL_HLCDC_UYVY_MODE			ATMEL_HLCDC_YUV_MODE(2)
> +#define ATMEL_HLCDC_YVYU_MODE			ATMEL_HLCDC_YUV_MODE(3)
> +#define ATMEL_HLCDC_VYUY_MODE			ATMEL_HLCDC_YUV_MODE(4)
> +#define ATMEL_HLCDC_NV61_MODE			ATMEL_HLCDC_YUV_MODE(5)
> +#define ATMEL_HLCDC_YUV422_MODE			ATMEL_HLCDC_YUV_MODE(6)
> +#define ATMEL_HLCDC_NV21_MODE			ATMEL_HLCDC_YUV_MODE(7)
> +#define ATMEL_HLCDC_YUV420_MODE			ATMEL_HLCDC_YUV_MODE(8)
> +
> +#define ATMEL_HLCDC_LAYER_POS(x, y)		((x) | ((y) << 16))
> +#define ATMEL_HLCDC_LAYER_SIZE(w, h)		(((w) - 1) | (((h) - 1) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
> +#define ATMEL_HLCDC_LAYER_INV			BIT(1)
> +#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
> +#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
> +#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
> +#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
> +#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
> +#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
> +#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
> +#define ATMEL_HLCDC_LAYER_REP			BIT(9)
> +#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
> +#define ATMEL_HLCDC_LAYER_DISCEN		BIT(11)
> +#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
> +#define ATMEL_HLCDC_LAYER_GA_MASK		\
> +	GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
> +#define ATMEL_HLCDC_LAYER_GA(x)			\
> +	((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
> +
> +#define ATMEL_HLCDC_LAYER_DISC_POS(x, y)	((x) | ((y) << 16))
> +#define ATMEL_HLCDC_LAYER_DISC_SIZE(w, h)	(((w) - 1) | (((h) - 1) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_SCALER_FACTORS(x, y)	((x) | ((y) << 16))
> +#define ATMEL_HLCDC_LAYER_SCALER_ENABLE		BIT(31)
> +
> +#define ATMEL_HLCDC_LAYER_MAX_PLANES		3
> +
> +#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
> +#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
> +#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
> +#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN	BIT(3)
> +
> +#define ATMEL_HLCDC_MAX_LAYERS			6
>  
>  /**
> - * Atmel HLCDC Display Controller description structure.
> + * Atmel HLCDC Layer registers layout structure
>   *
> - * This structure describe the HLCDC IP capabilities and depends on the
> - * HLCDC IP version (or Atmel SoC family).
> + * Each HLCDC layer has its own register organization and a given register
> + * can be placed differently on 2 different layers depending on its
> + * capabilities.
> + * This structure stores common registers layout for a given layer and is
> + * used by HLCDC layer code to choose the appropriate register to write to
> + * or to read from.
>   *
> - * @min_width: minimum width supported by the Display Controller
> - * @min_height: minimum height supported by the Display Controller
> - * @max_width: maximum width supported by the Display Controller
> - * @max_height: maximum height supported by the Display Controller
> - * @max_spw: maximum vertical/horizontal pulse width
> - * @max_vpw: maximum vertical back/front porch width
> - * @max_hpw: maximum horizontal back/front porch width
> - * @conflicting_output_formats: true if RGBXXX output formats conflict with
> - *				each other.
> - * @layers: a layer description table describing available layers
> - * @nlayers: layer description table size
> + * For all fields, a value of zero means "unsupported".
> + *
> + * See Atmel's datasheet for a detailled description of these registers.
> + *
> + * @xstride: xstride registers
> + * @pstride: pstride registers
> + * @pos: position register
> + * @size: displayed size register
> + * @memsize: memory size register
> + * @default_color: default color register
> + * @chroma_key: chroma key register
> + * @chroma_key_mask: chroma key mask register
> + * @general_config: general layer config register
> + * @sacler_config: scaler factors register
> + * @phicoeffs: X/Y PHI coefficient registers
> + * @disc_pos: discard area position register
> + * @disc_size: discard area size register
> + * @csc: color space conversion register
>   */
> -struct atmel_hlcdc_dc_desc {
> -	int min_width;
> -	int min_height;
> +struct atmel_hlcdc_layer_cfg_layout {
> +	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
> +	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
> +	int pos;
> +	int size;
> +	int memsize;
> +	int default_color;
> +	int chroma_key;
> +	int chroma_key_mask;
> +	int general_config;
> +	int scaler_config;
> +	struct {
> +		int x;
> +		int y;
> +	} phicoeffs;
> +	int disc_pos;
> +	int disc_size;
> +	int csc;
> +};
> +
> +/**
> + * Atmel HLCDC DMA descriptor structure
> + *
> + * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
> + *
> + * The structure fields must remain in this specific order, because they're
> + * used by the HLCDC DMA engine, which expect them in this order.
> + * HLCDC DMA descriptors must be aligned on 64 bits.
> + *
> + * @addr: buffer DMA address
> + * @ctrl: DMA transfer options
> + * @next: next DMA descriptor to fetch
> + * @self: descriptor DMA address
> + */
> +struct atmel_hlcdc_dma_channel_dscr {
> +	dma_addr_t addr;
> +	u32 ctrl;
> +	dma_addr_t next;
> +	dma_addr_t self;
> +} __aligned(sizeof(u64));
> +
> +/**
> + * Atmel HLCDC layer types
> + */
> +enum atmel_hlcdc_layer_type {
> +	ATMEL_HLCDC_NO_LAYER,
> +	ATMEL_HLCDC_BASE_LAYER,
> +	ATMEL_HLCDC_OVERLAY_LAYER,
> +	ATMEL_HLCDC_CURSOR_LAYER,
> +	ATMEL_HLCDC_PP_LAYER,
> +};
> +
> +/**
> + * Atmel HLCDC Supported formats structure
> + *
> + * This structure list all the formats supported by a given layer.
> + *
> + * @nformats: number of supported formats
> + * @formats: supported formats
> + */
> +struct atmel_hlcdc_formats {
> +	int nformats;
> +	u32 *formats;
> +};
> +
> +/**
> + * Atmel HLCDC Layer description structure
> + *
> + * This structure describe the capabilities provided by a given layer.
> + *
> + * @name: layer name
> + * @type: layer type
> + * @id: layer id
> + * @regs_offset: offset of the layer registers from the HLCDC registers base
> + * @cfgs_offset: CFGX registers offset from the layer registers base
> + * @formats: supported formats
> + * @layout: config registers layout
> + * @max_width: maximum width supported by this layer (0 means unlimited)
> + * @max_height: maximum height supported by this layer (0 means unlimited)
> + */
> +struct atmel_hlcdc_layer_desc {
> +	const char *name;
> +	enum atmel_hlcdc_layer_type type;
> +	int id;
> +	int regs_offset;
> +	int cfgs_offset;
> +	struct atmel_hlcdc_formats *formats;
> +	struct atmel_hlcdc_layer_cfg_layout layout;
>  	int max_width;
>  	int max_height;
> -	int max_spw;
> -	int max_vpw;
> -	int max_hpw;
> -	bool conflicting_output_formats;
> -	const struct atmel_hlcdc_layer_desc *layers;
> -	int nlayers;
>  };
>  
>  /**
> - * Atmel HLCDC Plane properties.
> + * Atmel HLCDC Layer.
>   *
> - * This structure stores plane property definitions.
> + * A layer can be a DRM plane of a post processing layer used to render
> + * HLCDC composition into memory.
>   *
> - * @alpha: alpha blending (or transparency) property
> - * @rotation: rotation property
> + * @type: layer type
> + * @plane: pointer to the DRM plane exposed by this layer
>   */
> -struct atmel_hlcdc_plane_properties {
> -	struct drm_property *alpha;
> +struct atmel_hlcdc_layer {
> +	const struct atmel_hlcdc_layer_desc *desc;
> +	struct regmap *regmap;
>  };
>  
>  /**
> @@ -89,12 +285,13 @@ struct atmel_hlcdc_plane_properties {
>   * @base: base DRM plane structure
>   * @layer: HLCDC layer structure
>   * @properties: pointer to the property definitions structure
> - * @rotation: current rotation status
> + * @regmap: HLCDC regmap
>   */
>  struct atmel_hlcdc_plane {
>  	struct drm_plane base;
>  	struct atmel_hlcdc_layer layer;
>  	struct atmel_hlcdc_plane_properties *properties;
> +	struct regmap *regmap;
>  };
>  
>  static inline struct atmel_hlcdc_plane *
> @@ -104,27 +301,52 @@ drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
>  }
>  
>  static inline struct atmel_hlcdc_plane *
> -atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
> +atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *layer)
>  {
> -	return container_of(l, struct atmel_hlcdc_plane, layer);
> +	return container_of(layer, struct atmel_hlcdc_plane, layer);
>  }
>  
>  /**
> - * Atmel HLCDC Planes.
> + * Atmel HLCDC Display Controller description structure.
>   *
> - * This structure stores the instantiated HLCDC Planes and can be accessed by
> - * the HLCDC Display Controller or the HLCDC CRTC.
> + * This structure describe the HLCDC IP capabilities and depends on the
> + * HLCDC IP version (or Atmel SoC family).
>   *
> - * @primary: primary plane
> - * @cursor: hardware cursor plane
> - * @overlays: overlay plane table
> - * @noverlays: number of overlay planes
> + * @min_width: minimum width supported by the Display Controller
> + * @min_height: minimum height supported by the Display Controller
> + * @max_width: maximum width supported by the Display Controller
> + * @max_height: maximum height supported by the Display Controller
> + * @max_spw: maximum vertical/horizontal pulse width
> + * @max_vpw: maximum vertical back/front porch width
> + * @max_hpw: maximum horizontal back/front porch width
> + * @conflicting_output_formats: true if RGBXXX output formats conflict with
> + *				each other.
> + * @layers: a layer description table describing available layers
> + * @nlayers: layer description table size
>   */
> -struct atmel_hlcdc_planes {
> -	struct atmel_hlcdc_plane *primary;
> -	struct atmel_hlcdc_plane *cursor;
> -	struct atmel_hlcdc_plane **overlays;
> -	int noverlays;
> +struct atmel_hlcdc_dc_desc {
> +	int min_width;
> +	int min_height;
> +	int max_width;
> +	int max_height;
> +	int max_spw;
> +	int max_vpw;
> +	int max_hpw;
> +	bool conflicting_output_formats;
> +	const struct atmel_hlcdc_layer_desc *layers;
> +	int nlayers;
> +};
> +
> +/**
> + * Atmel HLCDC Plane properties.
> + *
> + * This structure stores plane property definitions.
> + *
> + * @alpha: alpha blending (or transparency) property
> + * @rotation: rotation property
> + */
> +struct atmel_hlcdc_plane_properties {
> +	struct drm_property *alpha;
>  };
>  
>  /**
> @@ -135,18 +357,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;
> @@ -156,11 +378,51 @@ struct atmel_hlcdc_dc {
>  extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
>  extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
>  
> +static inline void atmel_hlcdc_layer_write_reg(struct atmel_hlcdc_layer *layer,
> +					       unsigned int reg, u32 val)
> +{
> +	regmap_write(layer->regmap, layer->desc->regs_offset + reg, val);
> +}
> +
> +static inline u32 atmel_hlcdc_layer_read_reg(struct atmel_hlcdc_layer *layer,
> +					     unsigned int reg)
> +{
> +	u32 val;
> +
> +	regmap_read(layer->regmap, layer->desc->regs_offset + reg, &val);
> +
> +	return val;
> +}
> +
> +static inline void atmel_hlcdc_layer_write_cfg(struct atmel_hlcdc_layer *layer,
> +					       unsigned int cfgid, u32 val)
> +{
> +	atmel_hlcdc_layer_write_reg(layer,
> +				    layer->desc->cfgs_offset +
> +				    (cfgid * sizeof(u32)), val);
> +}
> +
> +static inline u32 atmel_hlcdc_layer_read_cfg(struct atmel_hlcdc_layer *layer,
> +					     unsigned int cfgid)
> +{
> +	return atmel_hlcdc_layer_read_reg(layer,
> +					  layer->desc->cfgs_offset +
> +					  (cfgid * sizeof(u32)));
> +}
> +
> +static inline void atmel_hlcdc_layer_init(struct atmel_hlcdc_layer *layer,
> +				const struct atmel_hlcdc_layer_desc *desc,
> +				struct regmap *regmap)
> +{
> +	layer->desc = desc;
> +	layer->regmap = regmap;
> +}
> +
>  int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
>  			      struct drm_display_mode *mode);
>  
> -struct atmel_hlcdc_planes *
> -atmel_hlcdc_create_planes(struct drm_device *dev);
> +int atmel_hlcdc_create_planes(struct drm_device *dev);
> +void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane);
>  
>  int atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state);
>  int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state);
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
> deleted file mode 100644
> 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
> deleted file mode 100644
> index 9beabc940bce..000000000000
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> +++ /dev/null
> @@ -1,399 +0,0 @@
> -/*
> - * Copyright (C) 2014 Free Electrons
> - * Copyright (C) 2014 Atmel
> - *
> - * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
> - *
> - * This program is free software; you can redistribute it and/or modify it
> - * under the terms of the GNU General Public License version 2 as published by
> - * the Free Software Foundation.
> - *
> - * This program is distributed in the hope that it will be useful, but WITHOUT
> - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> - * more details.
> - *
> - * You should have received a copy of the GNU General Public License along with
> - * this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -#ifndef DRM_ATMEL_HLCDC_LAYER_H
> -#define DRM_ATMEL_HLCDC_LAYER_H
> -
> -#include <linux/mfd/atmel-hlcdc.h>
> -
> -#include <drm/drm_crtc.h>
> -#include <drm/drm_flip_work.h>
> -#include <drm/drmP.h>
> -
> -#define ATMEL_HLCDC_LAYER_CHER			0x0
> -#define ATMEL_HLCDC_LAYER_CHDR			0x4
> -#define ATMEL_HLCDC_LAYER_CHSR			0x8
> -#define ATMEL_HLCDC_LAYER_DMA_CHAN		BIT(0)
> -#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
> -#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
> -#define ATMEL_HLCDC_LAYER_RST			BIT(8)
> -
> -#define ATMEL_HLCDC_LAYER_IER			0xc
> -#define ATMEL_HLCDC_LAYER_IDR			0x10
> -#define ATMEL_HLCDC_LAYER_IMR			0x14
> -#define ATMEL_HLCDC_LAYER_ISR			0x18
> -#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
> -#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
> -#define ATMEL_HLCDC_LAYER_DMA_IRQ		BIT(2)
> -#define ATMEL_HLCDC_LAYER_DSCR_IRQ		BIT(3)
> -#define ATMEL_HLCDC_LAYER_ADD_IRQ		BIT(4)
> -#define ATMEL_HLCDC_LAYER_DONE_IRQ		BIT(5)
> -#define ATMEL_HLCDC_LAYER_OVR_IRQ		BIT(6)
> -
> -#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)		(((n) * 0x10) + 0x1c)
> -#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)		(((n) * 0x10) + 0x20)
> -#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)		(((n) * 0x10) + 0x24)
> -#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)		(((n) * 0x10) + 0x28)
> -#define ATMEL_HLCDC_LAYER_CFG(p, c)		(((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
> -
> -#define ATMEL_HLCDC_LAYER_DMA_CFG_ID		0
> -#define ATMEL_HLCDC_LAYER_DMA_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
> -#define ATMEL_HLCDC_LAYER_DMA_SIF		BIT(0)
> -#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK		GENMASK(5, 4)
> -#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE	(0 << 4)
> -#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4	(1 << 4)
> -#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8	(2 << 4)
> -#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16	(3 << 4)
> -#define ATMEL_HLCDC_LAYER_DMA_DLBO		BIT(8)
> -#define ATMEL_HLCDC_LAYER_DMA_ROTDIS		BIT(12)
> -#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS		BIT(13)
> -
> -#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID		1
> -#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
> -#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
> -#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
> -#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
> -#define ATMEL_HLCDC_RGB_MODE(m)			(((m) & 0xf) << 4)
> -#define ATMEL_HLCDC_CLUT_MODE(m)		(((m) & 0x3) << 8)
> -#define ATMEL_HLCDC_YUV_MODE(m)			(((m) & 0xf) << 12)
> -#define ATMEL_HLCDC_YUV422ROT			BIT(16)
> -#define ATMEL_HLCDC_YUV422SWP			BIT(17)
> -#define ATMEL_HLCDC_DSCALEOPT			BIT(20)
> -
> -#define ATMEL_HLCDC_XRGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
> -#define ATMEL_HLCDC_ARGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
> -#define ATMEL_HLCDC_RGBA4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
> -#define ATMEL_HLCDC_RGB565_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
> -#define ATMEL_HLCDC_ARGB1555_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
> -#define ATMEL_HLCDC_XRGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
> -#define ATMEL_HLCDC_RGB888_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
> -#define ATMEL_HLCDC_ARGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
> -#define ATMEL_HLCDC_RGBA8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
> -
> -#define ATMEL_HLCDC_AYUV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
> -#define ATMEL_HLCDC_YUYV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
> -#define ATMEL_HLCDC_UYVY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
> -#define ATMEL_HLCDC_YVYU_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
> -#define ATMEL_HLCDC_VYUY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
> -#define ATMEL_HLCDC_NV61_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
> -#define ATMEL_HLCDC_YUV422_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
> -#define ATMEL_HLCDC_NV21_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
> -#define ATMEL_HLCDC_YUV420_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
> -
> -#define ATMEL_HLCDC_LAYER_POS_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
> -#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
> -#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
> -#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
> -#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
> -#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
> -#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
> -#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
> -
> -#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
> -#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
> -#define ATMEL_HLCDC_LAYER_INV			BIT(1)
> -#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
> -#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
> -#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
> -#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
> -#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
> -#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
> -#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
> -#define ATMEL_HLCDC_LAYER_REP			BIT(9)
> -#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
> -#define ATMEL_HLCDC_LAYER_DISCEN		BIT(11)
> -#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
> -#define ATMEL_HLCDC_LAYER_GA_MASK		GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
> -#define ATMEL_HLCDC_LAYER_GA(x)			((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
> -
> -#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
> -
> -#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
> -
> -#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
> -
> -#define ATMEL_HLCDC_MAX_PLANES			3
> -
> -#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
> -#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
> -#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
> -#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN	BIT(3)
> -
> -/**
> - * Atmel HLCDC Layer registers layout structure
> - *
> - * Each HLCDC layer has its own register organization and a given register
> - * can be placed differently on 2 different layers depending on its
> - * capabilities.
> - * This structure stores common registers layout for a given layer and is
> - * used by HLCDC layer code to choose the appropriate register to write to
> - * or to read from.
> - *
> - * For all fields, a value of zero means "unsupported".
> - *
> - * See Atmel's datasheet for a detailled description of these registers.
> - *
> - * @xstride: xstride registers
> - * @pstride: pstride registers
> - * @pos: position register
> - * @size: displayed size register
> - * @memsize: memory size register
> - * @default_color: default color register
> - * @chroma_key: chroma key register
> - * @chroma_key_mask: chroma key mask register
> - * @general_config: general layer config register
> - * @disc_pos: discard area position register
> - * @disc_size: discard area size register
> - * @csc: color space conversion register
> - */
> -struct atmel_hlcdc_layer_cfg_layout {
> -	int xstride[ATMEL_HLCDC_MAX_PLANES];
> -	int pstride[ATMEL_HLCDC_MAX_PLANES];
> -	int pos;
> -	int size;
> -	int memsize;
> -	int default_color;
> -	int chroma_key;
> -	int chroma_key_mask;
> -	int general_config;
> -	int disc_pos;
> -	int disc_size;
> -	int csc;
> -};
> -
> -/**
> - * Atmel HLCDC framebuffer flip structure
> - *
> - * This structure is allocated when someone asked for a layer update (most
> - * likely a DRM plane update, either primary, overlay or cursor plane) and
> - * released when the layer do not need to reference the framebuffer object
> - * anymore (i.e. the layer was disabled or updated).
> - *
> - * @dscrs: DMA descriptors
> - * @fb: the referenced framebuffer object
> - * @ngems: number of GEM objects referenced by the fb element
> - * @status: fb flip operation status
> - */
> -struct atmel_hlcdc_layer_fb_flip {
> -	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
> -	struct drm_flip_task *task;
> -	struct drm_framebuffer *fb;
> -	int ngems;
> -	u32 status;
> -};
> -
> -/**
> - * Atmel HLCDC DMA descriptor structure
> - *
> - * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
> - *
> - * The structure fields must remain in this specific order, because they're
> - * used by the HLCDC DMA engine, which expect them in this order.
> - * HLCDC DMA descriptors must be aligned on 64 bits.
> - *
> - * @addr: buffer DMA address
> - * @ctrl: DMA transfer options
> - * @next: next DMA descriptor to fetch
> - * @gem_flip: the attached gem_flip operation
> - */
> -struct atmel_hlcdc_dma_channel_dscr {
> -	dma_addr_t addr;
> -	u32 ctrl;
> -	dma_addr_t next;
> -	u32 status;
> -} __aligned(sizeof(u64));
> -
> -/**
> - * Atmel HLCDC layer types
> - */
> -enum atmel_hlcdc_layer_type {
> -	ATMEL_HLCDC_BASE_LAYER,
> -	ATMEL_HLCDC_OVERLAY_LAYER,
> -	ATMEL_HLCDC_CURSOR_LAYER,
> -	ATMEL_HLCDC_PP_LAYER,
> -};
> -
> -/**
> - * Atmel HLCDC Supported formats structure
> - *
> - * This structure list all the formats supported by a given layer.
> - *
> - * @nformats: number of supported formats
> - * @formats: supported formats
> - */
> -struct atmel_hlcdc_formats {
> -	int nformats;
> -	uint32_t *formats;
> -};
> -
> -/**
> - * Atmel HLCDC Layer description structure
> - *
> - * This structure describe the capabilities provided by a given layer.
> - *
> - * @name: layer name
> - * @type: layer type
> - * @id: layer id
> - * @regs_offset: offset of the layer registers from the HLCDC registers base
> - * @nconfigs: number of config registers provided by this layer
> - * @formats: supported formats
> - * @layout: config registers layout
> - * @max_width: maximum width supported by this layer (0 means unlimited)
> - * @max_height: maximum height supported by this layer (0 means unlimited)
> - */
> -struct atmel_hlcdc_layer_desc {
> -	const char *name;
> -	enum atmel_hlcdc_layer_type type;
> -	int id;
> -	int regs_offset;
> -	int nconfigs;
> -	struct atmel_hlcdc_formats *formats;
> -	struct atmel_hlcdc_layer_cfg_layout layout;
> -	int max_width;
> -	int max_height;
> -};
> -
> -/**
> - * Atmel HLCDC Layer Update Slot structure
> - *
> - * This structure stores layer update requests to be applied on next frame.
> - * This is the base structure behind the atomic layer update infrastructure.
> - *
> - * Atomic layer update provides a way to update all layer's parameters
> - * simultaneously. This is needed to avoid incompatible sequential updates
> - * like this one:
> - * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
> - *    (2 planes/buffers)
> - * 2) the format update is applied but the DMA channel for the second
> - *    plane/buffer is not enabled
> - * 3) enable the DMA channel for the second plane
> - *
> - * @fb_flip: fb_flip object
> - * @updated_configs: bitmask used to record modified configs
> - * @configs: new config values
> - */
> -struct atmel_hlcdc_layer_update_slot {
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	unsigned long *updated_configs;
> -	u32 *configs;
> -};
> -
> -/**
> - * Atmel HLCDC Layer Update structure
> - *
> - * This structure provides a way to queue layer update requests.
> - *
> - * At a given time there is at most:
> - *  - one pending update request, which means the update request has been
> - *    committed (or validated) and is waiting for the DMA channel(s) to be
> - *    available
> - *  - one request being prepared, which means someone started a layer update
> - *    but has not committed it yet. There cannot be more than one started
> - *    request, because the update lock is taken when starting a layer update
> - *    and release when committing or rolling back the request.
> - *
> - * @slots: update slots. One is used for pending request and the other one
> - *	   for started update request
> - * @pending: the pending slot index or -1 if no request is pending
> - * @next: the started update slot index or -1 no update has been started
> - */
> -struct atmel_hlcdc_layer_update {
> -	struct atmel_hlcdc_layer_update_slot slots[2];
> -	int pending;
> -	int next;
> -};
> -
> -enum atmel_hlcdc_layer_dma_channel_status {
> -	ATMEL_HLCDC_LAYER_DISABLED,
> -	ATMEL_HLCDC_LAYER_ENABLED,
> -	ATMEL_HLCDC_LAYER_DISABLING,
> -};
> -
> -/**
> - * Atmel HLCDC Layer DMA channel structure
> - *
> - * This structure stores information on the DMA channel associated to a
> - * given layer.
> - *
> - * @status: DMA channel status
> - * @cur: current framebuffer
> - * @queue: next framebuffer
> - * @dscrs: allocated DMA descriptors
> - */
> -struct atmel_hlcdc_layer_dma_channel {
> -	enum atmel_hlcdc_layer_dma_channel_status status;
> -	struct atmel_hlcdc_layer_fb_flip *cur;
> -	struct atmel_hlcdc_layer_fb_flip *queue;
> -	struct atmel_hlcdc_dma_channel_dscr *dscrs;
> -};
> -
> -/**
> - * Atmel HLCDC Layer structure
> - *
> - * This structure stores information on the layer instance.
> - *
> - * @desc: layer description
> - * @max_planes: maximum planes/buffers that can be associated with this layer.
> - *	       This depends on the supported formats.
> - * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
> - * @dma: dma channel
> - * @gc: fb flip garbage collector
> - * @update: update handler
> - * @lock: layer lock
> - */
> -struct atmel_hlcdc_layer {
> -	const struct atmel_hlcdc_layer_desc *desc;
> -	int max_planes;
> -	struct atmel_hlcdc *hlcdc;
> -	struct workqueue_struct *wq;
> -	struct drm_flip_work gc;
> -	struct atmel_hlcdc_layer_dma_channel dma;
> -	struct atmel_hlcdc_layer_update update;
> -	spinlock_t lock;
> -};
> -
> -void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
> -
> -int atmel_hlcdc_layer_init(struct drm_device *dev,
> -			   struct atmel_hlcdc_layer *layer,
> -			   const struct atmel_hlcdc_layer_desc *desc);
> -
> -void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
> -			       struct atmel_hlcdc_layer *layer);
> -
> -void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
> -
> -int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
> -
> -void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
> -				  u32 mask, u32 val);
> -
> -void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
> -				     struct drm_framebuffer *fb,
> -				     unsigned int *offsets);
> -
> -void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
> -					   void (*finished)(void *data),
> -					   void *finished_data);
> -
> -void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
> -
> -void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
> -
> -#endif /* DRM_ATMEL_HLCDC_LAYER_H */
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 246ed1e33d8a..cb6b2d5ae50b 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;
> @@ -52,8 +51,6 @@ struct atmel_hlcdc_plane_state {
>  
>  	u8 alpha;
>  
> -	bool disc_updated;
> -
>  	int disc_x;
>  	int disc_y;
>  	int disc_w;
> @@ -62,12 +59,14 @@ struct atmel_hlcdc_plane_state {
>  	int ahb_id;
>  
>  	/* These fields are private and should not be touched */
> -	int bpp[ATMEL_HLCDC_MAX_PLANES];
> -	unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
> -	int xstride[ATMEL_HLCDC_MAX_PLANES];
> -	int pstride[ATMEL_HLCDC_MAX_PLANES];
> +	int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
> +	unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
> +	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
> +	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
>  	int nplanes;
> -	bool prepared;
> +
> +	/* DMA descriptors. */
> +	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
>  };
>  
>  static inline struct atmel_hlcdc_plane_state *
> @@ -259,125 +258,146 @@ static u32 heo_upscaling_ycoef[] = {
>  	0x00205907,
>  };
>  
> +#define ATMEL_HLCDC_XPHIDEF	4
> +#define ATMEL_HLCDC_YPHIDEF	4
> +
> +static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
> +						  u32 dstsize,
> +						  u32 phidef)
> +{
> +	u32 factor, max_memsize;
> +
> +	factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
> +	max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
> +
> +	if (max_memsize > srcsize - 1)
> +		factor--;
> +
> +	return factor;
> +}
> +
>  static void
> -atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
> -				      struct atmel_hlcdc_plane_state *state)
> +atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
> +				      const u32 *coeff_tab, int size,
> +				      unsigned int cfg_offs)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> -
> -	if (layout->size)
> -		atmel_hlcdc_layer_update_cfg(&plane->layer,
> -					     layout->size,
> -					     0xffffffff,
> -					     (state->crtc_w - 1) |
> -					     ((state->crtc_h - 1) << 16));
> -
> -	if (layout->memsize)
> -		atmel_hlcdc_layer_update_cfg(&plane->layer,
> -					     layout->memsize,
> -					     0xffffffff,
> -					     (state->src_w - 1) |
> -					     ((state->src_h - 1) << 16));
> -
> -	if (layout->pos)
> -		atmel_hlcdc_layer_update_cfg(&plane->layer,
> -					     layout->pos,
> -					     0xffffffff,
> -					     state->crtc_x |
> -					     (state->crtc_y  << 16));
> -
> -	/* TODO: rework the rescaling part */
> -	if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) {
> -		u32 factor_reg = 0;
> -
> -		if (state->crtc_w != state->src_w) {
> -			int i;
> -			u32 factor;
> -			u32 *coeff_tab = heo_upscaling_xcoef;
> -			u32 max_memsize;
> -
> -			if (state->crtc_w < state->src_w)
> -				coeff_tab = heo_downscaling_xcoef;
> -			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
> -				atmel_hlcdc_layer_update_cfg(&plane->layer,
> -							     17 + i,
> -							     0xffffffff,
> -							     coeff_tab[i]);
> -			factor = ((8 * 256 * state->src_w) - (256 * 4)) /
> -				 state->crtc_w;
> -			factor++;
> -			max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
> -				      2048;
> -			if (max_memsize > state->src_w)
> -				factor--;
> -			factor_reg |= factor | 0x80000000;
> -		}
> +	int i;
>  
> -		if (state->crtc_h != state->src_h) {
> -			int i;
> -			u32 factor;
> -			u32 *coeff_tab = heo_upscaling_ycoef;
> -			u32 max_memsize;
> -
> -			if (state->crtc_h < state->src_h)
> -				coeff_tab = heo_downscaling_ycoef;
> -			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
> -				atmel_hlcdc_layer_update_cfg(&plane->layer,
> -							     33 + i,
> -							     0xffffffff,
> -							     coeff_tab[i]);
> -			factor = ((8 * 256 * state->src_h) - (256 * 4)) /
> -				 state->crtc_h;
> -			factor++;
> -			max_memsize = ((factor * state->crtc_h) + (256 * 4)) /
> -				      2048;
> -			if (max_memsize > state->src_h)
> -				factor--;
> -			factor_reg |= (factor << 16) | 0x80000000;
> -		}
> +	for (i = 0; i < size; i++)
> +		atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
> +					    coeff_tab[i]);
> +}
> +
> +void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
> +				    struct atmel_hlcdc_plane_state *state)
> +{
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> +	u32 xfactor, yfactor;
> +
> +	if (!desc->layout.scaler_config)
> +		return;
>  
> -		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
> -					     factor_reg);
> +	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
> +		atmel_hlcdc_layer_write_cfg(&plane->layer,
> +					    desc->layout.scaler_config, 0);
> +		return;
> +	}
> +
> +	if (desc->layout.phicoeffs.x) {
> +		xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
> +							state->crtc_w,
> +							ATMEL_HLCDC_XPHIDEF);
> +
> +		yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
> +							state->crtc_h,
> +							ATMEL_HLCDC_YPHIDEF);
> +
> +		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
> +				state->crtc_w < state->src_w ?
> +				heo_downscaling_xcoef :
> +				heo_upscaling_xcoef,
> +				ARRAY_SIZE(heo_upscaling_xcoef),
> +				desc->layout.phicoeffs.x);
> +
> +		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
> +				state->crtc_h < state->src_h ?
> +				heo_downscaling_ycoef :
> +				heo_upscaling_ycoef,
> +				ARRAY_SIZE(heo_upscaling_ycoef),
> +				desc->layout.phicoeffs.y);
>  	} else {
> -		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 0);
> +		xfactor = (1024 * state->src_w) / state->crtc_w;
> +		yfactor = (1024 * state->src_h) / state->crtc_h;
>  	}
> +
> +	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
> +				    ATMEL_HLCDC_LAYER_SCALER_ENABLE |
> +				    ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
> +								     yfactor));
> +}
> +
> +static void
> +atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
> +				      struct atmel_hlcdc_plane_state *state)
> +{
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> +
> +	if (desc->layout.size)
> +		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
> +					ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
> +							       state->crtc_h));
> +
> +	if (desc->layout.memsize)
> +		atmel_hlcdc_layer_write_cfg(&plane->layer,
> +					desc->layout.memsize,
> +					ATMEL_HLCDC_LAYER_SIZE(state->src_w,
> +							       state->src_h));
> +
> +	if (desc->layout.pos)
> +		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
> +					ATMEL_HLCDC_LAYER_POS(state->crtc_x,
> +							      state->crtc_y));
> +
> +	atmel_hlcdc_plane_setup_scaler(plane, state);
>  }
>  
>  static void
>  atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
>  					struct atmel_hlcdc_plane_state *state)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> -	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
> +	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> +
> +	/*
> +	 * Rotation optimization is not working on RGB888 (rotation is still
> +	 * working but without any optimization).
> +	 */
> +	if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
> +		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
> +
> +	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
> +				    cfg);
> +
> +	cfg = ATMEL_HLCDC_LAYER_DMA;
>  
>  	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
> +		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);
> +	if (state->disc_h * state->disc_w)
> +		cfg |= ATMEL_HLCDC_LAYER_DISCEN;
> +
> +	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
> +				    cfg);
>  }
>  
>  static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
> @@ -396,50 +416,51 @@ 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);
> +	atmel_hlcdc_layer_write_cfg(&plane->layer,
> +				    ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
>  
> -	/*
> -	 * Rotation optimization is not working on RGB888 (rotation is still
> -	 * working but without any optimization).
> -	 */
> -	if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
> -		cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
> -	else
> -		cfg = 0;
> -
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
> -				     ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
>  }
>  
>  static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
>  					struct atmel_hlcdc_plane_state *state)
>  {
> -	struct atmel_hlcdc_layer *layer = &plane->layer;
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -							&layer->desc->layout;
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> +	struct drm_framebuffer *fb = state->base.fb;
> +	u32 sr;
>  	int i;
>  
> -	atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
> -					state->offsets);
> +	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
>  
>  	for (i = 0; i < state->nplanes; i++) {
> -		if (layout->xstride[i]) {
> -			atmel_hlcdc_layer_update_cfg(&plane->layer,
> -						layout->xstride[i],
> -						0xffffffff,
> -						state->xstride[i]);
> +		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
> +
> +		state->dscrs[i]->addr = gem->paddr + state->offsets[i];
> +
> +		atmel_hlcdc_layer_write_reg(&plane->layer,
> +					    ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
> +					    state->dscrs[i]->self);
> +
> +		if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
> +			atmel_hlcdc_layer_write_reg(&plane->layer,
> +					ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
> +					state->dscrs[i]->addr);
> +			atmel_hlcdc_layer_write_reg(&plane->layer,
> +					ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
> +					state->dscrs[i]->ctrl);
> +			atmel_hlcdc_layer_write_reg(&plane->layer,
> +					ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
> +					state->dscrs[i]->self);
>  		}
>  
> -		if (layout->pstride[i]) {
> -			atmel_hlcdc_layer_update_cfg(&plane->layer,
> -						layout->pstride[i],
> -						0xffffffff,
> -						state->pstride[i]);
> -		}
> +		if (desc->layout.xstride[i])
> +			atmel_hlcdc_layer_write_cfg(&plane->layer,
> +						    desc->layout.xstride[i],
> +						    state->xstride[i]);
> +
> +		if (desc->layout.pstride[i])
> +			atmel_hlcdc_layer_write_cfg(&plane->layer,
> +						    desc->layout.pstride[i],
> +						    state->pstride[i]);
>  	}
>  }
>  
> @@ -528,18 +549,10 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
>  		disc_w = ovl_state->crtc_w;
>  	}
>  
> -	if (disc_x == primary_state->disc_x &&
> -	    disc_y == primary_state->disc_y &&
> -	    disc_w == primary_state->disc_w &&
> -	    disc_h == primary_state->disc_h)
> -		return 0;
> -
> -
>  	primary_state->disc_x = disc_x;
>  	primary_state->disc_y = disc_y;
>  	primary_state->disc_w = disc_w;
>  	primary_state->disc_h = disc_h;
> -	primary_state->disc_updated = true;
>  
>  	return 0;
>  }
> @@ -548,32 +561,19 @@ static void
>  atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
>  				   struct atmel_hlcdc_plane_state *state)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> -	int disc_surface = 0;
> -
> -	if (!state->disc_updated)
> -		return;
> -
> -	disc_surface = state->disc_h * state->disc_w;
> -
> -	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
> -				ATMEL_HLCDC_LAYER_DISCEN,
> -				disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
> +	const struct atmel_hlcdc_layer_cfg_layout *layout;
>  
> -	if (!disc_surface)
> +	layout = &plane->layer.desc->layout;
> +	if (!layout->disc_pos || !layout->disc_size)
>  		return;
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     layout->disc_pos,
> -				     0xffffffff,
> -				     state->disc_x | (state->disc_y << 16));
> +	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
> +				ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
> +							   state->disc_y));
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     layout->disc_size,
> -				     0xffffffff,
> -				     (state->disc_w - 1) |
> -				     ((state->disc_h - 1) << 16));
> +	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
> +				ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
> +							    state->disc_h));
>  }
>  
>  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
> @@ -582,8 +582,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
>  	struct atmel_hlcdc_plane_state *state =
>  				drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
>  	struct drm_framebuffer *fb = state->base.fb;
>  	const struct drm_display_mode *mode;
>  	struct drm_crtc_state *crtc_state;
> @@ -622,7 +621,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	state->src_h >>= 16;
>  
>  	state->nplanes = drm_format_num_planes(fb->pixel_format);
> -	if (state->nplanes > ATMEL_HLCDC_MAX_PLANES)
> +	if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
>  		return -EINVAL;
>  
>  	/*
> @@ -726,21 +725,19 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	state->crtc_w = patched_crtc_w;
>  	state->crtc_h = patched_crtc_h;
>  
> -	if (!layout->size &&
> +	if (!desc->layout.size &&
>  	    (mode->hdisplay != state->crtc_w ||
>  	     mode->vdisplay != state->crtc_h))
>  		return -EINVAL;
>  
> -	if (plane->layer.desc->max_height &&
> -	    state->crtc_h > plane->layer.desc->max_height)
> +	if (desc->max_height && state->crtc_h > desc->max_height)
>  		return -EINVAL;
>  
> -	if (plane->layer.desc->max_width &&
> -	    state->crtc_w > plane->layer.desc->max_width)
> +	if (desc->max_width && state->crtc_w > desc->max_width)
>  		return -EINVAL;
>  
>  	if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
> -	    (!layout->memsize ||
> +	    (!desc->layout.memsize ||
>  	     atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)))
>  		return -EINVAL;
>  
> @@ -754,65 +751,13 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	return 0;
>  }
>  
> -static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
> -					struct drm_plane_state *new_state)
> -{
> -	/*
> -	 * FIXME: we should avoid this const -> non-const cast but it's
> -	 * currently the only solution we have to modify the ->prepared
> -	 * state and rollback the update request.
> -	 * Ideally, we should rework the code to attach all the resources
> -	 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
> -	 * but this require a complete rework of the atmel_hlcdc_layer
> -	 * code.
> -	 */
> -	struct drm_plane_state *s = (struct drm_plane_state *)new_state;
> -	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> -	struct atmel_hlcdc_plane_state *state =
> -			drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -	int ret;
> -
> -	ret = atmel_hlcdc_layer_update_start(&plane->layer);
> -	if (!ret)
> -		state->prepared = true;
> -
> -	return ret;
> -}
> -
> -static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
> -					 struct drm_plane_state *old_state)
> -{
> -	/*
> -	 * FIXME: we should avoid this const -> non-const cast but it's
> -	 * currently the only solution we have to modify the ->prepared
> -	 * state and rollback the update request.
> -	 * Ideally, we should rework the code to attach all the resources
> -	 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
> -	 * but this require a complete rework of the atmel_hlcdc_layer
> -	 * code.
> -	 */
> -	struct drm_plane_state *s = (struct drm_plane_state *)old_state;
> -	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> -	struct atmel_hlcdc_plane_state *state =
> -			drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -
> -	/*
> -	 * The Request has already been applied or cancelled, nothing to do
> -	 * here.
> -	 */
> -	if (!state->prepared)
> -		return;
> -
> -	atmel_hlcdc_layer_update_rollback(&plane->layer);
> -	state->prepared = false;
> -}
> -
>  static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
>  					    struct drm_plane_state *old_s)
>  {
>  	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
>  	struct atmel_hlcdc_plane_state *state =
>  			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
> +	u32 sr;
>  
>  	if (!p->state->crtc || !p->state->fb)
>  		return;
> @@ -823,7 +768,18 @@ static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
>  	atmel_hlcdc_plane_update_buffers(plane, state);
>  	atmel_hlcdc_plane_update_disc_area(plane, state);
>  
> -	atmel_hlcdc_layer_update_commit(&plane->layer);
> +	/* Enable the overrun interrupts. */
> +	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
> +				    ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
> +				    ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
> +				    ATMEL_HLCDC_LAYER_OVR_IRQ(2));
> +
> +	/* Apply the new config at the next SOF event. */
> +	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
> +	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
> +			ATMEL_HLCDC_LAYER_UPDATE |
> +			(sr & ATMEL_HLCDC_LAYER_EN ?
> +			 ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
>  }
>  
>  static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
> @@ -831,7 +787,18 @@ static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
>  {
>  	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
>  
> -	atmel_hlcdc_layer_disable(&plane->layer);
> +	/* Disable interrupts */
> +	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
> +				    0xffffffff);
> +
> +	/* Disable the layer */
> +	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
> +				    ATMEL_HLCDC_LAYER_RST |
> +				    ATMEL_HLCDC_LAYER_A2Q |
> +				    ATMEL_HLCDC_LAYER_UPDATE);
> +
> +	/* Clear all pending interrupts */
> +	atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
>  }
>  
>  static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
> @@ -841,10 +808,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,24 +848,15 @@ static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
>  }
>  
>  static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
> -					     const struct atmel_hlcdc_layer_desc *desc,
> -					     struct atmel_hlcdc_plane_properties *props)
> +				struct atmel_hlcdc_plane_properties *props)
>  {
> -	struct regmap *regmap = plane->layer.hlcdc->regmap;
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
>  
>  	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> -	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
> +	    desc->type == ATMEL_HLCDC_CURSOR_LAYER)
>  		drm_object_attach_property(&plane->base.base,
>  					   props->alpha, 255);
>  
> -		/* Set default alpha value */
> -		regmap_update_bits(regmap,
> -				desc->regs_offset +
> -				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
> -				ATMEL_HLCDC_LAYER_GA_MASK,
> -				ATMEL_HLCDC_LAYER_GA_MASK);
> -	}
> -
>  	if (desc->layout.xstride && desc->layout.pstride) {
>  		int ret;
>  
> @@ -920,31 +875,78 @@ static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
>  		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
>  		 * userspace modify these factors (using a BLOB property ?).
>  		 */
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
> -			     0x4c900091);
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
> -			     0x7a5f5090);
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
> -			     0x40040890);
> +		atmel_hlcdc_layer_write_cfg(&plane->layer,
> +					    desc->layout.csc,
> +					    0x4c900091);
> +		atmel_hlcdc_layer_write_cfg(&plane->layer,
> +					    desc->layout.csc + 1,
> +					    0x7a5f5090);
> +		atmel_hlcdc_layer_write_cfg(&plane->layer,
> +					    desc->layout.csc + 2,
> +					    0x40040890);
>  	}
>  
>  	return 0;
>  }
>  
> +void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
> +{
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> +	u32 isr;
> +
> +	isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
> +
> +	/*
> +	 * There's not much we can do in case of overrun except informing
> +	 * the user. However, we are in interrupt context here, hence the
> +	 * use of dev_dbg().
> +	 */
> +	if (isr &
> +	    (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
> +	     ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
> +		dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
> +			desc->name);
> +}
> +
>  static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
> -	.prepare_fb = atmel_hlcdc_plane_prepare_fb,
> -	.cleanup_fb = atmel_hlcdc_plane_cleanup_fb,
>  	.atomic_check = atmel_hlcdc_plane_atomic_check,
>  	.atomic_update = atmel_hlcdc_plane_atomic_update,
>  	.atomic_disable = atmel_hlcdc_plane_atomic_disable,
>  };
>  
> +static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
> +					 struct atmel_hlcdc_plane_state *state)
> +{
> +	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
> +		struct atmel_hlcdc_dma_channel_dscr *dscr;
> +		dma_addr_t dscr_dma;
> +
> +		dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
> +		if (!dscr)
> +			goto err;
> +
> +		dscr->addr = 0;
> +		dscr->next = dscr_dma;
> +		dscr->self = dscr_dma;
> +		dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
> +
> +		state->dscrs[i] = dscr;
> +	}
> +
> +	return 0;
> +
> +err:
> +	for (i--; i >= 0; i--) {
> +		dma_pool_free(dc->dscrpool, state->dscrs[i],
> +			      state->dscrs[i]->self);
> +	}
> +
> +	return -ENOMEM;
> +}
> +
>  static void atmel_hlcdc_plane_reset(struct drm_plane *p)
>  {
>  	struct atmel_hlcdc_plane_state *state;
> @@ -961,6 +963,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;
> @@ -978,8 +987,10 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
>  	if (!copy)
>  		return NULL;
>  
> -	copy->disc_updated = false;
> -	copy->prepared = false;
> +	if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
> +		kfree(copy);
> +		return NULL;
> +	}
>  
>  	if (copy->base.fb)
>  		drm_framebuffer_reference(copy->base.fb);
> @@ -987,11 +998,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 +1029,21 @@ static struct drm_plane_funcs layer_plane_funcs = {
>  	.atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
>  };
>  
> -static struct atmel_hlcdc_plane *
> -atmel_hlcdc_plane_create(struct drm_device *dev,
> -			 const struct atmel_hlcdc_layer_desc *desc,
> -			 struct atmel_hlcdc_plane_properties *props)
> +static int atmel_hlcdc_plane_create(struct drm_device *dev,
> +				    const struct atmel_hlcdc_layer_desc *desc,
> +				    struct atmel_hlcdc_plane_properties *props)
>  {
> +	struct atmel_hlcdc_dc *dc = dev->dev_private;
>  	struct atmel_hlcdc_plane *plane;
>  	enum drm_plane_type type;
>  	int ret;
>  
>  	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
>  	if (!plane)
> -		return ERR_PTR(-ENOMEM);
> +		return -ENOMEM;
>  
> -	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
> -	if (ret)
> -		return ERR_PTR(ret);
> +	atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
> +	plane->properties = props;
>  
>  	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
>  		type = DRM_PLANE_TYPE_PRIMARY;
> @@ -1040,17 +1057,19 @@ atmel_hlcdc_plane_create(struct drm_device *dev,
>  				       desc->formats->formats,
>  				       desc->formats->nformats, type, NULL);
>  	if (ret)
> -		return ERR_PTR(ret);
> +		return ret;
>  
>  	drm_plane_helper_add(&plane->base,
>  			     &atmel_hlcdc_layer_plane_helper_funcs);
>  
>  	/* Set default property values*/
> -	ret = atmel_hlcdc_plane_init_properties(plane, desc, props);
> +	ret = atmel_hlcdc_plane_init_properties(plane, props);
>  	if (ret)
> -		return ERR_PTR(ret);
> +		return ret;
> +
> +	dc->layers[desc->id] = &plane->layer;
>  
> -	return plane;
> +	return 0;
>  }
>  
>  static struct atmel_hlcdc_plane_properties *
> @@ -1069,72 +1088,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;
>  }
> -- 
> 2.7.4
>
Eric Anholt Feb. 27, 2017, 10:30 p.m. UTC | #2
Boris Brezillon <boris.brezillon@free-electrons.com> writes:

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

Deleting 20% of your driver?  Awesome.

This was tricky to review, though.  I wonder if the configuration layout
description restructuring (and some of the config update helpers) could
have been done separately from the rollback removal parts.  The merge of
atmel_hlcdc_layer.h into atmel_hlcdc_dc.h was also something that I only
reviewed pieces of by occasionally looking into a specific definition to
see if it had happened to change in the move.

I've gone through all the code, particularly with an eye to things like
copy/paste bugs, so I think with a couple of little comments below
addressed,

Reviewed-by: Eric Anholt <eric@anholt.net>

> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 246ed1e33d8a..cb6b2d5ae50b 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c

>  static void
>  atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
>  					struct atmel_hlcdc_plane_state *state)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> -	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
> +	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
> +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> +
> +	/*
> +	 * Rotation optimization is not working on RGB888 (rotation is still
> +	 * working but without any optimization).
> +	 */
> +	if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
> +		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
> +
> +	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
> +				    cfg);
> +
> +	cfg = ATMEL_HLCDC_LAYER_DMA;
>  
>  	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
> +		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);
>  	}

The format temp here seems gratuitous (unless you wanted to move it up
to the declarations at the top of the function and use it for ROTDIS).

>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
> -				     ATMEL_HLCDC_LAYER_DMA_BLEN_MASK |
> -				     ATMEL_HLCDC_LAYER_DMA_SIF,
> -				     ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 |
> -				     state->ahb_id);
> -
> -	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
> -				     ATMEL_HLCDC_LAYER_ITER2BL |
> -				     ATMEL_HLCDC_LAYER_ITER |
> -				     ATMEL_HLCDC_LAYER_GAEN |
> -				     ATMEL_HLCDC_LAYER_GA_MASK |
> -				     ATMEL_HLCDC_LAYER_LAEN |
> -				     ATMEL_HLCDC_LAYER_OVR |
> -				     ATMEL_HLCDC_LAYER_DMA, cfg);
> +	if (state->disc_h * state->disc_w)
> +		cfg |= ATMEL_HLCDC_LAYER_DISCEN;

This is a weird looking pattern for what I think is

if (state->disc_h && state->disc_w)
Boris BREZILLON Feb. 28, 2017, 8:08 a.m. UTC | #3
Hi Eric,

On Mon, 27 Feb 2017 14:30:13 -0800
Eric Anholt <eric@anholt.net> wrote:

> Boris Brezillon <boris.brezillon@free-electrons.com> writes:
> 
> > 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>  
> 
> Deleting 20% of your driver?  Awesome.
> 
> This was tricky to review, though.  I wonder if the configuration layout
> description restructuring (and some of the config update helpers) could
> have been done separately from the rollback removal parts.  The merge of
> atmel_hlcdc_layer.h into atmel_hlcdc_dc.h was also something that I only
> reviewed pieces of by occasionally looking into a specific definition to
> see if it had happened to change in the move.

Yes, I'm not happy with the diff either (it's really hard to review),
I'll try do these changes in several steps (as you suggest) and see if
it helps.

> 
> I've gone through all the code, particularly with an eye to things like
> copy/paste bugs, so I think with a couple of little comments below
> addressed,
> 
> Reviewed-by: Eric Anholt <eric@anholt.net>

Thanks a lot for your review.

> 
> > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> > index 246ed1e33d8a..cb6b2d5ae50b 100644
> > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c  
> 
> >  static void
> >  atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
> >  					struct atmel_hlcdc_plane_state *state)
> >  {
> > -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> > -						&plane->layer.desc->layout;
> > -	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
> > +	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
> > +	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
> > +
> > +	/*
> > +	 * Rotation optimization is not working on RGB888 (rotation is still
> > +	 * working but without any optimization).
> > +	 */
> > +	if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
> > +		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
> > +
> > +	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
> > +				    cfg);
> > +
> > +	cfg = ATMEL_HLCDC_LAYER_DMA;
> >  
> >  	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
> > +		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);
> >  	}  
> 
> The format temp here seems gratuitous (unless you wanted to move it up
> to the declarations at the top of the function and use it for ROTDIS).

Actually, I did that to avoid the "over 80 char" checkpatch warning,
but you're right, I should declare this variable at the top of the
function and use it for ROTDIS.

> 
> >  
> > -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> > -				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
> > -				     ATMEL_HLCDC_LAYER_DMA_BLEN_MASK |
> > -				     ATMEL_HLCDC_LAYER_DMA_SIF,
> > -				     ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 |
> > -				     state->ahb_id);
> > -
> > -	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
> > -				     ATMEL_HLCDC_LAYER_ITER2BL |
> > -				     ATMEL_HLCDC_LAYER_ITER |
> > -				     ATMEL_HLCDC_LAYER_GAEN |
> > -				     ATMEL_HLCDC_LAYER_GA_MASK |
> > -				     ATMEL_HLCDC_LAYER_LAEN |
> > -				     ATMEL_HLCDC_LAYER_OVR |
> > -				     ATMEL_HLCDC_LAYER_DMA, cfg);
> > +	if (state->disc_h * state->disc_w)
> > +		cfg |= ATMEL_HLCDC_LAYER_DISCEN;  
> 
> This is a weird looking pattern for what I think is
> 
> if (state->disc_h && state->disc_w)

Indeed, I'll fix that.

Thanks,

Boris
Boris BREZILLON March 16, 2017, 1:29 p.m. UTC | #4
On Fri, 10 Feb 2017 19:07:45 +0100
Boris Brezillon <boris.brezillon@free-electrons.com> 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.

Just forgot to mention that this patch has been applied to
drm-misc-next a few weeks ago.

> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
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..2fcec0a72567 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 atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
 	struct atmel_hlcdc_dc *dc = dev->dev_private;
-	struct atmel_hlcdc_planes *planes = dc->planes;
 	struct atmel_hlcdc_crtc *crtc;
 	int ret;
 	int i;
@@ -457,20 +457,41 @@  int atmel_hlcdc_crtc_create(struct drm_device *dev)
 
 	crtc->dc = dc;
 
-	ret = drm_crtc_init_with_planes(dev, &crtc->base,
-				&planes->primary->base,
-				planes->cursor ? &planes->cursor->base : NULL,
-				&atmel_hlcdc_crtc_funcs, NULL);
+	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+		if (!dc->layers[i])
+			continue;
+
+		switch (dc->layers[i]->desc->type) {
+		case ATMEL_HLCDC_BASE_LAYER:
+			primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
+			break;
+
+		case ATMEL_HLCDC_CURSOR_LAYER:
+			cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
+					&cursor->base, &atmel_hlcdc_crtc_funcs,
+					NULL);
 	if (ret < 0)
 		goto fail;
 
 	crtc->id = drm_crtc_index(&crtc->base);
 
-	if (planes->cursor)
-		planes->cursor->base.possible_crtcs = 1 << crtc->id;
+	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+		struct atmel_hlcdc_plane *overlay;
 
-	for (i = 0; i < planes->noverlays; i++)
-		planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
+		if (dc->layers[i] &&
+		    dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
+			overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
+			overlay->base.possible_crtcs = 1 << crtc->id;
+		}
+	}
 
 	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
 	drm_crtc_vblank_reset(&crtc->base);
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
index 0bf32d6ac39b..8eb1d7471c63 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,17 @@  int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
 	return MODE_OK;
 }
 
+static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
+{
+	if (!layer)
+		return;
+
+	if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER ||
+	    layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
+	    layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER)
+		atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer));
+}
+
 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
 {
 	struct drm_device *dev = data;
@@ -410,12 +433,8 @@  static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
 		atmel_hlcdc_crtc_irq(dc->crtc);
 
 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
-		struct atmel_hlcdc_layer *layer = dc->layers[i];
-
-		if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
-			continue;
-
-		atmel_hlcdc_layer_irq(layer);
+		if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
+			atmel_hlcdc_layer_irq(dc->layers[i]);
 	}
 
 	return IRQ_HANDLED;
@@ -537,9 +556,7 @@  static const struct drm_mode_config_funcs mode_config_funcs = {
 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
 {
 	struct atmel_hlcdc_dc *dc = dev->dev_private;
-	struct atmel_hlcdc_planes *planes;
 	int ret;
-	int i;
 
 	drm_mode_config_init(dev);
 
@@ -549,25 +566,12 @@  static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
 		return ret;
 	}
 
-	planes = atmel_hlcdc_create_planes(dev);
-	if (IS_ERR(planes)) {
-		dev_err(dev->dev, "failed to create planes\n");
-		return PTR_ERR(planes);
+	ret = atmel_hlcdc_create_planes(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create planes: %d\n", ret);
+		return ret;
 	}
 
-	dc->planes = planes;
-
-	dc->layers[planes->primary->layer.desc->id] =
-						&planes->primary->layer;
-
-	if (planes->cursor)
-		dc->layers[planes->cursor->layer.desc->id] =
-							&planes->cursor->layer;
-
-	for (i = 0; i < planes->noverlays; i++)
-		dc->layers[planes->overlays[i]->layer.desc->id] =
-						&planes->overlays[i]->layer;
-
 	ret = atmel_hlcdc_crtc_create(dev);
 	if (ret) {
 		dev_err(dev->dev, "failed to create crtc\n");
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
index 7a47f8c094d0..d84c9a56bbb5 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>
@@ -36,51 +38,245 @@ 
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 
-#include "atmel_hlcdc_layer.h"
+#define ATMEL_HLCDC_LAYER_CHER			0x0
+#define ATMEL_HLCDC_LAYER_CHDR			0x4
+#define ATMEL_HLCDC_LAYER_CHSR			0x8
+#define ATMEL_HLCDC_LAYER_EN			BIT(0)
+#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
+#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
+#define ATMEL_HLCDC_LAYER_RST			BIT(8)
 
-#define ATMEL_HLCDC_MAX_LAYERS		5
+#define ATMEL_HLCDC_LAYER_IER			0xc
+#define ATMEL_HLCDC_LAYER_IDR			0x10
+#define ATMEL_HLCDC_LAYER_IMR			0x14
+#define ATMEL_HLCDC_LAYER_ISR			0x18
+#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
+#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
+#define ATMEL_HLCDC_LAYER_DMA_IRQ(p)		BIT(2 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_DSCR_IRQ(p)		BIT(3 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_ADD_IRQ(p)		BIT(4 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_DONE_IRQ(p)		BIT(5 + (8 * (p)))
+#define ATMEL_HLCDC_LAYER_OVR_IRQ(p)		BIT(6 + (8 * (p)))
+
+#define ATMEL_HLCDC_LAYER_PLANE_HEAD(p)		(((p) * 0x10) + 0x1c)
+#define ATMEL_HLCDC_LAYER_PLANE_ADDR(p)		(((p) * 0x10) + 0x20)
+#define ATMEL_HLCDC_LAYER_PLANE_CTRL(p)		(((p) * 0x10) + 0x24)
+#define ATMEL_HLCDC_LAYER_PLANE_NEXT(p)		(((p) * 0x10) + 0x28)
+
+#define ATMEL_HLCDC_LAYER_DMA_CFG		0
+#define ATMEL_HLCDC_LAYER_DMA_SIF		BIT(0)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK		GENMASK(5, 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE	(0 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4	(1 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8	(2 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16	(3 << 4)
+#define ATMEL_HLCDC_LAYER_DMA_DLBO		BIT(8)
+#define ATMEL_HLCDC_LAYER_DMA_ROTDIS		BIT(12)
+#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS		BIT(13)
+
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG		1
+#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
+#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
+#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
+#define ATMEL_HLCDC_RGB_MODE(m)			\
+	(ATMEL_HLCDC_LAYER_RGB | (((m) & 0xf) << 4))
+#define ATMEL_HLCDC_CLUT_MODE(m)		\
+	(ATMEL_HLCDC_LAYER_CLUT | (((m) & 0x3) << 8))
+#define ATMEL_HLCDC_YUV_MODE(m)			\
+	(ATMEL_HLCDC_LAYER_YUV | (((m) & 0xf) << 12))
+#define ATMEL_HLCDC_YUV422ROT			BIT(16)
+#define ATMEL_HLCDC_YUV422SWP			BIT(17)
+#define ATMEL_HLCDC_DSCALEOPT			BIT(20)
+
+#define ATMEL_HLCDC_XRGB4444_MODE		ATMEL_HLCDC_RGB_MODE(0)
+#define ATMEL_HLCDC_ARGB4444_MODE		ATMEL_HLCDC_RGB_MODE(1)
+#define ATMEL_HLCDC_RGBA4444_MODE		ATMEL_HLCDC_RGB_MODE(2)
+#define ATMEL_HLCDC_RGB565_MODE			ATMEL_HLCDC_RGB_MODE(3)
+#define ATMEL_HLCDC_ARGB1555_MODE		ATMEL_HLCDC_RGB_MODE(4)
+#define ATMEL_HLCDC_XRGB8888_MODE		ATMEL_HLCDC_RGB_MODE(9)
+#define ATMEL_HLCDC_RGB888_MODE			ATMEL_HLCDC_RGB_MODE(10)
+#define ATMEL_HLCDC_ARGB8888_MODE		ATMEL_HLCDC_RGB_MODE(12)
+#define ATMEL_HLCDC_RGBA8888_MODE		ATMEL_HLCDC_RGB_MODE(13)
+
+#define ATMEL_HLCDC_AYUV_MODE			ATMEL_HLCDC_YUV_MODE(0)
+#define ATMEL_HLCDC_YUYV_MODE			ATMEL_HLCDC_YUV_MODE(1)
+#define ATMEL_HLCDC_UYVY_MODE			ATMEL_HLCDC_YUV_MODE(2)
+#define ATMEL_HLCDC_YVYU_MODE			ATMEL_HLCDC_YUV_MODE(3)
+#define ATMEL_HLCDC_VYUY_MODE			ATMEL_HLCDC_YUV_MODE(4)
+#define ATMEL_HLCDC_NV61_MODE			ATMEL_HLCDC_YUV_MODE(5)
+#define ATMEL_HLCDC_YUV422_MODE			ATMEL_HLCDC_YUV_MODE(6)
+#define ATMEL_HLCDC_NV21_MODE			ATMEL_HLCDC_YUV_MODE(7)
+#define ATMEL_HLCDC_YUV420_MODE			ATMEL_HLCDC_YUV_MODE(8)
+
+#define ATMEL_HLCDC_LAYER_POS(x, y)		((x) | ((y) << 16))
+#define ATMEL_HLCDC_LAYER_SIZE(w, h)		(((w) - 1) | (((h) - 1) << 16))
+
+#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
+#define ATMEL_HLCDC_LAYER_INV			BIT(1)
+#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
+#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
+#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
+#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
+#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
+#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
+#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
+#define ATMEL_HLCDC_LAYER_REP			BIT(9)
+#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
+#define ATMEL_HLCDC_LAYER_DISCEN		BIT(11)
+#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
+#define ATMEL_HLCDC_LAYER_GA_MASK		\
+	GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
+#define ATMEL_HLCDC_LAYER_GA(x)			\
+	((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
+
+#define ATMEL_HLCDC_LAYER_DISC_POS(x, y)	((x) | ((y) << 16))
+#define ATMEL_HLCDC_LAYER_DISC_SIZE(w, h)	(((w) - 1) | (((h) - 1) << 16))
+
+#define ATMEL_HLCDC_LAYER_SCALER_FACTORS(x, y)	((x) | ((y) << 16))
+#define ATMEL_HLCDC_LAYER_SCALER_ENABLE		BIT(31)
+
+#define ATMEL_HLCDC_LAYER_MAX_PLANES		3
+
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN	BIT(3)
+
+#define ATMEL_HLCDC_MAX_LAYERS			6
 
 /**
- * Atmel HLCDC Display Controller description structure.
+ * Atmel HLCDC Layer registers layout structure
  *
- * This structure describe the HLCDC IP capabilities and depends on the
- * HLCDC IP version (or Atmel SoC family).
+ * Each HLCDC layer has its own register organization and a given register
+ * can be placed differently on 2 different layers depending on its
+ * capabilities.
+ * This structure stores common registers layout for a given layer and is
+ * used by HLCDC layer code to choose the appropriate register to write to
+ * or to read from.
  *
- * @min_width: minimum width supported by the Display Controller
- * @min_height: minimum height supported by the Display Controller
- * @max_width: maximum width supported by the Display Controller
- * @max_height: maximum height supported by the Display Controller
- * @max_spw: maximum vertical/horizontal pulse width
- * @max_vpw: maximum vertical back/front porch width
- * @max_hpw: maximum horizontal back/front porch width
- * @conflicting_output_formats: true if RGBXXX output formats conflict with
- *				each other.
- * @layers: a layer description table describing available layers
- * @nlayers: layer description table size
+ * For all fields, a value of zero means "unsupported".
+ *
+ * See Atmel's datasheet for a detailled description of these registers.
+ *
+ * @xstride: xstride registers
+ * @pstride: pstride registers
+ * @pos: position register
+ * @size: displayed size register
+ * @memsize: memory size register
+ * @default_color: default color register
+ * @chroma_key: chroma key register
+ * @chroma_key_mask: chroma key mask register
+ * @general_config: general layer config register
+ * @sacler_config: scaler factors register
+ * @phicoeffs: X/Y PHI coefficient registers
+ * @disc_pos: discard area position register
+ * @disc_size: discard area size register
+ * @csc: color space conversion register
  */
-struct atmel_hlcdc_dc_desc {
-	int min_width;
-	int min_height;
+struct atmel_hlcdc_layer_cfg_layout {
+	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
+	int pos;
+	int size;
+	int memsize;
+	int default_color;
+	int chroma_key;
+	int chroma_key_mask;
+	int general_config;
+	int scaler_config;
+	struct {
+		int x;
+		int y;
+	} phicoeffs;
+	int disc_pos;
+	int disc_size;
+	int csc;
+};
+
+/**
+ * Atmel HLCDC DMA descriptor structure
+ *
+ * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
+ *
+ * The structure fields must remain in this specific order, because they're
+ * used by the HLCDC DMA engine, which expect them in this order.
+ * HLCDC DMA descriptors must be aligned on 64 bits.
+ *
+ * @addr: buffer DMA address
+ * @ctrl: DMA transfer options
+ * @next: next DMA descriptor to fetch
+ * @self: descriptor DMA address
+ */
+struct atmel_hlcdc_dma_channel_dscr {
+	dma_addr_t addr;
+	u32 ctrl;
+	dma_addr_t next;
+	dma_addr_t self;
+} __aligned(sizeof(u64));
+
+/**
+ * Atmel HLCDC layer types
+ */
+enum atmel_hlcdc_layer_type {
+	ATMEL_HLCDC_NO_LAYER,
+	ATMEL_HLCDC_BASE_LAYER,
+	ATMEL_HLCDC_OVERLAY_LAYER,
+	ATMEL_HLCDC_CURSOR_LAYER,
+	ATMEL_HLCDC_PP_LAYER,
+};
+
+/**
+ * Atmel HLCDC Supported formats structure
+ *
+ * This structure list all the formats supported by a given layer.
+ *
+ * @nformats: number of supported formats
+ * @formats: supported formats
+ */
+struct atmel_hlcdc_formats {
+	int nformats;
+	u32 *formats;
+};
+
+/**
+ * Atmel HLCDC Layer description structure
+ *
+ * This structure describe the capabilities provided by a given layer.
+ *
+ * @name: layer name
+ * @type: layer type
+ * @id: layer id
+ * @regs_offset: offset of the layer registers from the HLCDC registers base
+ * @cfgs_offset: CFGX registers offset from the layer registers base
+ * @formats: supported formats
+ * @layout: config registers layout
+ * @max_width: maximum width supported by this layer (0 means unlimited)
+ * @max_height: maximum height supported by this layer (0 means unlimited)
+ */
+struct atmel_hlcdc_layer_desc {
+	const char *name;
+	enum atmel_hlcdc_layer_type type;
+	int id;
+	int regs_offset;
+	int cfgs_offset;
+	struct atmel_hlcdc_formats *formats;
+	struct atmel_hlcdc_layer_cfg_layout layout;
 	int max_width;
 	int max_height;
-	int max_spw;
-	int max_vpw;
-	int max_hpw;
-	bool conflicting_output_formats;
-	const struct atmel_hlcdc_layer_desc *layers;
-	int nlayers;
 };
 
 /**
- * Atmel HLCDC Plane properties.
+ * Atmel HLCDC Layer.
  *
- * This structure stores plane property definitions.
+ * A layer can be a DRM plane of a post processing layer used to render
+ * HLCDC composition into memory.
  *
- * @alpha: alpha blending (or transparency) property
- * @rotation: rotation property
+ * @type: layer type
+ * @plane: pointer to the DRM plane exposed by this layer
  */
-struct atmel_hlcdc_plane_properties {
-	struct drm_property *alpha;
+struct atmel_hlcdc_layer {
+	const struct atmel_hlcdc_layer_desc *desc;
+	struct regmap *regmap;
 };
 
 /**
@@ -89,12 +285,13 @@  struct atmel_hlcdc_plane_properties {
  * @base: base DRM plane structure
  * @layer: HLCDC layer structure
  * @properties: pointer to the property definitions structure
- * @rotation: current rotation status
+ * @regmap: HLCDC regmap
  */
 struct atmel_hlcdc_plane {
 	struct drm_plane base;
 	struct atmel_hlcdc_layer layer;
 	struct atmel_hlcdc_plane_properties *properties;
+	struct regmap *regmap;
 };
 
 static inline struct atmel_hlcdc_plane *
@@ -104,27 +301,52 @@  drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
 }
 
 static inline struct atmel_hlcdc_plane *
-atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
+atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *layer)
 {
-	return container_of(l, struct atmel_hlcdc_plane, layer);
+	return container_of(layer, struct atmel_hlcdc_plane, layer);
 }
 
 /**
- * Atmel HLCDC Planes.
+ * Atmel HLCDC Display Controller description structure.
  *
- * This structure stores the instantiated HLCDC Planes and can be accessed by
- * the HLCDC Display Controller or the HLCDC CRTC.
+ * This structure describe the HLCDC IP capabilities and depends on the
+ * HLCDC IP version (or Atmel SoC family).
  *
- * @primary: primary plane
- * @cursor: hardware cursor plane
- * @overlays: overlay plane table
- * @noverlays: number of overlay planes
+ * @min_width: minimum width supported by the Display Controller
+ * @min_height: minimum height supported by the Display Controller
+ * @max_width: maximum width supported by the Display Controller
+ * @max_height: maximum height supported by the Display Controller
+ * @max_spw: maximum vertical/horizontal pulse width
+ * @max_vpw: maximum vertical back/front porch width
+ * @max_hpw: maximum horizontal back/front porch width
+ * @conflicting_output_formats: true if RGBXXX output formats conflict with
+ *				each other.
+ * @layers: a layer description table describing available layers
+ * @nlayers: layer description table size
  */
-struct atmel_hlcdc_planes {
-	struct atmel_hlcdc_plane *primary;
-	struct atmel_hlcdc_plane *cursor;
-	struct atmel_hlcdc_plane **overlays;
-	int noverlays;
+struct atmel_hlcdc_dc_desc {
+	int min_width;
+	int min_height;
+	int max_width;
+	int max_height;
+	int max_spw;
+	int max_vpw;
+	int max_hpw;
+	bool conflicting_output_formats;
+	const struct atmel_hlcdc_layer_desc *layers;
+	int nlayers;
+};
+
+/**
+ * Atmel HLCDC Plane properties.
+ *
+ * This structure stores plane property definitions.
+ *
+ * @alpha: alpha blending (or transparency) property
+ * @rotation: rotation property
+ */
+struct atmel_hlcdc_plane_properties {
+	struct drm_property *alpha;
 };
 
 /**
@@ -135,18 +357,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;
@@ -156,11 +378,51 @@  struct atmel_hlcdc_dc {
 extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
 extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
 
+static inline void atmel_hlcdc_layer_write_reg(struct atmel_hlcdc_layer *layer,
+					       unsigned int reg, u32 val)
+{
+	regmap_write(layer->regmap, layer->desc->regs_offset + reg, val);
+}
+
+static inline u32 atmel_hlcdc_layer_read_reg(struct atmel_hlcdc_layer *layer,
+					     unsigned int reg)
+{
+	u32 val;
+
+	regmap_read(layer->regmap, layer->desc->regs_offset + reg, &val);
+
+	return val;
+}
+
+static inline void atmel_hlcdc_layer_write_cfg(struct atmel_hlcdc_layer *layer,
+					       unsigned int cfgid, u32 val)
+{
+	atmel_hlcdc_layer_write_reg(layer,
+				    layer->desc->cfgs_offset +
+				    (cfgid * sizeof(u32)), val);
+}
+
+static inline u32 atmel_hlcdc_layer_read_cfg(struct atmel_hlcdc_layer *layer,
+					     unsigned int cfgid)
+{
+	return atmel_hlcdc_layer_read_reg(layer,
+					  layer->desc->cfgs_offset +
+					  (cfgid * sizeof(u32)));
+}
+
+static inline void atmel_hlcdc_layer_init(struct atmel_hlcdc_layer *layer,
+				const struct atmel_hlcdc_layer_desc *desc,
+				struct regmap *regmap)
+{
+	layer->desc = desc;
+	layer->regmap = regmap;
+}
+
 int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
 			      struct drm_display_mode *mode);
 
-struct atmel_hlcdc_planes *
-atmel_hlcdc_create_planes(struct drm_device *dev);
+int atmel_hlcdc_create_planes(struct drm_device *dev);
+void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane);
 
 int atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state);
 int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state);
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
deleted file mode 100644
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
deleted file mode 100644
index 9beabc940bce..000000000000
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
+++ /dev/null
@@ -1,399 +0,0 @@ 
-/*
- * Copyright (C) 2014 Free Electrons
- * Copyright (C) 2014 Atmel
- *
- * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DRM_ATMEL_HLCDC_LAYER_H
-#define DRM_ATMEL_HLCDC_LAYER_H
-
-#include <linux/mfd/atmel-hlcdc.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_flip_work.h>
-#include <drm/drmP.h>
-
-#define ATMEL_HLCDC_LAYER_CHER			0x0
-#define ATMEL_HLCDC_LAYER_CHDR			0x4
-#define ATMEL_HLCDC_LAYER_CHSR			0x8
-#define ATMEL_HLCDC_LAYER_DMA_CHAN		BIT(0)
-#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
-#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
-#define ATMEL_HLCDC_LAYER_RST			BIT(8)
-
-#define ATMEL_HLCDC_LAYER_IER			0xc
-#define ATMEL_HLCDC_LAYER_IDR			0x10
-#define ATMEL_HLCDC_LAYER_IMR			0x14
-#define ATMEL_HLCDC_LAYER_ISR			0x18
-#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
-#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
-#define ATMEL_HLCDC_LAYER_DMA_IRQ		BIT(2)
-#define ATMEL_HLCDC_LAYER_DSCR_IRQ		BIT(3)
-#define ATMEL_HLCDC_LAYER_ADD_IRQ		BIT(4)
-#define ATMEL_HLCDC_LAYER_DONE_IRQ		BIT(5)
-#define ATMEL_HLCDC_LAYER_OVR_IRQ		BIT(6)
-
-#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)		(((n) * 0x10) + 0x1c)
-#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)		(((n) * 0x10) + 0x20)
-#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)		(((n) * 0x10) + 0x24)
-#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)		(((n) * 0x10) + 0x28)
-#define ATMEL_HLCDC_LAYER_CFG(p, c)		(((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
-
-#define ATMEL_HLCDC_LAYER_DMA_CFG_ID		0
-#define ATMEL_HLCDC_LAYER_DMA_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
-#define ATMEL_HLCDC_LAYER_DMA_SIF		BIT(0)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK		GENMASK(5, 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE	(0 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4	(1 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8	(2 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16	(3 << 4)
-#define ATMEL_HLCDC_LAYER_DMA_DLBO		BIT(8)
-#define ATMEL_HLCDC_LAYER_DMA_ROTDIS		BIT(12)
-#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS		BIT(13)
-
-#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID		1
-#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
-#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
-#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
-#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
-#define ATMEL_HLCDC_RGB_MODE(m)			(((m) & 0xf) << 4)
-#define ATMEL_HLCDC_CLUT_MODE(m)		(((m) & 0x3) << 8)
-#define ATMEL_HLCDC_YUV_MODE(m)			(((m) & 0xf) << 12)
-#define ATMEL_HLCDC_YUV422ROT			BIT(16)
-#define ATMEL_HLCDC_YUV422SWP			BIT(17)
-#define ATMEL_HLCDC_DSCALEOPT			BIT(20)
-
-#define ATMEL_HLCDC_XRGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
-#define ATMEL_HLCDC_ARGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
-#define ATMEL_HLCDC_RGBA4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
-#define ATMEL_HLCDC_RGB565_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
-#define ATMEL_HLCDC_ARGB1555_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
-#define ATMEL_HLCDC_XRGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
-#define ATMEL_HLCDC_RGB888_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
-#define ATMEL_HLCDC_ARGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
-#define ATMEL_HLCDC_RGBA8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
-
-#define ATMEL_HLCDC_AYUV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
-#define ATMEL_HLCDC_YUYV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
-#define ATMEL_HLCDC_UYVY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
-#define ATMEL_HLCDC_YVYU_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
-#define ATMEL_HLCDC_VYUY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
-#define ATMEL_HLCDC_NV61_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
-#define ATMEL_HLCDC_YUV422_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
-#define ATMEL_HLCDC_NV21_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
-#define ATMEL_HLCDC_YUV420_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
-
-#define ATMEL_HLCDC_LAYER_POS_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
-#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
-#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
-#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
-#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
-#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
-#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
-#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
-
-#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
-#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
-#define ATMEL_HLCDC_LAYER_INV			BIT(1)
-#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
-#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
-#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
-#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
-#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
-#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
-#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
-#define ATMEL_HLCDC_LAYER_REP			BIT(9)
-#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
-#define ATMEL_HLCDC_LAYER_DISCEN		BIT(11)
-#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
-#define ATMEL_HLCDC_LAYER_GA_MASK		GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
-#define ATMEL_HLCDC_LAYER_GA(x)			((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
-
-#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
-
-#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
-
-#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
-
-#define ATMEL_HLCDC_MAX_PLANES			3
-
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
-#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN	BIT(3)
-
-/**
- * Atmel HLCDC Layer registers layout structure
- *
- * Each HLCDC layer has its own register organization and a given register
- * can be placed differently on 2 different layers depending on its
- * capabilities.
- * This structure stores common registers layout for a given layer and is
- * used by HLCDC layer code to choose the appropriate register to write to
- * or to read from.
- *
- * For all fields, a value of zero means "unsupported".
- *
- * See Atmel's datasheet for a detailled description of these registers.
- *
- * @xstride: xstride registers
- * @pstride: pstride registers
- * @pos: position register
- * @size: displayed size register
- * @memsize: memory size register
- * @default_color: default color register
- * @chroma_key: chroma key register
- * @chroma_key_mask: chroma key mask register
- * @general_config: general layer config register
- * @disc_pos: discard area position register
- * @disc_size: discard area size register
- * @csc: color space conversion register
- */
-struct atmel_hlcdc_layer_cfg_layout {
-	int xstride[ATMEL_HLCDC_MAX_PLANES];
-	int pstride[ATMEL_HLCDC_MAX_PLANES];
-	int pos;
-	int size;
-	int memsize;
-	int default_color;
-	int chroma_key;
-	int chroma_key_mask;
-	int general_config;
-	int disc_pos;
-	int disc_size;
-	int csc;
-};
-
-/**
- * Atmel HLCDC framebuffer flip structure
- *
- * This structure is allocated when someone asked for a layer update (most
- * likely a DRM plane update, either primary, overlay or cursor plane) and
- * released when the layer do not need to reference the framebuffer object
- * anymore (i.e. the layer was disabled or updated).
- *
- * @dscrs: DMA descriptors
- * @fb: the referenced framebuffer object
- * @ngems: number of GEM objects referenced by the fb element
- * @status: fb flip operation status
- */
-struct atmel_hlcdc_layer_fb_flip {
-	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
-	struct drm_flip_task *task;
-	struct drm_framebuffer *fb;
-	int ngems;
-	u32 status;
-};
-
-/**
- * Atmel HLCDC DMA descriptor structure
- *
- * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
- *
- * The structure fields must remain in this specific order, because they're
- * used by the HLCDC DMA engine, which expect them in this order.
- * HLCDC DMA descriptors must be aligned on 64 bits.
- *
- * @addr: buffer DMA address
- * @ctrl: DMA transfer options
- * @next: next DMA descriptor to fetch
- * @gem_flip: the attached gem_flip operation
- */
-struct atmel_hlcdc_dma_channel_dscr {
-	dma_addr_t addr;
-	u32 ctrl;
-	dma_addr_t next;
-	u32 status;
-} __aligned(sizeof(u64));
-
-/**
- * Atmel HLCDC layer types
- */
-enum atmel_hlcdc_layer_type {
-	ATMEL_HLCDC_BASE_LAYER,
-	ATMEL_HLCDC_OVERLAY_LAYER,
-	ATMEL_HLCDC_CURSOR_LAYER,
-	ATMEL_HLCDC_PP_LAYER,
-};
-
-/**
- * Atmel HLCDC Supported formats structure
- *
- * This structure list all the formats supported by a given layer.
- *
- * @nformats: number of supported formats
- * @formats: supported formats
- */
-struct atmel_hlcdc_formats {
-	int nformats;
-	uint32_t *formats;
-};
-
-/**
- * Atmel HLCDC Layer description structure
- *
- * This structure describe the capabilities provided by a given layer.
- *
- * @name: layer name
- * @type: layer type
- * @id: layer id
- * @regs_offset: offset of the layer registers from the HLCDC registers base
- * @nconfigs: number of config registers provided by this layer
- * @formats: supported formats
- * @layout: config registers layout
- * @max_width: maximum width supported by this layer (0 means unlimited)
- * @max_height: maximum height supported by this layer (0 means unlimited)
- */
-struct atmel_hlcdc_layer_desc {
-	const char *name;
-	enum atmel_hlcdc_layer_type type;
-	int id;
-	int regs_offset;
-	int nconfigs;
-	struct atmel_hlcdc_formats *formats;
-	struct atmel_hlcdc_layer_cfg_layout layout;
-	int max_width;
-	int max_height;
-};
-
-/**
- * Atmel HLCDC Layer Update Slot structure
- *
- * This structure stores layer update requests to be applied on next frame.
- * This is the base structure behind the atomic layer update infrastructure.
- *
- * Atomic layer update provides a way to update all layer's parameters
- * simultaneously. This is needed to avoid incompatible sequential updates
- * like this one:
- * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
- *    (2 planes/buffers)
- * 2) the format update is applied but the DMA channel for the second
- *    plane/buffer is not enabled
- * 3) enable the DMA channel for the second plane
- *
- * @fb_flip: fb_flip object
- * @updated_configs: bitmask used to record modified configs
- * @configs: new config values
- */
-struct atmel_hlcdc_layer_update_slot {
-	struct atmel_hlcdc_layer_fb_flip *fb_flip;
-	unsigned long *updated_configs;
-	u32 *configs;
-};
-
-/**
- * Atmel HLCDC Layer Update structure
- *
- * This structure provides a way to queue layer update requests.
- *
- * At a given time there is at most:
- *  - one pending update request, which means the update request has been
- *    committed (or validated) and is waiting for the DMA channel(s) to be
- *    available
- *  - one request being prepared, which means someone started a layer update
- *    but has not committed it yet. There cannot be more than one started
- *    request, because the update lock is taken when starting a layer update
- *    and release when committing or rolling back the request.
- *
- * @slots: update slots. One is used for pending request and the other one
- *	   for started update request
- * @pending: the pending slot index or -1 if no request is pending
- * @next: the started update slot index or -1 no update has been started
- */
-struct atmel_hlcdc_layer_update {
-	struct atmel_hlcdc_layer_update_slot slots[2];
-	int pending;
-	int next;
-};
-
-enum atmel_hlcdc_layer_dma_channel_status {
-	ATMEL_HLCDC_LAYER_DISABLED,
-	ATMEL_HLCDC_LAYER_ENABLED,
-	ATMEL_HLCDC_LAYER_DISABLING,
-};
-
-/**
- * Atmel HLCDC Layer DMA channel structure
- *
- * This structure stores information on the DMA channel associated to a
- * given layer.
- *
- * @status: DMA channel status
- * @cur: current framebuffer
- * @queue: next framebuffer
- * @dscrs: allocated DMA descriptors
- */
-struct atmel_hlcdc_layer_dma_channel {
-	enum atmel_hlcdc_layer_dma_channel_status status;
-	struct atmel_hlcdc_layer_fb_flip *cur;
-	struct atmel_hlcdc_layer_fb_flip *queue;
-	struct atmel_hlcdc_dma_channel_dscr *dscrs;
-};
-
-/**
- * Atmel HLCDC Layer structure
- *
- * This structure stores information on the layer instance.
- *
- * @desc: layer description
- * @max_planes: maximum planes/buffers that can be associated with this layer.
- *	       This depends on the supported formats.
- * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
- * @dma: dma channel
- * @gc: fb flip garbage collector
- * @update: update handler
- * @lock: layer lock
- */
-struct atmel_hlcdc_layer {
-	const struct atmel_hlcdc_layer_desc *desc;
-	int max_planes;
-	struct atmel_hlcdc *hlcdc;
-	struct workqueue_struct *wq;
-	struct drm_flip_work gc;
-	struct atmel_hlcdc_layer_dma_channel dma;
-	struct atmel_hlcdc_layer_update update;
-	spinlock_t lock;
-};
-
-void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
-
-int atmel_hlcdc_layer_init(struct drm_device *dev,
-			   struct atmel_hlcdc_layer *layer,
-			   const struct atmel_hlcdc_layer_desc *desc);
-
-void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
-			       struct atmel_hlcdc_layer *layer);
-
-void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
-
-int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
-
-void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
-				  u32 mask, u32 val);
-
-void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
-				     struct drm_framebuffer *fb,
-				     unsigned int *offsets);
-
-void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
-					   void (*finished)(void *data),
-					   void *finished_data);
-
-void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
-
-void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
-
-#endif /* DRM_ATMEL_HLCDC_LAYER_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index 246ed1e33d8a..cb6b2d5ae50b 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;
@@ -52,8 +51,6 @@  struct atmel_hlcdc_plane_state {
 
 	u8 alpha;
 
-	bool disc_updated;
-
 	int disc_x;
 	int disc_y;
 	int disc_w;
@@ -62,12 +59,14 @@  struct atmel_hlcdc_plane_state {
 	int ahb_id;
 
 	/* These fields are private and should not be touched */
-	int bpp[ATMEL_HLCDC_MAX_PLANES];
-	unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
-	int xstride[ATMEL_HLCDC_MAX_PLANES];
-	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
+	unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
+	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
 	int nplanes;
-	bool prepared;
+
+	/* DMA descriptors. */
+	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
 };
 
 static inline struct atmel_hlcdc_plane_state *
@@ -259,125 +258,146 @@  static u32 heo_upscaling_ycoef[] = {
 	0x00205907,
 };
 
+#define ATMEL_HLCDC_XPHIDEF	4
+#define ATMEL_HLCDC_YPHIDEF	4
+
+static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
+						  u32 dstsize,
+						  u32 phidef)
+{
+	u32 factor, max_memsize;
+
+	factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
+	max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
+
+	if (max_memsize > srcsize - 1)
+		factor--;
+
+	return factor;
+}
+
 static void
-atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
-				      struct atmel_hlcdc_plane_state *state)
+atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
+				      const u32 *coeff_tab, int size,
+				      unsigned int cfg_offs)
 {
-	const struct atmel_hlcdc_layer_cfg_layout *layout =
-						&plane->layer.desc->layout;
-
-	if (layout->size)
-		atmel_hlcdc_layer_update_cfg(&plane->layer,
-					     layout->size,
-					     0xffffffff,
-					     (state->crtc_w - 1) |
-					     ((state->crtc_h - 1) << 16));
-
-	if (layout->memsize)
-		atmel_hlcdc_layer_update_cfg(&plane->layer,
-					     layout->memsize,
-					     0xffffffff,
-					     (state->src_w - 1) |
-					     ((state->src_h - 1) << 16));
-
-	if (layout->pos)
-		atmel_hlcdc_layer_update_cfg(&plane->layer,
-					     layout->pos,
-					     0xffffffff,
-					     state->crtc_x |
-					     (state->crtc_y  << 16));
-
-	/* TODO: rework the rescaling part */
-	if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) {
-		u32 factor_reg = 0;
-
-		if (state->crtc_w != state->src_w) {
-			int i;
-			u32 factor;
-			u32 *coeff_tab = heo_upscaling_xcoef;
-			u32 max_memsize;
-
-			if (state->crtc_w < state->src_w)
-				coeff_tab = heo_downscaling_xcoef;
-			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
-				atmel_hlcdc_layer_update_cfg(&plane->layer,
-							     17 + i,
-							     0xffffffff,
-							     coeff_tab[i]);
-			factor = ((8 * 256 * state->src_w) - (256 * 4)) /
-				 state->crtc_w;
-			factor++;
-			max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
-				      2048;
-			if (max_memsize > state->src_w)
-				factor--;
-			factor_reg |= factor | 0x80000000;
-		}
+	int i;
 
-		if (state->crtc_h != state->src_h) {
-			int i;
-			u32 factor;
-			u32 *coeff_tab = heo_upscaling_ycoef;
-			u32 max_memsize;
-
-			if (state->crtc_h < state->src_h)
-				coeff_tab = heo_downscaling_ycoef;
-			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
-				atmel_hlcdc_layer_update_cfg(&plane->layer,
-							     33 + i,
-							     0xffffffff,
-							     coeff_tab[i]);
-			factor = ((8 * 256 * state->src_h) - (256 * 4)) /
-				 state->crtc_h;
-			factor++;
-			max_memsize = ((factor * state->crtc_h) + (256 * 4)) /
-				      2048;
-			if (max_memsize > state->src_h)
-				factor--;
-			factor_reg |= (factor << 16) | 0x80000000;
-		}
+	for (i = 0; i < size; i++)
+		atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
+					    coeff_tab[i]);
+}
+
+void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
+				    struct atmel_hlcdc_plane_state *state)
+{
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+	u32 xfactor, yfactor;
+
+	if (!desc->layout.scaler_config)
+		return;
 
-		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
-					     factor_reg);
+	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
+		atmel_hlcdc_layer_write_cfg(&plane->layer,
+					    desc->layout.scaler_config, 0);
+		return;
+	}
+
+	if (desc->layout.phicoeffs.x) {
+		xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
+							state->crtc_w,
+							ATMEL_HLCDC_XPHIDEF);
+
+		yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
+							state->crtc_h,
+							ATMEL_HLCDC_YPHIDEF);
+
+		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
+				state->crtc_w < state->src_w ?
+				heo_downscaling_xcoef :
+				heo_upscaling_xcoef,
+				ARRAY_SIZE(heo_upscaling_xcoef),
+				desc->layout.phicoeffs.x);
+
+		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
+				state->crtc_h < state->src_h ?
+				heo_downscaling_ycoef :
+				heo_upscaling_ycoef,
+				ARRAY_SIZE(heo_upscaling_ycoef),
+				desc->layout.phicoeffs.y);
 	} else {
-		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 0);
+		xfactor = (1024 * state->src_w) / state->crtc_w;
+		yfactor = (1024 * state->src_h) / state->crtc_h;
 	}
+
+	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
+				    ATMEL_HLCDC_LAYER_SCALER_ENABLE |
+				    ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
+								     yfactor));
+}
+
+static void
+atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
+				      struct atmel_hlcdc_plane_state *state)
+{
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+
+	if (desc->layout.size)
+		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
+					ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
+							       state->crtc_h));
+
+	if (desc->layout.memsize)
+		atmel_hlcdc_layer_write_cfg(&plane->layer,
+					desc->layout.memsize,
+					ATMEL_HLCDC_LAYER_SIZE(state->src_w,
+							       state->src_h));
+
+	if (desc->layout.pos)
+		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
+					ATMEL_HLCDC_LAYER_POS(state->crtc_x,
+							      state->crtc_y));
+
+	atmel_hlcdc_plane_setup_scaler(plane, state);
 }
 
 static void
 atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
 					struct atmel_hlcdc_plane_state *state)
 {
-	const struct atmel_hlcdc_layer_cfg_layout *layout =
-						&plane->layer.desc->layout;
-	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
+	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+
+	/*
+	 * Rotation optimization is not working on RGB888 (rotation is still
+	 * working but without any optimization).
+	 */
+	if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
+		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
+
+	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
+				    cfg);
+
+	cfg = ATMEL_HLCDC_LAYER_DMA;
 
 	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
+		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);
+	if (state->disc_h * state->disc_w)
+		cfg |= ATMEL_HLCDC_LAYER_DISCEN;
+
+	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
+				    cfg);
 }
 
 static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
@@ -396,50 +416,51 @@  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);
+	atmel_hlcdc_layer_write_cfg(&plane->layer,
+				    ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
 
-	/*
-	 * Rotation optimization is not working on RGB888 (rotation is still
-	 * working but without any optimization).
-	 */
-	if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
-		cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
-	else
-		cfg = 0;
-
-	atmel_hlcdc_layer_update_cfg(&plane->layer,
-				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
-				     ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
 }
 
 static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
 					struct atmel_hlcdc_plane_state *state)
 {
-	struct atmel_hlcdc_layer *layer = &plane->layer;
-	const struct atmel_hlcdc_layer_cfg_layout *layout =
-							&layer->desc->layout;
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+	struct drm_framebuffer *fb = state->base.fb;
+	u32 sr;
 	int i;
 
-	atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
-					state->offsets);
+	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 
 	for (i = 0; i < state->nplanes; i++) {
-		if (layout->xstride[i]) {
-			atmel_hlcdc_layer_update_cfg(&plane->layer,
-						layout->xstride[i],
-						0xffffffff,
-						state->xstride[i]);
+		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
+
+		state->dscrs[i]->addr = gem->paddr + state->offsets[i];
+
+		atmel_hlcdc_layer_write_reg(&plane->layer,
+					    ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
+					    state->dscrs[i]->self);
+
+		if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
+			atmel_hlcdc_layer_write_reg(&plane->layer,
+					ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
+					state->dscrs[i]->addr);
+			atmel_hlcdc_layer_write_reg(&plane->layer,
+					ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
+					state->dscrs[i]->ctrl);
+			atmel_hlcdc_layer_write_reg(&plane->layer,
+					ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
+					state->dscrs[i]->self);
 		}
 
-		if (layout->pstride[i]) {
-			atmel_hlcdc_layer_update_cfg(&plane->layer,
-						layout->pstride[i],
-						0xffffffff,
-						state->pstride[i]);
-		}
+		if (desc->layout.xstride[i])
+			atmel_hlcdc_layer_write_cfg(&plane->layer,
+						    desc->layout.xstride[i],
+						    state->xstride[i]);
+
+		if (desc->layout.pstride[i])
+			atmel_hlcdc_layer_write_cfg(&plane->layer,
+						    desc->layout.pstride[i],
+						    state->pstride[i]);
 	}
 }
 
@@ -528,18 +549,10 @@  atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
 		disc_w = ovl_state->crtc_w;
 	}
 
-	if (disc_x == primary_state->disc_x &&
-	    disc_y == primary_state->disc_y &&
-	    disc_w == primary_state->disc_w &&
-	    disc_h == primary_state->disc_h)
-		return 0;
-
-
 	primary_state->disc_x = disc_x;
 	primary_state->disc_y = disc_y;
 	primary_state->disc_w = disc_w;
 	primary_state->disc_h = disc_h;
-	primary_state->disc_updated = true;
 
 	return 0;
 }
@@ -548,32 +561,19 @@  static void
 atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
 				   struct atmel_hlcdc_plane_state *state)
 {
-	const struct atmel_hlcdc_layer_cfg_layout *layout =
-						&plane->layer.desc->layout;
-	int disc_surface = 0;
-
-	if (!state->disc_updated)
-		return;
-
-	disc_surface = state->disc_h * state->disc_w;
-
-	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
-				ATMEL_HLCDC_LAYER_DISCEN,
-				disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
+	const struct atmel_hlcdc_layer_cfg_layout *layout;
 
-	if (!disc_surface)
+	layout = &plane->layer.desc->layout;
+	if (!layout->disc_pos || !layout->disc_size)
 		return;
 
-	atmel_hlcdc_layer_update_cfg(&plane->layer,
-				     layout->disc_pos,
-				     0xffffffff,
-				     state->disc_x | (state->disc_y << 16));
+	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
+				ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
+							   state->disc_y));
 
-	atmel_hlcdc_layer_update_cfg(&plane->layer,
-				     layout->disc_size,
-				     0xffffffff,
-				     (state->disc_w - 1) |
-				     ((state->disc_h - 1) << 16));
+	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
+				ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
+							    state->disc_h));
 }
 
 static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
@@ -582,8 +582,7 @@  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 	struct atmel_hlcdc_plane_state *state =
 				drm_plane_state_to_atmel_hlcdc_plane_state(s);
-	const struct atmel_hlcdc_layer_cfg_layout *layout =
-						&plane->layer.desc->layout;
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 	struct drm_framebuffer *fb = state->base.fb;
 	const struct drm_display_mode *mode;
 	struct drm_crtc_state *crtc_state;
@@ -622,7 +621,7 @@  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 	state->src_h >>= 16;
 
 	state->nplanes = drm_format_num_planes(fb->pixel_format);
-	if (state->nplanes > ATMEL_HLCDC_MAX_PLANES)
+	if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
 		return -EINVAL;
 
 	/*
@@ -726,21 +725,19 @@  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 	state->crtc_w = patched_crtc_w;
 	state->crtc_h = patched_crtc_h;
 
-	if (!layout->size &&
+	if (!desc->layout.size &&
 	    (mode->hdisplay != state->crtc_w ||
 	     mode->vdisplay != state->crtc_h))
 		return -EINVAL;
 
-	if (plane->layer.desc->max_height &&
-	    state->crtc_h > plane->layer.desc->max_height)
+	if (desc->max_height && state->crtc_h > desc->max_height)
 		return -EINVAL;
 
-	if (plane->layer.desc->max_width &&
-	    state->crtc_w > plane->layer.desc->max_width)
+	if (desc->max_width && state->crtc_w > desc->max_width)
 		return -EINVAL;
 
 	if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
-	    (!layout->memsize ||
+	    (!desc->layout.memsize ||
 	     atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)))
 		return -EINVAL;
 
@@ -754,65 +751,13 @@  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 	return 0;
 }
 
-static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
-					struct drm_plane_state *new_state)
-{
-	/*
-	 * FIXME: we should avoid this const -> non-const cast but it's
-	 * currently the only solution we have to modify the ->prepared
-	 * state and rollback the update request.
-	 * Ideally, we should rework the code to attach all the resources
-	 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
-	 * but this require a complete rework of the atmel_hlcdc_layer
-	 * code.
-	 */
-	struct drm_plane_state *s = (struct drm_plane_state *)new_state;
-	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
-	struct atmel_hlcdc_plane_state *state =
-			drm_plane_state_to_atmel_hlcdc_plane_state(s);
-	int ret;
-
-	ret = atmel_hlcdc_layer_update_start(&plane->layer);
-	if (!ret)
-		state->prepared = true;
-
-	return ret;
-}
-
-static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
-					 struct drm_plane_state *old_state)
-{
-	/*
-	 * FIXME: we should avoid this const -> non-const cast but it's
-	 * currently the only solution we have to modify the ->prepared
-	 * state and rollback the update request.
-	 * Ideally, we should rework the code to attach all the resources
-	 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
-	 * but this require a complete rework of the atmel_hlcdc_layer
-	 * code.
-	 */
-	struct drm_plane_state *s = (struct drm_plane_state *)old_state;
-	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
-	struct atmel_hlcdc_plane_state *state =
-			drm_plane_state_to_atmel_hlcdc_plane_state(s);
-
-	/*
-	 * The Request has already been applied or cancelled, nothing to do
-	 * here.
-	 */
-	if (!state->prepared)
-		return;
-
-	atmel_hlcdc_layer_update_rollback(&plane->layer);
-	state->prepared = false;
-}
-
 static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
 					    struct drm_plane_state *old_s)
 {
 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 	struct atmel_hlcdc_plane_state *state =
 			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
+	u32 sr;
 
 	if (!p->state->crtc || !p->state->fb)
 		return;
@@ -823,7 +768,18 @@  static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
 	atmel_hlcdc_plane_update_buffers(plane, state);
 	atmel_hlcdc_plane_update_disc_area(plane, state);
 
-	atmel_hlcdc_layer_update_commit(&plane->layer);
+	/* Enable the overrun interrupts. */
+	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
+				    ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
+				    ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
+				    ATMEL_HLCDC_LAYER_OVR_IRQ(2));
+
+	/* Apply the new config at the next SOF event. */
+	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
+	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
+			ATMEL_HLCDC_LAYER_UPDATE |
+			(sr & ATMEL_HLCDC_LAYER_EN ?
+			 ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
 }
 
 static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
@@ -831,7 +787,18 @@  static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
 {
 	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 
-	atmel_hlcdc_layer_disable(&plane->layer);
+	/* Disable interrupts */
+	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
+				    0xffffffff);
+
+	/* Disable the layer */
+	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
+				    ATMEL_HLCDC_LAYER_RST |
+				    ATMEL_HLCDC_LAYER_A2Q |
+				    ATMEL_HLCDC_LAYER_UPDATE);
+
+	/* Clear all pending interrupts */
+	atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 }
 
 static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
@@ -841,10 +808,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,24 +848,15 @@  static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
 }
 
 static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
-					     const struct atmel_hlcdc_layer_desc *desc,
-					     struct atmel_hlcdc_plane_properties *props)
+				struct atmel_hlcdc_plane_properties *props)
 {
-	struct regmap *regmap = plane->layer.hlcdc->regmap;
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 
 	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
-	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
+	    desc->type == ATMEL_HLCDC_CURSOR_LAYER)
 		drm_object_attach_property(&plane->base.base,
 					   props->alpha, 255);
 
-		/* Set default alpha value */
-		regmap_update_bits(regmap,
-				desc->regs_offset +
-				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
-				ATMEL_HLCDC_LAYER_GA_MASK,
-				ATMEL_HLCDC_LAYER_GA_MASK);
-	}
-
 	if (desc->layout.xstride && desc->layout.pstride) {
 		int ret;
 
@@ -920,31 +875,78 @@  static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
 		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
 		 * userspace modify these factors (using a BLOB property ?).
 		 */
-		regmap_write(regmap,
-			     desc->regs_offset +
-			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
-			     0x4c900091);
-		regmap_write(regmap,
-			     desc->regs_offset +
-			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
-			     0x7a5f5090);
-		regmap_write(regmap,
-			     desc->regs_offset +
-			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
-			     0x40040890);
+		atmel_hlcdc_layer_write_cfg(&plane->layer,
+					    desc->layout.csc,
+					    0x4c900091);
+		atmel_hlcdc_layer_write_cfg(&plane->layer,
+					    desc->layout.csc + 1,
+					    0x7a5f5090);
+		atmel_hlcdc_layer_write_cfg(&plane->layer,
+					    desc->layout.csc + 2,
+					    0x40040890);
 	}
 
 	return 0;
 }
 
+void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
+{
+	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
+	u32 isr;
+
+	isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
+
+	/*
+	 * There's not much we can do in case of overrun except informing
+	 * the user. However, we are in interrupt context here, hence the
+	 * use of dev_dbg().
+	 */
+	if (isr &
+	    (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
+	     ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
+		dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
+			desc->name);
+}
+
 static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
-	.prepare_fb = atmel_hlcdc_plane_prepare_fb,
-	.cleanup_fb = atmel_hlcdc_plane_cleanup_fb,
 	.atomic_check = atmel_hlcdc_plane_atomic_check,
 	.atomic_update = atmel_hlcdc_plane_atomic_update,
 	.atomic_disable = atmel_hlcdc_plane_atomic_disable,
 };
 
+static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
+					 struct atmel_hlcdc_plane_state *state)
+{
+	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr;
+		dma_addr_t dscr_dma;
+
+		dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
+		if (!dscr)
+			goto err;
+
+		dscr->addr = 0;
+		dscr->next = dscr_dma;
+		dscr->self = dscr_dma;
+		dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
+
+		state->dscrs[i] = dscr;
+	}
+
+	return 0;
+
+err:
+	for (i--; i >= 0; i--) {
+		dma_pool_free(dc->dscrpool, state->dscrs[i],
+			      state->dscrs[i]->self);
+	}
+
+	return -ENOMEM;
+}
+
 static void atmel_hlcdc_plane_reset(struct drm_plane *p)
 {
 	struct atmel_hlcdc_plane_state *state;
@@ -961,6 +963,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;
@@ -978,8 +987,10 @@  atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
 	if (!copy)
 		return NULL;
 
-	copy->disc_updated = false;
-	copy->prepared = false;
+	if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
+		kfree(copy);
+		return NULL;
+	}
 
 	if (copy->base.fb)
 		drm_framebuffer_reference(copy->base.fb);
@@ -987,11 +998,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 +1029,21 @@  static struct drm_plane_funcs layer_plane_funcs = {
 	.atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
 };
 
-static struct atmel_hlcdc_plane *
-atmel_hlcdc_plane_create(struct drm_device *dev,
-			 const struct atmel_hlcdc_layer_desc *desc,
-			 struct atmel_hlcdc_plane_properties *props)
+static int atmel_hlcdc_plane_create(struct drm_device *dev,
+				    const struct atmel_hlcdc_layer_desc *desc,
+				    struct atmel_hlcdc_plane_properties *props)
 {
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
 	struct atmel_hlcdc_plane *plane;
 	enum drm_plane_type type;
 	int ret;
 
 	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
 	if (!plane)
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
 
-	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
-	if (ret)
-		return ERR_PTR(ret);
+	atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
+	plane->properties = props;
 
 	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
 		type = DRM_PLANE_TYPE_PRIMARY;
@@ -1040,17 +1057,19 @@  atmel_hlcdc_plane_create(struct drm_device *dev,
 				       desc->formats->formats,
 				       desc->formats->nformats, type, NULL);
 	if (ret)
-		return ERR_PTR(ret);
+		return ret;
 
 	drm_plane_helper_add(&plane->base,
 			     &atmel_hlcdc_layer_plane_helper_funcs);
 
 	/* Set default property values*/
-	ret = atmel_hlcdc_plane_init_properties(plane, desc, props);
+	ret = atmel_hlcdc_plane_init_properties(plane, props);
 	if (ret)
-		return ERR_PTR(ret);
+		return ret;
+
+	dc->layers[desc->id] = &plane->layer;
 
-	return plane;
+	return 0;
 }
 
 static struct atmel_hlcdc_plane_properties *
@@ -1069,72 +1088,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;
 }