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

Commit cd33788d 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: add cpu event timer triggered at VSYNC boundary"

parents a91b6b63 9cd866d7
Loading
Loading
Loading
Loading
+182 −0
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ enum sde_enc_rc_states {
 * @crtc_frame_event_cb_data:	callback handler private data
 * @frame_done_timeout:		frame done timeout in Hz
 * @frame_done_timer:		watchdog timer for frame done event
 * @vsync_event_timer:		vsync timer
 * @rsc_client:			rsc client pointer
 * @rsc_state_init:		boolean to indicate rsc config init
 * @disp_info:			local copy of msm_display_info struct
@@ -191,6 +192,7 @@ enum sde_enc_rc_states {
 * @rc_state:			resource controller state
 * @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
 * @topology:                   topology of the display
 * @mode_set_complete:          flag to indicate modeset completion
 * @rsc_config:			rsc configuration for display vtotal, fps, etc.
@@ -224,6 +226,7 @@ struct sde_encoder_virt {

	atomic_t frame_done_timeout;
	struct timer_list frame_done_timer;
	struct timer_list vsync_event_timer;

	struct sde_rsc_client *rsc_client;
	bool rsc_state_init;
@@ -236,6 +239,7 @@ struct sde_encoder_virt {
	struct mutex rc_lock;
	enum sde_enc_rc_states rc_state;
	struct kthread_delayed_work delayed_off_work;
	struct kthread_work vsync_event_work;
	struct msm_display_topology topology;
	bool mode_set_complete;

@@ -2844,6 +2848,165 @@ static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys)
		phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len);
}

static u32 _sde_encoder_calculate_linetime(struct sde_encoder_virt *sde_enc,
		struct drm_display_mode *mode)
{
	u64 pclk_rate;
	u32 pclk_period;
	u32 line_time;

	/*
	 * For linetime calculation, only operate on master encoder.
	 */
	if (!sde_enc->cur_master)
		return 0;

	if (!sde_enc->cur_master->ops.get_line_count) {
		SDE_ERROR("get_line_count function not defined\n");
		return 0;
	}

	pclk_rate = mode->clock; /* pixel clock in kHz */
	if (pclk_rate == 0) {
		SDE_ERROR("pclk is 0, cannot calculate line time\n");
		return 0;
	}

	pclk_period = DIV_ROUND_UP_ULL(1000000000ull, pclk_rate);
	if (pclk_period == 0) {
		SDE_ERROR("pclk period is 0\n");
		return 0;
	}

	/*
	 * Line time calculation based on Pixel clock and HTOTAL.
	 * Final unit is in ns.
	 */
	line_time = (pclk_period * mode->htotal) / 1000;
	if (line_time == 0) {
		SDE_ERROR("line time calculation is 0\n");
		return 0;
	}

	SDE_DEBUG_ENC(sde_enc,
			"clk_rate=%lldkHz, clk_period=%d, linetime=%dns\n",
			pclk_rate, pclk_period, line_time);

	return line_time;
}

static int _sde_encoder_wakeup_time(struct drm_encoder *drm_enc,
		ktime_t *wakeup_time)
{
	struct drm_display_mode *mode;
	struct sde_encoder_virt *sde_enc;
	u32 cur_line;
	u32 line_time;
	u32 vtotal, time_to_vsync;
	ktime_t cur_time;

	sde_enc = to_sde_encoder_virt(drm_enc);

	if (!drm_enc->crtc || !drm_enc->crtc->state) {
		SDE_ERROR("crtc/crtc state object is NULL\n");
		return -EINVAL;
	}
	mode = &drm_enc->crtc->state->adjusted_mode;

	line_time = _sde_encoder_calculate_linetime(sde_enc, mode);
	if (!line_time)
		return -EINVAL;

	cur_line = sde_enc->cur_master->ops.get_line_count(sde_enc->cur_master);

	vtotal = mode->vtotal;
	if (cur_line >= vtotal)
		time_to_vsync = line_time * vtotal;
	else
		time_to_vsync = line_time * (vtotal - cur_line);

	if (time_to_vsync == 0) {
		SDE_ERROR("time to vsync should not be zero, vtotal=%d\n",
				vtotal);
		return -EINVAL;
	}

	cur_time = ktime_get();
	*wakeup_time = ktime_add_ns(cur_time, time_to_vsync);

	SDE_DEBUG_ENC(sde_enc,
			"cur_line=%u vtotal=%u time_to_vsync=%u, cur_time=%lld, wakeup_time=%lld\n",
			cur_line, vtotal, time_to_vsync,
			ktime_to_ms(cur_time),
			ktime_to_ms(*wakeup_time));
	return 0;
}

static void sde_encoder_vsync_event_handler(unsigned long data)
{
	struct drm_encoder *drm_enc = (struct drm_encoder *) data;
	struct sde_encoder_virt *sde_enc;
	struct msm_drm_private *priv;
	struct msm_drm_thread *event_thread;
	bool autorefresh_enabled = false;

	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private ||
			!drm_enc->crtc) {
		SDE_ERROR("invalid parameters\n");
		return;
	}

	sde_enc = to_sde_encoder_virt(drm_enc);
	priv = drm_enc->dev->dev_private;

	if (drm_enc->crtc->index >= ARRAY_SIZE(priv->event_thread)) {
		SDE_ERROR("invalid crtc index\n");
		return;
	}
	event_thread = &priv->event_thread[drm_enc->crtc->index];
	if (!event_thread) {
		SDE_ERROR("event_thread not found for crtc:%d\n",
				drm_enc->crtc->index);
		return;
	}

	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);

	/*
	 * Queue work to update the vsync event timer
	 * if autorefresh is enabled.
	 */
	SDE_EVT32_VERBOSE(autorefresh_enabled);
	if (autorefresh_enabled)
		kthread_queue_work(&event_thread->worker,
				&sde_enc->vsync_event_work);
	else
		del_timer(&sde_enc->vsync_event_timer);
}

static void sde_encoder_vsync_event_work_handler(struct kthread_work *work)
{
	struct sde_encoder_virt *sde_enc = container_of(work,
			struct sde_encoder_virt, vsync_event_work);
	ktime_t wakeup_time;

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

	if (_sde_encoder_wakeup_time(&sde_enc->base, &wakeup_time))
		return;

	SDE_EVT32_VERBOSE(ktime_to_ms(wakeup_time));
	mod_timer(&sde_enc->vsync_event_timer,
			nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
}

void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
		struct sde_encoder_kickoff_params *params)
{
@@ -2911,6 +3074,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc)
{
	struct sde_encoder_virt *sde_enc;
	struct sde_encoder_phys *phys;
	ktime_t wakeup_time;
	unsigned int i;

	if (!drm_enc) {
@@ -2937,6 +3101,14 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc)
		if (phys && phys->ops.handle_post_kickoff)
			phys->ops.handle_post_kickoff(phys);
	}

	if (sde_enc->disp_info.intf_type == DRM_MODE_CONNECTOR_DSI &&
			!_sde_encoder_wakeup_time(drm_enc, &wakeup_time)) {
		SDE_EVT32_VERBOSE(ktime_to_ms(wakeup_time));
		mod_timer(&sde_enc->vsync_event_timer,
				nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
	}

	SDE_ATRACE_END("encoder_kickoff");
}

@@ -3555,6 +3727,12 @@ struct drm_encoder *sde_encoder_init(
	setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
			(unsigned long) sde_enc);

	if ((disp_info->intf_type == DRM_MODE_CONNECTOR_DSI) &&
			disp_info->is_primary)
		setup_timer(&sde_enc->vsync_event_timer,
				sde_encoder_vsync_event_handler,
				(unsigned long)sde_enc);

	snprintf(name, SDE_NAME_SIZE, "rsc_enc%u", drm_enc->base.id);
	sde_enc->rsc_client = sde_rsc_client_create(SDE_RSC_INDEX, name,
					disp_info->is_primary);
@@ -3568,6 +3746,10 @@ struct drm_encoder *sde_encoder_init(
	kthread_init_delayed_work(&sde_enc->delayed_off_work,
			sde_encoder_off_work);
	sde_enc->idle_timeout = IDLE_TIMEOUT;

	kthread_init_work(&sde_enc->vsync_event_work,
			sde_encoder_vsync_event_work_handler);

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

	SDE_DEBUG_ENC(sde_enc, "created\n");
+2 −0
Original line number Diff line number Diff line
@@ -130,6 +130,7 @@ struct sde_encoder_virt_ops {
 * @restore:			Restore all the encoder configs.
 * @is_autorefresh_enabled:	provides the autorefresh current
 *                              enable/disable state.
 * @get_line_count:		Obtain current vertical line count
 */

struct sde_encoder_phys_ops {
@@ -172,6 +173,7 @@ struct sde_encoder_phys_ops {
	void (*prepare_idle_pc)(struct sde_encoder_phys *phys_enc);
	void (*restore)(struct sde_encoder_phys *phys);
	bool (*is_autorefresh_enabled)(struct sde_encoder_phys *phys);
	int (*get_line_count)(struct sde_encoder_phys *phys);
};

/**
+19 −0
Original line number Diff line number Diff line
@@ -900,6 +900,24 @@ static void sde_encoder_phys_cmd_prepare_idle_pc(
	_sde_encoder_phys_cmd_connect_te(phys_enc, false);
}

static int sde_encoder_phys_cmd_get_line_count(
		struct sde_encoder_phys *phys_enc)
{
	struct sde_hw_pingpong *hw_pp;

	if (!phys_enc || !phys_enc->hw_pp)
		return -EINVAL;

	if (!sde_encoder_phys_cmd_is_master(phys_enc))
		return -EINVAL;

	hw_pp = phys_enc->hw_pp;
	if (!hw_pp->ops.get_line_count)
		return -EINVAL;

	return hw_pp->ops.get_line_count(hw_pp);
}

static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc)
{
	struct sde_encoder_phys_cmd *cmd_enc =
@@ -1246,6 +1264,7 @@ static void sde_encoder_phys_cmd_init_ops(
	ops->is_autorefresh_enabled =
			sde_encoder_phys_cmd_is_autorefresh_enabled;
	ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff;
	ops->get_line_count = sde_encoder_phys_cmd_get_line_count;
}

struct sde_encoder_phys *sde_encoder_phys_cmd_init(
+19 −0
Original line number Diff line number Diff line
@@ -905,6 +905,24 @@ static u32 sde_encoder_phys_vid_collect_misr(struct sde_encoder_phys *phys_enc)
		vid_enc->hw_intf->ops.collect_misr(vid_enc->hw_intf) : 0;
}

static int sde_encoder_phys_vid_get_line_count(
		struct sde_encoder_phys *phys_enc)
{
	struct sde_encoder_phys_vid *vid_enc;

	if (!phys_enc)
		return -EINVAL;

	if (!sde_encoder_phys_vid_is_master(phys_enc))
		return -EINVAL;

	vid_enc = to_sde_encoder_phys_vid(phys_enc);
	if (!vid_enc->hw_intf || !vid_enc->hw_intf->ops.get_line_count)
		return -EINVAL;

	return vid_enc->hw_intf->ops.get_line_count(vid_enc->hw_intf);
}

static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
{
	ops->is_master = sde_encoder_phys_vid_is_master;
@@ -925,6 +943,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
	ops->setup_misr = sde_encoder_phys_vid_setup_misr;
	ops->collect_misr = sde_encoder_phys_vid_collect_misr;
	ops->hw_reset = sde_encoder_helper_hw_reset;
	ops->get_line_count = sde_encoder_phys_vid_get_line_count;
}

struct sde_encoder_phys *sde_encoder_phys_vid_init(
+13 −0
Original line number Diff line number Diff line
@@ -289,6 +289,18 @@ static u32 sde_hw_intf_collect_misr(struct sde_hw_intf *intf)
	return SDE_REG_READ(c, INTF_MISR_SIGNATURE);
}

static u32 sde_hw_intf_get_line_count(struct sde_hw_intf *intf)
{
	struct sde_hw_blk_reg_map *c;

	if (!intf)
		return 0;

	c = &intf->hw;

	return SDE_REG_READ(c, INTF_LINE_COUNT);
}

static void _setup_intf_ops(struct sde_hw_intf_ops *ops,
		unsigned long cap)
{
@@ -298,6 +310,7 @@ static void _setup_intf_ops(struct sde_hw_intf_ops *ops,
	ops->enable_timing = sde_hw_intf_enable_timing_engine;
	ops->setup_misr = sde_hw_intf_setup_misr;
	ops->collect_misr = sde_hw_intf_collect_misr;
	ops->get_line_count = sde_hw_intf_get_line_count;
	if (cap & BIT(SDE_INTF_ROT_START))
		ops->setup_rot_start = sde_hw_intf_setup_rot_start;
}
Loading