For the starter a minimal color keying support is implemented, which is enough to provide userspace like Opentegra Xorg driver with ability to support color keying by the XVideo extension. Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> --- drivers/gpu/drm/tegra/dc.c | 25 +++++++++ drivers/gpu/drm/tegra/dc.h | 7 +++ drivers/gpu/drm/tegra/plane.c | 102 ++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index c3afe7b2237e..685a0fedb01d 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -162,6 +162,7 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | BLEND_COLOR_KEY_NONE; u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); + enum drm_plane_colorkey_mode mode; struct tegra_plane_state *state; u32 blending[2]; unsigned int i; @@ -171,7 +172,15 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); state = to_tegra_plane_state(plane->base.state); + mode = plane->base.state->colorkey.mode; + /* setup color keying */ + if (mode == DRM_PLANE_COLORKEY_MODE_FOREGROUND_CLIP) { + /* color key matched areas are transparent */ + foreground = background[0] | BLEND_COLOR_KEY_0; + } + + /* setup alpha blending */ if (state->opaque) { /* * Since custom fix-weight blending isn't utilized and weight @@ -792,6 +801,11 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, dev_err(dc->dev, "failed to create rotation property: %d\n", err); + if (dc->soc->has_legacy_blending) + drm_plane_create_colorkey_properties(&plane->base, + BIT(DRM_PLANE_COLORKEY_MODE_DISABLED) | + BIT(DRM_PLANE_COLORKEY_MODE_FOREGROUND_CLIP)); + return &plane->base; } @@ -1077,6 +1091,11 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, dev_err(dc->dev, "failed to create rotation property: %d\n", err); + if (dc->soc->has_legacy_blending) + drm_plane_create_colorkey_properties(&plane->base, + BIT(DRM_PLANE_COLORKEY_MODE_DISABLED) | + BIT(DRM_PLANE_COLORKEY_MODE_FOREGROUND_CLIP)); + return &plane->base; } @@ -1187,6 +1206,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) copy->pclk = state->pclk; copy->div = state->div; copy->planes = state->planes; + copy->ckey = state->ckey; return ©->base; } @@ -1917,6 +1937,11 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; + if (dc->soc->has_legacy_blending) { + tegra_dc_writel(dc, state->ckey.min, DC_DISP_COLOR_KEY0_LOWER); + tegra_dc_writel(dc, state->ckey.max, DC_DISP_COLOR_KEY0_UPPER); + } + value = state->planes << 8 | GENERAL_UPDATE; tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e96f582ca692..14ed31f0ff37 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -18,6 +18,11 @@ struct tegra_output; +struct tegra_dc_color_key_state { + u32 min; + u32 max; +}; + struct tegra_dc_state { struct drm_crtc_state base; @@ -26,6 +31,8 @@ struct tegra_dc_state { unsigned int div; u32 planes; + + struct tegra_dc_color_key_state ckey; }; static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index d068e8aa3553..b8f05fd6ffcc 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -465,6 +465,104 @@ static int tegra_plane_setup_transparency(struct tegra_plane *tegra, return 0; } +static u32 tegra_plane_convert_colorkey_format(u64 value) +{ + /* convert ARGB16161616 to ARGB8888 */ + u16 a = (value >> 48) & 0xFFFF; + u16 r = (value >> 32) & 0xFFFF; + u16 g = (value >> 16) & 0xFFFF; + u16 b = (value >> 0) & 0xFFFF; + + a = min_t(u16, 0xff, a / 256); + r = min_t(u16, 0xff, r / 256); + g = min_t(u16, 0xff, g / 256); + b = min_t(u16, 0xff, b / 256); + + return (a << 24) | (r << 16) | (g << 8) | b; +} + +static bool tegra_plane_format_invalid_for_colorkey( + struct drm_plane_state *state) +{ + /* + * Older Tegra's do not support alpha channel matching. It should + * be possible to support certain cases where planes format has alpha + * channel, but for simplicity these cases currently are unsupported. + */ + if (state->fb && __drm_format_has_alpha(state->fb->format->format)) + return true; + + return false; +} + +static int tegra_plane_setup_colorkey(struct tegra_plane *tegra, + struct tegra_plane_state *tegra_state) +{ + enum drm_plane_colorkey_mode mode; + struct drm_plane_state *old_plane; + struct drm_crtc_state *crtc_state; + struct tegra_dc_state *dc_state; + struct drm_plane_state *state; + u64 min, max; + + mode = tegra_state->base.colorkey.mode; + min = tegra_state->base.colorkey.min; + max = tegra_state->base.colorkey.max; + + /* no need to proceed if color keying is disabled */ + if (mode == DRM_PLANE_COLORKEY_MODE_DISABLED) + return 0; + + state = &tegra_state->base; + old_plane = drm_atomic_get_old_plane_state(state->state, &tegra->base); + + /* + * Currently color keying implemented for the middle plane only + * to simplify things. + */ + if (state->normalized_zpos != 1) { + /* foreground-clip has no effect when applied to bottom plane */ + if (state->normalized_zpos == 0) + return 0; + + return -EINVAL; + } + + /* + * There is no need to proceed, adding CRTC and other planes to + * the atomic update, if color keying state is unchanged. + */ + if (old_plane && + old_plane->colorkey.mode == mode && + old_plane->colorkey.min == min && + old_plane->colorkey.max == max) + return 0; + + /* convert color key values to HW format */ + min = tegra_plane_convert_colorkey_format(min); + max = tegra_plane_convert_colorkey_format(max); + + /* validate planes format */ + if (tegra_plane_format_invalid_for_colorkey(state)) + return -EINVAL; + + /* + * Tegra's HW stores color key values within CRTC, hence adjust + * planes CRTC atomic state. + */ + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + dc_state = to_dc_state(crtc_state); + + /* update CRTC's color key state */ + dc_state->ckey.min = min; + dc_state->ckey.max = max; + + return 0; +} + int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, struct tegra_plane_state *state) { @@ -478,5 +576,9 @@ int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, if (err < 0) return err; + err = tegra_plane_setup_colorkey(tegra, state); + if (err < 0) + return err; + return 0; } -- 2.17.0