drm/msm/sde: perform final commit on wb disable
This patch adds one additional flush/start to the writeback's
disable callflow. This allows the hardware to flush out any
necessary configuration clean up in preparation for future
commits.
CRs-Fixed: 2019295
Change-Id: Ia4090be49dd1f7689a973dd21732b429b299f11e
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 75eddc0..a26126f 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -434,10 +434,10 @@
}
/**
- * 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 @@
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 @@
memset(wb_roi, 0, sizeof(struct sde_rect));
- fb = sde_wb_get_output_fb(wb_enc->wb_dev);
+ 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 @@
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 @@
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 @@
phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
phys_enc);
+complete:
complete_all(&wb_enc->wbdone_complete);
}
@@ -700,8 +715,10 @@
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 @@
/* 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();
@@ -807,6 +824,111 @@
}
/**
+ * _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 @@
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 @@
if (!phys_enc)
return;
+ _sde_encoder_phys_wb_destroy_internal_fb(wb_enc);
+
kfree(wb_enc);
}
@@ -1009,8 +1145,15 @@
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 @@
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);