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

Commit 10117bb2 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: clean up idle pc notification implementation"

parents 472a4e75 e17e0ee2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -147,7 +147,7 @@ enum msm_mdp_crtc_property {
	CRTC_PROP_ROT_CLK,
	CRTC_PROP_ROI_V1,
	CRTC_PROP_SECURITY_LEVEL,
	CRTC_PROP_IDLE_TIME,
	CRTC_PROP_IDLE_TIMEOUT,

	/* total # of properties */
	CRTC_PROP_COUNT
+79 −74
Original line number Diff line number Diff line
@@ -67,13 +67,9 @@ static int sde_crtc_power_interrupt_handler(struct drm_crtc *crtc_drm,
static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm,
	bool en, struct sde_irq_callback *idle_irq);

static int sde_crtc_pm_event_handler(struct drm_crtc *crtc_drm,
	bool en, struct sde_irq_callback *noirq);

static struct sde_crtc_custom_events custom_events[] = {
	{DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt},
	{DRM_EVENT_CRTC_POWER, sde_crtc_power_interrupt_handler},
	{DRM_EVENT_SDE_POWER, sde_crtc_pm_event_handler},
	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler}
};

@@ -2026,6 +2022,47 @@ static void _sde_crtc_retire_event(struct drm_crtc *crtc, ktime_t ts)
	SDE_ATRACE_END("signal_retire_fence");
}

/* _sde_crtc_idle_notify - signal idle timeout to client */
static void _sde_crtc_idle_notify(struct sde_crtc *sde_crtc)
{
	struct drm_crtc *crtc;
	struct drm_event event;
	int ret = 0;

	if (!sde_crtc) {
		SDE_ERROR("invalid sde crtc\n");
		return;
	}

	crtc = &sde_crtc->base;
	event.type = DRM_EVENT_IDLE_NOTIFY;
	event.length = sizeof(u32);
	msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
								(u8 *)&ret);

	SDE_DEBUG("crtc:%d idle timeout notified\n", crtc->base.id);
}

/*
 * sde_crtc_handle_event - crtc frame event handle.
 * This API must manage only non-IRQ context events.
 */
static bool _sde_crtc_handle_event(struct sde_crtc *sde_crtc, u32 event)
{
	bool event_processed = false;

	/**
	 * idle events are originated from commit thread and can be processed
	 * in same context
	 */
	if (event & SDE_ENCODER_FRAME_EVENT_IDLE) {
		_sde_crtc_idle_notify(sde_crtc);
		event_processed = true;
	}

	return event_processed;
}

static void sde_crtc_frame_event_work(struct kthread_work *work)
{
	struct msm_drm_private *priv;
@@ -2119,6 +2156,15 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
	SDE_ATRACE_END("crtc_frame_event");
}

/*
 * sde_crtc_frame_event_cb - crtc frame event callback API. CRTC module
 * registers this API to encoder for all frame event callbacks like
 * release_fence, retire_fence, frame_error, frame_done, idle_timeout,
 * etc. Encoder may call different events from different context - IRQ,
 * user thread, commit_thread, etc. Each event should be carefully
 * reviewed and should be processed in proper task context to avoid scheduling
 * delay or properly manage the irq context's bottom half processing.
 */
static void sde_crtc_frame_event_cb(void *data, u32 event)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
@@ -2127,6 +2173,7 @@ static void sde_crtc_frame_event_cb(void *data, u32 event)
	struct sde_crtc_frame_event *fevent;
	unsigned long flags;
	u32 crtc_id;
	bool event_processed = false;

	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
@@ -2139,6 +2186,11 @@ static void sde_crtc_frame_event_cb(void *data, u32 event)
	SDE_DEBUG("crtc%d\n", crtc->base.id);
	SDE_EVT32_VERBOSE(DRMID(crtc), event);

	/* try to process the event in caller context */
	event_processed = _sde_crtc_handle_event(sde_crtc, event);
	if (event_processed)
		return;

	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);
@@ -2179,6 +2231,24 @@ void sde_crtc_complete_commit(struct drm_crtc *crtc,
		sde_crtc_secure_ctrl(crtc, true);
}

/* _sde_crtc_set_idle_timeout - update idle timeout wait duration */
static void _sde_crtc_set_idle_timeout(struct drm_crtc *crtc, u64 val)
{
	struct drm_encoder *encoder;

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

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

		sde_encoder_set_idle_timeout(encoder, (u32) val);
	}
}

/**
 * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout
 * @cstate: Pointer to sde crtc state
@@ -2494,12 +2564,6 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
	if (unlikely(!sde_crtc->num_mixers))
		return;

	/* cancel the idle notify delayed work */
	if (sde_encoder_check_mode(sde_crtc->mixers[0].encoder,
					MSM_DISPLAY_CAP_VID_MODE) &&
		kthread_cancel_delayed_work_sync(&sde_crtc->idle_notify_work))
		SDE_DEBUG("idle notify work cancelled\n");

	_sde_crtc_blend_setup(crtc);

	/*
@@ -2533,7 +2597,6 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
	struct msm_drm_thread *event_thread;
	unsigned long flags;
	struct sde_crtc_state *cstate;
	int idle_time = 0;

	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
		SDE_ERROR("invalid crtc\n");
@@ -2559,7 +2622,6 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
	}

	event_thread = &priv->event_thread[crtc->index];
	idle_time = sde_crtc_get_property(cstate, CRTC_PROP_IDLE_TIME);

	if (sde_crtc->event) {
		SDE_DEBUG("already received sde_crtc->event\n");
@@ -2590,15 +2652,6 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
	/* wait for acquire fences before anything else is done */
	_sde_crtc_wait_for_fences(crtc);

	/* schedule the idle notify delayed work */
	if (idle_time && sde_encoder_check_mode(sde_crtc->mixers[0].encoder,
						MSM_DISPLAY_CAP_VID_MODE)) {
		kthread_queue_delayed_work(&event_thread->worker,
					&sde_crtc->idle_notify_work,
					msecs_to_jiffies(idle_time));
		SDE_DEBUG("schedule idle notify work in %dms\n", idle_time);
	}

	if (!cstate->rsc_update) {
		drm_for_each_encoder(encoder, dev) {
			if (encoder->crtc != crtc)
@@ -2992,8 +3045,7 @@ static void sde_crtc_handle_power_event(u32 event_type, void *arg)
	struct drm_plane *plane;
	struct drm_encoder *encoder;
	struct sde_crtc_mixer *m;
	struct drm_event event;
	u32 power_on = 0, i, misr_status;
	u32 i, misr_status;

	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
@@ -3016,12 +3068,6 @@ static void sde_crtc_handle_power_event(u32 event_type, void *arg)
		}
		sde_cp_crtc_post_ipc(crtc);

		event.type = DRM_EVENT_SDE_POWER;
		event.length = sizeof(power_on);
		power_on = 1;
		msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
				(u8 *)&power_on);

		for (i = 0; i < sde_crtc->num_mixers; ++i) {
			m = &sde_crtc->mixers[i];
			if (!m->hw_lm || !m->hw_lm->ops.setup_misr ||
@@ -3054,12 +3100,6 @@ static void sde_crtc_handle_power_event(u32 event_type, void *arg)
			sde_plane_set_revalidate(plane, true);

		sde_cp_crtc_suspend(crtc);

		event.type = DRM_EVENT_SDE_POWER;
		event.length = sizeof(power_on);
		power_on = 0;
		msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
				(u8 *)&power_on);
		break;
	default:
		SDE_DEBUG("event:%d not handled\n", event_type);
@@ -3805,7 +3845,8 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
			CRTC_PROP_ROT_CLK);

	msm_property_install_range(&sde_crtc->property_info,
		"idle_time", 0x0, 0, U64_MAX, 0, CRTC_PROP_IDLE_TIME);
		"idle_timeout", IDLE_TIMEOUT, 0, U64_MAX, 0,
		CRTC_PROP_IDLE_TIMEOUT);

	msm_property_install_blob(&sde_crtc->property_info, "capabilities",
		DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
@@ -3938,6 +3979,8 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc,
				cstate->bw_control = true;
				cstate->bw_split_vote = true;
				break;
			case CRTC_PROP_IDLE_TIMEOUT:
				_sde_crtc_set_idle_timeout(crtc, val);
			default:
				/* nothing to do */
				break;
@@ -4539,31 +4582,6 @@ static int _sde_crtc_init_events(struct sde_crtc *sde_crtc)
	return rc;
}

/*
 * __sde_crtc_idle_notify_work - signal idle timeout to user space
 */
static void __sde_crtc_idle_notify_work(struct kthread_work *work)
{
	struct sde_crtc *sde_crtc = container_of(work, struct sde_crtc,
				idle_notify_work.work);
	struct drm_crtc *crtc;
	struct drm_event event;
	int ret = 0;

	if (!sde_crtc) {
		SDE_ERROR("invalid sde crtc\n");
	} else {
		crtc = &sde_crtc->base;
		event.type = DRM_EVENT_IDLE_NOTIFY;
		event.length = sizeof(u32);
		msm_mode_object_event_notify(&crtc->base, crtc->dev,
				&event, (u8 *)&ret);

		SDE_DEBUG("crtc[%d]: idle timeout notified\n", crtc->base.id);
	}
}


/* initialize crtc */
struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
{
@@ -4634,9 +4652,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
	sde_cp_crtc_init(crtc);
	sde_cp_crtc_install_properties(crtc);

	kthread_init_delayed_work(&sde_crtc->idle_notify_work,
					__sde_crtc_idle_notify_work);

	SDE_DEBUG("%s: successfully initialized crtc\n", sde_crtc->name);
	return crtc;
}
@@ -4773,16 +4788,6 @@ static int sde_crtc_power_interrupt_handler(struct drm_crtc *crtc_drm,
	return 0;
}

static int sde_crtc_pm_event_handler(struct drm_crtc *crtc, bool en,
		struct sde_irq_callback *noirq)
{
	/*
	 * IRQ object noirq is not being used here since there is
	 * no crtc irq from pm event.
	 */
	return 0;
}

static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm,
	bool en, struct sde_irq_callback *irq)
{
+0 −3
Original line number Diff line number Diff line
@@ -204,7 +204,6 @@ struct sde_crtc_event {
 * @misr_enable   : boolean entry indicates misr enable/disable status.
 * @misr_frame_count  : misr frame count provided by client
 * @misr_data     : store misr data before turning off the clocks.
 * @idle_notify_work: delayed worker to notify idle timeout to user space
 * @power_event   : registered power event handle
 * @cur_perf      : current performance committed to clock/bandwidth driver
 * @rp_lock       : serialization lock for resource pool
@@ -265,8 +264,6 @@ struct sde_crtc {
	u32 misr_frame_count;
	u32 misr_data[CRTC_DUAL_MIXERS];

	struct kthread_delayed_work idle_notify_work;

	struct sde_power_event *power_event;

	struct sde_core_perf_params cur_perf;
+68 −26
Original line number Diff line number Diff line
@@ -71,7 +71,6 @@

#define MISR_BUFF_SIZE			256

#define IDLE_TIMEOUT	(66 - 16/2)
#define IDLE_SHORT_TIMEOUT	1

/* Maximum number of VSYNC wait attempts for RSC state transition */
@@ -197,6 +196,7 @@ enum sde_enc_rc_states {
 * @rsc_config:			rsc configuration for display vtotal, fps, etc.
 * @cur_conn_roi:		current connector roi
 * @prv_conn_roi:		previous connector roi to optimize if unchanged
 * @idle_timeout:		idle timeout duration in milliseconds
 */
struct sde_encoder_virt {
	struct drm_encoder base;
@@ -242,6 +242,8 @@ struct sde_encoder_virt {
	struct sde_rsc_cmd_config rsc_config;
	struct sde_rect cur_conn_roi;
	struct sde_rect prv_conn_roi;

	u32 idle_timeout;
};

#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
@@ -261,6 +263,17 @@ bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc)
	return (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC);
}

void sde_encoder_set_idle_timeout(struct drm_encoder *drm_enc, u32 idle_timeout)
{
	struct sde_encoder_virt *sde_enc;

	if (!drm_enc)
		return;

	sde_enc = to_sde_encoder_virt(drm_enc);
	sde_enc->idle_timeout = idle_timeout;
}

bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc)
{
	enum sde_rm_topology_name topology;
@@ -1472,6 +1485,7 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
	struct msm_drm_private *priv;
	struct msm_drm_thread *disp_thread;
	int ret;
	bool is_vid_mode = false;

	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private ||
			!drm_enc->crtc) {
@@ -1480,6 +1494,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
	}
	sde_enc = to_sde_encoder_virt(drm_enc);
	priv = drm_enc->dev->dev_private;
	is_vid_mode = sde_enc->disp_info.capabilities &
						MSM_DISPLAY_CAP_VID_MODE;

	if (drm_enc->crtc->index >= ARRAY_SIZE(priv->disp_thread)) {
		SDE_ERROR("invalid crtc index\n");
@@ -1489,7 +1505,7 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,

	/*
	 * when idle_pc is not supported, process only KICKOFF, STOP and MODESET
	 * events and return early for other events (ie video mode).
	 * events and return early for other events (ie wb display).
	 */
	if (!sde_enc->idle_pc_supported &&
			(sw_event != SDE_ENC_RC_EVENT_KICKOFF &&
@@ -1518,6 +1534,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
		if (sde_enc->rc_state == SDE_ENC_RC_STATE_ON) {
			SDE_DEBUG_ENC(sde_enc, "sw_event:%d, rc in ON state\n",
					sw_event);
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_EVTLOG_FUNC_CASE1);
			mutex_unlock(&sde_enc->rc_lock);
			return 0;
		} else if (sde_enc->rc_state != SDE_ENC_RC_STATE_OFF &&
@@ -1530,9 +1548,13 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
			return -EINVAL;
		}

		if (is_vid_mode && sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) {
			_sde_encoder_irq_control(drm_enc, true);
		} else {
			/* enable all the clks and resources */
			_sde_encoder_resource_control_helper(drm_enc, true);
			_sde_encoder_resource_control_rsc_update(drm_enc, true);
		}

		SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_ENC_RC_STATE_ON, SDE_EVTLOG_FUNC_CASE1);
@@ -1562,6 +1584,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
		 */
		if (sde_crtc_frame_pending(drm_enc->crtc) > 1) {
			SDE_DEBUG_ENC(sde_enc, "skip schedule work");
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_EVTLOG_FUNC_CASE2);
			return 0;
		}

@@ -1582,7 +1606,7 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
		if (lp == SDE_MODE_DPMS_LP2)
			idle_timeout = IDLE_SHORT_TIMEOUT;
		else
			idle_timeout = IDLE_TIMEOUT;
			idle_timeout = sde_enc->idle_timeout;

		if (!autorefresh_enabled)
			kthread_queue_delayed_work(
@@ -1605,11 +1629,17 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,

		mutex_lock(&sde_enc->rc_lock);

		if (is_vid_mode &&
			  sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) {
			_sde_encoder_irq_control(drm_enc, true);
		}
		/* skip if is already OFF or IDLE, resources are off already */
		if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF ||
		else if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF ||
				sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) {
			SDE_DEBUG_ENC(sde_enc, "sw_event:%d, rc in %d state\n",
					sw_event, sde_enc->rc_state);
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_EVTLOG_FUNC_CASE3);
			mutex_unlock(&sde_enc->rc_lock);
			return 0;
		}
@@ -1636,6 +1666,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
		if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF) {
			SDE_DEBUG_ENC(sde_enc, "sw_event:%d, rc in OFF state\n",
					sw_event);
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_EVTLOG_FUNC_CASE4);
			mutex_unlock(&sde_enc->rc_lock);
			return 0;
		} else if (sde_enc->rc_state == SDE_ENC_RC_STATE_ON ||
@@ -1756,9 +1788,15 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
			return 0;
		}

		if (is_vid_mode) {
			_sde_encoder_irq_control(drm_enc, false);
		} else {
			/* disable all the clks and resources */
		_sde_encoder_resource_control_rsc_update(drm_enc, false);
			_sde_encoder_resource_control_rsc_update(drm_enc,
								false);
			_sde_encoder_resource_control_helper(drm_enc, false);
		}

		SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_ENC_RC_STATE_IDLE, SDE_EVTLOG_FUNC_CASE7);
		sde_enc->rc_state = SDE_ENC_RC_STATE_IDLE;
@@ -1777,20 +1815,6 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
	return 0;
}

static void sde_encoder_off_work(struct kthread_work *work)
{
	struct sde_encoder_virt *sde_enc = container_of(work,
			struct sde_encoder_virt, delayed_off_work.work);

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

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

static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
				      struct drm_display_mode *mode,
				      struct drm_display_mode *adj_mode)
@@ -2259,6 +2283,23 @@ static void sde_encoder_frame_done_callback(
	}
}

static void sde_encoder_off_work(struct kthread_work *work)
{
	struct sde_encoder_virt *sde_enc = container_of(work,
			struct sde_encoder_virt, delayed_off_work.work);

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

	sde_encoder_resource_control(&sde_enc->base,
						SDE_ENC_RC_EVENT_ENTER_IDLE);

	sde_encoder_frame_done_callback(&sde_enc->base, NULL,
				SDE_ENCODER_FRAME_EVENT_IDLE);
}

/**
 * _sde_encoder_trigger_flush - trigger flush for a physical encoder
 * drm_enc: Pointer to drm encoder structure
@@ -3246,7 +3287,8 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,

	SDE_DEBUG("dsi_info->num_of_h_tiles %d\n", disp_info->num_of_h_tiles);

	if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
	if ((disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) ||
	    (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE))
		sde_enc->idle_pc_supported = sde_kms->catalog->has_idle_pc;

	mutex_lock(&sde_enc->enc_lock);
@@ -3411,7 +3453,7 @@ struct drm_encoder *sde_encoder_init(
	mutex_init(&sde_enc->rc_lock);
	kthread_init_delayed_work(&sde_enc->delayed_off_work,
			sde_encoder_off_work);

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

	SDE_DEBUG_ENC(sde_enc, "created\n");
+12 −0
Original line number Diff line number Diff line
@@ -29,6 +29,9 @@
#define SDE_ENCODER_FRAME_EVENT_PANEL_DEAD		BIT(2)
#define SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE	BIT(3)
#define SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE	BIT(4)
#define SDE_ENCODER_FRAME_EVENT_IDLE			BIT(5)

#define IDLE_TIMEOUT	(66 - 16/2)

/**
 * Encoder functions and data types
@@ -205,4 +208,13 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc);
 */
void sde_encoder_prepare_commit(struct drm_encoder *drm_enc);

/**
 * sde_encoder_set_idle_timeout - set the idle timeout for video
 *                    and command mode encoders.
 * @drm_enc:    Pointer to previously created drm encoder structure
 * @idle_timeout:    idle timeout duration in milliseconds
 */
void sde_encoder_set_idle_timeout(struct drm_encoder *drm_enc,
							u32 idle_timeout);

#endif /* __SDE_ENCODER_H__ */
Loading