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

Nicolas Ferre nicolas.ferre at microchip.com
Tue Feb 28 10:56:28 UTC 2017


Le 06/02/2017 à 19:27, Boris Brezillon a écrit :
> An HLCDC layers in Atmel's nomenclature is either a DRM plane or a 'Post
> Processing Layer' which can be used to output the results of the HLCDC
> composition in a memory buffer.
> 
> atmel_hlcdc_layer.c was designed to be generic enough to be re-usable in
> both cases, but we're not exposing the post-processing layer yet, and
> even if we were, I'm not sure the code would provide the necessary tools
> to manipulate this kind of layer.
> 
> Moreover, the code in atmel_hlcdc_{plane,layer}.c was designed before the
> atomic modesetting API, and was trying solve the
> check-setting/commit-if-ok/rollback-otherwise problem, which is now
> entirely solved by the existing core infrastructure.
> 
> And finally, the code in atmel_hlcdc_layer.c in over-complicated compared
> to what we really need. This rework is a good excuse to simplify it. Note
> that this rework solves an existing resource leak (leading to a -EBUSY
> error) which I failed to clearly identify.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>

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

Tested-by: Nicolas Ferre <nicolas.ferre at microchip.com>

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

> ---
> Hi Daniel,
> 
> You might not remember, but this is something you asked me to do a while
> ago, and it's finally there.
> This patch reworks the Atmel HLCDC plane logic to get rid of all the
> complexity in atmel_hlcdc_layer.c and this includes getting rid of
> drm_flip_work, which you were trying to kill IIRC.
> 
> Regards,
> 
> Boris
> ---
>  drivers/gpu/drm/atmel-hlcdc/Makefile            |   1 -
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c  |  32 +-
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c    |  81 +--
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h    |  62 +--
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c | 666 ------------------------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h | 334 ++++--------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 618 +++++++++++-----------
>  7 files changed, 522 insertions(+), 1272 deletions(-)
>  delete mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
> 
> diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile
> index 10ae426e60bd..bb5f8507a8ce 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/Makefile
> +++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
> @@ -1,6 +1,5 @@
>  atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
>  		atmel_hlcdc_dc.o \
> -		atmel_hlcdc_layer.o \
>  		atmel_hlcdc_output.o \
>  		atmel_hlcdc_plane.o
>  
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> index 9b17a66cf0e1..cdf8aa2b7a8d 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> @@ -445,8 +445,8 @@ static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
>  
>  int atmel_hlcdc_crtc_create(struct drm_device *dev)
>  {
> +	struct drm_plane *primary = NULL, *cursor = NULL;
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
> -	struct atmel_hlcdc_planes *planes = dc->planes;
>  	struct atmel_hlcdc_crtc *crtc;
>  	int ret;
>  	int i;
> @@ -457,20 +457,32 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
>  
>  	crtc->dc = dc;
>  
> -	ret = drm_crtc_init_with_planes(dev, &crtc->base,
> -				&planes->primary->base,
> -				planes->cursor ? &planes->cursor->base : NULL,
> -				&atmel_hlcdc_crtc_funcs, NULL);
> +	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> +		switch (dc->layers[i].type) {
> +		case ATMEL_HLCDC_BASE_LAYER:
> +			primary = dc->layers[i].plane;
> +			break;
> +
> +		case ATMEL_HLCDC_CURSOR_LAYER:
> +			cursor = dc->layers[i].plane;
> +			break;
> +
> +		default:
> +			break;
> +		}
> +	}
> +
> +	ret = drm_crtc_init_with_planes(dev, &crtc->base, primary, cursor,
> +					&atmel_hlcdc_crtc_funcs, NULL);
>  	if (ret < 0)
>  		goto fail;
>  
>  	crtc->id = drm_crtc_index(&crtc->base);
>  
> -	if (planes->cursor)
> -		planes->cursor->base.possible_crtcs = 1 << crtc->id;
> -
> -	for (i = 0; i < planes->noverlays; i++)
> -		planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
> +	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> +		if (dc->layers[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
> +			dc->layers[i].plane->possible_crtcs = 1 << crtc->id;
> +	}
>  
>  	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
>  	drm_crtc_vblank_reset(&crtc->base);
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> index 0bf32d6ac39b..5e7ba6de1777 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> @@ -36,7 +36,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = {
>  		.regs_offset = 0x40,
>  		.id = 0,
>  		.type = ATMEL_HLCDC_BASE_LAYER,
> -		.nconfigs = 5,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.xstride = { 2 },
>  			.default_color = 3,
> @@ -65,7 +65,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
>  		.regs_offset = 0x40,
>  		.id = 0,
>  		.type = ATMEL_HLCDC_BASE_LAYER,
> -		.nconfigs = 5,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.xstride = { 2 },
>  			.default_color = 3,
> @@ -80,7 +80,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
>  		.regs_offset = 0x100,
>  		.id = 1,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 10,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -98,7 +98,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
>  		.regs_offset = 0x280,
>  		.id = 2,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 17,
> +		.cfgs_offset = 0x4c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -109,6 +109,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
>  			.chroma_key = 10,
>  			.chroma_key_mask = 11,
>  			.general_config = 12,
> +			.scaler_config = 13,
>  			.csc = 14,
>  		},
>  	},
> @@ -118,9 +119,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
>  		.regs_offset = 0x340,
>  		.id = 3,
>  		.type = ATMEL_HLCDC_CURSOR_LAYER,
> -		.nconfigs = 10,
>  		.max_width = 128,
>  		.max_height = 128,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -153,7 +154,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  		.regs_offset = 0x40,
>  		.id = 0,
>  		.type = ATMEL_HLCDC_BASE_LAYER,
> -		.nconfigs = 7,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.xstride = { 2 },
>  			.default_color = 3,
> @@ -168,7 +169,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  		.regs_offset = 0x140,
>  		.id = 1,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 10,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -186,7 +187,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  		.regs_offset = 0x240,
>  		.id = 2,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 10,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -204,7 +205,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  		.regs_offset = 0x340,
>  		.id = 3,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 42,
> +		.cfgs_offset = 0x4c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -215,6 +216,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  			.chroma_key = 10,
>  			.chroma_key_mask = 11,
>  			.general_config = 12,
> +			.scaler_config = 13,
> +			.phicoeffs = {
> +				.x = 17,
> +				.y = 33,
> +			},
>  			.csc = 14,
>  		},
>  	},
> @@ -224,9 +230,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  		.regs_offset = 0x440,
>  		.id = 4,
>  		.type = ATMEL_HLCDC_CURSOR_LAYER,
> -		.nconfigs = 10,
>  		.max_width = 128,
>  		.max_height = 128,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -236,6 +242,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
>  			.chroma_key = 7,
>  			.chroma_key_mask = 8,
>  			.general_config = 9,
> +			.scaler_config = 13,
>  		},
>  	},
>  };
> @@ -260,7 +267,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
>  		.regs_offset = 0x40,
>  		.id = 0,
>  		.type = ATMEL_HLCDC_BASE_LAYER,
> -		.nconfigs = 7,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.xstride = { 2 },
>  			.default_color = 3,
> @@ -275,7 +282,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
>  		.regs_offset = 0x140,
>  		.id = 1,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 10,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -293,7 +300,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
>  		.regs_offset = 0x240,
>  		.id = 2,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 10,
> +		.cfgs_offset = 0x2c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -311,7 +318,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
>  		.regs_offset = 0x340,
>  		.id = 3,
>  		.type = ATMEL_HLCDC_OVERLAY_LAYER,
> -		.nconfigs = 42,
> +		.cfgs_offset = 0x4c,
>  		.layout = {
>  			.pos = 2,
>  			.size = 3,
> @@ -322,6 +329,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
>  			.chroma_key = 10,
>  			.chroma_key_mask = 11,
>  			.general_config = 12,
> +			.scaler_config = 13,
> +			.phicoeffs = {
> +				.x = 17,
> +				.y = 33,
> +			},
>  			.csc = 14,
>  		},
>  	},
> @@ -392,6 +404,14 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
>  	return MODE_OK;
>  }
>  
> +static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
> +{
> +	if (layer->type == ATMEL_HLCDC_BASE_LAYER ||
> +	    layer->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> +	    layer->type == ATMEL_HLCDC_CURSOR_LAYER)
> +		atmel_hlcdc_plane_irq(layer->plane);
> +}
> +
>  static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
>  {
>  	struct drm_device *dev = data;
> @@ -410,12 +430,8 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
>  		atmel_hlcdc_crtc_irq(dc->crtc);
>  
>  	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> -		struct atmel_hlcdc_layer *layer = dc->layers[i];
> -
> -		if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
> -			continue;
> -
> -		atmel_hlcdc_layer_irq(layer);
> +		if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
> +			atmel_hlcdc_layer_irq(&dc->layers[i]);
>  	}
>  
>  	return IRQ_HANDLED;
> @@ -537,9 +553,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
>  static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
>  {
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
> -	struct atmel_hlcdc_planes *planes;
>  	int ret;
> -	int i;
>  
>  	drm_mode_config_init(dev);
>  
> @@ -549,25 +563,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
>  		return ret;
>  	}
>  
> -	planes = atmel_hlcdc_create_planes(dev);
> -	if (IS_ERR(planes)) {
> -		dev_err(dev->dev, "failed to create planes\n");
> -		return PTR_ERR(planes);
> +	ret = atmel_hlcdc_create_planes(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "failed to create planes: %d\n", ret);
> +		return ret;
>  	}
>  
> -	dc->planes = planes;
> -
> -	dc->layers[planes->primary->layer.desc->id] =
> -						&planes->primary->layer;
> -
> -	if (planes->cursor)
> -		dc->layers[planes->cursor->layer.desc->id] =
> -							&planes->cursor->layer;
> -
> -	for (i = 0; i < planes->noverlays; i++)
> -		dc->layers[planes->overlays[i]->layer.desc->id] =
> -						&planes->overlays[i]->layer;
> -
>  	ret = atmel_hlcdc_crtc_create(dev);
>  	if (ret) {
>  		dev_err(dev->dev, "failed to create crtc\n");
> @@ -703,7 +704,7 @@ static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
>  
>  	/* Enable interrupts on activated layers */
>  	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
> -		if (dc->layers[i])
> +		if (dc->layers[i].type != ATMEL_HLCDC_NO_LAYER)
>  			cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
>  	}
>  
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> index 7a47f8c094d0..67b80c3a2666 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> @@ -23,7 +23,9 @@
>  #define DRM_ATMEL_HLCDC_H
>  
>  #include <linux/clk.h>
> +#include <linux/dmapool.h>
>  #include <linux/irqdomain.h>
> +#include <linux/mfd/atmel-hlcdc.h>
>  #include <linux/pwm.h>
>  
>  #include <drm/drm_atomic.h>
> @@ -38,7 +40,7 @@
>  
>  #include "atmel_hlcdc_layer.h"
>  
> -#define ATMEL_HLCDC_MAX_LAYERS		5
> +#define ATMEL_HLCDC_MAX_LAYERS		6
>  
>  /**
>   * Atmel HLCDC Display Controller description structure.
> @@ -84,47 +86,19 @@ struct atmel_hlcdc_plane_properties {
>  };
>  
>  /**
> - * Atmel HLCDC Plane.
> + * Atmel HLCDC Layer.
>   *
> - * @base: base DRM plane structure
> - * @layer: HLCDC layer structure
> - * @properties: pointer to the property definitions structure
> - * @rotation: current rotation status
> - */
> -struct atmel_hlcdc_plane {
> -	struct drm_plane base;
> -	struct atmel_hlcdc_layer layer;
> -	struct atmel_hlcdc_plane_properties *properties;
> -};
> -
> -static inline struct atmel_hlcdc_plane *
> -drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
> -{
> -	return container_of(p, struct atmel_hlcdc_plane, base);
> -}
> -
> -static inline struct atmel_hlcdc_plane *
> -atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
> -{
> -	return container_of(l, struct atmel_hlcdc_plane, layer);
> -}
> -
> -/**
> - * Atmel HLCDC Planes.
> - *
> - * This structure stores the instantiated HLCDC Planes and can be accessed by
> - * the HLCDC Display Controller or the HLCDC CRTC.
> + * A layer can be a DRM plane of a post processing layer used to render
> + * HLCDC composition into memory.
>   *
> - * @primary: primary plane
> - * @cursor: hardware cursor plane
> - * @overlays: overlay plane table
> - * @noverlays: number of overlay planes
> + * @type: layer type
> + * @plane: pointer to the DRM plane exposed by this layer
>   */
> -struct atmel_hlcdc_planes {
> -	struct atmel_hlcdc_plane *primary;
> -	struct atmel_hlcdc_plane *cursor;
> -	struct atmel_hlcdc_plane **overlays;
> -	int noverlays;
> +struct atmel_hlcdc_layer {
> +	enum atmel_hlcdc_layer_type type;
> +	union {
> +		struct drm_plane *plane;
> +	};
>  };
>  
>  /**
> @@ -135,18 +109,18 @@ struct atmel_hlcdc_planes {
>   * @fbdev: framebuffer device attached to the Display Controller
>   * @crtc: CRTC provided by the display controller
>   * @planes: instantiated planes
> - * @layers: active HLCDC layer
> + * @layers: active HLCDC layers
>   * @wq: display controller workqueue
>   * @commit: used for async commit handling
>   */
>  struct atmel_hlcdc_dc {
>  	const struct atmel_hlcdc_dc_desc *desc;
> +	struct dma_pool *dscrpool;
>  	struct atmel_hlcdc *hlcdc;
>  	struct drm_fbdev_cma *fbdev;
>  	struct drm_crtc *crtc;
> -	struct atmel_hlcdc_planes *planes;
> -	struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
>  	struct workqueue_struct *wq;
> +	struct atmel_hlcdc_layer layers[ATMEL_HLCDC_MAX_LAYERS];
>  	struct {
>  		wait_queue_head_t wait;
>  		bool pending;
> @@ -159,8 +133,8 @@ extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
>  int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
>  			      struct drm_display_mode *mode);
>  
> -struct atmel_hlcdc_planes *
> -atmel_hlcdc_create_planes(struct drm_device *dev);
> +int atmel_hlcdc_create_planes(struct drm_device *dev);
> +void atmel_hlcdc_plane_irq(struct drm_plane *plane);
>  
>  int atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state);
>  int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state);
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
> deleted file mode 100644
> index 377e43cea9dd..000000000000
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
> +++ /dev/null
> @@ -1,666 +0,0 @@
> -/*
> - * Copyright (C) 2014 Free Electrons
> - * Copyright (C) 2014 Atmel
> - *
> - * Author: Boris BREZILLON <boris.brezillon at free-electrons.com>
> - *
> - * This program is free software; you can redistribute it and/or modify it
> - * under the terms of the GNU General Public License version 2 as published by
> - * the Free Software Foundation.
> - *
> - * This program is distributed in the hope that it will be useful, but WITHOUT
> - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> - * more details.
> - *
> - * You should have received a copy of the GNU General Public License along with
> - * this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -#include <linux/dma-mapping.h>
> -#include <linux/interrupt.h>
> -
> -#include "atmel_hlcdc_dc.h"
> -
> -static void
> -atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
> -{
> -	struct atmel_hlcdc_layer_fb_flip *flip = val;
> -
> -	if (flip->fb)
> -		drm_framebuffer_unreference(flip->fb);
> -	kfree(flip);
> -}
> -
> -static void
> -atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
> -{
> -	if (flip->fb)
> -		drm_framebuffer_unreference(flip->fb);
> -	kfree(flip->task);
> -	kfree(flip);
> -}
> -
> -static void
> -atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
> -					struct atmel_hlcdc_layer_fb_flip *flip)
> -{
> -	int i;
> -
> -	if (!flip)
> -		return;
> -
> -	for (i = 0; i < layer->max_planes; i++) {
> -		if (!flip->dscrs[i])
> -			break;
> -
> -		flip->dscrs[i]->status = 0;
> -		flip->dscrs[i] = NULL;
> -	}
> -
> -	drm_flip_work_queue_task(&layer->gc, flip->task);
> -	drm_flip_work_commit(&layer->gc, layer->wq);
> -}
> -
> -static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
> -					   int id)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -
> -	if (id < 0 || id > 1)
> -		return;
> -
> -	slot = &upd->slots[id];
> -	bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
> -	memset(slot->configs, 0,
> -	       sizeof(*slot->configs) * layer->desc->nconfigs);
> -
> -	if (slot->fb_flip) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
> -		slot->fb_flip = NULL;
> -	}
> -}
> -
> -static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	struct atmel_hlcdc_dma_channel_dscr *dscr;
> -	unsigned int cfg;
> -	u32 action = 0;
> -	int i = 0;
> -
> -	if (upd->pending < 0 || upd->pending > 1)
> -		return;
> -
> -	slot = &upd->slots[upd->pending];
> -
> -	for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CFG(layer, cfg),
> -			     slot->configs[cfg]);
> -		action |= ATMEL_HLCDC_LAYER_UPDATE;
> -	}
> -
> -	fb_flip = slot->fb_flip;
> -
> -	if (!fb_flip->fb)
> -		goto apply;
> -
> -	if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
> -		for (i = 0; i < fb_flip->ngems; i++) {
> -			dscr = fb_flip->dscrs[i];
> -			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
> -				     ATMEL_HLCDC_LAYER_DMA_IRQ |
> -				     ATMEL_HLCDC_LAYER_ADD_IRQ |
> -				     ATMEL_HLCDC_LAYER_DONE_IRQ;
> -
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
> -				     dscr->addr);
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
> -				     dscr->ctrl);
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
> -				     dscr->next);
> -		}
> -
> -		action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
> -		dma->status = ATMEL_HLCDC_LAYER_ENABLED;
> -	} else {
> -		for (i = 0; i < fb_flip->ngems; i++) {
> -			dscr =  fb_flip->dscrs[i];
> -			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
> -				     ATMEL_HLCDC_LAYER_DMA_IRQ |
> -				     ATMEL_HLCDC_LAYER_DSCR_IRQ |
> -				     ATMEL_HLCDC_LAYER_DONE_IRQ;
> -
> -			regmap_write(regmap,
> -				     desc->regs_offset +
> -				     ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
> -				     dscr->next);
> -		}
> -
> -		action |= ATMEL_HLCDC_LAYER_A2Q;
> -	}
> -
> -	/* Release unneeded descriptors */
> -	for (i = fb_flip->ngems; i < layer->max_planes; i++) {
> -		fb_flip->dscrs[i]->status = 0;
> -		fb_flip->dscrs[i] = NULL;
> -	}
> -
> -	dma->queue = fb_flip;
> -	slot->fb_flip = NULL;
> -
> -apply:
> -	if (action)
> -		regmap_write(regmap,
> -			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
> -			     action);
> -
> -	atmel_hlcdc_layer_update_reset(layer, upd->pending);
> -
> -	upd->pending = -1;
> -}
> -
> -void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	struct atmel_hlcdc_layer_fb_flip *flip;
> -	unsigned long flags;
> -	unsigned int isr, imr;
> -	unsigned int status;
> -	unsigned int plane_status;
> -	u32 flip_status;
> -
> -	int i;
> -
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
> -	status = imr & isr;
> -	if (!status)
> -		return;
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	flip = dma->queue ? dma->queue : dma->cur;
> -
> -	if (!flip) {
> -		spin_unlock_irqrestore(&layer->lock, flags);
> -		return;
> -	}
> -
> -	/*
> -	 * Set LOADED and DONE flags: they'll be cleared if at least one
> -	 * memory plane is not LOADED or DONE.
> -	 */
> -	flip_status = ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED |
> -		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
> -	for (i = 0; i < flip->ngems; i++) {
> -		plane_status = (status >> (8 * i));
> -
> -		if (plane_status &
> -		    (ATMEL_HLCDC_LAYER_ADD_IRQ |
> -		     ATMEL_HLCDC_LAYER_DSCR_IRQ) &
> -		    ~flip->dscrs[i]->ctrl) {
> -			flip->dscrs[i]->status |=
> -					ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
> -			flip->dscrs[i]->ctrl |=
> -					ATMEL_HLCDC_LAYER_ADD_IRQ |
> -					ATMEL_HLCDC_LAYER_DSCR_IRQ;
> -		}
> -
> -		if (plane_status &
> -		    ATMEL_HLCDC_LAYER_DONE_IRQ &
> -		    ~flip->dscrs[i]->ctrl) {
> -			flip->dscrs[i]->status |=
> -					ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
> -			flip->dscrs[i]->ctrl |=
> -					ATMEL_HLCDC_LAYER_DONE_IRQ;
> -		}
> -
> -		if (plane_status & ATMEL_HLCDC_LAYER_OVR_IRQ)
> -			flip->dscrs[i]->status |=
> -					ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
> -
> -		/*
> -		 * Clear LOADED and DONE flags if the memory plane is either
> -		 * not LOADED or not DONE.
> -		 */
> -		if (!(flip->dscrs[i]->status &
> -		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED))
> -			flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
> -
> -		if (!(flip->dscrs[i]->status &
> -		      ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE))
> -			flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
> -
> -		/*
> -		 * An overrun on one memory plane impact the whole framebuffer
> -		 * transfer, hence we set the OVERRUN flag as soon as there's
> -		 * one memory plane reporting such an overrun.
> -		 */
> -		flip_status |= flip->dscrs[i]->status &
> -			       ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
> -	}
> -
> -	/* Get changed bits */
> -	flip_status ^= flip->status;
> -	flip->status |= flip_status;
> -
> -	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
> -		dma->cur = dma->queue;
> -		dma->queue = NULL;
> -	}
> -
> -	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
> -		dma->cur = NULL;
> -	}
> -
> -	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN) {
> -		regmap_write(regmap,
> -			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
> -			     ATMEL_HLCDC_LAYER_RST);
> -		if (dma->queue)
> -			atmel_hlcdc_layer_fb_flip_release_queue(layer,
> -								dma->queue);
> -
> -		if (dma->cur)
> -			atmel_hlcdc_layer_fb_flip_release_queue(layer,
> -								dma->cur);
> -
> -		dma->cur = NULL;
> -		dma->queue = NULL;
> -	}
> -
> -	if (!dma->queue) {
> -		atmel_hlcdc_layer_update_apply(layer);
> -
> -		if (!dma->cur)
> -			dma->status = ATMEL_HLCDC_LAYER_DISABLED;
> -	}
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -}
> -
> -void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	unsigned long flags;
> -	unsigned int isr;
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	/* Disable the layer */
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
> -		     ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
> -		     ATMEL_HLCDC_LAYER_UPDATE);
> -
> -	/* Clear all pending interrupts */
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
> -
> -	/* Discard current and queued framebuffer transfers. */
> -	if (dma->cur) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
> -		dma->cur = NULL;
> -	}
> -
> -	if (dma->queue) {
> -		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->queue);
> -		dma->queue = NULL;
> -	}
> -
> -	/*
> -	 * Then discard the pending update request (if any) to prevent
> -	 * DMA irq handler from restarting the DMA channel after it has
> -	 * been disabled.
> -	 */
> -	if (upd->pending >= 0) {
> -		atmel_hlcdc_layer_update_reset(layer, upd->pending);
> -		upd->pending = -1;
> -	}
> -
> -	dma->status = ATMEL_HLCDC_LAYER_DISABLED;
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -}
> -
> -int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	unsigned long flags;
> -	int i, j = 0;
> -
> -	fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
> -	if (!fb_flip)
> -		return -ENOMEM;
> -
> -	fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
> -	if (!fb_flip->task) {
> -		kfree(fb_flip);
> -		return -ENOMEM;
> -	}
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	upd->next = upd->pending ? 0 : 1;
> -
> -	slot = &upd->slots[upd->next];
> -
> -	for (i = 0; i < layer->max_planes * 4; i++) {
> -		if (!dma->dscrs[i].status) {
> -			fb_flip->dscrs[j++] = &dma->dscrs[i];
> -			dma->dscrs[i].status =
> -				ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
> -			if (j == layer->max_planes)
> -				break;
> -		}
> -	}
> -
> -	if (j < layer->max_planes) {
> -		for (i = 0; i < j; i++)
> -			fb_flip->dscrs[i]->status = 0;
> -	}
> -
> -	if (j < layer->max_planes) {
> -		spin_unlock_irqrestore(&layer->lock, flags);
> -		atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
> -		return -EBUSY;
> -	}
> -
> -	slot->fb_flip = fb_flip;
> -
> -	if (upd->pending >= 0) {
> -		memcpy(slot->configs,
> -		       upd->slots[upd->pending].configs,
> -		       layer->desc->nconfigs * sizeof(u32));
> -		memcpy(slot->updated_configs,
> -		       upd->slots[upd->pending].updated_configs,
> -		       DIV_ROUND_UP(layer->desc->nconfigs,
> -				    BITS_PER_BYTE * sizeof(unsigned long)) *
> -		       sizeof(unsigned long));
> -		slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
> -		if (upd->slots[upd->pending].fb_flip->fb) {
> -			slot->fb_flip->fb =
> -				upd->slots[upd->pending].fb_flip->fb;
> -			slot->fb_flip->ngems =
> -				upd->slots[upd->pending].fb_flip->ngems;
> -			drm_framebuffer_reference(slot->fb_flip->fb);
> -		}
> -	} else {
> -		regmap_bulk_read(regmap,
> -				 layer->desc->regs_offset +
> -				 ATMEL_HLCDC_LAYER_CFG(layer, 0),
> -				 upd->slots[upd->next].configs,
> -				 layer->desc->nconfigs);
> -	}
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -
> -	return 0;
> -}
> -
> -void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -
> -	atmel_hlcdc_layer_update_reset(layer, upd->next);
> -	upd->next = -1;
> -}
> -
> -void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
> -				     struct drm_framebuffer *fb,
> -				     unsigned int *offsets)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	struct atmel_hlcdc_dma_channel_dscr *dscr;
> -	struct drm_framebuffer *old_fb;
> -	int nplanes = 0;
> -	int i;
> -
> -	if (upd->next < 0 || upd->next > 1)
> -		return;
> -
> -	if (fb)
> -		nplanes = drm_format_num_planes(fb->pixel_format);
> -
> -	if (nplanes > layer->max_planes)
> -		return;
> -
> -	slot = &upd->slots[upd->next];
> -
> -	fb_flip = slot->fb_flip;
> -	old_fb = slot->fb_flip->fb;
> -
> -	for (i = 0; i < nplanes; i++) {
> -		struct drm_gem_cma_object *gem;
> -
> -		dscr = slot->fb_flip->dscrs[i];
> -		gem = drm_fb_cma_get_gem_obj(fb, i);
> -		dscr->addr = gem->paddr + offsets[i];
> -	}
> -
> -	fb_flip->ngems = nplanes;
> -	fb_flip->fb = fb;
> -
> -	if (fb)
> -		drm_framebuffer_reference(fb);
> -
> -	if (old_fb)
> -		drm_framebuffer_unreference(old_fb);
> -}
> -
> -void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
> -				  u32 mask, u32 val)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -
> -	if (upd->next < 0 || upd->next > 1)
> -		return;
> -
> -	if (cfg >= layer->desc->nconfigs)
> -		return;
> -
> -	slot = &upd->slots[upd->next];
> -	slot->configs[cfg] &= ~mask;
> -	slot->configs[cfg] |= (val & mask);
> -	set_bit(cfg, slot->updated_configs);
> -}
> -
> -void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	struct atmel_hlcdc_layer_update_slot *slot;
> -	unsigned long flags;
> -
> -	if (upd->next < 0  || upd->next > 1)
> -		return;
> -
> -	slot = &upd->slots[upd->next];
> -
> -	spin_lock_irqsave(&layer->lock, flags);
> -
> -	/*
> -	 * Release pending update request and replace it by the new one.
> -	 */
> -	if (upd->pending >= 0)
> -		atmel_hlcdc_layer_update_reset(layer, upd->pending);
> -
> -	upd->pending = upd->next;
> -	upd->next = -1;
> -
> -	if (!dma->queue)
> -		atmel_hlcdc_layer_update_apply(layer);
> -
> -	spin_unlock_irqrestore(&layer->lock, flags);
> -
> -
> -	upd->next = -1;
> -}
> -
> -static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
> -				      struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	dma_addr_t dma_addr;
> -	int i;
> -
> -	dma->dscrs = dma_alloc_coherent(dev->dev,
> -					layer->max_planes * 4 *
> -					sizeof(*dma->dscrs),
> -					&dma_addr, GFP_KERNEL);
> -	if (!dma->dscrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < layer->max_planes * 4; i++) {
> -		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
> -
> -		dscr->next = dma_addr + (i * sizeof(*dscr));
> -	}
> -
> -	return 0;
> -}
> -
> -static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
> -					  struct atmel_hlcdc_layer *layer)
> -{
> -	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
> -	int i;
> -
> -	for (i = 0; i < layer->max_planes * 4; i++) {
> -		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
> -
> -		dscr->status = 0;
> -	}
> -
> -	dma_free_coherent(dev->dev, layer->max_planes * 4 *
> -			  sizeof(*dma->dscrs), dma->dscrs,
> -			  dma->dscrs[0].next);
> -}
> -
> -static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
> -				struct atmel_hlcdc_layer *layer,
> -				const struct atmel_hlcdc_layer_desc *desc)
> -{
> -	struct atmel_hlcdc_layer_update *upd = &layer->update;
> -	int updated_size;
> -	void *buffer;
> -	int i;
> -
> -	updated_size = DIV_ROUND_UP(desc->nconfigs,
> -				    BITS_PER_BYTE *
> -				    sizeof(unsigned long));
> -
> -	buffer = devm_kzalloc(dev->dev,
> -			      ((desc->nconfigs * sizeof(u32)) +
> -				(updated_size * sizeof(unsigned long))) * 2,
> -			      GFP_KERNEL);
> -	if (!buffer)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < 2; i++) {
> -		upd->slots[i].updated_configs = buffer;
> -		buffer += updated_size * sizeof(unsigned long);
> -		upd->slots[i].configs = buffer;
> -		buffer += desc->nconfigs * sizeof(u32);
> -	}
> -
> -	upd->pending = -1;
> -	upd->next = -1;
> -
> -	return 0;
> -}
> -
> -int atmel_hlcdc_layer_init(struct drm_device *dev,
> -			   struct atmel_hlcdc_layer *layer,
> -			   const struct atmel_hlcdc_layer_desc *desc)
> -{
> -	struct atmel_hlcdc_dc *dc = dev->dev_private;
> -	struct regmap *regmap = dc->hlcdc->regmap;
> -	unsigned int tmp;
> -	int ret;
> -	int i;
> -
> -	layer->hlcdc = dc->hlcdc;
> -	layer->wq = dc->wq;
> -	layer->desc = desc;
> -
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
> -		     ATMEL_HLCDC_LAYER_RST);
> -	for (i = 0; i < desc->formats->nformats; i++) {
> -		int nplanes = drm_format_num_planes(desc->formats->formats[i]);
> -
> -		if (nplanes > layer->max_planes)
> -			layer->max_planes = nplanes;
> -	}
> -
> -	spin_lock_init(&layer->lock);
> -	drm_flip_work_init(&layer->gc, desc->name,
> -			   atmel_hlcdc_layer_fb_flip_release);
> -	ret = atmel_hlcdc_layer_dma_init(dev, layer);
> -	if (ret)
> -		return ret;
> -
> -	ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
> -	if (ret)
> -		return ret;
> -
> -	/* Flush Status Register */
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
> -		     0xffffffff);
> -	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
> -		    &tmp);
> -
> -	tmp = 0;
> -	for (i = 0; i < layer->max_planes; i++)
> -		tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ |
> -			ATMEL_HLCDC_LAYER_DSCR_IRQ |
> -			ATMEL_HLCDC_LAYER_ADD_IRQ |
> -			ATMEL_HLCDC_LAYER_DONE_IRQ |
> -			ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i);
> -
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp);
> -
> -	return 0;
> -}
> -
> -void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
> -			       struct atmel_hlcdc_layer *layer)
> -{
> -	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
> -	struct regmap *regmap = layer->hlcdc->regmap;
> -
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
> -		     0xffffffff);
> -	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
> -		     ATMEL_HLCDC_LAYER_RST);
> -
> -	atmel_hlcdc_layer_dma_cleanup(dev, layer);
> -	drm_flip_work_cleanup(&layer->gc);
> -}
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> index 9beabc940bce..fd766827f651 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
> @@ -20,40 +20,39 @@
>  #ifndef DRM_ATMEL_HLCDC_LAYER_H
>  #define DRM_ATMEL_HLCDC_LAYER_H
>  
> -#include <linux/mfd/atmel-hlcdc.h>
> -
> -#include <drm/drm_crtc.h>
> -#include <drm/drm_flip_work.h>
> -#include <drm/drmP.h>
> -
> -#define ATMEL_HLCDC_LAYER_CHER			0x0
> -#define ATMEL_HLCDC_LAYER_CHDR			0x4
> -#define ATMEL_HLCDC_LAYER_CHSR			0x8
> -#define ATMEL_HLCDC_LAYER_DMA_CHAN		BIT(0)
> +#define ATMEL_HLCDC_LAYER_CHER(l)		((l)->regs_offset + 0x0)
> +#define ATMEL_HLCDC_LAYER_CHDR(l)		((l)->regs_offset + 0x4)
> +#define ATMEL_HLCDC_LAYER_CHSR(l)		((l)->regs_offset + 0x8)
> +#define ATMEL_HLCDC_LAYER_EN			BIT(0)
>  #define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
>  #define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
>  #define ATMEL_HLCDC_LAYER_RST			BIT(8)
>  
> -#define ATMEL_HLCDC_LAYER_IER			0xc
> -#define ATMEL_HLCDC_LAYER_IDR			0x10
> -#define ATMEL_HLCDC_LAYER_IMR			0x14
> -#define ATMEL_HLCDC_LAYER_ISR			0x18
> +#define ATMEL_HLCDC_LAYER_IER(l)		((l)->regs_offset + 0xc)
> +#define ATMEL_HLCDC_LAYER_IDR(l)		((l)->regs_offset + 0x10)
> +#define ATMEL_HLCDC_LAYER_IMR(l)		((l)->regs_offset + 0x14)
> +#define ATMEL_HLCDC_LAYER_ISR(l)		((l)->regs_offset + 0x18)
>  #define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
>  #define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
> -#define ATMEL_HLCDC_LAYER_DMA_IRQ		BIT(2)
> -#define ATMEL_HLCDC_LAYER_DSCR_IRQ		BIT(3)
> -#define ATMEL_HLCDC_LAYER_ADD_IRQ		BIT(4)
> -#define ATMEL_HLCDC_LAYER_DONE_IRQ		BIT(5)
> -#define ATMEL_HLCDC_LAYER_OVR_IRQ		BIT(6)
> -
> -#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)		(((n) * 0x10) + 0x1c)
> -#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)		(((n) * 0x10) + 0x20)
> -#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)		(((n) * 0x10) + 0x24)
> -#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)		(((n) * 0x10) + 0x28)
> -#define ATMEL_HLCDC_LAYER_CFG(p, c)		(((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
> -
> -#define ATMEL_HLCDC_LAYER_DMA_CFG_ID		0
> -#define ATMEL_HLCDC_LAYER_DMA_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
> +#define ATMEL_HLCDC_LAYER_DMA_IRQ(p)		BIT(2 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_DSCR_IRQ(p)		BIT(3 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_ADD_IRQ(p)		BIT(4 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_DONE_IRQ(p)		BIT(5 + (8 * (p)))
> +#define ATMEL_HLCDC_LAYER_OVR_IRQ(p)		BIT(6 + (8 * (p)))
> +
> +#define ATMEL_HLCDC_LAYER_PLANE_HEAD(l, p)	\
> +	((l)->regs_offset + ((p) * 0x10) + 0x1c)
> +#define ATMEL_HLCDC_LAYER_PLANE_ADDR(l, p)	\
> +	((l)->regs_offset + ((p) * 0x10) + 0x20)
> +#define ATMEL_HLCDC_LAYER_PLANE_CTRL(l, p)	\
> +	((l)->regs_offset + ((p) * 0x10) + 0x24)
> +#define ATMEL_HLCDC_LAYER_PLANE_NEXT(l, p)	\
> +	((l)->regs_offset + ((p) * 0x10) + 0x28)
> +
> +#define ATMEL_HLCDC_LAYER_CFG(l, c)		\
> +	((l)->regs_offset + (l)->cfgs_offset + ((c) * 4))
> +
> +#define ATMEL_HLCDC_LAYER_DMA_CFG(l)		ATMEL_HLCDC_LAYER_CFG(l, 0)
>  #define ATMEL_HLCDC_LAYER_DMA_SIF		BIT(0)
>  #define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK		GENMASK(5, 4)
>  #define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE	(0 << 4)
> @@ -64,48 +63,65 @@
>  #define ATMEL_HLCDC_LAYER_DMA_ROTDIS		BIT(12)
>  #define ATMEL_HLCDC_LAYER_DMA_LOCKDIS		BIT(13)
>  
> -#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID		1
> -#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
> +#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, 1)
>  #define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
>  #define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
>  #define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
> -#define ATMEL_HLCDC_RGB_MODE(m)			(((m) & 0xf) << 4)
> -#define ATMEL_HLCDC_CLUT_MODE(m)		(((m) & 0x3) << 8)
> -#define ATMEL_HLCDC_YUV_MODE(m)			(((m) & 0xf) << 12)
> +#define ATMEL_HLCDC_RGB_MODE(m)			\
> +	(ATMEL_HLCDC_LAYER_RGB | (((m) & 0xf) << 4))
> +#define ATMEL_HLCDC_CLUT_MODE(m)		\
> +	(ATMEL_HLCDC_LAYER_CLUT | (((m) & 0x3) << 8))
> +#define ATMEL_HLCDC_YUV_MODE(m)			\
> +	(ATMEL_HLCDC_LAYER_YUV | (((m) & 0xf) << 12))
>  #define ATMEL_HLCDC_YUV422ROT			BIT(16)
>  #define ATMEL_HLCDC_YUV422SWP			BIT(17)
>  #define ATMEL_HLCDC_DSCALEOPT			BIT(20)
>  
> -#define ATMEL_HLCDC_XRGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
> -#define ATMEL_HLCDC_ARGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
> -#define ATMEL_HLCDC_RGBA4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
> -#define ATMEL_HLCDC_RGB565_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
> -#define ATMEL_HLCDC_ARGB1555_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
> -#define ATMEL_HLCDC_XRGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
> -#define ATMEL_HLCDC_RGB888_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
> -#define ATMEL_HLCDC_ARGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
> -#define ATMEL_HLCDC_RGBA8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
> -
> -#define ATMEL_HLCDC_AYUV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
> -#define ATMEL_HLCDC_YUYV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
> -#define ATMEL_HLCDC_UYVY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
> -#define ATMEL_HLCDC_YVYU_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
> -#define ATMEL_HLCDC_VYUY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
> -#define ATMEL_HLCDC_NV61_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
> -#define ATMEL_HLCDC_YUV422_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
> -#define ATMEL_HLCDC_NV21_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
> -#define ATMEL_HLCDC_YUV420_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
> -
> -#define ATMEL_HLCDC_LAYER_POS_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
> -#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
> -#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
> -#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
> -#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
> -#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
> -#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
> -#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
> -
> -#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
> +#define ATMEL_HLCDC_XRGB4444_MODE		ATMEL_HLCDC_RGB_MODE(0)
> +#define ATMEL_HLCDC_ARGB4444_MODE		ATMEL_HLCDC_RGB_MODE(1)
> +#define ATMEL_HLCDC_RGBA4444_MODE		ATMEL_HLCDC_RGB_MODE(2)
> +#define ATMEL_HLCDC_RGB565_MODE			ATMEL_HLCDC_RGB_MODE(3)
> +#define ATMEL_HLCDC_ARGB1555_MODE		ATMEL_HLCDC_RGB_MODE(4)
> +#define ATMEL_HLCDC_XRGB8888_MODE		ATMEL_HLCDC_RGB_MODE(9)
> +#define ATMEL_HLCDC_RGB888_MODE			ATMEL_HLCDC_RGB_MODE(10)
> +#define ATMEL_HLCDC_ARGB8888_MODE		ATMEL_HLCDC_RGB_MODE(12)
> +#define ATMEL_HLCDC_RGBA8888_MODE		ATMEL_HLCDC_RGB_MODE(13)
> +
> +#define ATMEL_HLCDC_AYUV_MODE			ATMEL_HLCDC_YUV_MODE(0)
> +#define ATMEL_HLCDC_YUYV_MODE			ATMEL_HLCDC_YUV_MODE(1)
> +#define ATMEL_HLCDC_UYVY_MODE			ATMEL_HLCDC_YUV_MODE(2)
> +#define ATMEL_HLCDC_YVYU_MODE			ATMEL_HLCDC_YUV_MODE(3)
> +#define ATMEL_HLCDC_VYUY_MODE			ATMEL_HLCDC_YUV_MODE(4)
> +#define ATMEL_HLCDC_NV61_MODE			ATMEL_HLCDC_YUV_MODE(5)
> +#define ATMEL_HLCDC_YUV422_MODE			ATMEL_HLCDC_YUV_MODE(6)
> +#define ATMEL_HLCDC_NV21_MODE			ATMEL_HLCDC_YUV_MODE(7)
> +#define ATMEL_HLCDC_YUV420_MODE			ATMEL_HLCDC_YUV_MODE(8)
> +
> +#define ATMEL_HLCDC_LAYER_POS_CFG(l)		\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.pos)
> +#define ATMEL_HLCDC_LAYER_POS(x, y)		((x) | ((y) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_SIZE_CFG(l)		\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.size)
> +#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(l)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.memsize)
> +#define ATMEL_HLCDC_LAYER_SIZE(w, h)		(((w) - 1) | (((h) - 1) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(l, p)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.xstride[p])
> +
> +#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(l, p)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.pstride[p])
> +
> +#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(l)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.default_color)
> +#define ATMEL_HLCDC_LAYER_CRKEY_CFG(l)		\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.chroma_key)
> +#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(l)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.chroma_key_mask)
> +
> +#define ATMEL_HLCDC_LAYER_GENERAL_CFG(l)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.general_config)
>  #define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
>  #define ATMEL_HLCDC_LAYER_INV			BIT(1)
>  #define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
> @@ -119,14 +135,32 @@
>  #define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
>  #define ATMEL_HLCDC_LAYER_DISCEN		BIT(11)
>  #define ATMEL_HLCDC_LAYER_GA_SHIFT		16
> -#define ATMEL_HLCDC_LAYER_GA_MASK		GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
> -#define ATMEL_HLCDC_LAYER_GA(x)			((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
> +#define ATMEL_HLCDC_LAYER_GA_MASK		\
> +	GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
> +#define ATMEL_HLCDC_LAYER_GA(x)			\
> +	((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
>  
> -#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
> +#define ATMEL_HLCDC_LAYER_CSC_CFG(l, o)		\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.csc + (o))
>  
> -#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
> +#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(l)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.disc_pos)
> +#define ATMEL_HLCDC_LAYER_DISC_POS(x, y)	((x) | ((y) << 16))
>  
> -#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
> +#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(l)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.disc_size)
> +#define ATMEL_HLCDC_LAYER_DISC_SIZE(w, h)	(((w) - 1) | (((h) - 1) << 16))
> +
> +#define ATMEL_HLCDC_LAYER_SCALER_CFG(l)		\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.scaler_config)
> +#define ATMEL_HLCDC_LAYER_SCALER_FACTORS(x, y)	((x) | ((y) << 16))
> +#define ATMEL_HLCDC_LAYER_SCALER_ENABLE		BIT(31)
> +
> +#define ATMEL_HLCDC_LAYER_XPHICOEFF_CFG(l, i)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.phicoeffs.x + ((i) * 4))
> +
> +#define ATMEL_HLCDC_LAYER_YPHICOEFF_CFG(l, i)	\
> +	ATMEL_HLCDC_LAYER_CFG(l, (l)->layout.phicoeffs.y + ((i) * 4))
>  
>  #define ATMEL_HLCDC_MAX_PLANES			3
>  
> @@ -158,6 +192,8 @@
>   * @chroma_key: chroma key register
>   * @chroma_key_mask: chroma key mask register
>   * @general_config: general layer config register
> + * @sacler_config: scaler factors register
> + * @phicoeffs: X/Y PHI coefficient registers
>   * @disc_pos: discard area position register
>   * @disc_size: discard area size register
>   * @csc: color space conversion register
> @@ -172,33 +208,17 @@ struct atmel_hlcdc_layer_cfg_layout {
>  	int chroma_key;
>  	int chroma_key_mask;
>  	int general_config;
> +	int scaler_config;
> +	struct {
> +		int x;
> +		int y;
> +	} phicoeffs;
>  	int disc_pos;
>  	int disc_size;
>  	int csc;
>  };
>  
>  /**
> - * Atmel HLCDC framebuffer flip structure
> - *
> - * This structure is allocated when someone asked for a layer update (most
> - * likely a DRM plane update, either primary, overlay or cursor plane) and
> - * released when the layer do not need to reference the framebuffer object
> - * anymore (i.e. the layer was disabled or updated).
> - *
> - * @dscrs: DMA descriptors
> - * @fb: the referenced framebuffer object
> - * @ngems: number of GEM objects referenced by the fb element
> - * @status: fb flip operation status
> - */
> -struct atmel_hlcdc_layer_fb_flip {
> -	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
> -	struct drm_flip_task *task;
> -	struct drm_framebuffer *fb;
> -	int ngems;
> -	u32 status;
> -};
> -
> -/**
>   * Atmel HLCDC DMA descriptor structure
>   *
>   * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
> @@ -210,19 +230,20 @@ struct atmel_hlcdc_layer_fb_flip {
>   * @addr: buffer DMA address
>   * @ctrl: DMA transfer options
>   * @next: next DMA descriptor to fetch
> - * @gem_flip: the attached gem_flip operation
> + * @self: descriptor DMA address
>   */
>  struct atmel_hlcdc_dma_channel_dscr {
>  	dma_addr_t addr;
>  	u32 ctrl;
>  	dma_addr_t next;
> -	u32 status;
> +	dma_addr_t self;
>  } __aligned(sizeof(u64));
>  
>  /**
>   * Atmel HLCDC layer types
>   */
>  enum atmel_hlcdc_layer_type {
> +	ATMEL_HLCDC_NO_LAYER,
>  	ATMEL_HLCDC_BASE_LAYER,
>  	ATMEL_HLCDC_OVERLAY_LAYER,
>  	ATMEL_HLCDC_CURSOR_LAYER,
> @@ -251,7 +272,7 @@ struct atmel_hlcdc_formats {
>   * @type: layer type
>   * @id: layer id
>   * @regs_offset: offset of the layer registers from the HLCDC registers base
> - * @nconfigs: number of config registers provided by this layer
> + * @cfgs_offset: CFGX registers offset from the layer registers base
>   * @formats: supported formats
>   * @layout: config registers layout
>   * @max_width: maximum width supported by this layer (0 means unlimited)
> @@ -262,138 +283,11 @@ struct atmel_hlcdc_layer_desc {
>  	enum atmel_hlcdc_layer_type type;
>  	int id;
>  	int regs_offset;
> -	int nconfigs;
> +	int cfgs_offset;
>  	struct atmel_hlcdc_formats *formats;
>  	struct atmel_hlcdc_layer_cfg_layout layout;
>  	int max_width;
>  	int max_height;
>  };
>  
> -/**
> - * Atmel HLCDC Layer Update Slot structure
> - *
> - * This structure stores layer update requests to be applied on next frame.
> - * This is the base structure behind the atomic layer update infrastructure.
> - *
> - * Atomic layer update provides a way to update all layer's parameters
> - * simultaneously. This is needed to avoid incompatible sequential updates
> - * like this one:
> - * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
> - *    (2 planes/buffers)
> - * 2) the format update is applied but the DMA channel for the second
> - *    plane/buffer is not enabled
> - * 3) enable the DMA channel for the second plane
> - *
> - * @fb_flip: fb_flip object
> - * @updated_configs: bitmask used to record modified configs
> - * @configs: new config values
> - */
> -struct atmel_hlcdc_layer_update_slot {
> -	struct atmel_hlcdc_layer_fb_flip *fb_flip;
> -	unsigned long *updated_configs;
> -	u32 *configs;
> -};
> -
> -/**
> - * Atmel HLCDC Layer Update structure
> - *
> - * This structure provides a way to queue layer update requests.
> - *
> - * At a given time there is at most:
> - *  - one pending update request, which means the update request has been
> - *    committed (or validated) and is waiting for the DMA channel(s) to be
> - *    available
> - *  - one request being prepared, which means someone started a layer update
> - *    but has not committed it yet. There cannot be more than one started
> - *    request, because the update lock is taken when starting a layer update
> - *    and release when committing or rolling back the request.
> - *
> - * @slots: update slots. One is used for pending request and the other one
> - *	   for started update request
> - * @pending: the pending slot index or -1 if no request is pending
> - * @next: the started update slot index or -1 no update has been started
> - */
> -struct atmel_hlcdc_layer_update {
> -	struct atmel_hlcdc_layer_update_slot slots[2];
> -	int pending;
> -	int next;
> -};
> -
> -enum atmel_hlcdc_layer_dma_channel_status {
> -	ATMEL_HLCDC_LAYER_DISABLED,
> -	ATMEL_HLCDC_LAYER_ENABLED,
> -	ATMEL_HLCDC_LAYER_DISABLING,
> -};
> -
> -/**
> - * Atmel HLCDC Layer DMA channel structure
> - *
> - * This structure stores information on the DMA channel associated to a
> - * given layer.
> - *
> - * @status: DMA channel status
> - * @cur: current framebuffer
> - * @queue: next framebuffer
> - * @dscrs: allocated DMA descriptors
> - */
> -struct atmel_hlcdc_layer_dma_channel {
> -	enum atmel_hlcdc_layer_dma_channel_status status;
> -	struct atmel_hlcdc_layer_fb_flip *cur;
> -	struct atmel_hlcdc_layer_fb_flip *queue;
> -	struct atmel_hlcdc_dma_channel_dscr *dscrs;
> -};
> -
> -/**
> - * Atmel HLCDC Layer structure
> - *
> - * This structure stores information on the layer instance.
> - *
> - * @desc: layer description
> - * @max_planes: maximum planes/buffers that can be associated with this layer.
> - *	       This depends on the supported formats.
> - * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
> - * @dma: dma channel
> - * @gc: fb flip garbage collector
> - * @update: update handler
> - * @lock: layer lock
> - */
> -struct atmel_hlcdc_layer {
> -	const struct atmel_hlcdc_layer_desc *desc;
> -	int max_planes;
> -	struct atmel_hlcdc *hlcdc;
> -	struct workqueue_struct *wq;
> -	struct drm_flip_work gc;
> -	struct atmel_hlcdc_layer_dma_channel dma;
> -	struct atmel_hlcdc_layer_update update;
> -	spinlock_t lock;
> -};
> -
> -void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
> -
> -int atmel_hlcdc_layer_init(struct drm_device *dev,
> -			   struct atmel_hlcdc_layer *layer,
> -			   const struct atmel_hlcdc_layer_desc *desc);
> -
> -void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
> -			       struct atmel_hlcdc_layer *layer);
> -
> -void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
> -
> -int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
> -
> -void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
> -				  u32 mask, u32 val);
> -
> -void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
> -				     struct drm_framebuffer *fb,
> -				     unsigned int *offsets);
> -
> -void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
> -					   void (*finished)(void *data),
> -					   void *finished_data);
> -
> -void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
> -
> -void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
> -
>  #endif /* DRM_ATMEL_HLCDC_LAYER_H */
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 246ed1e33d8a..4fcd91f3d124 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> @@ -37,7 +37,6 @@
>   * @xstride: value to add to the pixel pointer between each line
>   * @pstride: value to add to the pixel pointer between each pixel
>   * @nplanes: number of planes (deduced from pixel_format)
> - * @prepared: plane update has been prepared
>   */
>  struct atmel_hlcdc_plane_state {
>  	struct drm_plane_state base;
> @@ -67,7 +66,9 @@ struct atmel_hlcdc_plane_state {
>  	int xstride[ATMEL_HLCDC_MAX_PLANES];
>  	int pstride[ATMEL_HLCDC_MAX_PLANES];
>  	int nplanes;
> -	bool prepared;
> +
> +	/* DMA descriptors. */
> +	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
>  };
>  
>  static inline struct atmel_hlcdc_plane_state *
> @@ -76,6 +77,27 @@ drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
>  	return container_of(s, struct atmel_hlcdc_plane_state, base);
>  }
>  
> +/**
> + * Atmel HLCDC Plane.
> + *
> + * @base: base DRM plane structure
> + * @desc: HLCDC layer desc structure
> + * @properties: pointer to the property definitions structure
> + * @regmap: HLCDC regmap
> + */
> +struct atmel_hlcdc_plane {
> +	struct drm_plane base;
> +	const struct atmel_hlcdc_layer_desc *desc;
> +	struct atmel_hlcdc_plane_properties *properties;
> +	struct regmap *regmap;
> +};
> +
> +static inline struct atmel_hlcdc_plane *
> +drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
> +{
> +	return container_of(p, struct atmel_hlcdc_plane, base);
> +}
> +
>  #define SUBPIXEL_MASK			0xffff
>  
>  static uint32_t rgb_formats[] = {
> @@ -259,130 +281,145 @@ static u32 heo_upscaling_ycoef[] = {
>  	0x00205907,
>  };
>  
> +#define ATMEL_HLCDC_XPHIDEF	4
> +#define ATMEL_HLCDC_YPHIDEF	4
> +
> +static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
> +						  u32 dstsize,
> +						  u32 phidef)
> +{
> +	u32 factor, max_memsize;
> +
> +	factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
> +	max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
> +
> +	if (max_memsize > srcsize - 1)
> +		factor--;
> +
> +	return factor;
> +}
> +
>  static void
> -atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
> -				      struct atmel_hlcdc_plane_state *state)
> +atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
> +				      const u32 *coeff_tab, int size,
> +				      unsigned int reg_offs)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> -
> -	if (layout->size)
> -		atmel_hlcdc_layer_update_cfg(&plane->layer,
> -					     layout->size,
> -					     0xffffffff,
> -					     (state->crtc_w - 1) |
> -					     ((state->crtc_h - 1) << 16));
> -
> -	if (layout->memsize)
> -		atmel_hlcdc_layer_update_cfg(&plane->layer,
> -					     layout->memsize,
> -					     0xffffffff,
> -					     (state->src_w - 1) |
> -					     ((state->src_h - 1) << 16));
> -
> -	if (layout->pos)
> -		atmel_hlcdc_layer_update_cfg(&plane->layer,
> -					     layout->pos,
> -					     0xffffffff,
> -					     state->crtc_x |
> -					     (state->crtc_y  << 16));
> -
> -	/* TODO: rework the rescaling part */
> -	if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) {
> -		u32 factor_reg = 0;
> -
> -		if (state->crtc_w != state->src_w) {
> -			int i;
> -			u32 factor;
> -			u32 *coeff_tab = heo_upscaling_xcoef;
> -			u32 max_memsize;
> -
> -			if (state->crtc_w < state->src_w)
> -				coeff_tab = heo_downscaling_xcoef;
> -			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
> -				atmel_hlcdc_layer_update_cfg(&plane->layer,
> -							     17 + i,
> -							     0xffffffff,
> -							     coeff_tab[i]);
> -			factor = ((8 * 256 * state->src_w) - (256 * 4)) /
> -				 state->crtc_w;
> -			factor++;
> -			max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
> -				      2048;
> -			if (max_memsize > state->src_w)
> -				factor--;
> -			factor_reg |= factor | 0x80000000;
> -		}
> +	struct regmap *regmap = plane->regmap;
> +	int i;
>  
> -		if (state->crtc_h != state->src_h) {
> -			int i;
> -			u32 factor;
> -			u32 *coeff_tab = heo_upscaling_ycoef;
> -			u32 max_memsize;
> -
> -			if (state->crtc_h < state->src_h)
> -				coeff_tab = heo_downscaling_ycoef;
> -			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
> -				atmel_hlcdc_layer_update_cfg(&plane->layer,
> -							     33 + i,
> -							     0xffffffff,
> -							     coeff_tab[i]);
> -			factor = ((8 * 256 * state->src_h) - (256 * 4)) /
> -				 state->crtc_h;
> -			factor++;
> -			max_memsize = ((factor * state->crtc_h) + (256 * 4)) /
> -				      2048;
> -			if (max_memsize > state->src_h)
> -				factor--;
> -			factor_reg |= (factor << 16) | 0x80000000;
> -		}
> +	for (i = 0; i < size; i++)
> +		regmap_write(regmap, reg_offs + (i * 4), coeff_tab[i]);
> +}
>  
> -		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
> -					     factor_reg);
> +void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
> +				    struct atmel_hlcdc_plane_state *state)
> +{
> +	const struct atmel_hlcdc_layer_desc *desc = plane->desc;
> +	struct regmap *regmap = plane->regmap;
> +	u32 xfactor, yfactor;
> +
> +	if (!desc->layout.scaler_config)
> +		return;
> +
> +	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_SCALER_CFG(desc), 0);
> +		return;
> +	}
> +
> +	if (desc->layout.phicoeffs.x) {
> +		xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
> +							state->crtc_w,
> +							ATMEL_HLCDC_XPHIDEF);
> +
> +		yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
> +							state->crtc_h,
> +							ATMEL_HLCDC_YPHIDEF);
> +
> +		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
> +				state->crtc_w < state->src_w ?
> +				heo_downscaling_xcoef :
> +				heo_upscaling_xcoef,
> +				ARRAY_SIZE(heo_upscaling_xcoef),
> +				ATMEL_HLCDC_LAYER_XPHICOEFF_CFG(desc, 0));
> +
> +		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
> +				state->crtc_h < state->src_h ?
> +				heo_downscaling_ycoef :
> +				heo_upscaling_ycoef,
> +				ARRAY_SIZE(heo_upscaling_ycoef),
> +				ATMEL_HLCDC_LAYER_YPHICOEFF_CFG(desc, 0));
>  	} else {
> -		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 0);
> +		xfactor = (1024 * state->src_w) / state->crtc_w;
> +		yfactor = (1024 * state->src_h) / state->crtc_h;
>  	}
> +
> +	regmap_write(regmap,
> +		     ATMEL_HLCDC_LAYER_SCALER_CFG(desc),
> +		     ATMEL_HLCDC_LAYER_SCALER_ENABLE |
> +		     ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor, yfactor));
> +}
> +
> +static void
> +atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
> +				      struct atmel_hlcdc_plane_state *state)
> +{
> +	const struct atmel_hlcdc_layer_desc *desc = plane->desc;
> +	struct regmap *regmap = plane->regmap;
> +
> +	if (desc->layout.size)
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_SIZE_CFG(desc),
> +			     ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
> +						    state->crtc_h));
> +
> +	if (desc->layout.memsize)
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_MEMSIZE_CFG(desc),
> +			     ATMEL_HLCDC_LAYER_SIZE(state->src_w,
> +						    state->src_h));
> +
> +	if (desc->layout.pos)
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_POS_CFG(desc),
> +			     ATMEL_HLCDC_LAYER_POS(state->crtc_x,
> +						   state->crtc_y));
> +
> +	atmel_hlcdc_plane_setup_scaler(plane, state);
>  }
>  
>  static void
>  atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
>  					struct atmel_hlcdc_plane_state *state)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
>  	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
> +	struct regmap *regmap = plane->regmap;
>  
>  	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
> +		u32 format = state->base.fb->pixel_format;
> +
>  		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
>  		       ATMEL_HLCDC_LAYER_ITER;
>  
> -		if (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format))
> +		if (atmel_hlcdc_format_embeds_alpha(format))
>  			cfg |= ATMEL_HLCDC_LAYER_LAEN;
>  		else
>  			cfg |= ATMEL_HLCDC_LAYER_GAEN |
>  			       ATMEL_HLCDC_LAYER_GA(state->alpha);
>  	}
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
> -				     ATMEL_HLCDC_LAYER_DMA_BLEN_MASK |
> -				     ATMEL_HLCDC_LAYER_DMA_SIF,
> -				     ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 |
> -				     state->ahb_id);
> -
> -	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
> -				     ATMEL_HLCDC_LAYER_ITER2BL |
> -				     ATMEL_HLCDC_LAYER_ITER |
> -				     ATMEL_HLCDC_LAYER_GAEN |
> -				     ATMEL_HLCDC_LAYER_GA_MASK |
> -				     ATMEL_HLCDC_LAYER_LAEN |
> -				     ATMEL_HLCDC_LAYER_OVR |
> -				     ATMEL_HLCDC_LAYER_DMA, cfg);
> +	regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_DMA_CFG(plane->desc),
> +			   ATMEL_HLCDC_LAYER_DMA_BLEN_MASK |
> +			   ATMEL_HLCDC_LAYER_DMA_SIF,
> +			   ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id);
> +
> +	regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_GENERAL_CFG(plane->desc),
> +			   ATMEL_HLCDC_LAYER_ITER2BL | ATMEL_HLCDC_LAYER_ITER |
> +			   ATMEL_HLCDC_LAYER_GAEN | ATMEL_HLCDC_LAYER_GA_MASK |
> +			   ATMEL_HLCDC_LAYER_LAEN | ATMEL_HLCDC_LAYER_OVR |
> +			   ATMEL_HLCDC_LAYER_DMA, cfg);
>  }
>  
>  static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
>  					struct atmel_hlcdc_plane_state *state)
>  {
> +	struct regmap *regmap = plane->regmap;
>  	u32 cfg;
>  	int ret;
>  
> @@ -396,10 +433,7 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
>  	    drm_rotation_90_or_270(state->base.rotation))
>  		cfg |= ATMEL_HLCDC_YUV422ROT;
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
> -				     0xffffffff,
> -				     cfg);
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_FORMAT_CFG(plane->desc), cfg);
>  
>  	/*
>  	 * Rotation optimization is not working on RGB888 (rotation is still
> @@ -410,36 +444,51 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
>  	else
>  		cfg = 0;
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     ATMEL_HLCDC_LAYER_DMA_CFG_ID,
> -				     ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
> +	regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_DMA_CFG(plane->desc),
> +			   ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
>  }
>  
>  static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
>  					struct atmel_hlcdc_plane_state *state)
>  {
> -	struct atmel_hlcdc_layer *layer = &plane->layer;
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -							&layer->desc->layout;
> +	const struct atmel_hlcdc_layer_desc *desc = plane->desc;
> +	struct drm_framebuffer *fb = state->base.fb;
> +	struct regmap *regmap = plane->regmap;
> +	u32 sr;
>  	int i;
>  
> -	atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
> -					state->offsets);
> +	regmap_read(regmap, ATMEL_HLCDC_LAYER_CHSR(desc), &sr);
>  
>  	for (i = 0; i < state->nplanes; i++) {
> -		if (layout->xstride[i]) {
> -			atmel_hlcdc_layer_update_cfg(&plane->layer,
> -						layout->xstride[i],
> -						0xffffffff,
> -						state->xstride[i]);
> -		}
> +		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
>  
> -		if (layout->pstride[i]) {
> -			atmel_hlcdc_layer_update_cfg(&plane->layer,
> -						layout->pstride[i],
> -						0xffffffff,
> -						state->pstride[i]);
> +		state->dscrs[i]->addr = gem->paddr + state->offsets[i];
> +
> +		regmap_write(regmap,
> +			     ATMEL_HLCDC_LAYER_PLANE_HEAD(desc, i),
> +			     state->dscrs[i]->self);
> +
> +		if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
> +			regmap_write(regmap,
> +				     ATMEL_HLCDC_LAYER_PLANE_ADDR(desc, i),
> +				     state->dscrs[i]->addr);
> +			regmap_write(regmap,
> +				     ATMEL_HLCDC_LAYER_PLANE_CTRL(desc, i),
> +				     state->dscrs[i]->ctrl);
> +			regmap_write(regmap,
> +				     ATMEL_HLCDC_LAYER_PLANE_NEXT(desc, i),
> +				     state->dscrs[i]->self);
>  		}
> +
> +		if (desc->layout.xstride[i])
> +			regmap_write(regmap,
> +				     ATMEL_HLCDC_LAYER_XSTRIDE_CFG(desc, i),
> +				     state->xstride[i]);
> +
> +		if (desc->layout.pstride[i])
> +			regmap_write(regmap,
> +				     ATMEL_HLCDC_LAYER_PSTRIDE_CFG(desc, i),
> +				     state->pstride[i]);
>  	}
>  }
>  
> @@ -489,7 +538,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
>  	struct drm_plane *ovl;
>  
>  	primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
> -	layout = &primary->layer.desc->layout;
> +	layout = &primary->desc->layout;
>  	if (!layout->disc_pos || !layout->disc_size)
>  		return 0;
>  
> @@ -548,8 +597,7 @@ static void
>  atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
>  				   struct atmel_hlcdc_plane_state *state)
>  {
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
> +	struct regmap *regmap = plane->regmap;
>  	int disc_surface = 0;
>  
>  	if (!state->disc_updated)
> @@ -557,23 +605,19 @@ atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
>  
>  	disc_surface = state->disc_h * state->disc_w;
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
> -				ATMEL_HLCDC_LAYER_DISCEN,
> -				disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
> +	regmap_update_bits(regmap, ATMEL_HLCDC_LAYER_GENERAL_CFG(plane->desc),
> +			   ATMEL_HLCDC_LAYER_DISCEN,
> +			   disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
>  
>  	if (!disc_surface)
>  		return;
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     layout->disc_pos,
> -				     0xffffffff,
> -				     state->disc_x | (state->disc_y << 16));
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_DISC_POS_CFG(plane->desc),
> +		     ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x, state->disc_y));
>  
> -	atmel_hlcdc_layer_update_cfg(&plane->layer,
> -				     layout->disc_size,
> -				     0xffffffff,
> -				     (state->disc_w - 1) |
> -				     ((state->disc_h - 1) << 16));
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(plane->desc),
> +		     ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
> +						 state->disc_h));
>  }
>  
>  static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
> @@ -582,8 +626,6 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
>  	struct atmel_hlcdc_plane_state *state =
>  				drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -	const struct atmel_hlcdc_layer_cfg_layout *layout =
> -						&plane->layer.desc->layout;
>  	struct drm_framebuffer *fb = state->base.fb;
>  	const struct drm_display_mode *mode;
>  	struct drm_crtc_state *crtc_state;
> @@ -726,21 +768,19 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	state->crtc_w = patched_crtc_w;
>  	state->crtc_h = patched_crtc_h;
>  
> -	if (!layout->size &&
> +	if (!plane->desc->layout.size &&
>  	    (mode->hdisplay != state->crtc_w ||
>  	     mode->vdisplay != state->crtc_h))
>  		return -EINVAL;
>  
> -	if (plane->layer.desc->max_height &&
> -	    state->crtc_h > plane->layer.desc->max_height)
> +	if (plane->desc->max_height && state->crtc_h > plane->desc->max_height)
>  		return -EINVAL;
>  
> -	if (plane->layer.desc->max_width &&
> -	    state->crtc_w > plane->layer.desc->max_width)
> +	if (plane->desc->max_width && state->crtc_w > plane->desc->max_width)
>  		return -EINVAL;
>  
>  	if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
> -	    (!layout->memsize ||
> +	    (!plane->desc->layout.memsize ||
>  	     atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)))
>  		return -EINVAL;
>  
> @@ -754,65 +794,14 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  	return 0;
>  }
>  
> -static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
> -					struct drm_plane_state *new_state)
> -{
> -	/*
> -	 * FIXME: we should avoid this const -> non-const cast but it's
> -	 * currently the only solution we have to modify the ->prepared
> -	 * state and rollback the update request.
> -	 * Ideally, we should rework the code to attach all the resources
> -	 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
> -	 * but this require a complete rework of the atmel_hlcdc_layer
> -	 * code.
> -	 */
> -	struct drm_plane_state *s = (struct drm_plane_state *)new_state;
> -	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> -	struct atmel_hlcdc_plane_state *state =
> -			drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -	int ret;
> -
> -	ret = atmel_hlcdc_layer_update_start(&plane->layer);
> -	if (!ret)
> -		state->prepared = true;
> -
> -	return ret;
> -}
> -
> -static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
> -					 struct drm_plane_state *old_state)
> -{
> -	/*
> -	 * FIXME: we should avoid this const -> non-const cast but it's
> -	 * currently the only solution we have to modify the ->prepared
> -	 * state and rollback the update request.
> -	 * Ideally, we should rework the code to attach all the resources
> -	 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
> -	 * but this require a complete rework of the atmel_hlcdc_layer
> -	 * code.
> -	 */
> -	struct drm_plane_state *s = (struct drm_plane_state *)old_state;
> -	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> -	struct atmel_hlcdc_plane_state *state =
> -			drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -
> -	/*
> -	 * The Request has already been applied or cancelled, nothing to do
> -	 * here.
> -	 */
> -	if (!state->prepared)
> -		return;
> -
> -	atmel_hlcdc_layer_update_rollback(&plane->layer);
> -	state->prepared = false;
> -}
> -
>  static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
>  					    struct drm_plane_state *old_s)
>  {
>  	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
>  	struct atmel_hlcdc_plane_state *state =
>  			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
> +	struct regmap *regmap = plane->regmap;
> +	u32 sr;
>  
>  	if (!p->state->crtc || !p->state->fb)
>  		return;
> @@ -823,15 +812,37 @@ static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
>  	atmel_hlcdc_plane_update_buffers(plane, state);
>  	atmel_hlcdc_plane_update_disc_area(plane, state);
>  
> -	atmel_hlcdc_layer_update_commit(&plane->layer);
> +	/* Enable the overrun interrupts. */
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_IER(plane->desc),
> +		     ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
> +		     ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
> +		     ATMEL_HLCDC_LAYER_OVR_IRQ(2));
> +
> +	/* Apply the new config at the next SOF event. */
> +	regmap_read(regmap, ATMEL_HLCDC_LAYER_CHSR(plane->desc), &sr);
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_CHER(plane->desc),
> +		     ATMEL_HLCDC_LAYER_UPDATE |
> +		     (sr & ATMEL_HLCDC_LAYER_EN ?
> +		      ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
>  }
>  
>  static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
>  					     struct drm_plane_state *old_state)
>  {
>  	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> +	struct regmap *regmap = plane->regmap;
> +	unsigned int isr;
> +
> +	/* Disable interrupts */
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_IDR(plane->desc), 0xffffffff);
> +
> +	/* Disable the layer */
> +	regmap_write(regmap, ATMEL_HLCDC_LAYER_CHDR(plane->desc),
> +		     ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
> +		     ATMEL_HLCDC_LAYER_UPDATE);
>  
> -	atmel_hlcdc_layer_disable(&plane->layer);
> +	/* Clear all pending interrupts */
> +	regmap_read(regmap, ATMEL_HLCDC_LAYER_ISR(plane->desc), &isr);
>  }
>  
>  static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
> @@ -841,10 +852,7 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
>  	if (plane->base.fb)
>  		drm_framebuffer_unreference(plane->base.fb);
>  
> -	atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
> -
>  	drm_plane_cleanup(p);
> -	devm_kfree(p->dev->dev, plane);
>  }
>  
>  static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
> @@ -884,25 +892,23 @@ static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
>  }
>  
>  static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
> -					     const struct atmel_hlcdc_layer_desc *desc,
> -					     struct atmel_hlcdc_plane_properties *props)
> +				struct atmel_hlcdc_plane_properties *props)
>  {
> -	struct regmap *regmap = plane->layer.hlcdc->regmap;
> +	struct regmap *regmap = plane->regmap;
>  
> -	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> -	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
> +	if (plane->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> +	    plane->desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
>  		drm_object_attach_property(&plane->base.base,
>  					   props->alpha, 255);
>  
>  		/* Set default alpha value */
>  		regmap_update_bits(regmap,
> -				desc->regs_offset +
> -				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
> -				ATMEL_HLCDC_LAYER_GA_MASK,
> -				ATMEL_HLCDC_LAYER_GA_MASK);
> +				   ATMEL_HLCDC_LAYER_GENERAL_CFG(plane->desc),
> +				   ATMEL_HLCDC_LAYER_GA_MASK,
> +				   ATMEL_HLCDC_LAYER_GA_MASK);
>  	}
>  
> -	if (desc->layout.xstride && desc->layout.pstride) {
> +	if (plane->desc->layout.xstride && plane->desc->layout.pstride) {
>  		int ret;
>  
>  		ret = drm_plane_create_rotation_property(&plane->base,
> @@ -915,36 +921,81 @@ static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
>  			return ret;
>  	}
>  
> -	if (desc->layout.csc) {
> +	if (plane->desc->layout.csc) {
>  		/*
>  		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
>  		 * userspace modify these factors (using a BLOB property ?).
>  		 */
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_CSC_CFG(plane->desc, 0),
>  			     0x4c900091);
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_CSC_CFG(plane->desc, 1),
>  			     0x7a5f5090);
> -		regmap_write(regmap,
> -			     desc->regs_offset +
> -			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
> +		regmap_write(regmap, ATMEL_HLCDC_LAYER_CSC_CFG(plane->desc, 2),
>  			     0x40040890);
>  	}
>  
>  	return 0;
>  }
>  
> +void atmel_hlcdc_plane_irq(struct drm_plane *p)
> +{
> +	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> +	struct regmap *regmap = plane->regmap;
> +	u32 isr;
> +
> +	regmap_read(regmap, ATMEL_HLCDC_LAYER_ISR(plane->desc), &isr);
> +
> +	/*
> +	 * There's not much we can do in case of overrun except informing
> +	 * the user. However, we are in interrupt context here, hence the
> +	 * use of dev_dbg().
> +	 */
> +	if (isr &
> +	    (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
> +	     ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
> +		dev_dbg(p->dev->dev, "overrun on plane %s\n",
> +			plane->desc->name);
> +}
> +
>  static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
> -	.prepare_fb = atmel_hlcdc_plane_prepare_fb,
> -	.cleanup_fb = atmel_hlcdc_plane_cleanup_fb,
>  	.atomic_check = atmel_hlcdc_plane_atomic_check,
>  	.atomic_update = atmel_hlcdc_plane_atomic_update,
>  	.atomic_disable = atmel_hlcdc_plane_atomic_disable,
>  };
>  
> +static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
> +					 struct atmel_hlcdc_plane_state *state)
> +{
> +	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
> +		struct atmel_hlcdc_dma_channel_dscr *dscr;
> +		dma_addr_t dscr_dma;
> +
> +		dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
> +		if (!dscr)
> +			goto err;
> +
> +		dscr->addr = 0;
> +		dscr->next = dscr_dma;
> +		dscr->self = dscr_dma;
> +		dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
> +
> +		state->dscrs[i] = dscr;
> +	}
> +
> +	return 0;
> +
> +err:
> +	for (i--; i >= 0; i--) {
> +		dma_pool_free(dc->dscrpool, state->dscrs[i],
> +			      state->dscrs[i]->self);
> +	}
> +
> +	return -ENOMEM;
> +}
> +
>  static void atmel_hlcdc_plane_reset(struct drm_plane *p)
>  {
>  	struct atmel_hlcdc_plane_state *state;
> @@ -961,6 +1012,13 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
>  
>  	state = kzalloc(sizeof(*state), GFP_KERNEL);
>  	if (state) {
> +		if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
> +			kfree(state);
> +			dev_err(p->dev->dev,
> +				"Failed to allocate initial plane state\n");
> +			return;
> +		}
> +
>  		state->alpha = 255;
>  		p->state = &state->base;
>  		p->state->plane = p;
> @@ -979,7 +1037,13 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
>  		return NULL;
>  
>  	copy->disc_updated = false;
> -	copy->prepared = false;
> +	copy->nplanes = 0;
> +	memset(copy->dscrs, 0, sizeof(copy->dscrs));
> +
> +	if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
> +		kfree(copy);
> +		return NULL;
> +	}
>  
>  	if (copy->base.fb)
>  		drm_framebuffer_reference(copy->base.fb);
> @@ -987,11 +1051,18 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
>  	return &copy->base;
>  }
>  
> -static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane,
> +static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
>  						   struct drm_plane_state *s)
>  {
>  	struct atmel_hlcdc_plane_state *state =
>  			drm_plane_state_to_atmel_hlcdc_plane_state(s);
> +	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
> +		dma_pool_free(dc->dscrpool, state->dscrs[i],
> +			      state->dscrs[i]->self);
> +	}
>  
>  	if (s->fb)
>  		drm_framebuffer_unreference(s->fb);
> @@ -1011,22 +1082,22 @@ static struct drm_plane_funcs layer_plane_funcs = {
>  	.atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
>  };
>  
> -static struct atmel_hlcdc_plane *
> -atmel_hlcdc_plane_create(struct drm_device *dev,
> -			 const struct atmel_hlcdc_layer_desc *desc,
> -			 struct atmel_hlcdc_plane_properties *props)
> +static int atmel_hlcdc_plane_create(struct drm_device *dev,
> +				    const struct atmel_hlcdc_layer_desc *desc,
> +				    struct atmel_hlcdc_plane_properties *props)
>  {
> +	struct atmel_hlcdc_dc *dc = dev->dev_private;
>  	struct atmel_hlcdc_plane *plane;
>  	enum drm_plane_type type;
>  	int ret;
>  
>  	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
>  	if (!plane)
> -		return ERR_PTR(-ENOMEM);
> +		return -ENOMEM;
>  
> -	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
> -	if (ret)
> -		return ERR_PTR(ret);
> +	plane->regmap = dc->hlcdc->regmap;
> +	plane->desc = desc;
> +	plane->properties = props;
>  
>  	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
>  		type = DRM_PLANE_TYPE_PRIMARY;
> @@ -1040,17 +1111,20 @@ atmel_hlcdc_plane_create(struct drm_device *dev,
>  				       desc->formats->formats,
>  				       desc->formats->nformats, type, NULL);
>  	if (ret)
> -		return ERR_PTR(ret);
> +		return ret;
>  
>  	drm_plane_helper_add(&plane->base,
>  			     &atmel_hlcdc_layer_plane_helper_funcs);
>  
>  	/* Set default property values*/
> -	ret = atmel_hlcdc_plane_init_properties(plane, desc, props);
> +	ret = atmel_hlcdc_plane_init_properties(plane, props);
>  	if (ret)
> -		return ERR_PTR(ret);
> +		return ret;
> +
> +	dc->layers[desc->id].type = desc->type;
> +	dc->layers[desc->id].plane = &plane->base;
>  
> -	return plane;
> +	return 0;
>  }
>  
>  static struct atmel_hlcdc_plane_properties *
> @@ -1069,72 +1143,34 @@ atmel_hlcdc_plane_create_properties(struct drm_device *dev)
>  	return props;
>  }
>  
> -struct atmel_hlcdc_planes *
> -atmel_hlcdc_create_planes(struct drm_device *dev)
> +int atmel_hlcdc_create_planes(struct drm_device *dev)
>  {
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
>  	struct atmel_hlcdc_plane_properties *props;
> -	struct atmel_hlcdc_planes *planes;
>  	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
>  	int nlayers = dc->desc->nlayers;
> -	int i;
> -
> -	planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
> -	if (!planes)
> -		return ERR_PTR(-ENOMEM);
> -
> -	for (i = 0; i < nlayers; i++) {
> -		if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
> -			planes->noverlays++;
> -	}
> -
> -	if (planes->noverlays) {
> -		planes->overlays = devm_kzalloc(dev->dev,
> -						planes->noverlays *
> -						sizeof(*planes->overlays),
> -						GFP_KERNEL);
> -		if (!planes->overlays)
> -			return ERR_PTR(-ENOMEM);
> -	}
> +	int i, ret;
>  
>  	props = atmel_hlcdc_plane_create_properties(dev);
>  	if (IS_ERR(props))
> -		return ERR_CAST(props);
> +		return PTR_ERR(props);
>  
> -	planes->noverlays = 0;
> -	for (i = 0; i < nlayers; i++) {
> -		struct atmel_hlcdc_plane *plane;
> +	dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
> +				sizeof(struct atmel_hlcdc_dma_channel_dscr),
> +				sizeof(u64), 0);
> +	if (!dc->dscrpool)
> +		return -ENOMEM;
>  
> -		if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
> +	for (i = 0; i < nlayers; i++) {
> +		if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
> +		    descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
> +		    descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
>  			continue;
>  
> -		plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
> -		if (IS_ERR(plane))
> -			return ERR_CAST(plane);
> -
> -		plane->properties = props;
> -
> -		switch (descs[i].type) {
> -		case ATMEL_HLCDC_BASE_LAYER:
> -			if (planes->primary)
> -				return ERR_PTR(-EINVAL);
> -			planes->primary = plane;
> -			break;
> -
> -		case ATMEL_HLCDC_OVERLAY_LAYER:
> -			planes->overlays[planes->noverlays++] = plane;
> -			break;
> -
> -		case ATMEL_HLCDC_CURSOR_LAYER:
> -			if (planes->cursor)
> -				return ERR_PTR(-EINVAL);
> -			planes->cursor = plane;
> -			break;
> -
> -		default:
> -			break;
> -		}
> +		ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
> +		if (ret)
> +			return ret;
>  	}
>  
> -	return planes;
> +	return 0;
>  }
> 


-- 
Nicolas Ferre


More information about the dri-devel mailing list