From: Yashas D <y-d@ti.com>
To: <u-kumar1@ti.com>, <devarsht@ti.com>, <s-jain1@ti.com>,
<d-mittal@ti.com>, <b-padhi@ti.com>, <jyri.sarha@iki.fi>,
<tomi.valkeinen@ideasonboard.com>
Cc: <maarten.lankhorst@linux.intel.com>, <mripard@kernel.org>,
<tzimmermann@suse.de>, <airlied@gmail.com>, <simona@ffwll.ch>,
<dri-devel@lists.freedesktop.org>, <linux-kernel@vger.kernel.org>
Subject: [PATCH v2] drm/tidss: Fix synclost issue while disabling layer due to erratum i2097
Date: Wed, 1 Jul 2026 12:03:37 +0530 [thread overview]
Message-ID: <20260701063337.2165966-1-y-d@ti.com> (raw)
A gap beyond a frame duration for disabling the layer in overlay manager
and go bit setting on the corresponding video port may lead to SYNC_LOST.
Fix by moving the layer to the non-visible area and deferring the ENABLE=0
write, immediately before the GO bit, guaranteeing both occur within the
same frame window.
Link: https://www.ti.com/lit/pdf/sprz530 # i2097
Signed-off-by: Yashas D <y-d@ti.com>
---
changelog v1->v2:
- Flushed the pending layers in dispc_vp_disable to prevent hanging layers in hardware
when dispc_vp_go is not called in CRTC teardown and modeset paths.
- Removed the AM62L switch case as its the same as the default case.
Link to v1: https://lore.kernel.org/all/20260630103802.1853564-1-y-d@ti.com/
drivers/gpu/drm/tidss/tidss_dispc.c | 118 +++++++++++++++++++++++++++-
drivers/gpu/drm/tidss/tidss_dispc.h | 1 +
2 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index 58d5eb033bdb..02a3cf50e0ce 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -38,6 +38,8 @@
#include "tidss_dispc_regs.h"
#include "tidss_scale_coefs.h"
+#define OVR_LAYER_MAX_POS(mask) FIELD_MAX(mask)
+
static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
[DSS_REVISION_OFF] = 0x00,
[DSS_SYSCONFIG_OFF] = 0x04,
@@ -467,6 +469,8 @@ struct dispc_device {
struct clk *fclk;
+ unsigned long fclk_rate;
+
bool is_enabled;
struct dss_vp_data vp_data[TIDSS_MAX_PORTS];
@@ -477,6 +481,9 @@ struct dispc_device {
u32 memory_bandwidth_limit;
struct dispc_errata errata;
+
+ /* WA for erratum i2097: OVR Layer Disable May Cause Sync Lost. */
+ u32 pending_disable_layers[TIDSS_MAX_PORTS];
};
static void dispc_write(struct dispc_device *dispc, u16 reg, u32 val)
@@ -1134,6 +1141,12 @@ void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport,
bool align, onoff, rf, ieo, ipc, ihs, ivs;
u32 hsw, hfp, hbp, vsw, vfp, vbp;
+ /*
+ * WA for erratum i2097: clear any stale layer disable tracking
+ * state left over from the previous VP enable/disable cycle.
+ */
+ dispc->pending_disable_layers[hw_videoport] = 0;
+
fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format,
tstate->bus_flags);
@@ -1210,6 +1223,23 @@ void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport)
void dispc_vp_disable(struct dispc_device *dispc, u32 hw_videoport)
{
+ u32 layer;
+
+ /*
+ * WA for erratum i2097: flush any pending layer disables directly.
+ * dispc_vp_go() is not called in the CRTC teardown and modeset paths
+ * so pending_disable_layers would otherwise never be consumed, leaving
+ * layers enabled in hardware. Sync lost does not matter here since the
+ * VP is being disabled.
+ */
+ for (layer = 0; layer < dispc->feat->num_vids; layer++) {
+ if (dispc->pending_disable_layers[hw_videoport] & BIT(layer))
+ OVR_REG_FLD_MOD(dispc, hw_videoport,
+ DISPC_OVR_ATTRIBUTES(layer),
+ 0, DISPC_OVR_ATTRIBUTES_ENABLE_MASK);
+ }
+ dispc->pending_disable_layers[hw_videoport] = 0;
+
VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 0,
DISPC_VP_CONTROL_ENABLE_MASK);
}
@@ -1233,6 +1263,38 @@ void dispc_vp_go(struct dispc_device *dispc, u32 hw_videoport)
{
WARN_ON(VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL,
DISPC_VP_CONTROL_GOBIT_MASK));
+
+ if (dispc->errata.i2097 &&
+ dispc->pending_disable_layers[hw_videoport]) {
+ u32 layer;
+ u32 delay_ns;
+
+ /* WA for erratum i2097: set GO bit #1 to latch position
+ * changes into the DSS pipeline, wait 10 DSS functional clock
+ * cycles, then write ENABLE=0.
+ */
+ VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1,
+ DISPC_VP_CONTROL_GOBIT_MASK);
+
+ if (dispc->fclk_rate)
+ delay_ns = DIV_ROUND_UP_ULL((u64)10 * NSEC_PER_SEC,
+ dispc->fclk_rate);
+ else
+ delay_ns = 500;
+
+ ndelay(delay_ns);
+
+ for (layer = 0; layer < dispc->feat->num_vids; layer++) {
+ if (dispc->pending_disable_layers[hw_videoport] &
+ BIT(layer))
+ OVR_REG_FLD_MOD(dispc, hw_videoport,
+ DISPC_OVR_ATTRIBUTES(layer),
+ 0,
+ DISPC_OVR_ATTRIBUTES_ENABLE_MASK);
+ }
+ dispc->pending_disable_layers[hw_videoport] = 0;
+ }
+
VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1,
DISPC_VP_CONTROL_GOBIT_MASK);
}
@@ -1503,6 +1565,53 @@ void dispc_ovr_enable_layer(struct dispc_device *dispc,
if (dispc->feat->subrev == DISPC_K2G)
return;
+ if (dispc->errata.i2097 && !enable) {
+ /*
+ * WA for erratum i2097:
+ *
+ * Do not write ENABLE=0 directly. Instead move the layer to
+ * the non-visible area so it contributes no pixels.
+ *
+ * Position register layout differs per SoC:
+ * J721E : DISPC_OVR_ATTRIBUTES2, X[13:0], Y[29:16] (14-bit)
+ * Others: DISPC_OVR_ATTRIBUTES, X[17:6], Y[30:19] (12-bit)
+ */
+ switch (dispc->feat->subrev) {
+ case DISPC_J721E:
+ OVR_REG_FLD_MOD(dispc, hw_videoport,
+ DISPC_OVR_ATTRIBUTES2(layer),
+ OVR_LAYER_MAX_POS(DISPC_OVR_ATTRIBUTES2_POSX_MASK),
+ DISPC_OVR_ATTRIBUTES2_POSX_MASK);
+ OVR_REG_FLD_MOD(dispc, hw_videoport,
+ DISPC_OVR_ATTRIBUTES2(layer),
+ OVR_LAYER_MAX_POS(DISPC_OVR_ATTRIBUTES2_POSY_MASK),
+ DISPC_OVR_ATTRIBUTES2_POSY_MASK);
+ break;
+ default:
+ OVR_REG_FLD_MOD(dispc, hw_videoport,
+ DISPC_OVR_ATTRIBUTES(layer),
+ OVR_LAYER_MAX_POS(DISPC_OVR_ATTRIBUTES_POSX_MASK),
+ DISPC_OVR_ATTRIBUTES_POSX_MASK);
+ OVR_REG_FLD_MOD(dispc, hw_videoport,
+ DISPC_OVR_ATTRIBUTES(layer),
+ OVR_LAYER_MAX_POS(DISPC_OVR_ATTRIBUTES_POSY_MASK),
+ DISPC_OVR_ATTRIBUTES_POSY_MASK);
+ break;
+ }
+
+ dispc->pending_disable_layers[hw_videoport] |= BIT(layer);
+ return;
+ }
+
+ if (dispc->errata.i2097 && enable) {
+ /*
+ * Layer being re-enabled: cancel any pending disable so
+ * dispc_vp_go() does not write ENABLE=0 after we have
+ * just written ENABLE=1 here.
+ */
+ dispc->pending_disable_layers[hw_videoport] &= ~BIT(layer);
+ }
+
OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer),
!!enable, DISPC_OVR_ATTRIBUTES_ENABLE_MASK);
}
@@ -2871,6 +2980,12 @@ static void dispc_init_errata(struct dispc_device *dispc)
dispc->errata.i2000 = true;
dev_info(dispc->dev, "WA for erratum i2000: YUV formats disabled\n");
}
+
+ if (dispc->feat->subrev != DISPC_K2G) {
+ dispc->errata.i2097 = true;
+ dev_info(dispc->dev,
+ "WA for erratum i2097: OVR layer disable uses non-visible area\n");
+ }
}
/*
@@ -3059,7 +3174,8 @@ int dispc_init(struct tidss_device *tidss)
__func__, PTR_ERR(dispc->fclk));
return PTR_ERR(dispc->fclk);
}
- dev_dbg(dev, "DSS fclk %lu Hz\n", clk_get_rate(dispc->fclk));
+ dispc->fclk_rate = clk_get_rate(dispc->fclk);
+ dev_dbg(dev, "DSS fclk %lu Hz\n", dispc->fclk_rate);
of_property_read_u32(dispc->dev->of_node, "max-memory-bandwidth",
&dispc->memory_bandwidth_limit);
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
index 739d211d0018..e2ce9fdf1279 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -57,6 +57,7 @@ struct dispc_vid_info {
struct dispc_errata {
bool i2000; /* DSS Does Not Support YUV Pixel Data Formats */
+ bool i2097; /* OVR Layer Disable May Cause Sync Lost */
};
enum dispc_vp_bus_type {
--
2.34.1
next reply other threads:[~2026-07-01 6:35 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-01 6:33 Yashas D [this message]
2026-07-01 6:51 ` [PATCH v2] drm/tidss: Fix synclost issue while disabling layer due to erratum i2097 sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260701063337.2165966-1-y-d@ti.com \
--to=y-d@ti.com \
--cc=airlied@gmail.com \
--cc=b-padhi@ti.com \
--cc=d-mittal@ti.com \
--cc=devarsht@ti.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=jyri.sarha@iki.fi \
--cc=linux-kernel@vger.kernel.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=s-jain1@ti.com \
--cc=simona@ffwll.ch \
--cc=tomi.valkeinen@ideasonboard.com \
--cc=tzimmermann@suse.de \
--cc=u-kumar1@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.