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

Commit 7a534900 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: concurrent writeback support"

parents dbd7af6d 77cc0ee3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ enum msm_mdp_crtc_property {
	CRTC_PROP_SECURITY_LEVEL,
	CRTC_PROP_IDLE_TIMEOUT,
	CRTC_PROP_DEST_SCALER,
	CRTC_PROP_CAPTURE_OUTPUT,

	CRTC_PROP_ENABLE_SUI_ENHANCEMENT,

+5 −3
Original line number Diff line number Diff line
@@ -1236,7 +1236,7 @@ void sde_connector_prepare_fence(struct drm_connector *connector)
}

void sde_connector_complete_commit(struct drm_connector *connector,
		ktime_t ts)
		ktime_t ts, enum sde_fence_event fence_event)
{
	if (!connector) {
		SDE_ERROR("invalid connector\n");
@@ -1244,7 +1244,8 @@ void sde_connector_complete_commit(struct drm_connector *connector,
	}

	/* signal connector's retire fence */
	sde_fence_signal(&to_sde_connector(connector)->retire_fence, ts, false);
	sde_fence_signal(&to_sde_connector(connector)->retire_fence,
			ts, fence_event);
}

void sde_connector_commit_reset(struct drm_connector *connector, ktime_t ts)
@@ -1255,7 +1256,8 @@ void sde_connector_commit_reset(struct drm_connector *connector, ktime_t ts)
	}

	/* signal connector's retire fence */
	sde_fence_signal(&to_sde_connector(connector)->retire_fence, ts, true);
	sde_fence_signal(&to_sde_connector(connector)->retire_fence,
			ts, SDE_FENCE_RESET_TIMELINE);
}

static void sde_connector_update_hdr_props(struct drm_connector *connector)
+3 −1
Original line number Diff line number Diff line
@@ -585,8 +585,10 @@ void sde_connector_prepare_fence(struct drm_connector *connector);
 * sde_connector_complete_commit - signal completion of current commit
 * @connector: Pointer to drm connector object
 * @ts: timestamp to be updated in the fence signalling
 * @fence_event: enum value to indicate nature of fence event
 */
void sde_connector_complete_commit(struct drm_connector *connector, ktime_t ts);
void sde_connector_complete_commit(struct drm_connector *connector,
		ktime_t ts, enum sde_fence_event fence_event);

/**
 * sde_connector_commit_reset - reset the completion signal
+135 −144
Original line number Diff line number Diff line
@@ -936,7 +936,9 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc,
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	struct sde_rect *crtc_roi;
	int i, num_attached_conns = 0;
	struct msm_mode_info mode_info;
	int i = 0;
	int rc;
	bool is_crtc_roi_dirty;
	bool is_any_conn_roi_dirty;

@@ -958,13 +960,14 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc,
		if (!conn_state || conn_state->crtc != crtc)
			continue;

		if (num_attached_conns) {
			SDE_ERROR(
				"crtc%d: unsupported: roi on crtc w/ >1 connectors\n",
					DRMID(crtc));
		rc = sde_connector_get_mode_info(conn_state, &mode_info);
		if (rc) {
			SDE_ERROR("failed to get mode info\n");
			return -EINVAL;
		}
		++num_attached_conns;

		if (!mode_info.roi_caps.enabled)
			continue;

		sde_conn = to_sde_connector(conn_state->connector);
		sde_conn_state = to_sde_connector_state(conn_state);
@@ -1285,13 +1288,6 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc,
	sde_crtc = to_sde_crtc(crtc);
	sde_crtc_state = to_sde_crtc_state(state);

	if (hweight_long(state->connector_mask) != 1) {
		SDE_ERROR("invalid connector count(%d) for crtc: %d\n",
			(int)hweight_long(state->connector_mask),
			crtc->base.id);
		return -EINVAL;
	}

	/*
	 * check connector array cached at modeset time since incoming atomic
	 * state may not include any connectors if they aren't modified
@@ -1307,15 +1303,13 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc,
			SDE_ERROR("failed to get mode info\n");
			return -EINVAL;
		}
		break;
	}

		if (!mode_info.roi_caps.enabled)
		return 0;
			continue;

		if (sde_crtc_state->user_roi_list.num_rects >
				mode_info.roi_caps.num_roi) {
		SDE_ERROR("roi count is more than supported limit, %d > %d\n",
			SDE_ERROR("roi count is exceeding limit, %d > %d\n",
					sde_crtc_state->user_roi_list.num_rects,
					mode_info.roi_caps.num_roi);
			return -E2BIG;
@@ -1342,6 +1336,7 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc,
		rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state);
		if (rc)
			return rc;
	}

	return 0;
}
@@ -2116,15 +2111,62 @@ static void _sde_crtc_dest_scaler_setup(struct drm_crtc *crtc)
	}
}

static void sde_crtc_frame_event_cb(void *data, u32 event)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
	struct sde_crtc *sde_crtc;
	struct msm_drm_private *priv;
	struct sde_crtc_frame_event *fevent;
	struct sde_crtc_frame_event_cb_data *cb_data;
	unsigned long flags;
	u32 crtc_id;

	cb_data = (struct sde_crtc_frame_event_cb_data *)data;
	if (!data) {
		SDE_ERROR("invalid parameters\n");
		return;
	}

	crtc = cb_data->crtc;
	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
		return;
	}
	sde_crtc = to_sde_crtc(crtc);
	priv = crtc->dev->dev_private;
	crtc_id = drm_crtc_index(crtc);

	SDE_DEBUG("crtc%d\n", crtc->base.id);
	SDE_EVT32_VERBOSE(DRMID(crtc), event);

	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
			struct sde_crtc_frame_event, list);
	if (fevent)
		list_del_init(&fevent->list);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!fevent) {
		SDE_ERROR("crtc%d event %d overflow\n",
				crtc->base.id, event);
		SDE_EVT32(DRMID(crtc), event);
		return;
	}

	fevent->event = event;
	fevent->crtc = crtc;
	fevent->connector = cb_data->connector;
	fevent->ts = ktime_get();
	kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
}

void sde_crtc_prepare_commit(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *cstate;
	struct drm_connector *conn;
	struct sde_crtc_retire_event *retire_event = NULL;
	unsigned long flags;
	int i;
	struct drm_encoder *encoder;

	if (!crtc || !crtc->state) {
		SDE_ERROR("invalid crtc\n");
@@ -2141,31 +2183,17 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
	drm_for_each_connector(conn, crtc->dev)
		if (conn->state && conn->state->crtc == crtc &&
				cstate->num_connectors < MAX_CONNECTORS) {
			encoder = conn->state->best_encoder;
			if (encoder)
				sde_encoder_register_frame_event_callback(
						encoder,
						sde_crtc_frame_event_cb,
						crtc);

			cstate->connectors[cstate->num_connectors++] = conn;
			sde_connector_prepare_fence(conn);
		}

	for (i = 0; i < SDE_CRTC_FRAME_EVENT_SIZE; i++) {
		retire_event = &sde_crtc->retire_events[i];
		if (list_empty(&retire_event->list))
			break;
		retire_event = NULL;
	}

	if (retire_event) {
		retire_event->num_connectors = cstate->num_connectors;
		for (i = 0; i < cstate->num_connectors; i++)
			retire_event->connectors[i] = cstate->connectors[i];

		spin_lock_irqsave(&sde_crtc->spin_lock, flags);
		list_add_tail(&retire_event->list,
						&sde_crtc->retire_event_list);
		spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
	} else {
		SDE_ERROR("crtc%d retire event overflow\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
	}

	/* prepare main output fence */
	sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -2216,9 +2244,16 @@ enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc)
		return INTF_MODE_NONE;
	}

	drm_for_each_encoder(encoder, crtc->dev)
		if (encoder->crtc == crtc)
	drm_for_each_encoder(encoder, crtc->dev) {
		if (encoder->crtc != crtc)
			continue;

		/* continue if copy encoder is encountered */
		if (sde_encoder_in_clone_mode(encoder))
			continue;

		return sde_encoder_get_intf_mode(encoder);
	}

	return INTF_MODE_NONE;
}
@@ -2243,38 +2278,16 @@ static void sde_crtc_vblank_cb(void *data)
	SDE_EVT32_VERBOSE(DRMID(crtc));
}

static void _sde_crtc_retire_event(struct drm_crtc *crtc, ktime_t ts)
static void _sde_crtc_retire_event(struct drm_connector *connector,
		ktime_t ts, bool is_error)
{
	struct sde_crtc_retire_event *retire_event;
	struct sde_crtc *sde_crtc;
	unsigned long flags;
	int i;

	if (!crtc) {
	if (!connector) {
		SDE_ERROR("invalid param\n");
		return;
	}

	sde_crtc = to_sde_crtc(crtc);
	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	retire_event = list_first_entry_or_null(&sde_crtc->retire_event_list,
				struct sde_crtc_retire_event, list);
	if (retire_event)
		list_del_init(&retire_event->list);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!retire_event) {
		SDE_ERROR("crtc%d retire event without kickoff\n",
								crtc->base.id);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
		return;
	}

	SDE_ATRACE_BEGIN("signal_retire_fence");
	for (i = 0; (i < retire_event->num_connectors) &&
					retire_event->connectors[i]; ++i)
		sde_connector_complete_commit(
					retire_event->connectors[i], ts);
	sde_connector_complete_commit(connector, ts, is_error);
	SDE_ATRACE_END("signal_retire_fence");
}

@@ -2287,6 +2300,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
	struct sde_kms *sde_kms;
	unsigned long flags;
	bool frame_done = false;
	bool in_clone_mode = false;

	if (!work) {
		SDE_ERROR("invalid work handle\n");
@@ -2315,10 +2329,11 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)

	SDE_EVT32_VERBOSE(DRMID(crtc), fevent->event, SDE_EVTLOG_FUNC_ENTRY);

	if (fevent->event & (SDE_ENCODER_FRAME_EVENT_DONE
				| SDE_ENCODER_FRAME_EVENT_ERROR
				| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
	in_clone_mode = sde_encoder_in_clone_mode(fevent->connector->encoder);

	if (!in_clone_mode && (fevent->event & (SDE_ENCODER_FRAME_EVENT_ERROR
					| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD
					| SDE_ENCODER_FRAME_EVENT_DONE))) {
		if (atomic_read(&sde_crtc->frame_pending) < 1) {
			/* this should not happen */
			SDE_ERROR("crtc%d ts:%lld invalid frame_pending:%d\n",
@@ -2347,13 +2362,17 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)

	if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE) {
		SDE_ATRACE_BEGIN("signal_release_fence");
		sde_fence_signal(&sde_crtc->output_fence, fevent->ts, false);
		sde_fence_signal(&sde_crtc->output_fence, fevent->ts,
				(fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR)
				? SDE_FENCE_SIGNAL_ERROR : SDE_FENCE_SIGNAL);
		SDE_ATRACE_END("signal_release_fence");
	}

	if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE)
		/* this api should be called without spin_lock */
		_sde_crtc_retire_event(crtc, fevent->ts);
		_sde_crtc_retire_event(fevent->connector, fevent->ts,
				(fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR)
				? SDE_FENCE_SIGNAL_ERROR : SDE_FENCE_SIGNAL);

	if (fevent->event & SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)
		SDE_ERROR("crtc%d ts:%lld received panel dead event\n",
@@ -2368,46 +2387,6 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
	SDE_ATRACE_END("crtc_frame_event");
}

static void sde_crtc_frame_event_cb(void *data, u32 event)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
	struct sde_crtc *sde_crtc;
	struct msm_drm_private *priv;
	struct sde_crtc_frame_event *fevent;
	unsigned long flags;
	u32 crtc_id;

	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
		return;
	}
	sde_crtc = to_sde_crtc(crtc);
	priv = crtc->dev->dev_private;
	crtc_id = drm_crtc_index(crtc);

	SDE_DEBUG("crtc%d\n", crtc->base.id);
	SDE_EVT32_VERBOSE(DRMID(crtc), event);

	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
			struct sde_crtc_frame_event, list);
	if (fevent)
		list_del_init(&fevent->list);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!fevent) {
		SDE_ERROR("crtc%d event %d overflow\n",
				crtc->base.id, event);
		SDE_EVT32(DRMID(crtc), event);
		return;
	}

	fevent->event = event;
	fevent->crtc = crtc;
	fevent->ts = ktime_get();
	kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
}

void sde_crtc_complete_commit(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
@@ -2977,6 +2956,10 @@ static void _sde_crtc_setup_mixers(struct drm_crtc *crtc)
		if (enc->crtc != crtc)
			continue;

		/* avoid overwriting mixers info from a copy encoder */
		if (sde_encoder_in_clone_mode(enc))
			continue;

		_sde_crtc_setup_mixer_for_encoder(crtc, enc);
	}

@@ -4211,7 +4194,8 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
	 * reset the fence timeline if crtc will not be enabled for this commit
	 */
	if (!crtc->state->active || !crtc->state->enable) {
		sde_fence_signal(&sde_crtc->output_fence, ktime_get(), true);
		sde_fence_signal(&sde_crtc->output_fence,
				ktime_get(), SDE_FENCE_RESET_TIMELINE);
		for (i = 0; i < cstate->num_connectors; ++i)
			sde_connector_commit_reset(cstate->connectors[i],
					ktime_get());
@@ -4276,7 +4260,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
		if (encoder->crtc != crtc)
			continue;
		sde_encoder_register_frame_event_callback(encoder,
				sde_crtc_frame_event_cb, (void *)crtc);
				sde_crtc_frame_event_cb, crtc);
	}

	if (!sde_crtc->enabled && !sde_crtc->suspend &&
@@ -4935,6 +4919,11 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
		{SDE_DRM_SEC_ONLY, "sec_only"},
	};

	static const struct drm_prop_enum_list e_cwb_data_points[] = {
		{CAPTURE_MIXER_OUT, "capture_mixer_out"},
		{CAPTURE_DSPP_OUT, "capture_pp_out"},
	};

	SDE_DEBUG("\n");

	if (!crtc || !catalog) {
@@ -5014,6 +5003,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
		"enable_sui_enhancement", 0, 0, U64_MAX, 0,
		CRTC_PROP_ENABLE_SUI_ENHANCEMENT);

	if (catalog->has_cwb_support)
		msm_property_install_enum(&sde_crtc->property_info,
				"capture_mode", 0, 0, e_cwb_data_points,
				ARRAY_SIZE(e_cwb_data_points),
				CRTC_PROP_CAPTURE_OUTPUT);

	msm_property_install_blob(&sde_crtc->property_info, "capabilities",
		DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);

@@ -5887,10 +5882,6 @@ static int _sde_crtc_init_events(struct sde_crtc *sde_crtc)
		list_add_tail(&sde_crtc->event_cache[i].list,
				&sde_crtc->event_free_list);

	INIT_LIST_HEAD(&sde_crtc->retire_event_list);
	for (i = 0; i < ARRAY_SIZE(sde_crtc->retire_events); i++)
		INIT_LIST_HEAD(&sde_crtc->retire_events[i].list);

	return rc;
}

+24 −5
Original line number Diff line number Diff line
@@ -31,7 +31,8 @@
#define SDE_CRTC_NAME_SIZE	12

/* define the maximum number of in-flight frame events */
#define SDE_CRTC_FRAME_EVENT_SIZE	4
/* Expand it to 2x for handling atleast 2 connectors safely */
#define SDE_CRTC_FRAME_EVENT_SIZE	(4 * 2)

/**
 * enum sde_crtc_client_type: crtc client type
@@ -47,6 +48,16 @@ enum sde_crtc_client_type {
	RT_RSC_CLIENT,
};

/**
 * enum sde_crtc_output_capture_point
 * @MIXER_OUT : capture mixer output
 * @DSPP_OUT : capture output of dspp
 */
enum sde_crtc_output_capture_point {
	CAPTURE_MIXER_OUT,
	CAPTURE_DSPP_OUT
};

/**
 * @connectors    : Currently associated drm connectors for retire event
 * @num_connectors: Number of associated drm connectors for retire event
@@ -80,10 +91,21 @@ struct sde_crtc_mixer {
	u32 pipe_mask;
};

/**
 * struct sde_crtc_frame_event_cb_data : info of drm objects of a frame event
 * @crtc:       pointer to drm crtc object registered for frame event
 * @connector:  pointer to drm connector which is source of frame event
 */
struct sde_crtc_frame_event_cb_data {
	 struct drm_crtc *crtc;
	 struct drm_connector *connector;
};

/**
 * struct sde_crtc_frame_event: stores crtc frame event for crtc processing
 * @work:	base work structure
 * @crtc:	Pointer to crtc handling this event
 * @connector:  pointer to drm connector which is source of frame event
 * @list:	event list
 * @ts:		timestamp at queue entry
 * @event:	event identifier
@@ -91,6 +113,7 @@ struct sde_crtc_mixer {
struct sde_crtc_frame_event {
	struct kthread_work work;
	struct drm_crtc *crtc;
	struct drm_connector *connector;
	struct list_head list;
	ktime_t ts;
	u32 event;
@@ -161,8 +184,6 @@ struct sde_crtc_event {
 * @frame_events  : static allocation of in-flight frame events
 * @frame_event_list : available frame event list
 * @spin_lock     : spin lock for frame event, transaction status, etc...
 * @retire_events  : static allocation of retire fence connector
 * @retire_event_list : available retire fence connector list
 * @frame_done_comp    : for frame_event_done synchronization
 * @event_thread  : Pointer to event handler thread
 * @event_worker  : Event worker queue
@@ -234,8 +255,6 @@ struct sde_crtc {
	struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
	struct list_head frame_event_list;
	spinlock_t spin_lock;
	struct sde_crtc_retire_event retire_events[SDE_CRTC_FRAME_EVENT_SIZE];
	struct list_head retire_event_list;
	struct completion frame_done_comp;

	/* for handling internal event thread */
Loading