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

Unverified Commit 65f7fa3a authored by Maxime Ripard's avatar Maxime Ripard
Browse files

drm/sun4i: backend: Check for the number of alpha planes



Due to the way the composition is done in hardware, we can only have a
single alpha-enabled plane active at a time, placed in the second (highest
priority) pipe.

Make sure of that in our atomic_check to not end up in an impossible
scenario.

Reviewed-by: default avatarChen-Yu Tsai <wens@csie.org>
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Link: https://patchwork.freedesktop.org/patch/msgid/7371f62a1385f2cbe3ed75dfca2e746338eb2286.1516617243.git-series.maxime.ripard@free-electrons.com
parent 47a05f4a
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -328,6 +328,8 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
	struct drm_atomic_state *state = crtc_state->state;
	struct drm_device *drm = state->dev;
	struct drm_plane *plane;
	unsigned int num_planes = 0;
	unsigned int num_alpha_planes = 0;
	unsigned int num_frontend_planes = 0;

	DRM_DEBUG_DRIVER("Starting checking our planes\n");
@@ -340,6 +342,8 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
			drm_atomic_get_plane_state(state, plane);
		struct sun4i_layer_state *layer_state =
			state_to_sun4i_layer_state(plane_state);
		struct drm_framebuffer *fb = plane_state->fb;
		struct drm_format_name_buf format_name;

		if (sun4i_backend_plane_uses_frontend(plane_state)) {
			DRM_DEBUG_DRIVER("Using the frontend for plane %d\n",
@@ -350,6 +354,50 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
		} else {
			layer_state->uses_frontend = false;
		}

		DRM_DEBUG_DRIVER("Plane FB format is %s\n",
				 drm_get_format_name(fb->format->format,
						     &format_name));
		if (fb->format->has_alpha)
			num_alpha_planes++;

		num_planes++;
	}

	/*
	 * The hardware is a bit unusual here.
	 *
	 * Even though it supports 4 layers, it does the composition
	 * in two separate steps.
	 *
	 * The first one is assigning a layer to one of its two
	 * pipes. If more that 1 layer is assigned to the same pipe,
	 * and if pixels overlaps, the pipe will take the pixel from
	 * the layer with the highest priority.
	 *
	 * The second step is the actual alpha blending, that takes
	 * the two pipes as input, and uses the eventual alpha
	 * component to do the transparency between the two.
	 *
	 * This two steps scenario makes us unable to guarantee a
	 * robust alpha blending between the 4 layers in all
	 * situations, since this means that we need to have one layer
	 * with alpha at the lowest position of our two pipes.
	 *
	 * However, we cannot even do that, since the hardware has a
	 * bug where the lowest plane of the lowest pipe (pipe 0,
	 * priority 0), if it has any alpha, will discard the pixel
	 * entirely and just display the pixels in the background
	 * color (black by default).
	 *
	 * This means that we effectively have only three valid
	 * configurations with alpha, all of them with the alpha being
	 * on pipe1 with the lowest position, which can be 1, 2 or 3
	 * depending on the number of planes and their zpos.
	 */
	if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) {
		DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n");
		return -EINVAL;
	}

	if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) {
@@ -357,6 +405,9 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
		return -EINVAL;
	}

	DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video\n",
			 num_planes, num_alpha_planes, num_frontend_planes);

	return 0;
}

+2 −0
Original line number Diff line number Diff line
@@ -146,6 +146,8 @@
#define SUN4I_BACKEND_HWCCOLORTAB_OFF		0x4c00
#define SUN4I_BACKEND_PIPE_OFF(p)		(0x5000 + (0x400 * (p)))

#define SUN4I_BACKEND_NUM_LAYERS		4
#define SUN4I_BACKEND_NUM_ALPHA_LAYERS		1
#define SUN4I_BACKEND_NUM_FRONTEND_LAYERS	1

struct sun4i_backend {
+2 −22
Original line number Diff line number Diff line
@@ -201,32 +201,12 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
	struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
	int i;

	planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
	/* We need to have a sentinel at the need, hence the overallocation */
	planes = devm_kcalloc(drm->dev, SUN4I_BACKEND_NUM_LAYERS + 1,
			      sizeof(*planes), GFP_KERNEL);
	if (!planes)
		return ERR_PTR(-ENOMEM);

	/*
	 * The hardware is a bit unusual here.
	 *
	 * Even though it supports 4 layers, it does the composition
	 * in two separate steps.
	 *
	 * The first one is assigning a layer to one of its two
	 * pipes. If more that 1 layer is assigned to the same pipe,
	 * and if pixels overlaps, the pipe will take the pixel from
	 * the layer with the highest priority.
	 *
	 * The second step is the actual alpha blending, that takes
	 * the two pipes as input, and uses the eventual alpha
	 * component to do the transparency between the two.
	 *
	 * This two steps scenario makes us unable to guarantee a
	 * robust alpha blending between the 4 layers in all
	 * situations. So we just expose two layers, one per pipe. On
	 * SoCs that support it, sprites could fill the need for more
	 * layers.
	 */
	for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) {
		const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i];
		struct sun4i_layer *layer;