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

Commit 4a0f012d authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm/mdp5: dynamically assign hw pipes to planes



(re)assign the hw pipes to planes based on required caps, and to handle
situations where we could not modify an in-use plane (ie. SMP block
reallocation).

This means all planes advertise the superset of formats and properties.
Userspace must (as always) use atomic TEST_ONLY step for atomic updates,
as not all planes may be available for use on every frame.

The mapping of hwpipe to plane is stored in mdp5_state, so that state
updates are atomically committed in the same way that plane/etc state
updates are managed.  This is needed because the mdp5_plane_state keeps
a pointer to the hwpipe, and we don't want global state to become out
of sync with the plane state if an atomic update fails, we hit deadlock/
backoff scenario, etc.  The use of state_lock keeps multiple parallel
updates which both re-assign hwpipes properly serialized.

Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent ac2a3fd3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -403,7 +403,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
	for (i = 0; i < cnt; i++) {
		pstates[i].state->stage = STAGE_BASE + i + base;
		DBG("%s: assign pipe %s on stage=%d", crtc->name,
				pipe2name(mdp5_plane_pipe(pstates[i].plane)),
				pstates[i].plane->name,
				pstates[i].state->stage);
	}

+2 −2
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ struct mdp5_state *mdp5_get_state(struct drm_atomic_state *s)
		return ERR_PTR(-ENOMEM);

	/* Copy state: */
	/* TODO */
	new_state->hwpipe = mdp5_kms->state->hwpipe;

	state->state = new_state;

@@ -377,7 +377,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
		struct drm_plane *plane;
		struct drm_crtc *crtc;

		plane = mdp5_plane_init(dev, mdp5_kms->hwpipes[i], primary);
		plane = mdp5_plane_init(dev, primary);
		if (IS_ERR(plane)) {
			ret = PTR_ERR(plane);
			dev_err(dev->dev, "failed to construct plane %d (%d)\n", i, ret);
+4 −3
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ struct mdp5_kms {
 * For atomic updates which require modifying global state,
 */
struct mdp5_state {
	uint32_t dummy;
	struct mdp5_hw_pipe_state hwpipe;
};

struct mdp5_state *__must_check
@@ -94,6 +94,8 @@ mdp5_get_state(struct drm_atomic_state *s);
struct mdp5_plane_state {
	struct drm_plane_state base;

	struct mdp5_hw_pipe *hwpipe;

	/* aligned with property */
	uint8_t premultiplied;
	uint8_t zpos;
@@ -232,8 +234,7 @@ uint32_t mdp5_plane_get_flush(struct drm_plane *plane);
void mdp5_plane_complete_commit(struct drm_plane *plane,
	struct drm_plane_state *state);
enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
struct drm_plane *mdp5_plane_init(struct drm_device *dev,
		struct mdp5_hw_pipe *hwpipe, bool primary);
struct drm_plane *mdp5_plane_init(struct drm_device *dev, bool primary);

uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);

+71 −0
Original line number Diff line number Diff line
@@ -17,6 +17,77 @@

#include "mdp5_kms.h"

struct mdp5_hw_pipe *mdp5_pipe_assign(struct drm_atomic_state *s,
		struct drm_plane *plane, uint32_t caps)
{
	struct msm_drm_private *priv = s->dev->dev_private;
	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
	struct mdp5_state *state;
	struct mdp5_hw_pipe_state *old_state, *new_state;
	struct mdp5_hw_pipe *hwpipe = NULL;
	int i;

	state = mdp5_get_state(s);
	if (IS_ERR(state))
		return ERR_CAST(state);

	/* grab old_state after mdp5_get_state(), since now we hold lock: */
	old_state = &mdp5_kms->state->hwpipe;
	new_state = &state->hwpipe;

	for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
		struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i];

		/* skip if already in-use.. check both new and old state,
		 * since we cannot immediately re-use a pipe that is
		 * released in the current update in some cases:
		 *  (1) mdp5 can have SMP (non-double-buffered)
		 *  (2) hw pipe previously assigned to different CRTC
		 *      (vblanks might not be aligned)
		 */
		if (new_state->hwpipe_to_plane[cur->idx] ||
				old_state->hwpipe_to_plane[cur->idx])
			continue;

		/* skip if doesn't support some required caps: */
		if (caps & ~cur->caps)
			continue;

		/* possible candidate, take the one with the
		 * fewest unneeded caps bits set:
		 */
		if (!hwpipe || (hweight_long(cur->caps & ~caps) <
				hweight_long(hwpipe->caps & ~caps)))
			hwpipe = cur;
	}

	if (!hwpipe)
		return ERR_PTR(-ENOMEM);

	DBG("%s: assign to plane %s for caps %x",
			hwpipe->name, plane->name, caps);
	new_state->hwpipe_to_plane[hwpipe->idx] = plane;

	return hwpipe;
}

void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
{
	struct mdp5_state *state = mdp5_get_state(s);
	struct mdp5_hw_pipe_state *new_state = &state->hwpipe;

	if (!hwpipe)
		return;

	if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx]))
		return;

	DBG("%s: release from plane %s", hwpipe->name,
		new_state->hwpipe_to_plane[hwpipe->idx]->name);

	new_state->hwpipe_to_plane[hwpipe->idx] = NULL;
}

void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe)
{
	kfree(hwpipe);
+10 −0
Original line number Diff line number Diff line
@@ -34,6 +34,16 @@ struct mdp5_hw_pipe {
	uint32_t flush_mask;      /* used to commit pipe registers */
};

/* global atomic state of assignment between pipes and planes: */
struct mdp5_hw_pipe_state {
	struct drm_plane *hwpipe_to_plane[SSPP_MAX];
};

struct mdp5_hw_pipe *__must_check
mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
		uint32_t caps);
void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe);

struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe,
		uint32_t reg_offset, uint32_t caps);
void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe);
Loading