[PATCH] drm/tegra: Expose color key and plane blending controls to userspace

From: Dmitry Osipenko
Date: Fri Sep 02 2016 - 05:34:39 EST


Chromakey is a simple way of video overlay overlap implementation. This
patch adds 2 new IOCTL's: first - sets color key and is common across of
all Tegra SoC's, second - sets plane blending controls and allows to
utilize the color key, this one is exclusive to Tegra20/30.

Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx>
---
drivers/gpu/drm/tegra/dc.c | 150 +++++++++++++++++++++++++++++++++-------
drivers/gpu/drm/tegra/dc.h | 6 ++
drivers/gpu/drm/tegra/drm.c | 159 +++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/tegra/drm.h | 14 ++++
include/uapi/drm/tegra_drm.h | 34 +++++++++
5 files changed, 337 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index ddac53c..5956382 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -42,6 +42,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
return container_of(plane, struct tegra_plane, base);
}

+struct tegra_dc_color_key {
+ u32 upper;
+ u32 lower;
+};
+
struct tegra_dc_state {
struct drm_crtc_state base;

@@ -50,6 +55,9 @@ struct tegra_dc_state {
unsigned int div;

u32 planes;
+
+ struct tegra_dc_color_key color_key0;
+ struct tegra_dc_color_key color_key1;
};

static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -66,6 +74,11 @@ struct tegra_plane_state {
struct tegra_bo_tiling tiling;
u32 format;
u32 swap;
+ u32 blend_nokey;
+ u32 blend_1win;
+ u32 blend_2win_x;
+ u32 blend_2win_y;
+ u32 blend_3win_xy;
};

static inline struct tegra_plane_state *
@@ -77,6 +90,66 @@ to_tegra_plane_state(struct drm_plane_state *state)
return NULL;
}

+void tegra_dc_set_color_key(struct drm_crtc_state *crtc_state,
+ int key_id, u32 upper, u32 lower)
+{
+ struct tegra_dc_state *state = to_dc_state(crtc_state);
+ struct tegra_dc_color_key *color_key;
+
+ if (key_id == 0)
+ color_key = &state->color_key0;
+ else
+ color_key = &state->color_key1;
+
+ color_key->lower = lower;
+ color_key->upper = upper;
+}
+
+void tegra20_dc_plane_set_blending(struct drm_plane_state *plane_state,
+ unsigned int blend_config,
+ unsigned int blend_control,
+ unsigned int blend_weight0,
+ unsigned int blend_weight1,
+ bool use_color_key0,
+ bool use_color_key1)
+{
+ struct tegra_plane_state *state = to_tegra_plane_state(plane_state);
+ u32 value;
+
+ if (blend_config == DRM_TEGRA_PLANE_BLEND_CONFIG_NOKEY) {
+ value = DC_WIN_BLEND_CONTROL_NOKEY(blend_control);
+ } else {
+ value = DC_WIN_BLEND_CONTROL(blend_control);
+
+ if (use_color_key0)
+ value |= DC_WIN_BLEND_CKEY0;
+
+ if (use_color_key1)
+ value |= DC_WIN_BLEND_CKEY1;
+ }
+
+ value |= DC_WIN_BLEND_WEIGHT0(blend_weight0);
+ value |= DC_WIN_BLEND_WEIGHT1(blend_weight1);
+
+ switch (blend_config) {
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_NOKEY:
+ state->blend_nokey = value;
+ break;
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_1WIN:
+ state->blend_1win = value;
+ break;
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_2WIN_X:
+ state->blend_2win_x = value;
+ break;
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_2WIN_Y:
+ state->blend_2win_y = value;
+ break;
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_3WIN_XY:
+ state->blend_3win_xy = value;
+ break;
+ }
+}
+
static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
{
stats->frames = 0;
@@ -381,32 +454,11 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,

tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);

- /*
- * Disable blending and assume Window A is the bottom-most window,
- * Window C is the top-most window and Window B is in the middle.
- */
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
-
- switch (index) {
- case 0:
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
- break;
-
- case 1:
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
- break;
-
- case 2:
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
- break;
- }
+ tegra_dc_writel(dc, window->blend_nokey, DC_WIN_BLEND_NOKEY);
+ tegra_dc_writel(dc, window->blend_1win, DC_WIN_BLEND_1WIN);
+ tegra_dc_writel(dc, window->blend_2win_x, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, window->blend_2win_y, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, window->blend_3win_xy, DC_WIN_BLEND_3WIN_XY);

spin_unlock_irqrestore(&dc->lock, flags);
}
@@ -444,6 +496,34 @@ static void tegra_plane_reset(struct drm_plane *plane)
if (state) {
plane->state = &state->base;
plane->state->plane = plane;
+
+ /*
+ * By default, disable blending and assume Window A is the
+ * bottom-most window, Window C is the top-most window and
+ * Window B is in the middle.
+ */
+ state->blend_nokey = 0xffff00;
+ state->blend_1win = 0xffff00;
+
+ switch (plane->index) {
+ case 0:
+ state->blend_2win_x = 0x000000;
+ state->blend_2win_y = 0x000000;
+ state->blend_3win_xy = 0x000000;
+ break;
+
+ case 1:
+ state->blend_2win_x = 0xffff00;
+ state->blend_2win_y = 0x000000;
+ state->blend_3win_xy = 0x000000;
+ break;
+
+ case 2:
+ state->blend_2win_x = 0xffff00;
+ state->blend_2win_y = 0xffff00;
+ state->blend_3win_xy = 0xffff00;
+ break;
+ }
}
}

@@ -460,6 +540,11 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla
copy->tiling = state->tiling;
copy->format = state->format;
copy->swap = state->swap;
+ copy->blend_nokey = state->blend_nokey;
+ copy->blend_1win = state->blend_1win;
+ copy->blend_2win_x = state->blend_2win_x;
+ copy->blend_2win_y = state->blend_2win_y;
+ copy->blend_3win_xy = state->blend_3win_xy;

return &copy->base;
}
@@ -593,6 +678,11 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
window.tiling = state->tiling;
window.format = state->format;
window.swap = state->swap;
+ window.blend_nokey = state->blend_nokey;
+ window.blend_1win = state->blend_1win;
+ window.blend_2win_x = state->blend_2win_x;
+ window.blend_2win_y = state->blend_2win_y;
+ window.blend_3win_xy = state->blend_3win_xy;

for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@@ -1045,6 +1135,8 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
copy->pclk = state->pclk;
copy->div = state->div;
copy->planes = state->planes;
+ copy->color_key0 = state->color_key0;
+ copy->color_key1 = state->color_key1;

return &copy->base;
}
@@ -1343,6 +1435,12 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,

tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
+
+ tegra_dc_writel(dc, state->color_key0.lower, DC_DISP_COLOR_KEY0_LOWER);
+ tegra_dc_writel(dc, state->color_key0.upper, DC_DISP_COLOR_KEY0_UPPER);
+ tegra_dc_writel(dc, state->color_key1.lower, DC_DISP_COLOR_KEY1_LOWER);
+ tegra_dc_writel(dc, state->color_key1.upper, DC_DISP_COLOR_KEY1_UPPER);
+ tegra_dc_commit(dc);
}

static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 4a26863..45d8142 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -433,6 +433,12 @@
#define DC_WIN_BLEND_2WIN_X 0x711
#define DC_WIN_BLEND_2WIN_Y 0x712
#define DC_WIN_BLEND_3WIN_XY 0x713
+#define DC_WIN_BLEND_CKEY0 (1 << 0)
+#define DC_WIN_BLEND_CKEY1 (1 << 1)
+#define DC_WIN_BLEND_CONTROL(x) ((x) << 2)
+#define DC_WIN_BLEND_CONTROL_NOKEY(x) ((x) << 0)
+#define DC_WIN_BLEND_WEIGHT0(x) ((x) << 8)
+#define DC_WIN_BLEND_WEIGHT1(x) ((x) << 16)

#define DC_WIN_HP_FETCH_CONTROL 0x714

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 755264d..917b75b 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -13,6 +13,8 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>

+#include <soc/tegra/fuse.h>
+
#include "drm.h"
#include "gem.h"

@@ -771,6 +773,161 @@ static int tegra_gem_get_flags(struct drm_device *drm, void *data,

return 0;
}
+
+static int tegra_set_color_key(struct drm_device *drm, void *data,
+ struct drm_file *file)
+{
+ struct drm_tegra_set_color_key *args = data;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ bool crtc_mask_invalid = true;
+ int ret;
+
+ if (args->key_id > 1)
+ return -EINVAL;
+
+ drm_for_each_crtc(crtc, drm) {
+ if (!(args->crtc_mask & drm_crtc_mask(crtc)))
+ continue;
+
+ crtc_mask_invalid = false;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_atomic_state_alloc(drm);
+ if (!state) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ state->acquire_ctx = &ctx;
+retry:
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ goto unlock;
+ }
+
+ tegra_dc_set_color_key(crtc_state, args->key_id,
+ args->upper, args->lower);
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK)
+ goto backoff;
+ if (ret)
+ drm_atomic_state_free(state);
+unlock:
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
+ if (ret)
+ return ret;
+
+ continue;
+backoff:
+ drm_atomic_state_clear(state);
+ drm_modeset_backoff(&ctx);
+
+ goto retry;
+ }
+
+ if (crtc_mask_invalid)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int tegra20_plane_set_blending(struct drm_device *drm, void *data,
+ struct drm_file *file)
+{
+ struct drm_tegra20_plane_set_blending *args = data;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_plane *plane;
+ struct drm_plane_state *plane_state;
+ struct drm_atomic_state *state;
+ u8 chip = tegra_get_chip_id();
+ int ret;
+
+ switch (chip) {
+ case TEGRA20:
+ case TEGRA30:
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ plane = drm_plane_find(drm, args->plane_id);
+ if (!plane)
+ return -ENOENT;
+
+ switch (args->blend_config) {
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_NOKEY:
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_1WIN:
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_2WIN_X:
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_2WIN_Y:
+ case DRM_TEGRA_PLANE_BLEND_CONFIG_3WIN_XY:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (args->blend_control) {
+ case DRM_TEGRA_PLANE_BLEND_CONTROL_FIX_WEIGHT:
+ case DRM_TEGRA_PLANE_BLEND_CONTROL_ALPHA_WEIGHT:
+ case DRM_TEGRA_PLANE_BLEND_CONTROL_DEPENDENT_WEIGHT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (args->blend_weight0 > 0xff)
+ return -EINVAL;
+
+ if (args->blend_weight1 > 0xff)
+ return -EINVAL;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_atomic_state_alloc(drm);
+ if (!state) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ state->acquire_ctx = &ctx;
+retry:
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ if (IS_ERR(plane_state)) {
+ ret = PTR_ERR(plane_state);
+ goto unlock;
+ }
+
+ tegra20_dc_plane_set_blending(plane_state,
+ args->blend_config,
+ args->blend_control,
+ args->blend_weight0,
+ args->blend_weight1,
+ args->use_color_key0,
+ args->use_color_key1);
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK)
+ goto backoff;
+ if (ret)
+ drm_atomic_state_free(state);
+unlock:
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
+ return ret;
+backoff:
+ drm_atomic_state_clear(state);
+ drm_modeset_backoff(&ctx);
+
+ goto retry;
+}
#endif

static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
@@ -789,6 +946,8 @@ static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, 0),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, 0),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, 0),
+ DRM_IOCTL_DEF_DRV(TEGRA_SET_COLOR_KEY, tegra_set_color_key, 0),
+ DRM_IOCTL_DEF_DRV(TEGRA20_PLANE_SET_BLENDING, tegra20_plane_set_blending, 0),
#endif
};

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 0ddcce1..d2eef79 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -189,6 +189,11 @@ struct tegra_dc_window {
struct tegra_bo_tiling tiling;
u32 format;
u32 swap;
+ u32 blend_nokey;
+ u32 blend_1win;
+ u32 blend_2win_x;
+ u32 blend_2win_y;
+ u32 blend_3win_xy;
};

/* from dc.c */
@@ -200,6 +205,15 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
struct drm_crtc_state *crtc_state,
struct clk *clk, unsigned long pclk,
unsigned int div);
+void tegra_dc_set_color_key(struct drm_crtc_state *crtc_state,
+ int key_id, u32 upper, u32 lower);
+void tegra20_dc_plane_set_blending(struct drm_plane_state *plane_state,
+ unsigned int blend_config,
+ unsigned int blend_control,
+ unsigned int blend_weight0,
+ unsigned int blend_weight1,
+ bool use_color_key0,
+ bool use_color_key1);

struct tegra_output {
struct device_node *of_node;
diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h
index d954f8c..dff9fac 100644
--- a/include/uapi/drm/tegra_drm.h
+++ b/include/uapi/drm/tegra_drm.h
@@ -172,6 +172,36 @@ struct drm_tegra_gem_get_flags {
__u32 flags;
};

+struct drm_tegra_set_color_key {
+ /* input */
+ __u32 crtc_mask; /* Display controllers to use that key */
+ __u32 key_id; /* Specify what color key to set, 0 or 1 */
+ __u32 upper; /* Color key itself in ARGB_8888 format */
+ __u32 lower; /* in range lower..upper */
+};
+
+#define DRM_TEGRA_PLANE_BLEND_CONFIG_NOKEY 0
+#define DRM_TEGRA_PLANE_BLEND_CONFIG_1WIN 1
+#define DRM_TEGRA_PLANE_BLEND_CONFIG_2WIN_X 2
+#define DRM_TEGRA_PLANE_BLEND_CONFIG_2WIN_Y 3
+#define DRM_TEGRA_PLANE_BLEND_CONFIG_3WIN_XY 4
+
+#define DRM_TEGRA_PLANE_BLEND_CONTROL_FIX_WEIGHT 0
+#define DRM_TEGRA_PLANE_BLEND_CONTROL_ALPHA_WEIGHT 1
+#define DRM_TEGRA_PLANE_BLEND_CONTROL_DEPENDENT_WEIGHT 2
+
+struct drm_tegra20_plane_set_blending {
+ /* input */
+ __u32 plane_id;
+ __u32 blend_config; /* Specify blending configuration to set */
+ __u32 blend_control;
+ __u32 blend_weight0;
+ __u32 blend_weight1;
+ __u32 use_color_key0; /* Ignored by the NOKEY blending config */
+ __u32 use_color_key1; /* Ignored by the NOKEY blending config */
+ __u32 pad;
+};
+
#define DRM_TEGRA_GEM_CREATE 0x00
#define DRM_TEGRA_GEM_MMAP 0x01
#define DRM_TEGRA_SYNCPT_READ 0x02
@@ -186,6 +216,8 @@ struct drm_tegra_gem_get_flags {
#define DRM_TEGRA_GEM_GET_TILING 0x0b
#define DRM_TEGRA_GEM_SET_FLAGS 0x0c
#define DRM_TEGRA_GEM_GET_FLAGS 0x0d
+#define DRM_TEGRA_SET_COLOR_KEY 0x0e
+#define DRM_TEGRA20_PLANE_SET_BLENDING 0x0f

#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create)
#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap)
@@ -201,6 +233,8 @@ struct drm_tegra_gem_get_flags {
#define DRM_IOCTL_TEGRA_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_TILING, struct drm_tegra_gem_get_tiling)
#define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags)
#define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags)
+#define DRM_IOCTL_TEGRA_SET_COLOR_KEY DRM_IOW(DRM_COMMAND_BASE + DRM_TEGRA_SET_COLOR_KEY, struct drm_tegra_set_color_key)
+#define DRM_IOCTL_TEGRA20_PLANE_SET_BLENDING DRM_IOW(DRM_COMMAND_BASE + DRM_TEGRA20_PLANE_SET_BLENDING, struct drm_tegra20_plane_set_blending)

#if defined(__cplusplus)
}
--
2.9.3