提交 9a45d33c 编写于 作者: B Boris Brezillon

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: NBoris Brezillon <boris.brezillon@free-electrons.com>
Acked-by: NDaniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: NEric Anholt <eric@anholt.net>
Tested-by: NNicolas Ferre <nicolas.ferre@microchip.com>
上级 6140cf20
atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \ atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
atmel_hlcdc_dc.o \ atmel_hlcdc_dc.o \
atmel_hlcdc_layer.o \
atmel_hlcdc_output.o \ atmel_hlcdc_output.o \
atmel_hlcdc_plane.o atmel_hlcdc_plane.o
......
...@@ -466,8 +466,8 @@ static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { ...@@ -466,8 +466,8 @@ static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
int atmel_hlcdc_crtc_create(struct drm_device *dev) 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_dc *dc = dev->dev_private;
struct atmel_hlcdc_planes *planes = dc->planes;
struct atmel_hlcdc_crtc *crtc; struct atmel_hlcdc_crtc *crtc;
int ret; int ret;
int i; int i;
...@@ -478,20 +478,41 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev) ...@@ -478,20 +478,41 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
crtc->dc = dc; crtc->dc = dc;
ret = drm_crtc_init_with_planes(dev, &crtc->base, for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
&planes->primary->base, if (!dc->layers[i])
planes->cursor ? &planes->cursor->base : NULL, continue;
&atmel_hlcdc_crtc_funcs, NULL);
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) if (ret < 0)
goto fail; goto fail;
crtc->id = drm_crtc_index(&crtc->base); crtc->id = drm_crtc_index(&crtc->base);
if (planes->cursor) for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
planes->cursor->base.possible_crtcs = 1 << crtc->id; struct atmel_hlcdc_plane *overlay;
for (i = 0; i < planes->noverlays; i++) if (dc->layers[i] &&
planes->overlays[i]->base.possible_crtcs = 1 << crtc->id; 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_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
drm_crtc_vblank_reset(&crtc->base); drm_crtc_vblank_reset(&crtc->base);
......
...@@ -36,7 +36,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = { ...@@ -36,7 +36,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = {
.regs_offset = 0x40, .regs_offset = 0x40,
.id = 0, .id = 0,
.type = ATMEL_HLCDC_BASE_LAYER, .type = ATMEL_HLCDC_BASE_LAYER,
.nconfigs = 5, .cfgs_offset = 0x2c,
.layout = { .layout = {
.xstride = { 2 }, .xstride = { 2 },
.default_color = 3, .default_color = 3,
...@@ -65,7 +65,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { ...@@ -65,7 +65,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
.regs_offset = 0x40, .regs_offset = 0x40,
.id = 0, .id = 0,
.type = ATMEL_HLCDC_BASE_LAYER, .type = ATMEL_HLCDC_BASE_LAYER,
.nconfigs = 5, .cfgs_offset = 0x2c,
.layout = { .layout = {
.xstride = { 2 }, .xstride = { 2 },
.default_color = 3, .default_color = 3,
...@@ -80,7 +80,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { ...@@ -80,7 +80,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
.regs_offset = 0x100, .regs_offset = 0x100,
.id = 1, .id = 1,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10, .cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -98,7 +98,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { ...@@ -98,7 +98,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
.regs_offset = 0x280, .regs_offset = 0x280,
.id = 2, .id = 2,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 17, .cfgs_offset = 0x4c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -109,6 +109,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { ...@@ -109,6 +109,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
.chroma_key = 10, .chroma_key = 10,
.chroma_key_mask = 11, .chroma_key_mask = 11,
.general_config = 12, .general_config = 12,
.scaler_config = 13,
.csc = 14, .csc = 14,
}, },
}, },
...@@ -118,9 +119,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { ...@@ -118,9 +119,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
.regs_offset = 0x340, .regs_offset = 0x340,
.id = 3, .id = 3,
.type = ATMEL_HLCDC_CURSOR_LAYER, .type = ATMEL_HLCDC_CURSOR_LAYER,
.nconfigs = 10,
.max_width = 128, .max_width = 128,
.max_height = 128, .max_height = 128,
.cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -153,7 +154,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -153,7 +154,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.regs_offset = 0x40, .regs_offset = 0x40,
.id = 0, .id = 0,
.type = ATMEL_HLCDC_BASE_LAYER, .type = ATMEL_HLCDC_BASE_LAYER,
.nconfigs = 7, .cfgs_offset = 0x2c,
.layout = { .layout = {
.xstride = { 2 }, .xstride = { 2 },
.default_color = 3, .default_color = 3,
...@@ -168,7 +169,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -168,7 +169,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.regs_offset = 0x140, .regs_offset = 0x140,
.id = 1, .id = 1,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10, .cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -186,7 +187,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -186,7 +187,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.regs_offset = 0x240, .regs_offset = 0x240,
.id = 2, .id = 2,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10, .cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -204,7 +205,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -204,7 +205,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.regs_offset = 0x340, .regs_offset = 0x340,
.id = 3, .id = 3,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 42, .cfgs_offset = 0x4c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -215,6 +216,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -215,6 +216,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.chroma_key = 10, .chroma_key = 10,
.chroma_key_mask = 11, .chroma_key_mask = 11,
.general_config = 12, .general_config = 12,
.scaler_config = 13,
.phicoeffs = {
.x = 17,
.y = 33,
},
.csc = 14, .csc = 14,
}, },
}, },
...@@ -224,9 +230,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -224,9 +230,9 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.regs_offset = 0x440, .regs_offset = 0x440,
.id = 4, .id = 4,
.type = ATMEL_HLCDC_CURSOR_LAYER, .type = ATMEL_HLCDC_CURSOR_LAYER,
.nconfigs = 10,
.max_width = 128, .max_width = 128,
.max_height = 128, .max_height = 128,
.cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -236,6 +242,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { ...@@ -236,6 +242,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
.chroma_key = 7, .chroma_key = 7,
.chroma_key_mask = 8, .chroma_key_mask = 8,
.general_config = 9, .general_config = 9,
.scaler_config = 13,
}, },
}, },
}; };
...@@ -260,7 +267,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { ...@@ -260,7 +267,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
.regs_offset = 0x40, .regs_offset = 0x40,
.id = 0, .id = 0,
.type = ATMEL_HLCDC_BASE_LAYER, .type = ATMEL_HLCDC_BASE_LAYER,
.nconfigs = 7, .cfgs_offset = 0x2c,
.layout = { .layout = {
.xstride = { 2 }, .xstride = { 2 },
.default_color = 3, .default_color = 3,
...@@ -275,7 +282,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { ...@@ -275,7 +282,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
.regs_offset = 0x140, .regs_offset = 0x140,
.id = 1, .id = 1,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10, .cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -293,7 +300,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { ...@@ -293,7 +300,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
.regs_offset = 0x240, .regs_offset = 0x240,
.id = 2, .id = 2,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10, .cfgs_offset = 0x2c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -311,7 +318,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { ...@@ -311,7 +318,7 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
.regs_offset = 0x340, .regs_offset = 0x340,
.id = 3, .id = 3,
.type = ATMEL_HLCDC_OVERLAY_LAYER, .type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 42, .cfgs_offset = 0x4c,
.layout = { .layout = {
.pos = 2, .pos = 2,
.size = 3, .size = 3,
...@@ -322,6 +329,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { ...@@ -322,6 +329,11 @@ static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
.chroma_key = 10, .chroma_key = 10,
.chroma_key_mask = 11, .chroma_key_mask = 11,
.general_config = 12, .general_config = 12,
.scaler_config = 13,
.phicoeffs = {
.x = 17,
.y = 33,
},
.csc = 14, .csc = 14,
}, },
}, },
...@@ -392,6 +404,17 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, ...@@ -392,6 +404,17 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
return MODE_OK; 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) static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
{ {
struct drm_device *dev = data; struct drm_device *dev = data;
...@@ -410,12 +433,8 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) ...@@ -410,12 +433,8 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
atmel_hlcdc_crtc_irq(dc->crtc); atmel_hlcdc_crtc_irq(dc->crtc);
for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
struct atmel_hlcdc_layer *layer = dc->layers[i]; if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
atmel_hlcdc_layer_irq(dc->layers[i]);
if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
continue;
atmel_hlcdc_layer_irq(layer);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -537,9 +556,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = { ...@@ -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) static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
{ {
struct atmel_hlcdc_dc *dc = dev->dev_private; struct atmel_hlcdc_dc *dc = dev->dev_private;
struct atmel_hlcdc_planes *planes;
int ret; int ret;
int i;
drm_mode_config_init(dev); drm_mode_config_init(dev);
...@@ -549,25 +566,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) ...@@ -549,25 +566,12 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
return ret; return ret;
} }
planes = atmel_hlcdc_create_planes(dev); ret = atmel_hlcdc_create_planes(dev);
if (IS_ERR(planes)) { if (ret) {
dev_err(dev->dev, "failed to create planes\n"); dev_err(dev->dev, "failed to create planes: %d\n", ret);
return PTR_ERR(planes); 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); ret = atmel_hlcdc_crtc_create(dev);
if (ret) { if (ret) {
dev_err(dev->dev, "failed to create crtc\n"); dev_err(dev->dev, "failed to create crtc\n");
......
...@@ -23,7 +23,9 @@ ...@@ -23,7 +23,9 @@
#define DRM_ATMEL_HLCDC_H #define DRM_ATMEL_HLCDC_H
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dmapool.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/mfd/atmel-hlcdc.h>
#include <linux/pwm.h> #include <linux/pwm.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
...@@ -36,51 +38,245 @@ ...@@ -36,51 +38,245 @@
#include <drm/drm_plane_helper.h> #include <drm/drm_plane_helper.h>
#include <drm/drmP.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 * Each HLCDC layer has its own register organization and a given register
* HLCDC IP version (or Atmel SoC family). * 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 * For all fields, a value of zero means "unsupported".
* @min_height: minimum height supported by the Display Controller *
* @max_width: maximum width supported by the Display Controller * See Atmel's datasheet for a detailled description of these registers.
* @max_height: maximum height supported by the Display Controller *
* @max_spw: maximum vertical/horizontal pulse width * @xstride: xstride registers
* @max_vpw: maximum vertical back/front porch width * @pstride: pstride registers
* @max_hpw: maximum horizontal back/front porch width * @pos: position register
* @conflicting_output_formats: true if RGBXXX output formats conflict with * @size: displayed size register
* each other. * @memsize: memory size register
* @layers: a layer description table describing available layers * @default_color: default color register
* @nlayers: layer description table size * @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 { struct atmel_hlcdc_layer_cfg_layout {
int min_width; int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
int min_height; 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_width;
int max_height; 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 * @desc: layer description
* @rotation: rotation property * @regmap: pointer to the HLCDC regmap
*/ */
struct atmel_hlcdc_plane_properties { struct atmel_hlcdc_layer {
struct drm_property *alpha; const struct atmel_hlcdc_layer_desc *desc;
struct regmap *regmap;
}; };
/** /**
...@@ -89,7 +285,6 @@ struct atmel_hlcdc_plane_properties { ...@@ -89,7 +285,6 @@ struct atmel_hlcdc_plane_properties {
* @base: base DRM plane structure * @base: base DRM plane structure
* @layer: HLCDC layer structure * @layer: HLCDC layer structure
* @properties: pointer to the property definitions structure * @properties: pointer to the property definitions structure
* @rotation: current rotation status
*/ */
struct atmel_hlcdc_plane { struct atmel_hlcdc_plane {
struct drm_plane base; struct drm_plane base;
...@@ -104,47 +299,73 @@ drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p) ...@@ -104,47 +299,73 @@ drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
} }
static inline struct atmel_hlcdc_plane * 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 * This structure describes the HLCDC IP capabilities and depends on the
* the HLCDC Display Controller or the HLCDC CRTC. * HLCDC IP version (or Atmel SoC family).
* *
* @primary: primary plane * @min_width: minimum width supported by the Display Controller
* @cursor: hardware cursor plane * @min_height: minimum height supported by the Display Controller
* @overlays: overlay plane table * @max_width: maximum width supported by the Display Controller
* @noverlays: number of overlay planes * @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_dc_desc {
struct atmel_hlcdc_plane *primary; int min_width;
struct atmel_hlcdc_plane *cursor; int min_height;
struct atmel_hlcdc_plane **overlays; int max_width;
int noverlays; 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. * Atmel HLCDC Display Controller.
* *
* @desc: HLCDC Display Controller description * @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 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
* @fbdev: framebuffer device attached to the Display Controller * @fbdev: framebuffer device attached to the Display Controller
* @crtc: CRTC provided by the display controller * @crtc: CRTC provided by the display controller
* @planes: instantiated planes * @planes: instantiated planes
* @layers: active HLCDC layer * @layers: active HLCDC layers
* @wq: display controller workqueue * @wq: display controller workqueue
* @commit: used for async commit handling * @commit: used for async commit handling
*/ */
struct atmel_hlcdc_dc { struct atmel_hlcdc_dc {
const struct atmel_hlcdc_dc_desc *desc; const struct atmel_hlcdc_dc_desc *desc;
struct dma_pool *dscrpool;
struct atmel_hlcdc *hlcdc; struct atmel_hlcdc *hlcdc;
struct drm_fbdev_cma *fbdev; struct drm_fbdev_cma *fbdev;
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct atmel_hlcdc_planes *planes;
struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct { struct {
...@@ -156,11 +377,51 @@ struct atmel_hlcdc_dc { ...@@ -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_formats;
extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_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, int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
struct drm_display_mode *mode); struct drm_display_mode *mode);
struct atmel_hlcdc_planes * int atmel_hlcdc_create_planes(struct drm_device *dev);
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_disc_area(struct drm_crtc_state *c_state);
int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state); int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state);
......
/*
* 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);
}
/*
* 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 */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册