diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2017-02-06 18:57:19 +0100 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@free-electrons.com> | 2017-02-28 11:57:56 +0100 |
commit | 9a45d33cdf82f138cc290236cde83483c6567394 (patch) | |
tree | 2b42c76e129812506c0087debd510baa8d409606 | |
parent | 6140cf20341975400fb2ee7a72fa6316c1bc74f0 (diff) | |
download | linux-9a45d33cdf82f138cc290236cde83483c6567394.tar.bz2 |
drm/atmel-hlcdc: Simplify the HLCDC layer logic
An HLCDC layers in Atmel's nomenclature is either a DRM plane or a 'Post
Processing Layer' which can be used to output the results of the HLCDC
composition in a memory buffer.
atmel_hlcdc_layer.c was designed to be generic enough to be re-usable in
both cases, but we're not exposing the post-processing layer yet, and
even if we were, I'm not sure the code would provide the necessary tools
to manipulate this kind of layer.
Moreover, the code in atmel_hlcdc_{plane,layer}.c was designed before the
atomic modesetting API, and was trying solve the
check-setting/commit-if-ok/rollback-otherwise problem, which is now
entirely solved by the existing core infrastructure.
And finally, the code in atmel_hlcdc_layer.c is over-complicated compared
to what we really need. This rework is a good excuse to simplify it. Note
that this rework solves an existing resource leak (leading to a -EBUSY
error) which I failed to clearly identify.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Eric Anholt <eric@anholt.net>
Tested-by: Nicolas Ferre <nicolas.ferre@microchip.com>
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 39 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 82 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 361 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c | 666 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h | 399 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 645 |
7 files changed, 697 insertions, 1496 deletions
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 fabeeea0e899..6b50fb706c0e 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -466,8 +466,8 @@ static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { int atmel_hlcdc_crtc_create(struct drm_device *dev) { + struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL; struct atmel_hlcdc_dc *dc = dev->dev_private; - struct atmel_hlcdc_planes *planes = dc->planes; struct atmel_hlcdc_crtc *crtc; int ret; int i; @@ -478,20 +478,41 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev) crtc->dc = dc; - ret = drm_crtc_init_with_planes(dev, &crtc->base, - &planes->primary->base, - planes->cursor ? &planes->cursor->base : NULL, - &atmel_hlcdc_crtc_funcs, NULL); + for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { + if (!dc->layers[i]) + continue; + + switch (dc->layers[i]->desc->type) { + case ATMEL_HLCDC_BASE_LAYER: + primary = atmel_hlcdc_layer_to_plane(dc->layers[i]); + break; + + case ATMEL_HLCDC_CURSOR_LAYER: + cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]); + break; + + default: + break; + } + } + + ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base, + &cursor->base, &atmel_hlcdc_crtc_funcs, + NULL); if (ret < 0) goto fail; crtc->id = drm_crtc_index(&crtc->base); - if (planes->cursor) - planes->cursor->base.possible_crtcs = 1 << crtc->id; + for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { + struct atmel_hlcdc_plane *overlay; - for (i = 0; i < planes->noverlays; i++) - planes->overlays[i]->base.possible_crtcs = 1 << crtc->id; + if (dc->layers[i] && + dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) { + overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]); + overlay->base.possible_crtcs = 1 << crtc->id; + } + } drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); drm_crtc_vblank_reset(&crtc->base); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index fd1a2d0c870d..970bd87d7cce 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..da7f25a59be5 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 describes the capabilities provided by a given layer. + * + * @name: layer name + * @type: layer type + * @id: layer id + * @regs_offset: offset of the layer registers from the HLCDC registers base + * @cfgs_offset: CFGX registers offset from the layer registers base + * @formats: supported formats + * @layout: config registers layout + * @max_width: maximum width supported by this layer (0 means unlimited) + * @max_height: maximum height supported by this layer (0 means unlimited) + */ +struct atmel_hlcdc_layer_desc { + const char *name; + enum atmel_hlcdc_layer_type type; + int id; + int regs_offset; + int cfgs_offset; + struct atmel_hlcdc_formats *formats; + struct atmel_hlcdc_layer_cfg_layout layout; int max_width; int max_height; - int max_spw; - int max_vpw; - int max_hpw; - bool conflicting_output_formats; - const struct atmel_hlcdc_layer_desc *layers; - int nlayers; }; /** - * Atmel HLCDC Plane properties. + * Atmel HLCDC Layer. * - * This structure stores plane property definitions. + * A layer can be a DRM plane of a post processing layer used to render + * HLCDC composition into memory. * - * @alpha: alpha blending (or transparency) property - * @rotation: rotation property + * @desc: layer description + * @regmap: pointer to the HLCDC regmap */ -struct atmel_hlcdc_plane_properties { - struct drm_property *alpha; +struct atmel_hlcdc_layer { + const struct atmel_hlcdc_layer_desc *desc; + struct regmap *regmap; }; /** @@ -89,7 +285,6 @@ struct atmel_hlcdc_plane_properties { * @base: base DRM plane structure * @layer: HLCDC layer structure * @properties: pointer to the property definitions structure - * @rotation: current rotation status */ struct atmel_hlcdc_plane { struct drm_plane base; @@ -104,47 +299,73 @@ drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p) } static inline struct atmel_hlcdc_plane * -atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l) +atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *layer) { - return container_of(l, struct atmel_hlcdc_plane, layer); + return container_of(layer, struct atmel_hlcdc_plane, layer); } /** - * Atmel HLCDC Planes. + * Atmel HLCDC Display Controller description structure. * - * This structure stores the instantiated HLCDC Planes and can be accessed by - * the HLCDC Display Controller or the HLCDC CRTC. + * This structure describes the HLCDC IP capabilities and depends on the + * HLCDC IP version (or Atmel SoC family). * - * @primary: primary plane - * @cursor: hardware cursor plane - * @overlays: overlay plane table - * @noverlays: number of overlay planes + * @min_width: minimum width supported by the Display Controller + * @min_height: minimum height supported by the Display Controller + * @max_width: maximum width supported by the Display Controller + * @max_height: maximum height supported by the Display Controller + * @max_spw: maximum vertical/horizontal pulse width + * @max_vpw: maximum vertical back/front porch width + * @max_hpw: maximum horizontal back/front porch width + * @conflicting_output_formats: true if RGBXXX output formats conflict with + * each other. + * @layers: a layer description table describing available layers + * @nlayers: layer description table size */ -struct atmel_hlcdc_planes { - struct atmel_hlcdc_plane *primary; - struct atmel_hlcdc_plane *cursor; - struct atmel_hlcdc_plane **overlays; - int noverlays; +struct atmel_hlcdc_dc_desc { + int min_width; + int min_height; + int max_width; + int max_height; + int max_spw; + int max_vpw; + int max_hpw; + bool conflicting_output_formats; + const struct atmel_hlcdc_layer_desc *layers; + int nlayers; +}; + +/** + * Atmel HLCDC Plane properties. + * + * This structure stores plane property definitions. + * + * @alpha: alpha blending (or transparency) property + * @rotation: rotation property + */ +struct atmel_hlcdc_plane_properties { + struct drm_property *alpha; }; /** * Atmel HLCDC Display Controller. * * @desc: HLCDC Display Controller description + * @dscrpool: DMA coherent pool used to allocate DMA descriptors * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device * @fbdev: framebuffer device attached to the Display Controller * @crtc: CRTC provided by the display controller * @planes: instantiated planes - * @layers: active HLCDC layer + * @layers: active HLCDC layers * @wq: display controller workqueue * @commit: used for async commit handling */ struct atmel_hlcdc_dc { const struct atmel_hlcdc_dc_desc *desc; + struct dma_pool *dscrpool; struct atmel_hlcdc *hlcdc; struct drm_fbdev_cma *fbdev; struct drm_crtc *crtc; - struct atmel_hlcdc_planes *planes; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct workqueue_struct *wq; struct { @@ -156,11 +377,51 @@ struct atmel_hlcdc_dc { extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats; +static inline void atmel_hlcdc_layer_write_reg(struct atmel_hlcdc_layer *layer, + unsigned int reg, u32 val) +{ + regmap_write(layer->regmap, layer->desc->regs_offset + reg, val); +} + +static inline u32 atmel_hlcdc_layer_read_reg(struct atmel_hlcdc_layer *layer, + unsigned int reg) +{ + u32 val; + + regmap_read(layer->regmap, layer->desc->regs_offset + reg, &val); + + return val; +} + +static inline void atmel_hlcdc_layer_write_cfg(struct atmel_hlcdc_layer *layer, + unsigned int cfgid, u32 val) +{ + atmel_hlcdc_layer_write_reg(layer, + layer->desc->cfgs_offset + + (cfgid * sizeof(u32)), val); +} + +static inline u32 atmel_hlcdc_layer_read_cfg(struct atmel_hlcdc_layer *layer, + unsigned int cfgid) +{ + return atmel_hlcdc_layer_read_reg(layer, + layer->desc->cfgs_offset + + (cfgid * sizeof(u32))); +} + +static inline void atmel_hlcdc_layer_init(struct atmel_hlcdc_layer *layer, + const struct atmel_hlcdc_layer_desc *desc, + struct regmap *regmap) +{ + layer->desc = desc; + layer->regmap = regmap; +} + int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, struct drm_display_mode *mode); -struct atmel_hlcdc_planes * -atmel_hlcdc_create_planes(struct drm_device *dev); +int atmel_hlcdc_create_planes(struct drm_device *dev); +void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane); int atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state); int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c deleted file mode 100644 index 63dfdbf34f80..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 = fb->format->num_planes; - - if (nplanes > layer->max_planes) - return; - - slot = &upd->slots[upd->next]; - - fb_flip = slot->fb_flip; - old_fb = slot->fb_flip->fb; - - for (i = 0; i < nplanes; i++) { - struct drm_gem_cma_object *gem; - - dscr = slot->fb_flip->dscrs[i]; - gem = drm_fb_cma_get_gem_obj(fb, i); - dscr->addr = gem->paddr + offsets[i]; - } - - fb_flip->ngems = nplanes; - fb_flip->fb = fb; - - if (fb) - drm_framebuffer_reference(fb); - - if (old_fb) - drm_framebuffer_unreference(old_fb); -} - -void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg, - u32 mask, u32 val) -{ - struct atmel_hlcdc_layer_update *upd = &layer->update; - struct atmel_hlcdc_layer_update_slot *slot; - - if (upd->next < 0 || upd->next > 1) - return; - - if (cfg >= layer->desc->nconfigs) - return; - - slot = &upd->slots[upd->next]; - slot->configs[cfg] &= ~mask; - slot->configs[cfg] |= (val & mask); - set_bit(cfg, slot->updated_configs); -} - -void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer) -{ - struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; - struct atmel_hlcdc_layer_update *upd = &layer->update; - struct atmel_hlcdc_layer_update_slot *slot; - unsigned long flags; - - if (upd->next < 0 || upd->next > 1) - return; - - slot = &upd->slots[upd->next]; - - spin_lock_irqsave(&layer->lock, flags); - - /* - * Release pending update request and replace it by the new one. - */ - if (upd->pending >= 0) - atmel_hlcdc_layer_update_reset(layer, upd->pending); - - upd->pending = upd->next; - upd->next = -1; - - if (!dma->queue) - atmel_hlcdc_layer_update_apply(layer); - - spin_unlock_irqrestore(&layer->lock, flags); - - - upd->next = -1; -} - -static int atmel_hlcdc_layer_dma_init(struct drm_device *dev, - struct atmel_hlcdc_layer *layer) -{ - struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; - dma_addr_t dma_addr; - int i; - - dma->dscrs = dma_alloc_coherent(dev->dev, - layer->max_planes * 4 * - sizeof(*dma->dscrs), - &dma_addr, GFP_KERNEL); - if (!dma->dscrs) - return -ENOMEM; - - for (i = 0; i < layer->max_planes * 4; i++) { - struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i]; - - dscr->next = dma_addr + (i * sizeof(*dscr)); - } - - return 0; -} - -static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev, - struct atmel_hlcdc_layer *layer) -{ - struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; - int i; - - for (i = 0; i < layer->max_planes * 4; i++) { - struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i]; - - dscr->status = 0; - } - - dma_free_coherent(dev->dev, layer->max_planes * 4 * - sizeof(*dma->dscrs), dma->dscrs, - dma->dscrs[0].next); -} - -static int atmel_hlcdc_layer_update_init(struct drm_device *dev, - struct atmel_hlcdc_layer *layer, - const struct atmel_hlcdc_layer_desc *desc) -{ - struct atmel_hlcdc_layer_update *upd = &layer->update; - int updated_size; - void *buffer; - int i; - - updated_size = DIV_ROUND_UP(desc->nconfigs, - BITS_PER_BYTE * - sizeof(unsigned long)); - - buffer = devm_kzalloc(dev->dev, - ((desc->nconfigs * sizeof(u32)) + - (updated_size * sizeof(unsigned long))) * 2, - GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - for (i = 0; i < 2; i++) { - upd->slots[i].updated_configs = buffer; - buffer += updated_size * sizeof(unsigned long); - upd->slots[i].configs = buffer; - buffer += desc->nconfigs * sizeof(u32); - } - - upd->pending = -1; - upd->next = -1; - - return 0; -} - -int atmel_hlcdc_layer_init(struct drm_device *dev, - struct atmel_hlcdc_layer *layer, - const struct atmel_hlcdc_layer_desc *desc) -{ - struct atmel_hlcdc_dc *dc = dev->dev_private; - struct regmap *regmap = dc->hlcdc->regmap; - unsigned int tmp; - int ret; - int i; - - layer->hlcdc = dc->hlcdc; - layer->wq = dc->wq; - layer->desc = desc; - - regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, - ATMEL_HLCDC_LAYER_RST); - for (i = 0; i < desc->formats->nformats; i++) { - int nplanes = drm_format_num_planes(desc->formats->formats[i]); - - if (nplanes > layer->max_planes) - layer->max_planes = nplanes; - } - - spin_lock_init(&layer->lock); - drm_flip_work_init(&layer->gc, desc->name, - atmel_hlcdc_layer_fb_flip_release); - ret = atmel_hlcdc_layer_dma_init(dev, layer); - if (ret) - return ret; - - ret = atmel_hlcdc_layer_update_init(dev, layer, desc); - if (ret) - return ret; - - /* Flush Status Register */ - regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR, - 0xffffffff); - regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, - &tmp); - - tmp = 0; - for (i = 0; i < layer->max_planes; i++) - tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ | - ATMEL_HLCDC_LAYER_DSCR_IRQ | - ATMEL_HLCDC_LAYER_ADD_IRQ | - ATMEL_HLCDC_LAYER_DONE_IRQ | - ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i); - - regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp); - - return 0; -} - -void atmel_hlcdc_layer_cleanup(struct drm_device *dev, - struct atmel_hlcdc_layer *layer) -{ - const struct atmel_hlcdc_layer_desc *desc = layer->desc; - struct regmap *regmap = layer->hlcdc->regmap; - - regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR, - 0xffffffff); - regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, - ATMEL_HLCDC_LAYER_RST); - - atmel_hlcdc_layer_dma_cleanup(dev, layer); - drm_flip_work_cleanup(&layer->gc); -} diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h deleted file mode 100644 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 886ed5d8e304..29cc10d053eb 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -32,12 +32,16 @@ * @src_w: buffer width * @src_h: buffer height * @alpha: alpha blending of the plane + * @disc_x: x discard position + * @disc_y: y discard position + * @disc_w: discard width + * @disc_h: discard height * @bpp: bytes per pixel deduced from pixel_format * @offsets: offsets to apply to the GEM buffers * @xstride: value to add to the pixel pointer between each line * @pstride: value to add to the pixel pointer between each pixel * @nplanes: number of planes (deduced from pixel_format) - * @prepared: plane update has been prepared + * @dscrs: DMA descriptors */ struct atmel_hlcdc_plane_state { struct drm_plane_state base; @@ -52,8 +56,6 @@ struct atmel_hlcdc_plane_state { u8 alpha; - bool disc_updated; - int disc_x; int disc_y; int disc_w; @@ -62,12 +64,14 @@ struct atmel_hlcdc_plane_state { int ahb_id; /* These fields are private and should not be touched */ - int bpp[ATMEL_HLCDC_MAX_PLANES]; - unsigned int offsets[ATMEL_HLCDC_MAX_PLANES]; - int xstride[ATMEL_HLCDC_MAX_PLANES]; - int pstride[ATMEL_HLCDC_MAX_PLANES]; + int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES]; + unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES]; + int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; + int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; int nplanes; - bool prepared; + + /* DMA descriptors. */ + struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES]; }; static inline struct atmel_hlcdc_plane_state * @@ -259,125 +263,145 @@ static u32 heo_upscaling_ycoef[] = { 0x00205907, }; +#define ATMEL_HLCDC_XPHIDEF 4 +#define ATMEL_HLCDC_YPHIDEF 4 + +static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize, + u32 dstsize, + u32 phidef) +{ + u32 factor, max_memsize; + + factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1); + max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048; + + if (max_memsize > srcsize - 1) + factor--; + + return factor; +} + static void -atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, - struct atmel_hlcdc_plane_state *state) +atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane, + const u32 *coeff_tab, int size, + unsigned int cfg_offs) { - const struct atmel_hlcdc_layer_cfg_layout *layout = - &plane->layer.desc->layout; - - if (layout->size) - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->size, - 0xffffffff, - (state->crtc_w - 1) | - ((state->crtc_h - 1) << 16)); - - if (layout->memsize) - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->memsize, - 0xffffffff, - (state->src_w - 1) | - ((state->src_h - 1) << 16)); - - if (layout->pos) - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->pos, - 0xffffffff, - state->crtc_x | - (state->crtc_y << 16)); - - /* TODO: rework the rescaling part */ - if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) { - u32 factor_reg = 0; - - if (state->crtc_w != state->src_w) { - int i; - u32 factor; - u32 *coeff_tab = heo_upscaling_xcoef; - u32 max_memsize; - - if (state->crtc_w < state->src_w) - coeff_tab = heo_downscaling_xcoef; - for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++) - atmel_hlcdc_layer_update_cfg(&plane->layer, - 17 + i, - 0xffffffff, - coeff_tab[i]); - factor = ((8 * 256 * state->src_w) - (256 * 4)) / - state->crtc_w; - factor++; - max_memsize = ((factor * state->crtc_w) + (256 * 4)) / - 2048; - if (max_memsize > state->src_w) - factor--; - factor_reg |= factor | 0x80000000; - } + int i; - if (state->crtc_h != state->src_h) { - int i; - u32 factor; - u32 *coeff_tab = heo_upscaling_ycoef; - u32 max_memsize; - - if (state->crtc_h < state->src_h) - coeff_tab = heo_downscaling_ycoef; - for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++) - atmel_hlcdc_layer_update_cfg(&plane->layer, - 33 + i, - 0xffffffff, - coeff_tab[i]); - factor = ((8 * 256 * state->src_h) - (256 * 4)) / - state->crtc_h; - factor++; - max_memsize = ((factor * state->crtc_h) + (256 * 4)) / - 2048; - if (max_memsize > state->src_h) - factor--; - factor_reg |= (factor << 16) | 0x80000000; - } + for (i = 0; i < size; i++) + atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i, + coeff_tab[i]); +} + +void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state) +{ + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + u32 xfactor, yfactor; + + if (!desc->layout.scaler_config) + return; - atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, - factor_reg); + if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) { + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.scaler_config, 0); + return; + } + + if (desc->layout.phicoeffs.x) { + xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w, + state->crtc_w, + ATMEL_HLCDC_XPHIDEF); + + yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h, + state->crtc_h, + ATMEL_HLCDC_YPHIDEF); + + atmel_hlcdc_plane_scaler_set_phicoeff(plane, + state->crtc_w < state->src_w ? + heo_downscaling_xcoef : + heo_upscaling_xcoef, + ARRAY_SIZE(heo_upscaling_xcoef), + desc->layout.phicoeffs.x); + + atmel_hlcdc_plane_scaler_set_phicoeff(plane, + state->crtc_h < state->src_h ? + heo_downscaling_ycoef : + heo_upscaling_ycoef, + ARRAY_SIZE(heo_upscaling_ycoef), + desc->layout.phicoeffs.y); } else { - atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 0); + xfactor = (1024 * state->src_w) / state->crtc_w; + yfactor = (1024 * state->src_h) / state->crtc_h; } + + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config, + ATMEL_HLCDC_LAYER_SCALER_ENABLE | + ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor, + yfactor)); +} + +static void +atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state) +{ + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + + if (desc->layout.size) + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size, + ATMEL_HLCDC_LAYER_SIZE(state->crtc_w, + state->crtc_h)); + + if (desc->layout.memsize) + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.memsize, + ATMEL_HLCDC_LAYER_SIZE(state->src_w, + state->src_h)); + + if (desc->layout.pos) + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos, + ATMEL_HLCDC_LAYER_POS(state->crtc_x, + state->crtc_y)); + + atmel_hlcdc_plane_setup_scaler(plane, state); } static void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state) { - const struct atmel_hlcdc_layer_cfg_layout *layout = - &plane->layer.desc->layout; - unsigned int cfg = ATMEL_HLCDC_LAYER_DMA; + unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id; + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + u32 format = state->base.fb->format->format; + + /* + * Rotation optimization is not working on RGB888 (rotation is still + * working but without any optimization). + */ + if (format == DRM_FORMAT_RGB888) + cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS; + + atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG, + cfg); + + cfg = ATMEL_HLCDC_LAYER_DMA; if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | ATMEL_HLCDC_LAYER_ITER; - if (atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format)) + if (atmel_hlcdc_format_embeds_alpha(format)) cfg |= ATMEL_HLCDC_LAYER_LAEN; else cfg |= ATMEL_HLCDC_LAYER_GAEN | ATMEL_HLCDC_LAYER_GA(state->alpha); } - atmel_hlcdc_layer_update_cfg(&plane->layer, - ATMEL_HLCDC_LAYER_DMA_CFG_ID, - ATMEL_HLCDC_LAYER_DMA_BLEN_MASK | - ATMEL_HLCDC_LAYER_DMA_SIF, - ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | - state->ahb_id); - - atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config, - ATMEL_HLCDC_LAYER_ITER2BL | - ATMEL_HLCDC_LAYER_ITER | - ATMEL_HLCDC_LAYER_GAEN | - ATMEL_HLCDC_LAYER_GA_MASK | - ATMEL_HLCDC_LAYER_LAEN | - ATMEL_HLCDC_LAYER_OVR | - ATMEL_HLCDC_LAYER_DMA, cfg); + if (state->disc_h && state->disc_w) + cfg |= ATMEL_HLCDC_LAYER_DISCEN; + + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config, + cfg); } static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, @@ -396,50 +420,50 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, drm_rotation_90_or_270(state->base.rotation)) cfg |= ATMEL_HLCDC_YUV422ROT; - atmel_hlcdc_layer_update_cfg(&plane->layer, - ATMEL_HLCDC_LAYER_FORMAT_CFG_ID, - 0xffffffff, - cfg); - - /* - * Rotation optimization is not working on RGB888 (rotation is still - * working but without any optimization). - */ - if (state->base.fb->format->format == DRM_FORMAT_RGB888) - cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS; - else - cfg = 0; - - atmel_hlcdc_layer_update_cfg(&plane->layer, - ATMEL_HLCDC_LAYER_DMA_CFG_ID, - ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg); + atmel_hlcdc_layer_write_cfg(&plane->layer, + ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg); } static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state) { - struct atmel_hlcdc_layer *layer = &plane->layer; - const struct atmel_hlcdc_layer_cfg_layout *layout = - &layer->desc->layout; + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + struct drm_framebuffer *fb = state->base.fb; + u32 sr; int i; - atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb, - state->offsets); + sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); for (i = 0; i < state->nplanes; i++) { - if (layout->xstride[i]) { - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->xstride[i], - 0xffffffff, - state->xstride[i]); + struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); + + state->dscrs[i]->addr = gem->paddr + state->offsets[i]; + + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_HEAD(i), + state->dscrs[i]->self); + + if (!(sr & ATMEL_HLCDC_LAYER_EN)) { + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_ADDR(i), + state->dscrs[i]->addr); + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_CTRL(i), + state->dscrs[i]->ctrl); + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_NEXT(i), + state->dscrs[i]->self); } - if (layout->pstride[i]) { - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->pstride[i], - 0xffffffff, - state->pstride[i]); - } + if (desc->layout.xstride[i]) + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.xstride[i], + state->xstride[i]); + + if (desc->layout.pstride[i]) + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.pstride[i], + state->pstride[i]); } } @@ -528,18 +552,10 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state) disc_w = ovl_state->crtc_w; } - if (disc_x == primary_state->disc_x && - disc_y == primary_state->disc_y && - disc_w == primary_state->disc_w && - disc_h == primary_state->disc_h) - return 0; - - primary_state->disc_x = disc_x; primary_state->disc_y = disc_y; primary_state->disc_w = disc_w; primary_state->disc_h = disc_h; - primary_state->disc_updated = true; return 0; } @@ -548,32 +564,19 @@ static void atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state) { - const struct atmel_hlcdc_layer_cfg_layout *layout = - &plane->layer.desc->layout; - int disc_surface = 0; - - if (!state->disc_updated) - return; - - disc_surface = state->disc_h * state->disc_w; - - atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config, - ATMEL_HLCDC_LAYER_DISCEN, - disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0); + const struct atmel_hlcdc_layer_cfg_layout *layout; - if (!disc_surface) + layout = &plane->layer.desc->layout; + if (!layout->disc_pos || !layout->disc_size) return; - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->disc_pos, - 0xffffffff, - state->disc_x | (state->disc_y << 16)); + atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos, + ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x, + state->disc_y)); - atmel_hlcdc_layer_update_cfg(&plane->layer, - layout->disc_size, - 0xffffffff, - (state->disc_w - 1) | - ((state->disc_h - 1) << 16)); + atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size, + ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w, + state->disc_h)); } static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, @@ -582,8 +585,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); struct atmel_hlcdc_plane_state *state = drm_plane_state_to_atmel_hlcdc_plane_state(s); - const struct atmel_hlcdc_layer_cfg_layout *layout = - &plane->layer.desc->layout; + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; struct drm_framebuffer *fb = state->base.fb; const struct drm_display_mode *mode; struct drm_crtc_state *crtc_state; @@ -622,7 +624,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, state->src_h >>= 16; state->nplanes = fb->format->num_planes; - if (state->nplanes > ATMEL_HLCDC_MAX_PLANES) + if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES) return -EINVAL; /* @@ -726,21 +728,19 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, state->crtc_w = patched_crtc_w; state->crtc_h = patched_crtc_h; - if (!layout->size && + if (!desc->layout.size && (mode->hdisplay != state->crtc_w || mode->vdisplay != state->crtc_h)) return -EINVAL; - if (plane->layer.desc->max_height && - state->crtc_h > plane->layer.desc->max_height) + if (desc->max_height && state->crtc_h > desc->max_height) return -EINVAL; - if (plane->layer.desc->max_width && - state->crtc_w > plane->layer.desc->max_width) + if (desc->max_width && state->crtc_w > desc->max_width) return -EINVAL; if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) && - (!layout->memsize || + (!desc->layout.memsize || atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format))) return -EINVAL; @@ -754,68 +754,13 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, return 0; } -static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, - struct drm_plane_state *new_state) -{ - /* - * FIXME: we should avoid this const -> non-const cast but it's - * currently the only solution we have to modify the ->prepared - * state and rollback the update request. - * Ideally, we should rework the code to attach all the resources - * to atmel_hlcdc_plane_state (including the DMA desc allocation), - * but this require a complete rework of the atmel_hlcdc_layer - * code. - */ - struct drm_plane_state *s = (struct drm_plane_state *)new_state; - struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); - struct atmel_hlcdc_plane_state *state = - drm_plane_state_to_atmel_hlcdc_plane_state(s); - int ret; - - if (!new_state->fb) - return 0; - - ret = atmel_hlcdc_layer_update_start(&plane->layer); - if (!ret) - state->prepared = true; - - return ret; -} - -static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p, - struct drm_plane_state *old_state) -{ - /* - * FIXME: we should avoid this const -> non-const cast but it's - * currently the only solution we have to modify the ->prepared - * state and rollback the update request. - * Ideally, we should rework the code to attach all the resources - * to atmel_hlcdc_plane_state (including the DMA desc allocation), - * but this require a complete rework of the atmel_hlcdc_layer - * code. - */ - struct drm_plane_state *s = (struct drm_plane_state *)old_state; - struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); - struct atmel_hlcdc_plane_state *state = - drm_plane_state_to_atmel_hlcdc_plane_state(s); - - /* - * The Request has already been applied or cancelled, nothing to do - * here. - */ - if (!state->prepared) - return; - - atmel_hlcdc_layer_update_rollback(&plane->layer); - state->prepared = false; -} - static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, struct drm_plane_state *old_s) { struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); struct atmel_hlcdc_plane_state *state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state); + u32 sr; if (!p->state->crtc || !p->state->fb) return; @@ -826,7 +771,18 @@ static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, atmel_hlcdc_plane_update_buffers(plane, state); atmel_hlcdc_plane_update_disc_area(plane, state); - atmel_hlcdc_layer_update_commit(&plane->layer); + /* Enable the overrun interrupts. */ + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER, + ATMEL_HLCDC_LAYER_OVR_IRQ(0) | + ATMEL_HLCDC_LAYER_OVR_IRQ(1) | + ATMEL_HLCDC_LAYER_OVR_IRQ(2)); + + /* Apply the new config at the next SOF event. */ + sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER, + ATMEL_HLCDC_LAYER_UPDATE | + (sr & ATMEL_HLCDC_LAYER_EN ? + ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); } static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, @@ -834,7 +790,18 @@ static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, { struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); - atmel_hlcdc_layer_disable(&plane->layer); + /* Disable interrupts */ + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, + 0xffffffff); + + /* Disable the layer */ + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR, + ATMEL_HLCDC_LAYER_RST | + ATMEL_HLCDC_LAYER_A2Q | + ATMEL_HLCDC_LAYER_UPDATE); + + /* Clear all pending interrupts */ + atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); } static void atmel_hlcdc_plane_destroy(struct drm_plane *p) @@ -844,10 +811,7 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p) if (plane->base.fb) drm_framebuffer_unreference(plane->base.fb); - atmel_hlcdc_layer_cleanup(p->dev, &plane->layer); - drm_plane_cleanup(p); - devm_kfree(p->dev->dev, plane); } static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p, @@ -887,24 +851,15 @@ static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p, } static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, - const struct atmel_hlcdc_layer_desc *desc, - struct atmel_hlcdc_plane_properties *props) + struct atmel_hlcdc_plane_properties *props) { - struct regmap *regmap = plane->layer.hlcdc->regmap; + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || - desc->type == ATMEL_HLCDC_CURSOR_LAYER) { + desc->type == ATMEL_HLCDC_CURSOR_LAYER) drm_object_attach_property(&plane->base.base, props->alpha, 255); - /* Set default alpha value */ - regmap_update_bits(regmap, - desc->regs_offset + - ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer), - ATMEL_HLCDC_LAYER_GA_MASK, - ATMEL_HLCDC_LAYER_GA_MASK); - } - if (desc->layout.xstride && desc->layout.pstride) { int ret; @@ -923,31 +878,78 @@ static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, * TODO: decare a "yuv-to-rgb-conv-factors" property to let * userspace modify these factors (using a BLOB property ?). */ - regmap_write(regmap, - desc->regs_offset + - ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0), - 0x4c900091); - regmap_write(regmap, - desc->regs_offset + - ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1), - 0x7a5f5090); - regmap_write(regmap, - desc->regs_offset + - ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2), - 0x40040890); + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.csc, + 0x4c900091); + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.csc + 1, + 0x7a5f5090); + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.csc + 2, + 0x40040890); } return 0; } +void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane) +{ + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + u32 isr; + + isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); + + /* + * There's not much we can do in case of overrun except informing + * the user. However, we are in interrupt context here, hence the + * use of dev_dbg(). + */ + if (isr & + (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | + ATMEL_HLCDC_LAYER_OVR_IRQ(2))) + dev_dbg(plane->base.dev->dev, "overrun on plane %s\n", + desc->name); +} + static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { - .prepare_fb = atmel_hlcdc_plane_prepare_fb, - .cleanup_fb = atmel_hlcdc_plane_cleanup_fb, .atomic_check = atmel_hlcdc_plane_atomic_check, .atomic_update = atmel_hlcdc_plane_atomic_update, .atomic_disable = atmel_hlcdc_plane_atomic_disable, }; +static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p, + struct atmel_hlcdc_plane_state *state) +{ + struct atmel_hlcdc_dc *dc = p->dev->dev_private; + int i; + + for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { + struct atmel_hlcdc_dma_channel_dscr *dscr; + dma_addr_t dscr_dma; + + dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma); + if (!dscr) + goto err; + + dscr->addr = 0; + dscr->next = dscr_dma; + dscr->self = dscr_dma; + dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH; + + state->dscrs[i] = dscr; + } + + return 0; + +err: + for (i--; i >= 0; i--) { + dma_pool_free(dc->dscrpool, state->dscrs[i], + state->dscrs[i]->self); + } + + return -ENOMEM; +} + static void atmel_hlcdc_plane_reset(struct drm_plane *p) { struct atmel_hlcdc_plane_state *state; @@ -964,6 +966,13 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p) state = kzalloc(sizeof(*state), GFP_KERNEL); if (state) { + if (atmel_hlcdc_plane_alloc_dscrs(p, state)) { + kfree(state); + dev_err(p->dev->dev, + "Failed to allocate initial plane state\n"); + return; + } + state->alpha = 255; p->state = &state->base; p->state->plane = p; @@ -981,8 +990,10 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) if (!copy) return NULL; - copy->disc_updated = false; - copy->prepared = false; + if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) { + kfree(copy); + return NULL; + } if (copy->base.fb) drm_framebuffer_reference(copy->base.fb); @@ -990,11 +1001,18 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) return ©->base; } -static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane, +static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p, struct drm_plane_state *s) { struct atmel_hlcdc_plane_state *state = drm_plane_state_to_atmel_hlcdc_plane_state(s); + struct atmel_hlcdc_dc *dc = p->dev->dev_private; + int i; + + for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { + dma_pool_free(dc->dscrpool, state->dscrs[i], + state->dscrs[i]->self); + } if (s->fb) drm_framebuffer_unreference(s->fb); @@ -1014,22 +1032,21 @@ static struct drm_plane_funcs layer_plane_funcs = { .atomic_get_property = atmel_hlcdc_plane_atomic_get_property, }; -static struct atmel_hlcdc_plane * -atmel_hlcdc_plane_create(struct drm_device *dev, - const struct atmel_hlcdc_layer_desc *desc, - struct atmel_hlcdc_plane_properties *props) +static int atmel_hlcdc_plane_create(struct drm_device *dev, + const struct atmel_hlcdc_layer_desc *desc, + struct atmel_hlcdc_plane_properties *props) { + struct atmel_hlcdc_dc *dc = dev->dev_private; struct atmel_hlcdc_plane *plane; enum drm_plane_type type; int ret; plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); if (!plane) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc); - if (ret) - return ERR_PTR(ret); + atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap); + plane->properties = props; if (desc->type == ATMEL_HLCDC_BASE_LAYER) type = DRM_PLANE_TYPE_PRIMARY; @@ -1043,17 +1060,19 @@ atmel_hlcdc_plane_create(struct drm_device *dev, desc->formats->formats, desc->formats->nformats, type, NULL); if (ret) - return ERR_PTR(ret); + return ret; drm_plane_helper_add(&plane->base, &atmel_hlcdc_layer_plane_helper_funcs); /* Set default property values*/ - ret = atmel_hlcdc_plane_init_properties(plane, desc, props); + ret = atmel_hlcdc_plane_init_properties(plane, props); if (ret) - return ERR_PTR(ret); + return ret; + + dc->layers[desc->id] = &plane->layer; - return plane; + return 0; } static struct atmel_hlcdc_plane_properties * @@ -1072,72 +1091,34 @@ atmel_hlcdc_plane_create_properties(struct drm_device *dev) return props; } -struct atmel_hlcdc_planes * -atmel_hlcdc_create_planes(struct drm_device *dev) +int atmel_hlcdc_create_planes(struct drm_device *dev) { struct atmel_hlcdc_dc *dc = dev->dev_private; struct atmel_hlcdc_plane_properties *props; - struct atmel_hlcdc_planes *planes; const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; int nlayers = dc->desc->nlayers; - int i; - - planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL); - if (!planes) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < nlayers; i++) { - if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER) - planes->noverlays++; - } - - if (planes->noverlays) { - planes->overlays = devm_kzalloc(dev->dev, - planes->noverlays * - sizeof(*planes->overlays), - GFP_KERNEL); - if (!planes->overlays) - return ERR_PTR(-ENOMEM); - } + int i, ret; props = atmel_hlcdc_plane_create_properties(dev); if (IS_ERR(props)) - return ERR_CAST(props); + return PTR_ERR(props); - planes->noverlays = 0; - for (i = 0; i < nlayers; i++) { - struct atmel_hlcdc_plane *plane; + dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev, + sizeof(struct atmel_hlcdc_dma_channel_dscr), + sizeof(u64), 0); + if (!dc->dscrpool) + return -ENOMEM; - if (descs[i].type == ATMEL_HLCDC_PP_LAYER) + for (i = 0; i < nlayers; i++) { + if (descs[i].type != ATMEL_HLCDC_BASE_LAYER && + descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER && + descs[i].type != ATMEL_HLCDC_CURSOR_LAYER) continue; - plane = atmel_hlcdc_plane_create(dev, &descs[i], props); - if (IS_ERR(plane)) - return ERR_CAST(plane); - - plane->properties = props; - - switch (descs[i].type) { - case ATMEL_HLCDC_BASE_LAYER: - if (planes->primary) - return ERR_PTR(-EINVAL); - planes->primary = plane; - break; - - case ATMEL_HLCDC_OVERLAY_LAYER: - planes->overlays[planes->noverlays++] = plane; - break; - - case ATMEL_HLCDC_CURSOR_LAYER: - if (planes->cursor) - return ERR_PTR(-EINVAL); - planes->cursor = plane; - break; - - default: - break; - } + ret = atmel_hlcdc_plane_create(dev, &descs[i], props); + if (ret) + return ret; } - return planes; + return 0; } |