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

Commit effb5eed authored by Alan Kwong's avatar Alan Kwong Committed by Narendra Muppalla
Browse files

msm: sde: Add dynamic OT support for rotator REGDMA



Dynamic OT is applied before rotator operation to control QoS.
Since REGDMA cannot access QoS registers directly, rotator
driver needs to wait for rotator idle before changing any
OT settings.  Once new OT is applied, REGDMA can resume
queueing until OT changes again.

CRs-Fixed: 989206
Change-Id: I2fd07a957b0d8414c855fafcff7a2613695efff0
Signed-off-by: default avatarAlan Kwong <akwong@codeaurora.org>
parent 9487de23
Loading
Loading
Loading
Loading
+47 −29
Original line number Diff line number Diff line
@@ -138,47 +138,68 @@ static bool force_on_xin_clk(u32 bit_off, u32 clk_ctl_reg_off, bool enable)
	return clk_forced_on;
}

static void apply_dynamic_ot_limit(u32 *ot_lim,
	struct sde_mdp_set_ot_params *params)
u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd)
{
	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
	struct sde_mdp_format_params *fmt;
	u32 ot_lim;
	u32 is_yuv;
	u32 res;

	ot_lim = (is_rd) ? mdata->default_ot_rd_limit :
				mdata->default_ot_wr_limit;

	/*
	 * If default ot is not set from dt,
	 * then do not configure it.
	 */
	if (ot_lim == 0)
		goto exit;

	/* Modify the limits if the target and the use case requires it */
	if (false == test_bit(SDE_QOS_OTLIM, mdata->sde_qos_map))
		return;
		goto exit;

	res = params->width * params->height;
	res = width * height;

	SDEROT_DBG("w:%d h:%d rot:%d yuv:%d wb:%d res:%d\n",
		params->width, params->height, params->is_rot,
		params->is_yuv, params->is_wb, res);
	fmt = sde_get_format_params(pixfmt);

	if ((params->is_rot && params->is_yuv) ||
		params->is_wb) {
		if (res <= RES_1080p) {
			*ot_lim = 2;
		} else if (res <= RES_UHD) {
			if (params->is_rot && params->is_yuv)
				*ot_lim = 8;
			else
				*ot_lim = 16;
		}
	if (!fmt) {
		SDEROT_WARN("invalid format %8.8x\n", pixfmt);
		goto exit;
	}

	is_yuv = sde_mdp_is_yuv_format(fmt);

	SDEROT_DBG("w:%d h:%d fps:%d pixfmt:%8.8x yuv:%d res:%d rd:%d\n",
		width, height, fps, pixfmt, is_yuv, res, is_rd);

	if (!is_yuv)
		goto exit;

	if ((res <= RES_1080p) && (fps <= 30))
		ot_lim = 2;
	else if ((res <= RES_1080p) && (fps <= 60))
		ot_lim = 4;
	else if ((res <= RES_UHD) && (fps <= 30))
		ot_lim = 8;

exit:
	SDEROT_DBG("ot_lim=%d\n", ot_lim);
	return ot_lim;
}

static u32 get_ot_limit(u32 reg_off, u32 bit_off,
	struct sde_mdp_set_ot_params *params)
{
	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
	u32 ot_lim = 0;
	u32 ot_lim;
	u32 val;

	if (mdata->default_ot_wr_limit &&
		(params->reg_off_vbif_lim_conf == MMSS_VBIF_WR_LIM_CONF))
		ot_lim = mdata->default_ot_wr_limit;
	else if (mdata->default_ot_rd_limit &&
		(params->reg_off_vbif_lim_conf == MMSS_VBIF_RD_LIM_CONF))
		ot_lim = mdata->default_ot_rd_limit;
	ot_lim = sde_mdp_get_ot_limit(
			params->width, params->height,
			params->fmt, params->fps,
			params->reg_off_vbif_lim_conf == MMSS_VBIF_RD_LIM_CONF);

	/*
	 * If default ot is not set from dt,
@@ -187,9 +208,6 @@ static u32 get_ot_limit(u32 reg_off, u32 bit_off,
	if (ot_lim == 0)
		goto exit;

	/* Modify the limits if the target and the use case requires it */
	apply_dynamic_ot_limit(&ot_lim, params);

	val = SDE_VBIF_READ(mdata, reg_off);
	val &= (0xFF << bit_off);
	val = val >> bit_off;
+4 −3
Original line number Diff line number Diff line
@@ -34,9 +34,8 @@ struct sde_mdp_set_ot_params {
	u32 num;
	u32 width;
	u32 height;
	bool is_rot;
	bool is_wb;
	bool is_yuv;
	u32 fps;
	u32 fmt;
	u32 reg_off_vbif_lim_conf;
	u32 reg_off_mdp_clk_ctrl;
	u32 bit_off_mdp_clk_ctrl;
@@ -143,6 +142,8 @@ u32 sde_apply_comp_ratio_factor(u32 quota,
	struct sde_mdp_format_params *fmt,
	struct sde_mult_factor *factor);

u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd);

void sde_mdp_set_ot_limit(struct sde_mdp_set_ot_params *params);

#define SDE_VBIF_WRITE(mdata, offset, value) \
+134 −20
Original line number Diff line number Diff line
@@ -607,59 +607,165 @@ static int sde_rotator_import_data(struct sde_rot_mgr *mgr,
	return ret;
}

/*
 * sde_rotator_require_reconfiguration - check if reconfiguration is required
 * @mgr: Pointer to rotator manager
 * @hw: Pointer to rotator hw resource
 * @entry: Pointer to next rotation entry
 *
 * Parameters are validated by caller.
 */
static int sde_rotator_require_reconfiguration(struct sde_rot_mgr *mgr,
		struct sde_rot_hw_resource *hw, struct sde_rot_entry *entry)
{
	/* OT setting change may impact queued entries */
	if (entry->perf && (entry->perf->rdot_limit != mgr->rdot_limit ||
			entry->perf->wrot_limit != mgr->wrot_limit))
		return true;

	return false;
}

/*
 * sde_rotator_is_hw_idle - check if hw block is not processing request
 * @mgr: Pointer to rotator manager
 * @hw: Pointer to rotator hw resource
 *
 * Parameters are validated by caller.
 */
static int sde_rotator_is_hw_idle(struct sde_rot_mgr *mgr,
		struct sde_rot_hw_resource *hw)
{
	int i;

	/*
	 * Wait until all queues are idle in order to update global
	 * setting such as VBIF QoS.  This check can be relaxed if global
	 * settings can be updated individually by entries already
	 * queued in hw queue, i.e. REGDMA can update VBIF directly.
	 */
	for (i = 0; i < mgr->queue_count; i++) {
		struct sde_rot_hw_resource *hw_res = mgr->commitq[i].hw;

		if (hw_res && atomic_read(&hw_res->num_active))
			return false;
	}

	return true;
}

/*
 * sde_rotator_is_hw_available - check if hw is available for the given entry
 * @mgr: Pointer to rotator manager
 * @hw: Pointer to rotator hw resource
 * @entry: Pointer to rotation entry
 *
 * Parameters are validated by caller.
 */
static int sde_rotator_is_hw_available(struct sde_rot_mgr *mgr,
		struct sde_rot_hw_resource *hw, struct sde_rot_entry *entry)
{
	/*
	 * Wait until hw is idle if reconfiguration is required; otherwise,
	 * wait until free queue entry is available
	 */
	if (sde_rotator_require_reconfiguration(mgr, hw, entry)) {
		SDEROT_DBG(
			"wait4idle active=%d pending=%d rdot:%u/%u wrot:%u/%u s:%d.%d\n",
				atomic_read(&hw->num_active), hw->pending_count,
				mgr->rdot_limit, entry->perf->rdot_limit,
				mgr->wrot_limit, entry->perf->wrot_limit,
				entry->item.session_id,
				entry->item.sequence_id);
		return sde_rotator_is_hw_idle(mgr, hw);
	} else {
		return (atomic_read(&hw->num_active) < hw->max_active);
	}
}

/*
 * sde_rotator_get_hw_resource - block waiting for hw availability or timeout
 * @queue: Pointer to rotator queue
 * @entry: Pointer to rotation entry
 */
static struct sde_rot_hw_resource *sde_rotator_get_hw_resource(
	struct sde_rot_queue *queue, struct sde_rot_entry *entry)
{
	struct sde_rot_hw_resource *hw = queue->hw;
	struct sde_rot_hw_resource *hw;
	struct sde_rot_mgr *mgr;
	int ret;

	if (!hw) {
		SDEROT_ERR("no hw in the queue\n");
	if (!queue || !entry || !queue->hw) {
		SDEROT_ERR("null parameters\n");
		return NULL;
	}

	hw = queue->hw;
	mgr = entry->private->mgr;

	WARN_ON(atomic_read(&hw->num_active) > hw->max_active);
	while (atomic_read(&hw->num_active) >= hw->max_active) {
		sde_rot_mgr_unlock(entry->private->mgr);
	while (!sde_rotator_is_hw_available(mgr, hw, entry)) {
		sde_rot_mgr_unlock(mgr);
		ret = wait_event_timeout(hw->wait_queue,
			(atomic_read(&hw->num_active) < hw->max_active),
			sde_rotator_is_hw_available(mgr, hw, entry),
			msecs_to_jiffies(mgr->hwacquire_timeout));
		sde_rot_mgr_lock(entry->private->mgr);
		sde_rot_mgr_lock(mgr);
		if (!ret) {
			SDEROT_ERR(
				"timeout waiting for hw resource, a:%d p:%d\n",
				atomic_read(&hw->num_active),
				hw->pending_count);
			if (hw->workload)
				SDEROT_ERR("possible faulty workload s:%d.%d\n",
						hw->workload->item.session_id,
						hw->workload->item.sequence_id);
			return NULL;
		}
	}
	atomic_inc(&hw->num_active);
	SDEROT_DBG("active=%d pending=%d s:%d.%d\n",
	SDEROT_DBG("active=%d pending=%d rdot=%u/%u wrot=%u/%u s:%d.%d\n",
			atomic_read(&hw->num_active), hw->pending_count,
			mgr->rdot_limit, entry->perf->rdot_limit,
			mgr->wrot_limit, entry->perf->wrot_limit,
			entry->item.session_id, entry->item.sequence_id);
	hw->workload = entry;
	mgr->rdot_limit = entry->perf->rdot_limit;
	mgr->wrot_limit = entry->perf->wrot_limit;
	return hw;
}

/*
 * sde_rotator_put_hw_resource - return hw resource and wake up waiting clients
 * @queue: Pointer to rotator queue
 * @entry: Pointer to rotation entry
 * @hw: Pointer to hw resource to be returned
 */
static void sde_rotator_put_hw_resource(struct sde_rot_queue *queue,
	struct sde_rot_hw_resource *hw)
		struct sde_rot_entry *entry, struct sde_rot_hw_resource *hw)
{
	struct sde_rot_entry *entry = hw->workload;
	struct sde_rot_mgr *mgr;
	int i;

	if (!queue || !entry || !hw) {
		SDEROT_ERR("null parameters\n");
		return;
	}

	mgr = entry->private->mgr;

	WARN_ON(atomic_read(&hw->num_active) < 1);
	hw->workload = NULL;
	if (!atomic_add_unless(&hw->num_active, -1, 0))
		SDEROT_ERR("underflow active=%d pending=%d s:%d.%d\n",
			atomic_read(&hw->num_active), hw->pending_count,
			entry->item.session_id, entry->item.sequence_id);
	wake_up(&hw->wait_queue);
	/*
	 * Wake up all queues in case any entry is waiting for hw idle,
	 * in order to update global settings, such as VBIF QoS.
	 * This can be relaxed to the given hw resource if global
	 * settings can be updated individually by entries already
	 * queued in hw queue.
	 */
	for (i = 0; i < mgr->queue_count; i++) {
		struct sde_rot_hw_resource *hw_res = mgr->commitq[i].hw;

		if (hw_res)
			wake_up(&hw_res->wait_queue);
	}
	SDEROT_DBG("active=%d pending=%d s:%d.%d\n",
			atomic_read(&hw->num_active), hw->pending_count,
			entry->item.session_id, entry->item.sequence_id);
@@ -908,6 +1014,14 @@ static int sde_rotator_calc_perf(struct sde_rot_mgr *mgr,
			&config->output.comp_ratio);

	perf->bw = read_bw + write_bw;

	perf->rdot_limit = sde_mdp_get_ot_limit(
			config->input.width, config->input.height,
			config->input.format, config->frame_rate, true);
	perf->wrot_limit = sde_mdp_get_ot_limit(
			config->input.width, config->input.height,
			config->input.format, config->frame_rate, false);

	return 0;
}

@@ -1069,7 +1183,7 @@ static void sde_rotator_commit_handler(struct work_struct *work)
	sde_rot_mgr_unlock(mgr);
	return;
error:
	sde_rotator_put_hw_resource(entry->commitq, hw);
	sde_rotator_put_hw_resource(entry->commitq, entry, hw);
get_hw_res_err:
	sde_rotator_signal_output(entry);
	sde_rotator_release_entry(mgr, entry);
@@ -1141,7 +1255,7 @@ static void sde_rotator_done_handler(struct work_struct *work)
		entry->item.dst_rect.w, entry->item.dst_rect.h);

	sde_rot_mgr_lock(mgr);
	sde_rotator_put_hw_resource(entry->commitq, entry->commitq->hw);
	sde_rotator_put_hw_resource(entry->commitq, entry, entry->commitq->hw);
	sde_rotator_signal_output(entry);
	sde_rotator_release_entry(mgr, entry);
	atomic_dec(&request->pending_count);
@@ -2275,7 +2389,7 @@ int sde_rotator_core_init(struct sde_rot_mgr **pmgr,
	sde_rotator_clk_ctrl(mgr, true);

	mdata->mdss_version = SDE_REG_READ(mdata, SDE_REG_HW_VERSION);
	SDEROT_INFO("mdss revision %x\n", mdata->mdss_version);
	SDEROT_DBG("mdss revision %x\n", mdata->mdss_version);

	if ((mdata->mdss_version & 0xFFFF0000) == 0x10070000) {
		mgr->ops_hw_init = sde_rotator_r1_init;
+4 −1
Original line number Diff line number Diff line
@@ -169,7 +169,6 @@ struct sde_rot_hw_resource {
	atomic_t num_active;
	int max_active;
	wait_queue_head_t wait_queue;
	struct sde_rot_entry *workload;
};

struct sde_rot_queue {
@@ -225,6 +224,8 @@ struct sde_rot_perf {
	struct mutex work_dis_lock;
	u32 *work_distribution;
	int last_wb_idx; /* last known wb index, used when above count is 0 */
	u32 rdot_limit;
	u32 wrot_limit;
};

struct sde_rot_file_private {
@@ -275,6 +276,8 @@ struct sde_rot_mgr {
	struct sde_rot_clk *rot_clk;
	int num_rot_clk;
	int core_clk_idx;
	u32 rdot_limit;
	u32 wrot_limit;

	u32 hwacquire_timeout;
	struct sde_mult_factor pixel_per_clk;
+1 −0
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ struct sde_mdp_pipe {
	struct sde_mdp_plane_sizes src_planes;
	struct sde_mdp_mixer *mixer_left;
	struct sde_mdp_mixer *mixer_right;
	struct sde_mdp_shared_reg_ctrl clk_ctrl;
	u32 params_changed;
	u32 offset;
};
Loading