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

Commit a4193d40 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: delay inline rotation clock drops by one commit"

parents c673cc97 2078a866
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -285,13 +285,17 @@ int dsi_core_clk_stop(struct dsi_core_clks *c_clks)
	return rc;
}

static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks)
static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks, int index)
{
	int rc = 0;
	struct dsi_clk_mngr *mngr;

	mngr = container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
	if (index >= MAX_DSI_CTRL) {
		pr_err("Invalid DSI ctrl index\n");
		return -EINVAL;
	}

	mngr = container_of(l_clks, struct dsi_clk_mngr, link_clks[index]);
	if (mngr->is_cont_splash_enabled)
		return 0;
	/*
@@ -443,11 +447,16 @@ static void dsi_link_clk_disable(struct dsi_link_clks *l_clks)
/**
 * dsi_link_clk_start() - enable dsi link clocks
 */
int dsi_link_clk_start(struct dsi_link_clks *clks)
static int dsi_link_clk_start(struct dsi_link_clks *clks, int index)
{
	int rc = 0;

	rc = dsi_link_clk_set_rate(clks);
	if (index >= MAX_DSI_CTRL) {
		pr_err("Invalid DSI ctrl index\n");
		return -EINVAL;
	}

	rc = dsi_link_clk_set_rate(clks, index);
	if (rc) {
		pr_err("failed to set clk rates, rc = %d\n", rc);
		goto error;
@@ -561,7 +570,7 @@ static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,

	m_clks = &clks[master_ndx];

	rc = dsi_link_clk_start(m_clks);
	rc = dsi_link_clk_start(m_clks, master_ndx);
	if (rc) {
		pr_err("failed to turn on master clocks, rc=%d\n", rc);
		goto error;
@@ -573,7 +582,7 @@ static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
		if (!clk || (clk == m_clks))
			continue;

		rc = dsi_link_clk_start(clk);
		rc = dsi_link_clk_start(clk, i);
		if (rc) {
			pr_err("failed to turn on clocks, rc=%d\n", rc);
			goto error_disable_master;
+1 −55
Original line number Diff line number Diff line
@@ -1500,42 +1500,6 @@ static int msm_release(struct inode *inode, struct file *filp)
	return drm_release(inode, filp);
}

/**
 * msm_drv_framebuffer_remove - remove and unreference a framebuffer object
 * @fb: framebuffer to remove
 */
void msm_drv_framebuffer_remove(struct drm_framebuffer *fb)
{
	struct drm_device *dev;

	if (!fb)
		return;

	dev = fb->dev;

	WARN_ON(!list_empty(&fb->filp_head));

	drm_framebuffer_unreference(fb);
}

struct msm_drv_rmfb2_work {
	struct work_struct work;
	struct list_head fbs;
};

static void msm_drv_rmfb2_work_fn(struct work_struct *w)
{
	struct msm_drv_rmfb2_work *arg = container_of(w, typeof(*arg), work);

	while (!list_empty(&arg->fbs)) {
		struct drm_framebuffer *fb =
			list_first_entry(&arg->fbs, typeof(*fb), filp_head);

		list_del_init(&fb->filp_head);
		msm_drv_framebuffer_remove(fb);
	}
}

/**
 * msm_ioctl_rmfb2 - remove an FB from the configuration
 * @dev: drm device for the ioctl
@@ -1579,24 +1543,6 @@ int msm_ioctl_rmfb2(struct drm_device *dev, void *data,
	list_del_init(&fb->filp_head);
	mutex_unlock(&file_priv->fbs_lock);

	/*
	 * we now own the reference that was stored in the fbs list
	 *
	 * drm_framebuffer_remove may fail with -EINTR on pending signals,
	 * so run this in a separate stack as there's no way to correctly
	 * handle this after the fb is already removed from the lookup table.
	 */
	if (drm_framebuffer_read_refcount(fb) > 1) {
		struct msm_drv_rmfb2_work arg;

		INIT_WORK_ONSTACK(&arg.work, msm_drv_rmfb2_work_fn);
		INIT_LIST_HEAD(&arg.fbs);
		list_add_tail(&fb->filp_head, &arg.fbs);

		schedule_work(&arg.work);
		flush_work(&arg.work);
		destroy_work_on_stack(&arg.work);
	} else
	drm_framebuffer_unreference(fb);

	return 0;
+39 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
 * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
 * Copyright (C) 2013 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
@@ -1426,6 +1426,35 @@ static u32 _sde_crtc_calc_inline_prefill(struct drm_crtc *crtc)
	return sde_kms->catalog->sbuf_prefill + sde_kms->catalog->sbuf_headroom;
}

uint64_t sde_crtc_get_sbuf_clk(struct drm_crtc_state *state)
{
	struct sde_crtc_state *cstate;
	u64 tmp;

	if (!state) {
		SDE_ERROR("invalid crtc state\n");
		return 0;
	}
	cstate = to_sde_crtc_state(state);

	/*
	 * Select the max of the current and previous frame's user mode
	 * clock setting so that reductions in clock voting don't take effect
	 * until the current frame has completed.
	 *
	 * If the sbuf_clk_rate[] FIFO hasn't yet been updated in this commit
	 * cycle (as part of the CRTC's atomic check), compare the current
	 * clock value against sbuf_clk_rate[1] instead of comparing the
	 * sbuf_clk_rate[0]/sbuf_clk_rate[1] values.
	 */
	if (cstate->sbuf_clk_shifted)
		tmp = cstate->sbuf_clk_rate[0];
	else
		tmp = sde_crtc_get_property(cstate, CRTC_PROP_ROT_CLK);

	return max_t(u64, cstate->sbuf_clk_rate[1], tmp);
}

static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state, struct sde_crtc *sde_crtc,
		struct sde_crtc_mixer *mixer)
@@ -4030,6 +4059,9 @@ static struct drm_crtc_state *sde_crtc_duplicate_state(struct drm_crtc *crtc)
	/* clear destination scaler dirty bit */
	cstate->ds_dirty = false;

	/* record whether or not the sbuf_clk_rate fifo has been shifted */
	cstate->sbuf_clk_shifted = false;

	/* duplicate base helper */
	__drm_atomic_helper_crtc_duplicate_state(crtc, &cstate->base);

@@ -4672,6 +4704,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
	_sde_crtc_setup_is_ppsplit(state);
	_sde_crtc_setup_lm_bounds(crtc, state);

	/* record current/previous sbuf clock rate for later */
	cstate->sbuf_clk_rate[0] = cstate->sbuf_clk_rate[1];
	cstate->sbuf_clk_rate[1] = sde_crtc_get_property(
			cstate, CRTC_PROP_ROT_CLK);
	cstate->sbuf_clk_shifted = true;

	 /* get plane state for all drm planes associated with crtc state */
	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
		if (IS_ERR_OR_NULL(pstate)) {
+13 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
 * Copyright (C) 2013 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
@@ -391,6 +391,9 @@ struct sde_crtc_respool {
 * @new_perf: new performance state being requested
 * @sbuf_cfg: stream buffer configuration
 * @sbuf_prefill_line: number of line for inline rotator prefetch
 * @sbuf_clk_rate : previous and current user specified inline rotator clock
 * @sbuf_clk_shifted : whether or not sbuf_clk_rate has been shifted as part
 *	of crtc atomic check
 */
struct sde_crtc_state {
	struct drm_crtc_state base;
@@ -422,6 +425,8 @@ struct sde_crtc_state {
	struct sde_core_perf_params new_perf;
	struct sde_ctl_sbuf_cfg sbuf_cfg;
	u32 sbuf_prefill_line;
	u64 sbuf_clk_rate[2];
	bool sbuf_clk_shifted;

	struct sde_crtc_respool rp;
};
@@ -769,4 +774,11 @@ void sde_crtc_timeline_status(struct drm_crtc *crtc);
void sde_crtc_update_cont_splash_mixer_settings(
		struct drm_crtc *crtc);

/**
 * sde_crtc_get_sbuf_clk - get user specified sbuf clock settings
 * @state: Pointer to DRM crtc state object
 * Returns: Filtered sbuf clock setting from user space
 */
uint64_t sde_crtc_get_sbuf_clk(struct drm_crtc_state *state);

#endif /* _SDE_CRTC_H_ */
+228 −2
Original line number Diff line number Diff line
@@ -120,6 +120,13 @@
 *	Event signals that there were no frame updates for
 *	IDLE_POWERCOLLAPSE_DURATION time. This would disable MDP/DSI core clocks
 *      and request RSC with IDLE state and change the resource state to IDLE.
 * @SDE_ENC_RC_EVENT_EARLY_WAKEUP:
 *	This event is triggered from the input event thread when touch event is
 *	received from the input device. On receiving this event,
 *      - If the device is in SDE_ENC_RC_STATE_IDLE state, it turns ON the
	  clocks and enable RSC.
 *      - If the device is in SDE_ENC_RC_STATE_ON state, it resets the delayed
 *        off work since a new commit is imminent.
 */
enum sde_enc_rc_events {
	SDE_ENC_RC_EVENT_KICKOFF = 1,
@@ -128,7 +135,8 @@ enum sde_enc_rc_events {
	SDE_ENC_RC_EVENT_STOP,
	SDE_ENC_RC_EVENT_PRE_MODESET,
	SDE_ENC_RC_EVENT_POST_MODESET,
	SDE_ENC_RC_EVENT_ENTER_IDLE
	SDE_ENC_RC_EVENT_ENTER_IDLE,
	SDE_ENC_RC_EVENT_EARLY_WAKEUP,
};

/*
@@ -193,6 +201,8 @@ enum sde_enc_rc_states {
 * @delayed_off_work:		delayed worker to schedule disabling of
 *				clks and resources after IDLE_TIMEOUT time.
 * @vsync_event_work:		worker to handle vsync event for autorefresh
 * @input_event_work:		worker to handle input device touch events
 * @input_handler:			handler for input device events
 * @topology:                   topology of the display
 * @vblank_enabled:		boolean to track userspace vblank vote
 * @rsc_config:			rsc configuration for display vtotal, fps, etc.
@@ -237,6 +247,8 @@ struct sde_encoder_virt {
	enum sde_enc_rc_states rc_state;
	struct kthread_delayed_work delayed_off_work;
	struct kthread_work vsync_event_work;
	struct kthread_work input_event_work;
	struct input_handler *input_handler;
	struct msm_display_topology topology;
	bool vblank_enabled;

@@ -708,6 +720,11 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc)
	drm_encoder_cleanup(drm_enc);
	mutex_destroy(&sde_enc->enc_lock);

	if (sde_enc->input_handler) {
		input_unregister_handler(sde_enc->input_handler);
		kfree(sde_enc->input_handler);
	}

	kfree(sde_enc);
}

@@ -1815,6 +1832,45 @@ static int _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
	return 0;
}

static void sde_encoder_input_event_handler(struct input_handle *handle,
	unsigned int type, unsigned int code, int value)
{
	struct drm_encoder *drm_enc = NULL;
	struct sde_encoder_virt *sde_enc = NULL;
	struct msm_drm_thread *disp_thread = NULL;
	struct msm_drm_private *priv = NULL;

	if (!handle || !handle->handler || !handle->handler->private) {
		SDE_ERROR("invalid encoder for the input event\n");
		return;
	}

	drm_enc = (struct drm_encoder *)handle->handler->private;
	if (!drm_enc->dev || !drm_enc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
		return;
	}

	priv = drm_enc->dev->dev_private;
	sde_enc = to_sde_encoder_virt(drm_enc);
	if (!sde_enc->crtc || (sde_enc->crtc->index
			>= ARRAY_SIZE(priv->disp_thread))) {
		SDE_DEBUG_ENC(sde_enc,
			"invalid cached CRTC: %d or crtc index: %d\n",
			sde_enc->crtc == NULL,
			sde_enc->crtc ? sde_enc->crtc->index : -EINVAL);
		return;
	}

	SDE_EVT32_VERBOSE(DRMID(drm_enc));

	disp_thread = &priv->disp_thread[sde_enc->crtc->index];

	kthread_queue_work(&disp_thread->worker,
				&sde_enc->input_event_work);
}


static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
		u32 sw_event)
{
@@ -1966,7 +2022,7 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
			idle_pc_duration = IDLE_POWERCOLLAPSE_DURATION;

		if (!autorefresh_enabled)
			kthread_queue_delayed_work(
			kthread_mod_delayed_work(
				&disp_thread->worker,
				&sde_enc->delayed_off_work,
				msecs_to_jiffies(idle_pc_duration));
@@ -2177,7 +2233,57 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,

		mutex_unlock(&sde_enc->rc_lock);
		break;
	case SDE_ENC_RC_EVENT_EARLY_WAKEUP:
		if (!sde_enc->crtc ||
			sde_enc->crtc->index >= ARRAY_SIZE(priv->disp_thread)) {
			SDE_DEBUG_ENC(sde_enc,
				"invalid crtc:%d or crtc index:%d , sw_event:%u\n",
				sde_enc->crtc == NULL,
				sde_enc->crtc ? sde_enc->crtc->index : -EINVAL,
				sw_event);
			return -EINVAL;
		}

		disp_thread = &priv->disp_thread[sde_enc->crtc->index];

		mutex_lock(&sde_enc->rc_lock);

		if (sde_enc->rc_state == SDE_ENC_RC_STATE_ON) {
			if (sde_enc->cur_master &&
				sde_enc->cur_master->ops.is_autorefresh_enabled)
				autorefresh_enabled =
				sde_enc->cur_master->ops.is_autorefresh_enabled(
							sde_enc->cur_master);
			if (autorefresh_enabled) {
				SDE_DEBUG_ENC(sde_enc,
					"not handling early wakeup since auto refresh is enabled\n");
				mutex_lock(&sde_enc->rc_lock);
				return 0;
			}

			if (!sde_crtc_frame_pending(sde_enc->crtc))
				kthread_mod_delayed_work(&disp_thread->worker,
						&sde_enc->delayed_off_work,
						msecs_to_jiffies(
						IDLE_POWERCOLLAPSE_DURATION));
		} else if (sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) {
			/* enable all the clks and resources */
			_sde_encoder_resource_control_rsc_update(drm_enc, true);
			_sde_encoder_resource_control_helper(drm_enc, true);

			kthread_mod_delayed_work(&disp_thread->worker,
						&sde_enc->delayed_off_work,
						msecs_to_jiffies(
						IDLE_POWERCOLLAPSE_DURATION));

			sde_enc->rc_state = SDE_ENC_RC_STATE_ON;
		}

		SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_ENC_RC_STATE_ON, SDE_EVTLOG_FUNC_CASE8);

		mutex_unlock(&sde_enc->rc_lock);
		break;
	default:
		SDE_EVT32(DRMID(drm_enc), sw_event, SDE_EVTLOG_ERROR);
		SDE_ERROR("unexpected sw_event: %d\n", sw_event);
@@ -3402,6 +3508,116 @@ static void sde_encoder_vsync_event_handler(unsigned long data)
				&sde_enc->vsync_event_work);
}

static void sde_encoder_input_event_work_handler(struct kthread_work *work)
{
	struct sde_encoder_virt *sde_enc = container_of(work,
				struct sde_encoder_virt, input_event_work);

	if (!sde_enc) {
		SDE_ERROR("invalid sde encoder\n");
		return;
	}

	sde_encoder_resource_control(&sde_enc->base,
			SDE_ENC_RC_EVENT_EARLY_WAKEUP);
}

static int _sde_encoder_input_connect(struct input_handler *handler,
	struct input_dev *dev, const struct input_device_id *id)
{
	struct input_handle *handle;
	int rc = 0;

	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
	if (!handle)
		return -ENOMEM;

	handle->dev = dev;
	handle->handler = handler;
	handle->name = handler->name;

	rc = input_register_handle(handle);
	if (rc) {
		pr_err("failed to register input handle\n");
		goto error;
	}

	rc = input_open_device(handle);
	if (rc) {
		pr_err("failed to open input device\n");
		goto error_unregister;
	}

	return 0;

error_unregister:
	input_unregister_handle(handle);

error:
	kfree(handle);

	return rc;
}

static void _sde_encoder_input_disconnect(struct input_handle *handle)
{
	 input_close_device(handle);
	 input_unregister_handle(handle);
	 kfree(handle);
}

/**
 * Structure for specifying event parameters on which to receive callbacks.
 * This structure will trigger a callback in case of a touch event (specified by
 * EV_ABS) where there is a change in X and Y coordinates,
 */
static const struct input_device_id sde_input_ids[] = {
	{
		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
		.evbit = { BIT_MASK(EV_ABS) },
		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
					BIT_MASK(ABS_MT_POSITION_X) |
					BIT_MASK(ABS_MT_POSITION_Y) },
	},
	{ },
};

static int _sde_encoder_input_handler(
		struct sde_encoder_virt *sde_enc)
{
	struct input_handler *input_handler = NULL;
	int rc = 0;

	if (sde_enc->input_handler) {
		SDE_ERROR_ENC(sde_enc,
				"input_handle is active. unexpected\n");
		return -EINVAL;
	}

	input_handler = kzalloc(sizeof(*sde_enc->input_handler), GFP_KERNEL);
	if (!input_handler)
		return -ENOMEM;

	input_handler->event = sde_encoder_input_event_handler;
	input_handler->connect = _sde_encoder_input_connect;
	input_handler->disconnect = _sde_encoder_input_disconnect;
	input_handler->name = "sde";
	input_handler->id_table = sde_input_ids;
	input_handler->private = sde_enc;

	rc = input_register_handler(input_handler);
	if (rc) {
		SDE_ERROR_ENC(sde_enc,
			"input_register_handler failed, rc= %d\n", rc);
		kfree(input_handler);
		return rc;
	}

	sde_enc->input_handler = input_handler;

	return rc;
}

static void sde_encoder_vsync_event_work_handler(struct kthread_work *work)
{
	struct sde_encoder_virt *sde_enc = container_of(work,
@@ -4262,6 +4478,13 @@ struct drm_encoder *sde_encoder_init(
		sde_enc->rsc_client = NULL;
	}

	if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
		ret = _sde_encoder_input_handler(sde_enc);
		if (ret)
			SDE_ERROR(
			"input handler registration failed, rc = %d\n", ret);
	}

	mutex_init(&sde_enc->rc_lock);
	kthread_init_delayed_work(&sde_enc->delayed_off_work,
			sde_encoder_off_work);
@@ -4270,6 +4493,9 @@ struct drm_encoder *sde_encoder_init(
	kthread_init_work(&sde_enc->vsync_event_work,
			sde_encoder_vsync_event_work_handler);

	kthread_init_work(&sde_enc->input_event_work,
			sde_encoder_input_event_work_handler);

	memcpy(&sde_enc->disp_info, disp_info, sizeof(*disp_info));

	SDE_DEBUG_ENC(sde_enc, "created\n");
Loading