aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c')
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c642
1 files changed, 313 insertions, 329 deletions
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index bd2791c4b002..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,65 +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;
-
- ret = atmel_hlcdc_layer_update_start(&plane->layer);
- if (!ret)
- state->prepared = true;
-
- return ret;
-}
-
-static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
- struct drm_plane_state *old_state)
-{
- /*
- * FIXME: we should avoid this const -> non-const cast but it's
- * currently the only solution we have to modify the ->prepared
- * state and rollback the update request.
- * Ideally, we should rework the code to attach all the resources
- * to atmel_hlcdc_plane_state (including the DMA desc allocation),
- * but this require a complete rework of the atmel_hlcdc_layer
- * code.
- */
- struct drm_plane_state *s = (struct drm_plane_state *)old_state;
- struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- struct atmel_hlcdc_plane_state *state =
- drm_plane_state_to_atmel_hlcdc_plane_state(s);
-
- /*
- * The Request has already been applied or cancelled, nothing to do
- * here.
- */
- if (!state->prepared)
- return;
-
- atmel_hlcdc_layer_update_rollback(&plane->layer);
- state->prepared = false;
-}
-
static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
struct drm_plane_state *old_s)
{
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
struct atmel_hlcdc_plane_state *state =
drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
+ u32 sr;
if (!p->state->crtc || !p->state->fb)
return;
@@ -823,7 +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,
@@ -831,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)
@@ -841,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,
@@ -884,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;
@@ -920,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;
@@ -961,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;
@@ -978,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);
@@ -987,11 +1001,18 @@ atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
return &copy->base;
}
-static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane,
+static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
struct drm_plane_state *s)
{
struct atmel_hlcdc_plane_state *state =
drm_plane_state_to_atmel_hlcdc_plane_state(s);
+ struct atmel_hlcdc_dc *dc = p->dev->dev_private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
+ dma_pool_free(dc->dscrpool, state->dscrs[i],
+ state->dscrs[i]->self);
+ }
if (s->fb)
drm_framebuffer_unreference(s->fb);
@@ -1011,22 +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;
@@ -1040,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 *
@@ -1069,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;
}