Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ebae8d07 authored by Thierry Reding's avatar Thierry Reding
Browse files

drm/tegra: dc: Implement legacy blending



This implements alpha blending on legacy display controllers (Tegra20,
Tegra30 and Tegra114). While it's theoretically possible to support the
zpos property to enable userspace to specify the Z-order of each plane
individually, this is not currently supported and the same fixed Z-
order as previously defined is used.

Reverts commit 71835caa ("drm/tegra: fb: Force alpha formats") since
the opaque formats are now supported.

Reported-by: default avatarDmitry Osipenko <digetx@gmail.com>
Fixes: 7772fdae ("drm/tegra: Support ARGB and ABGR formats")
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 4c69ac12
Loading
Loading
Loading
Loading
+68 −13
Original line number Diff line number Diff line
@@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in)

static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
{
	u32 background[3] = {
		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
	};
	u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
			 BLEND_COLOR_KEY_NONE;
	u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
	struct tegra_plane_state *state;
	unsigned int i;

	state = to_tegra_plane_state(plane->base.state);

	/* alpha contribution is 1 minus sum of overlapping windows */
	for (i = 0; i < 3; i++) {
		if (state->dependent[i])
			background[i] |= BLEND_CONTROL_DEPENDENT;
	}

	/* enable alpha blending if pixel format has an alpha component */
	if (!state->opaque)
		foreground |= BLEND_CONTROL_ALPHA;

	/*
	 * 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_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY);
	tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN);
	tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
	tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);

	switch (plane->index) {
	case 0:
		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X);
		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
		tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
		break;

	case 1:
		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
		break;

	case 2:
		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y);
		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY);
		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
		break;
	}
}
@@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = {
	DRM_FORMAT_RGBA5551,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_ARGB8888,
	/* non-native formats */
	DRM_FORMAT_XRGB1555,
	DRM_FORMAT_RGBX5551,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_XRGB8888,
};

static const u32 tegra114_primary_formats[] = {
@@ -409,18 +437,40 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
	struct tegra_bo_tiling *tiling = &plane_state->tiling;
	struct tegra_plane *tegra = to_tegra_plane(plane);
	struct tegra_dc *dc = to_tegra_dc(state->crtc);
	unsigned int format;
	int err;

	/* no need for further checks if the plane is being disabled */
	if (!state->crtc)
		return 0;

	err = tegra_plane_format(state->fb->format->format,
				 &plane_state->format,
	err = tegra_plane_format(state->fb->format->format, &format,
				 &plane_state->swap);
	if (err < 0)
		return err;

	/*
	 * Tegra20 and Tegra30 are special cases here because they support
	 * only variants of specific formats with an alpha component, but not
	 * the corresponding opaque formats. However, the opaque formats can
	 * be emulated by disabling alpha blending for the plane.
	 */
	if (!dc->soc->supports_blending) {
		if (!tegra_plane_format_has_alpha(format)) {
			err = tegra_plane_format_get_alpha(format, &format);
			if (err < 0)
				return err;

			plane_state->opaque = true;
		} else {
			plane_state->opaque = false;
		}

		tegra_plane_check_dependent(tegra, plane_state);
	}

	plane_state->format = format;

	err = tegra_fb_get_tiling(state->fb, tiling);
	if (err < 0)
		return err;
@@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = {
	DRM_FORMAT_RGBA5551,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_ARGB8888,
	/* non-native formats */
	DRM_FORMAT_XRGB1555,
	DRM_FORMAT_RGBX5551,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_XRGB8888,
	/* planar formats */
	DRM_FORMAT_UYVY,
	DRM_FORMAT_YUYV,
+12 −0
Original line number Diff line number Diff line
@@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_WIN_DV_CONTROL			0x70e

#define DC_WIN_BLEND_NOKEY			0x70f
#define  BLEND_WEIGHT1(x) (((x) & 0xff) << 16)
#define  BLEND_WEIGHT0(x) (((x) & 0xff) <<  8)

#define DC_WIN_BLEND_1WIN			0x710
#define  BLEND_CONTROL_FIX    (0 << 2)
#define  BLEND_CONTROL_ALPHA  (1 << 2)
#define  BLEND_COLOR_KEY_NONE (0 << 0)
#define  BLEND_COLOR_KEY_0    (1 << 0)
#define  BLEND_COLOR_KEY_1    (2 << 0)
#define  BLEND_COLOR_KEY_BOTH (3 << 0)

#define DC_WIN_BLEND_2WIN_X			0x711
#define  BLEND_CONTROL_DEPENDENT (2 << 2)

#define DC_WIN_BLEND_2WIN_Y			0x712
#define DC_WIN_BLEND_3WIN_XY			0x713

+0 −12
Original line number Diff line number Diff line
@@ -254,18 +254,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
	cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
				  tegra->pitch_align);

	/*
	 * Early generations of Tegra (Tegra20 and Tegra30) do not support any
	 * of the X* or *X formats, only their A* or *A equivalents. Force the
	 * legacy framebuffer format to include an alpha component so that the
	 * framebuffer emulation can be supported on all generations.
	 */
	if (sizes->surface_bpp == 32 && sizes->surface_depth == 24)
		sizes->surface_depth = 32;

	if (sizes->surface_bpp == 16 && sizes->surface_depth == 15)
		sizes->surface_depth = 16;

	cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
						     sizes->surface_depth);

+138 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
{
	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
	struct tegra_plane_state *copy;
	unsigned int i;

	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
	if (!copy)
@@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
	copy->tiling = state->tiling;
	copy->format = state->format;
	copy->swap = state->swap;
	copy->opaque = state->opaque;

	for (i = 0; i < 3; i++)
		copy->dependent[i] = state->dependent[i];

	return &copy->base;
}
@@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar)

	return false;
}

static bool __drm_format_has_alpha(u32 format)
{
	switch (format) {
	case DRM_FORMAT_ARGB1555:
	case DRM_FORMAT_RGBA5551:
	case DRM_FORMAT_ABGR8888:
	case DRM_FORMAT_ARGB8888:
		return true;
	}

	return false;
}

/*
 * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
 * be emulated using the alpha formats and alpha blending disabled.
 */
bool tegra_plane_format_has_alpha(unsigned int format)
{
	switch (format) {
	case WIN_COLOR_DEPTH_B5G5R5A1:
	case WIN_COLOR_DEPTH_A1B5G5R5:
	case WIN_COLOR_DEPTH_R8G8B8A8:
	case WIN_COLOR_DEPTH_B8G8R8A8:
		return true;
	}

	return false;
}

int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
{
	switch (opaque) {
	case WIN_COLOR_DEPTH_B5G5R5X1:
		*alpha = WIN_COLOR_DEPTH_B5G5R5A1;
		return 0;

	case WIN_COLOR_DEPTH_X1B5G5R5:
		*alpha = WIN_COLOR_DEPTH_A1B5G5R5;
		return 0;

	case WIN_COLOR_DEPTH_R8G8B8X8:
		*alpha = WIN_COLOR_DEPTH_R8G8B8A8;
		return 0;

	case WIN_COLOR_DEPTH_B8G8R8X8:
		*alpha = WIN_COLOR_DEPTH_B8G8R8A8;
		return 0;
	}

	return -EINVAL;
}

unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
					   struct tegra_plane *other)
{
	unsigned int index = 0, i;

	WARN_ON(plane == other);

	for (i = 0; i < 3; i++) {
		if (i == plane->index)
			continue;

		if (i == other->index)
			break;

		index++;
	}

	return index;
}

void tegra_plane_check_dependent(struct tegra_plane *tegra,
				 struct tegra_plane_state *state)
{
	struct drm_plane_state *old, *new;
	struct drm_plane *plane;
	unsigned int zpos[2];
	unsigned int i;

	for (i = 0; i < 3; i++)
		state->dependent[i] = false;

	for (i = 0; i < 2; i++)
		zpos[i] = 0;

	for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {
		struct tegra_plane *p = to_tegra_plane(plane);
		unsigned index;

		/* skip this plane and planes on different CRTCs */
		if (p == tegra || new->crtc != state->base.crtc)
			continue;

		index = tegra_plane_get_overlap_index(tegra, p);

		/*
		 * If any of the other planes is on top of this plane and uses
		 * a format with an alpha component, mark this plane as being
		 * dependent, meaning it's alpha value will be 1 minus the sum
		 * of alpha components of the overlapping planes.
		 */
		if (p->index > tegra->index) {
			if (__drm_format_has_alpha(new->fb->format->format))
				state->dependent[index] = true;

			/* keep track of the Z position */
			zpos[index] = p->index;
		}
	}

	/*
	 * The region where three windows overlap is the intersection of the
	 * two regions where two windows overlap. It contributes to the area
	 * if any of the windows on top of it have an alpha component.
	 */
	for (i = 0; i < 2; i++)
		state->dependent[2] = state->dependent[2] ||
				      state->dependent[i];

	/*
	 * However, if any of the windows on top of this window is opaque, it
	 * will completely conceal this window within that area, so avoid the
	 * window from contributing to the area.
	 */
	for (i = 0; i < 2; i++) {
		if (zpos[i] > tegra->index)
			state->dependent[2] = state->dependent[2] &&
					      state->dependent[i];
	}
}
+8 −0
Original line number Diff line number Diff line
@@ -40,6 +40,10 @@ struct tegra_plane_state {
	struct tegra_bo_tiling tiling;
	u32 format;
	u32 swap;

	/* used for legacy blending support only */
	bool opaque;
	bool dependent[3];
};

static inline struct tegra_plane_state *
@@ -58,5 +62,9 @@ int tegra_plane_state_add(struct tegra_plane *plane,

int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap);
bool tegra_plane_format_is_yuv(unsigned int format, bool *planar);
bool tegra_plane_format_has_alpha(unsigned int format);
int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha);
void tegra_plane_check_dependent(struct tegra_plane *tegra,
				 struct tegra_plane_state *state);

#endif /* TEGRA_PLANE_H */