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

Commit 258a756c authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: perform final commit on wb disable" into msm-4.9

parents 2aeb0753 9c65f7bf
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -532,6 +532,9 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
					ktime_to_ns(fevent->ts),
					atomic_read(&sde_crtc->frame_pending));
			SDE_EVT32(DRMID(crtc), fevent->event, 0);

			/* don't propagate unexpected frame done events */
			return;
		} else if (atomic_dec_return(&sde_crtc->frame_pending) == 0) {
			/* release bandwidth and other resources */
			SDE_DEBUG("crtc%d ts:%lld last pending\n",
+65 −0
Original line number Diff line number Diff line
@@ -1263,6 +1263,71 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc)
	}
}

int sde_encoder_helper_hw_release(struct sde_encoder_phys *phys_enc,
		struct drm_framebuffer *fb)
{
	struct drm_encoder *drm_enc;
	struct sde_hw_mixer_cfg mixer;
	struct sde_rm_hw_iter lm_iter;
	bool lm_valid = false;

	if (!phys_enc || !phys_enc->parent) {
		SDE_ERROR("invalid encoder\n");
		return -EINVAL;
	}

	drm_enc = phys_enc->parent;
	memset(&mixer, 0, sizeof(mixer));

	/* reset associated CTL/LMs */
	if (phys_enc->hw_ctl->ops.clear_pending_flush)
		phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
	if (phys_enc->hw_ctl->ops.clear_all_blendstages)
		phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);

	sde_rm_init_hw_iter(&lm_iter, drm_enc->base.id, SDE_HW_BLK_LM);
	while (sde_rm_get_hw(&phys_enc->sde_kms->rm, &lm_iter)) {
		struct sde_hw_mixer *hw_lm = (struct sde_hw_mixer *)lm_iter.hw;

		if (!hw_lm)
			continue;

		/* need to flush LM to remove it */
		if (phys_enc->hw_ctl->ops.get_bitmask_mixer &&
				phys_enc->hw_ctl->ops.update_pending_flush)
			phys_enc->hw_ctl->ops.update_pending_flush(
					phys_enc->hw_ctl,
					phys_enc->hw_ctl->ops.get_bitmask_mixer(
					phys_enc->hw_ctl, hw_lm->idx));

		if (fb) {
			/* assume a single LM if targeting a frame buffer */
			if (lm_valid)
				continue;

			mixer.out_height = fb->height;
			mixer.out_width = fb->width;

			if (hw_lm->ops.setup_mixer_out)
				hw_lm->ops.setup_mixer_out(hw_lm, &mixer);
		}

		lm_valid = true;

		/* only enable border color on LM */
		if (phys_enc->hw_ctl->ops.setup_blendstage)
			phys_enc->hw_ctl->ops.setup_blendstage(
					phys_enc->hw_ctl,
					hw_lm->idx, 0, 0);
	}

	if (!lm_valid) {
		SDE_DEBUG_ENC(to_sde_encoder_virt(drm_enc), "lm not found\n");
		return -EFAULT;
	}
	return 0;
}

static int _sde_encoder_status_show(struct seq_file *s, void *data)
{
	struct sde_encoder_virt *sde_enc;
+19 −0
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ enum sde_enc_split_role {

/**
 * enum sde_enc_enable_state - current enabled state of the physical encoder
 * @SDE_ENC_DISABLING:	Encoder transitioning to disable state
 *			Events bounding transition are encoder type specific
 * @SDE_ENC_DISABLED:	Encoder is disabled
 * @SDE_ENC_ENABLING:	Encoder transitioning to enabled
 *			Events bounding transition are encoder type specific
@@ -59,6 +61,7 @@ enum sde_enc_split_role {
 *				to recover from a previous error
 */
enum sde_enc_enable_state {
	SDE_ENC_DISABLING,
	SDE_ENC_DISABLED,
	SDE_ENC_ENABLING,
	SDE_ENC_ENABLED,
@@ -286,6 +289,8 @@ struct sde_encoder_phys_cmd {
 * @wb_dev:		Pointer to writeback device
 * @start_time:		Start time of writeback latest request
 * @end_time:		End time of writeback latest request
 * @bo_disable:		Buffer object(s) to use during the disabling state
 * @fb_disable:		Frame buffer to use during the disabling state
 */
struct sde_encoder_phys_wb {
	struct sde_encoder_phys base;
@@ -305,6 +310,8 @@ struct sde_encoder_phys_wb {
	struct sde_wb_device *wb_dev;
	ktime_t start_time;
	ktime_t end_time;
	struct drm_gem_object *bo_disable[SDE_MAX_PLANES];
	struct drm_framebuffer *fb_disable;
};

/**
@@ -406,6 +413,9 @@ static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode(
{
	enum sde_rm_topology_name topology;

	if (!phys_enc || phys_enc->enable_state == SDE_ENC_DISABLING)
		return BLEND_3D_NONE;

	topology = sde_connector_get_topology_name(phys_enc->connector);
	if (phys_enc->split_role == ENC_ROLE_SOLO &&
			topology == SDE_RM_TOPOLOGY_DUALPIPEMERGE &&
@@ -426,4 +436,13 @@ void sde_encoder_helper_split_config(
		struct sde_encoder_phys *phys_enc,
		enum sde_intf interface);

/**
 * sde_encoder_helper_hw_release - prepare for h/w reset during disable
 * @phys_enc: Pointer to physical encoder structure
 * @fb: Optional fb for specifying new mixer output resolution, may be NULL
 * Return: Zero on success
 */
int sde_encoder_helper_hw_release(struct sde_encoder_phys *phys_enc,
		struct drm_framebuffer *fb);

#endif /* __sde_encoder_phys_H__ */
+160 −10
Original line number Diff line number Diff line
@@ -434,10 +434,10 @@ static int sde_encoder_phys_wb_atomic_check(
}

/**
 * sde_encoder_phys_wb_flush - flush hardware update
 * _sde_encoder_phys_wb_update_flush - flush hardware update
 * @phys_enc:	Pointer to physical encoder
 */
static void sde_encoder_phys_wb_flush(struct sde_encoder_phys *phys_enc)
static void _sde_encoder_phys_wb_update_flush(struct sde_encoder_phys *phys_enc)
{
	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
	struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
@@ -461,7 +461,10 @@ static void sde_encoder_phys_wb_flush(struct sde_encoder_phys *phys_enc)
	if (hw_ctl->ops.update_pending_flush)
		hw_ctl->ops.update_pending_flush(hw_ctl, flush_mask);

	SDE_DEBUG("Flushing CTL_ID %d, flush_mask %x, WB %d\n",
	if (hw_ctl->ops.get_pending_flush)
		flush_mask = hw_ctl->ops.get_pending_flush(hw_ctl);

	SDE_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
			hw_ctl->idx - CTL_0, flush_mask, hw_wb->idx - WB_0);
}

@@ -484,7 +487,15 @@ static void sde_encoder_phys_wb_setup(

	memset(wb_roi, 0, sizeof(struct sde_rect));

	if (phys_enc->enable_state == SDE_ENC_DISABLING) {
		fb = wb_enc->fb_disable;
		wb_roi->w = 0;
		wb_roi->h = 0;
	} else {
		fb = sde_wb_get_output_fb(wb_enc->wb_dev);
		sde_wb_get_output_roi(wb_enc->wb_dev, wb_roi);
	}

	if (!fb) {
		SDE_DEBUG("no output framebuffer\n");
		return;
@@ -493,7 +504,6 @@ static void sde_encoder_phys_wb_setup(
	SDE_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
			fb->width, fb->height);

	sde_wb_get_output_roi(wb_enc->wb_dev, wb_roi);
	if (wb_roi->w == 0 || wb_roi->h == 0) {
		wb_roi->x = 0;
		wb_roi->y = 0;
@@ -564,6 +574,10 @@ static void sde_encoder_phys_wb_done_irq(void *arg, int irq_idx)
	SDE_DEBUG("[wb:%d,%u]\n", hw_wb->idx - WB_0,
			wb_enc->frame_count);

	/* don't notify upper layer for internal commit */
	if (phys_enc->enable_state == SDE_ENC_DISABLING)
		goto complete;

	if (phys_enc->parent_ops.handle_frame_done)
		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
				phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
@@ -571,6 +585,7 @@ static void sde_encoder_phys_wb_done_irq(void *arg, int irq_idx)
	phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
			phys_enc);

complete:
	complete_all(&wb_enc->wbdone_complete);
}

@@ -700,8 +715,10 @@ static int sde_encoder_phys_wb_wait_for_commit_done(
	u32 timeout = max_t(u32, wb_enc->wbdone_timeout, KICKOFF_TIMEOUT_MS);

	/* Return EWOULDBLOCK since we know the wait isn't necessary */
	if (WARN_ON(phys_enc->enable_state != SDE_ENC_ENABLED))
	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
		SDE_ERROR("encoder already disabled\n");
		return -EWOULDBLOCK;
	}

	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->frame_count);

@@ -784,7 +801,7 @@ static void sde_encoder_phys_wb_prepare_for_kickoff(
	/* set OT limit & enable traffic shaper */
	sde_encoder_phys_wb_setup(phys_enc);

	sde_encoder_phys_wb_flush(phys_enc);
	_sde_encoder_phys_wb_update_flush(phys_enc);

	/* vote for iommu/clk/bus */
	wb_enc->start_time = ktime_get();
@@ -806,6 +823,111 @@ static void sde_encoder_phys_wb_handle_post_kickoff(
	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc));
}

/**
 * _sde_encoder_phys_wb_init_internal_fb - create fb for internal commit
 * @wb_enc:		Pointer to writeback encoder
 * @pixel_format:	DRM pixel format
 * @width:		Desired fb width
 * @height:		Desired fb height
 */
static int _sde_encoder_phys_wb_init_internal_fb(
		struct sde_encoder_phys_wb *wb_enc,
		uint32_t pixel_format, uint32_t width, uint32_t height)
{
	struct drm_device *dev;
	struct drm_framebuffer *fb;
	struct drm_mode_fb_cmd2 mode_cmd;
	uint32_t size;
	int nplanes, i, ret;

	if (!wb_enc || !wb_enc->base.parent || !wb_enc->base.sde_kms) {
		SDE_ERROR("invalid params\n");
		return -EINVAL;
	}

	dev = wb_enc->base.sde_kms->dev;
	if (!dev) {
		SDE_ERROR("invalid dev\n");
		return -EINVAL;
	}

	memset(&mode_cmd, 0, sizeof(mode_cmd));
	mode_cmd.pixel_format = pixel_format;
	mode_cmd.width = width;
	mode_cmd.height = height;

	size = sde_format_get_framebuffer_size(pixel_format,
			mode_cmd.width, mode_cmd.height, 0, 0);
	if (!size) {
		SDE_DEBUG("not creating zero size buffer\n");
		return -EINVAL;
	}

	/* allocate gem tracking object */
	nplanes = drm_format_num_planes(pixel_format);
	if (nplanes > SDE_MAX_PLANES) {
		SDE_ERROR("requested format has too many planes\n");
		return -EINVAL;
	}
	mutex_lock(&dev->struct_mutex);
	wb_enc->bo_disable[0] = msm_gem_new(dev, size,
			MSM_BO_SCANOUT | MSM_BO_WC);
	mutex_unlock(&dev->struct_mutex);

	if (IS_ERR_OR_NULL(wb_enc->bo_disable[0])) {
		ret = PTR_ERR(wb_enc->bo_disable[0]);
		wb_enc->bo_disable[0] = NULL;

		SDE_ERROR("failed to create bo, %d\n", ret);
		return ret;
	}

	for (i = 0; i < nplanes; ++i) {
		wb_enc->bo_disable[i] = wb_enc->bo_disable[0];
		mode_cmd.pitches[i] = width *
			drm_format_plane_cpp(pixel_format, i);
	}

	fb = msm_framebuffer_init(dev, &mode_cmd, wb_enc->bo_disable);
	if (IS_ERR_OR_NULL(fb)) {
		ret = PTR_ERR(fb);
		drm_gem_object_unreference(wb_enc->bo_disable[0]);
		wb_enc->bo_disable[0] = NULL;

		SDE_ERROR("failed to init fb, %d\n", ret);
		return ret;
	}

	/* prepare the backing buffer now so that it's available later */
	ret = msm_framebuffer_prepare(fb,
			wb_enc->mmu_id[SDE_IOMMU_DOMAIN_UNSECURE]);
	if (!ret)
		wb_enc->fb_disable = fb;
	return ret;
}

/**
 * _sde_encoder_phys_wb_destroy_internal_fb - deconstruct internal fb
 * @wb_enc:		Pointer to writeback encoder
 */
static void _sde_encoder_phys_wb_destroy_internal_fb(
		struct sde_encoder_phys_wb *wb_enc)
{
	if (!wb_enc)
		return;

	if (wb_enc->fb_disable) {
		drm_framebuffer_unregister_private(wb_enc->fb_disable);
		drm_framebuffer_remove(wb_enc->fb_disable);
		wb_enc->fb_disable = NULL;
	}

	if (wb_enc->bo_disable[0]) {
		drm_gem_object_unreference(wb_enc->bo_disable[0]);
		wb_enc->bo_disable[0] = NULL;
	}
}

/**
 * sde_encoder_phys_wb_enable - enable writeback encoder
 * @phys_enc:	Pointer to physical encoder
@@ -865,11 +987,23 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc)
		sde_encoder_phys_wb_wait_for_commit_done(phys_enc);
	}

	if (phys_enc->hw_cdm && phys_enc->hw_cdm->ops.disable) {
		SDE_DEBUG_DRIVER("[cdm_disable]\n");
		phys_enc->hw_cdm->ops.disable(phys_enc->hw_cdm);
	if (!phys_enc->hw_ctl || !phys_enc->parent ||
			!phys_enc->sde_kms || !wb_enc->fb_disable) {
		SDE_DEBUG("invalid enc, skipping extra commit\n");
		goto exit;
	}

	/* reset h/w before final flush */
	if (sde_encoder_helper_hw_release(phys_enc, wb_enc->fb_disable))
		goto exit;

	phys_enc->enable_state = SDE_ENC_DISABLING;
	sde_encoder_phys_wb_prepare_for_kickoff(phys_enc);
	if (phys_enc->hw_ctl->ops.trigger_flush)
		phys_enc->hw_ctl->ops.trigger_flush(phys_enc->hw_ctl);
	sde_encoder_helper_trigger_start(phys_enc);
	sde_encoder_phys_wb_wait_for_commit_done(phys_enc);
exit:
	phys_enc->enable_state = SDE_ENC_DISABLED;
}

@@ -971,6 +1105,8 @@ static void sde_encoder_phys_wb_destroy(struct sde_encoder_phys *phys_enc)
	if (!phys_enc)
		return;

	_sde_encoder_phys_wb_destroy_internal_fb(wb_enc);

	kfree(wb_enc);
}

@@ -1009,8 +1145,15 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(

	SDE_DEBUG("\n");

	if (!p || !p->parent) {
		SDE_ERROR("invalid params\n");
		ret = -EINVAL;
		goto fail_alloc;
	}

	wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
	if (!wb_enc) {
		SDE_ERROR("failed to allocate wb enc\n");
		ret = -ENOMEM;
		goto fail_alloc;
	}
@@ -1078,6 +1221,13 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(
	phys_enc->enc_spinlock = p->enc_spinlock;
	INIT_LIST_HEAD(&wb_enc->irq_cb.list);

	/* create internal buffer for disable logic */
	if (_sde_encoder_phys_wb_init_internal_fb(wb_enc,
				DRM_FORMAT_RGB888, 2, 1)) {
		SDE_ERROR("failed to init internal fb\n");
		goto fail_wb_init;
	}

	SDE_DEBUG("Created sde_encoder_phys_wb for wb %d\n",
			wb_enc->hw_wb->idx - WB_0);

+20 −0
Original line number Diff line number Diff line
@@ -689,6 +689,26 @@ static int _sde_format_get_plane_sizes(
	return _sde_format_get_plane_sizes_linear(fmt, w, h, layout);
}

uint32_t sde_format_get_framebuffer_size(
		const uint32_t format,
		const uint32_t width,
		const uint32_t height,
		const uint64_t *modifiers,
		const uint32_t modifiers_len)
{
	const struct sde_format *fmt;
	struct sde_hw_fmt_layout layout;

	fmt = sde_get_sde_format_ext(format, modifiers, modifiers_len);
	if (!fmt)
		return 0;

	if (_sde_format_get_plane_sizes(fmt, width, height, &layout))
		layout.total_size = 0;

	return layout.total_size;
}

static int _sde_format_populate_addrs_ubwc(
		int mmu_id,
		struct drm_framebuffer *fb,
Loading