|
@@ -76,7 +76,9 @@ enum mixer_flag_bits {
|
|
|
|
|
|
static const uint32_t mixer_formats[] = {
|
|
|
DRM_FORMAT_XRGB4444,
|
|
|
+ DRM_FORMAT_ARGB4444,
|
|
|
DRM_FORMAT_XRGB1555,
|
|
|
+ DRM_FORMAT_ARGB1555,
|
|
|
DRM_FORMAT_RGB565,
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
DRM_FORMAT_ARGB8888,
|
|
@@ -117,19 +119,22 @@ static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
|
|
|
.type = DRM_PLANE_TYPE_PRIMARY,
|
|
|
.pixel_formats = mixer_formats,
|
|
|
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
|
|
|
- .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
|
|
|
+ .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
|
|
|
+ EXYNOS_DRM_PLANE_CAP_ZPOS,
|
|
|
}, {
|
|
|
.zpos = 1,
|
|
|
.type = DRM_PLANE_TYPE_CURSOR,
|
|
|
.pixel_formats = mixer_formats,
|
|
|
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
|
|
|
- .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
|
|
|
+ .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
|
|
|
+ EXYNOS_DRM_PLANE_CAP_ZPOS,
|
|
|
}, {
|
|
|
.zpos = 2,
|
|
|
.type = DRM_PLANE_TYPE_OVERLAY,
|
|
|
.pixel_formats = vp_formats,
|
|
|
.num_pixel_formats = ARRAY_SIZE(vp_formats),
|
|
|
- .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE,
|
|
|
+ .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE |
|
|
|
+ EXYNOS_DRM_PLANE_CAP_ZPOS,
|
|
|
},
|
|
|
};
|
|
|
|
|
@@ -162,6 +167,18 @@ static const u8 filter_cr_horiz_tap4[] = {
|
|
|
70, 59, 48, 37, 27, 19, 11, 5,
|
|
|
};
|
|
|
|
|
|
+static inline bool is_alpha_format(unsigned int pixel_format)
|
|
|
+{
|
|
|
+ switch (pixel_format) {
|
|
|
+ case DRM_FORMAT_ARGB8888:
|
|
|
+ case DRM_FORMAT_ARGB1555:
|
|
|
+ case DRM_FORMAT_ARGB4444:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
|
|
|
{
|
|
|
return readl(res->vp_regs + reg_id);
|
|
@@ -291,6 +308,37 @@ static void vp_default_filter(struct mixer_resources *res)
|
|
|
filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
|
|
|
}
|
|
|
|
|
|
+static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win,
|
|
|
+ bool alpha)
|
|
|
+{
|
|
|
+ struct mixer_resources *res = &ctx->mixer_res;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
|
|
|
+ if (alpha) {
|
|
|
+ /* blending based on pixel alpha */
|
|
|
+ val |= MXR_GRP_CFG_BLEND_PRE_MUL;
|
|
|
+ val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
|
|
|
+ }
|
|
|
+ mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
|
|
|
+ val, MXR_GRP_CFG_MISC_MASK);
|
|
|
+}
|
|
|
+
|
|
|
+static void mixer_cfg_vp_blend(struct mixer_context *ctx)
|
|
|
+{
|
|
|
+ struct mixer_resources *res = &ctx->mixer_res;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * No blending at the moment since the NV12/NV21 pixelformats don't
|
|
|
+ * have an alpha channel. However the mixer supports a global alpha
|
|
|
+ * value for a layer. Once this functionality is exposed, we can
|
|
|
+ * support blending of the video layer through this.
|
|
|
+ */
|
|
|
+ val = 0;
|
|
|
+ mixer_reg_write(res, MXR_VIDEO_CFG, val);
|
|
|
+}
|
|
|
+
|
|
|
static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
|
|
|
{
|
|
|
struct mixer_resources *res = &ctx->mixer_res;
|
|
@@ -372,7 +420,7 @@ static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
|
|
|
}
|
|
|
|
|
|
static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
|
|
|
- bool enable)
|
|
|
+ unsigned int priority, bool enable)
|
|
|
{
|
|
|
struct mixer_resources *res = &ctx->mixer_res;
|
|
|
u32 val = enable ? ~0 : 0;
|
|
@@ -380,20 +428,24 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
|
|
|
switch (win) {
|
|
|
case 0:
|
|
|
mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
|
|
|
+ mixer_reg_writemask(res, MXR_LAYER_CFG,
|
|
|
+ MXR_LAYER_CFG_GRP0_VAL(priority),
|
|
|
+ MXR_LAYER_CFG_GRP0_MASK);
|
|
|
break;
|
|
|
case 1:
|
|
|
mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
|
|
|
+ mixer_reg_writemask(res, MXR_LAYER_CFG,
|
|
|
+ MXR_LAYER_CFG_GRP1_VAL(priority),
|
|
|
+ MXR_LAYER_CFG_GRP1_MASK);
|
|
|
break;
|
|
|
- case 2:
|
|
|
+ case VP_DEFAULT_WIN:
|
|
|
if (ctx->vp_enabled) {
|
|
|
vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
|
|
|
mixer_reg_writemask(res, MXR_CFG, val,
|
|
|
MXR_CFG_VP_ENABLE);
|
|
|
-
|
|
|
- /* control blending of graphic layer 0 */
|
|
|
- mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
|
|
|
- MXR_GRP_CFG_BLEND_PRE_MUL |
|
|
|
- MXR_GRP_CFG_PIXEL_BLEND_EN);
|
|
|
+ mixer_reg_writemask(res, MXR_LAYER_CFG,
|
|
|
+ MXR_LAYER_CFG_VP_VAL(priority),
|
|
|
+ MXR_LAYER_CFG_VP_MASK);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
@@ -464,7 +516,6 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
|
|
}
|
|
|
|
|
|
spin_lock_irqsave(&res->reg_slock, flags);
|
|
|
- mixer_vsync_set_update(ctx, false);
|
|
|
|
|
|
/* interlace or progressive scan mode */
|
|
|
val = (ctx->interlace ? ~0 : 0);
|
|
@@ -511,10 +562,10 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
|
|
|
|
|
mixer_cfg_scan(ctx, mode->vdisplay);
|
|
|
mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
|
|
|
- mixer_cfg_layer(ctx, plane->zpos, true);
|
|
|
+ mixer_cfg_layer(ctx, plane->index, state->zpos + 1, true);
|
|
|
+ mixer_cfg_vp_blend(ctx);
|
|
|
mixer_run(ctx);
|
|
|
|
|
|
- mixer_vsync_set_update(ctx, true);
|
|
|
spin_unlock_irqrestore(&res->reg_slock, flags);
|
|
|
|
|
|
mixer_regs_dump(ctx);
|
|
@@ -537,7 +588,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|
|
struct mixer_resources *res = &ctx->mixer_res;
|
|
|
struct drm_framebuffer *fb = state->base.fb;
|
|
|
unsigned long flags;
|
|
|
- unsigned int win = plane->zpos;
|
|
|
+ unsigned int win = plane->index;
|
|
|
unsigned int x_ratio = 0, y_ratio = 0;
|
|
|
unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
|
|
|
dma_addr_t dma_addr;
|
|
@@ -546,10 +597,12 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|
|
|
|
|
switch (fb->pixel_format) {
|
|
|
case DRM_FORMAT_XRGB4444:
|
|
|
+ case DRM_FORMAT_ARGB4444:
|
|
|
fmt = MXR_FORMAT_ARGB4444;
|
|
|
break;
|
|
|
|
|
|
case DRM_FORMAT_XRGB1555:
|
|
|
+ case DRM_FORMAT_ARGB1555:
|
|
|
fmt = MXR_FORMAT_ARGB1555;
|
|
|
break;
|
|
|
|
|
@@ -587,7 +640,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|
|
ctx->interlace = false;
|
|
|
|
|
|
spin_lock_irqsave(&res->reg_slock, flags);
|
|
|
- mixer_vsync_set_update(ctx, false);
|
|
|
|
|
|
/* setup format */
|
|
|
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
|
|
@@ -626,7 +678,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|
|
|
|
|
mixer_cfg_scan(ctx, mode->vdisplay);
|
|
|
mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
|
|
|
- mixer_cfg_layer(ctx, win, true);
|
|
|
+ mixer_cfg_layer(ctx, win, state->zpos + 1, true);
|
|
|
+ mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->pixel_format));
|
|
|
|
|
|
/* layer update mandatory for mixer 16.0.33.0 */
|
|
|
if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
|
|
@@ -635,7 +688,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|
|
|
|
|
mixer_run(ctx);
|
|
|
|
|
|
- mixer_vsync_set_update(ctx, true);
|
|
|
spin_unlock_irqrestore(&res->reg_slock, flags);
|
|
|
|
|
|
mixer_regs_dump(ctx);
|
|
@@ -660,10 +712,8 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
|
|
{
|
|
|
struct mixer_resources *res = &ctx->mixer_res;
|
|
|
unsigned long flags;
|
|
|
- u32 val; /* value stored to register */
|
|
|
|
|
|
spin_lock_irqsave(&res->reg_slock, flags);
|
|
|
- mixer_vsync_set_update(ctx, false);
|
|
|
|
|
|
mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
|
|
|
|
|
@@ -674,40 +724,14 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
|
|
mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
|
|
|
MXR_STATUS_BURST_MASK);
|
|
|
|
|
|
- /* setting default layer priority: layer1 > layer0 > video
|
|
|
- * because typical usage scenario would be
|
|
|
- * layer1 - OSD
|
|
|
- * layer0 - framebuffer
|
|
|
- * video - video overlay
|
|
|
- */
|
|
|
- val = MXR_LAYER_CFG_GRP1_VAL(3);
|
|
|
- val |= MXR_LAYER_CFG_GRP0_VAL(2);
|
|
|
- if (ctx->vp_enabled)
|
|
|
- val |= MXR_LAYER_CFG_VP_VAL(1);
|
|
|
- mixer_reg_write(res, MXR_LAYER_CFG, val);
|
|
|
+ /* reset default layer priority */
|
|
|
+ mixer_reg_write(res, MXR_LAYER_CFG, 0);
|
|
|
|
|
|
/* setting background color */
|
|
|
mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
|
|
|
mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
|
|
|
mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
|
|
|
|
|
|
- /* setting graphical layers */
|
|
|
- val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
|
|
|
- val |= MXR_GRP_CFG_WIN_BLEND_EN;
|
|
|
- val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
|
|
|
-
|
|
|
- /* Don't blend layer 0 onto the mixer background */
|
|
|
- mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
|
|
|
-
|
|
|
- /* Blend layer 1 into layer 0 */
|
|
|
- val |= MXR_GRP_CFG_BLEND_PRE_MUL;
|
|
|
- val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
|
|
|
- mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
|
|
|
-
|
|
|
- /* setting video layers */
|
|
|
- val = MXR_GRP_CFG_ALPHA_VAL(0);
|
|
|
- mixer_reg_write(res, MXR_VIDEO_CFG, val);
|
|
|
-
|
|
|
if (ctx->vp_enabled) {
|
|
|
/* configuration of Video Processor Registers */
|
|
|
vp_win_reset(ctx);
|
|
@@ -720,7 +744,6 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
|
|
if (ctx->vp_enabled)
|
|
|
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
|
|
|
|
|
|
- mixer_vsync_set_update(ctx, true);
|
|
|
spin_unlock_irqrestore(&res->reg_slock, flags);
|
|
|
}
|
|
|
|
|
@@ -951,17 +974,27 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
|
|
|
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
|
|
|
}
|
|
|
|
|
|
+static void mixer_atomic_begin(struct exynos_drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct mixer_context *mixer_ctx = crtc->ctx;
|
|
|
+
|
|
|
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
|
|
+ return;
|
|
|
+
|
|
|
+ mixer_vsync_set_update(mixer_ctx, false);
|
|
|
+}
|
|
|
+
|
|
|
static void mixer_update_plane(struct exynos_drm_crtc *crtc,
|
|
|
struct exynos_drm_plane *plane)
|
|
|
{
|
|
|
struct mixer_context *mixer_ctx = crtc->ctx;
|
|
|
|
|
|
- DRM_DEBUG_KMS("win: %d\n", plane->zpos);
|
|
|
+ DRM_DEBUG_KMS("win: %d\n", plane->index);
|
|
|
|
|
|
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
|
|
return;
|
|
|
|
|
|
- if (plane->zpos > 1 && mixer_ctx->vp_enabled)
|
|
|
+ if (plane->index == VP_DEFAULT_WIN)
|
|
|
vp_video_buffer(mixer_ctx, plane);
|
|
|
else
|
|
|
mixer_graph_buffer(mixer_ctx, plane);
|
|
@@ -974,18 +1007,24 @@ static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
|
|
|
struct mixer_resources *res = &mixer_ctx->mixer_res;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- DRM_DEBUG_KMS("win: %d\n", plane->zpos);
|
|
|
+ DRM_DEBUG_KMS("win: %d\n", plane->index);
|
|
|
|
|
|
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
|
|
return;
|
|
|
|
|
|
spin_lock_irqsave(&res->reg_slock, flags);
|
|
|
- mixer_vsync_set_update(mixer_ctx, false);
|
|
|
+ mixer_cfg_layer(mixer_ctx, plane->index, 0, false);
|
|
|
+ spin_unlock_irqrestore(&res->reg_slock, flags);
|
|
|
+}
|
|
|
|
|
|
- mixer_cfg_layer(mixer_ctx, plane->zpos, false);
|
|
|
+static void mixer_atomic_flush(struct exynos_drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct mixer_context *mixer_ctx = crtc->ctx;
|
|
|
+
|
|
|
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
|
|
|
+ return;
|
|
|
|
|
|
mixer_vsync_set_update(mixer_ctx, true);
|
|
|
- spin_unlock_irqrestore(&res->reg_slock, flags);
|
|
|
}
|
|
|
|
|
|
static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
|
|
@@ -1026,6 +1065,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
|
|
|
|
|
|
pm_runtime_get_sync(ctx->dev);
|
|
|
|
|
|
+ mixer_vsync_set_update(ctx, false);
|
|
|
+
|
|
|
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
|
|
|
|
|
|
if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
|
|
@@ -1034,6 +1075,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
|
|
|
}
|
|
|
mixer_win_reset(ctx);
|
|
|
|
|
|
+ mixer_vsync_set_update(ctx, true);
|
|
|
+
|
|
|
set_bit(MXR_BIT_POWERED, &ctx->flags);
|
|
|
}
|
|
|
|
|
@@ -1084,8 +1127,10 @@ static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
|
|
|
.enable_vblank = mixer_enable_vblank,
|
|
|
.disable_vblank = mixer_disable_vblank,
|
|
|
.wait_for_vblank = mixer_wait_for_vblank,
|
|
|
+ .atomic_begin = mixer_atomic_begin,
|
|
|
.update_plane = mixer_update_plane,
|
|
|
.disable_plane = mixer_disable_plane,
|
|
|
+ .atomic_flush = mixer_atomic_flush,
|
|
|
.atomic_check = mixer_atomic_check,
|
|
|
};
|
|
|
|
|
@@ -1160,7 +1205,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
|
|
|
if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
|
|
|
continue;
|
|
|
|
|
|
- ret = exynos_plane_init(drm_dev, &ctx->planes[i],
|
|
|
+ ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
|
|
|
1 << ctx->pipe, &plane_configs[i]);
|
|
|
if (ret)
|
|
|
return ret;
|