[Concept,17/22] video: Provide a way for expo to control video sync

Message ID 20251003165525.440173-18-sjg@u-boot.org
State New
Headers
Series video: Enhancements to support a pointer |

Commit Message

Simon Glass Oct. 3, 2025, 4:55 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

When expo is being used, it should only redraw the display when it has
made changes. Add a new 'manual sync' mode which tells the video
subsystem to ignore video syncs from other sources.

This also disables the idle feature, since it can interfere with tests.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 drivers/video/video-uclass.c | 35 ++++++++++++++++++++++++
 include/video.h              | 10 +++++++
 test/dm/video.c              | 52 ++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+)
  

Patch

diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index bf633861ef3..f8ba3abc5f4 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -65,11 +65,13 @@  struct cyclic_info;
  *	gd->video_top and works downwards, running out of space when it hits
  *	gd->video_bottom.
  * @cyc_active: true if cyclic video sync is currently registered
+ * @manual_sync: true if manual-sync mode is active (caller controls video sync)
  * @cyc: handle for cyclic-execution function, or NULL if none
  */
 struct video_uc_priv {
 	ulong video_ptr;
 	bool cyc_active;
+	bool manual_sync;
 	struct cyclic_info cyc;
 };
 
@@ -501,9 +503,14 @@  static void video_flush_copy(struct udevice *vid)
 int video_sync(struct udevice *vid, bool force)
 {
 	struct video_priv *priv = dev_get_uclass_priv(vid);
+	struct video_uc_priv *uc_priv = uclass_get_priv(vid->uclass);
 	struct video_ops *ops = video_get_ops(vid);
 	int ret;
 
+	/* Skip sync if manual-sync mode is active */
+	if (uc_priv->manual_sync)
+		return 0;
+
 	if (IS_ENABLED(CONFIG_VIDEO_COPY))
 		video_flush_copy(vid);
 
@@ -622,6 +629,20 @@  int video_default_font_height(struct udevice *dev)
 
 static void video_idle(struct cyclic_info *cyc)
 {
+	struct video_uc_priv *uc_priv;
+	struct uclass *uc;
+	int ret;
+
+	ret = uclass_get(UCLASS_VIDEO, &uc);
+	if (ret)
+		return;
+
+	uc_priv = uclass_get_priv(uc);
+
+	/* Skip sync if manual-sync mode is active */
+	if (uc_priv->manual_sync)
+		return;
+
 	if (CONFIG_IS_ENABLED(CURSOR)) {
 		struct udevice *cons;
 		struct uclass *uc;
@@ -809,6 +830,20 @@  __maybe_unused static int video_destroy(struct uclass *uc)
 	return 0;
 }
 
+void video_set_manual_sync(bool enable)
+{
+	struct video_uc_priv *uc_priv;
+	struct uclass *uc;
+	int ret;
+
+	ret = uclass_get(UCLASS_VIDEO, &uc);
+	if (ret)
+		return;
+
+	uc_priv = uclass_get_priv(uc);
+	uc_priv->manual_sync = enable;
+}
+
 UCLASS_DRIVER(video) = {
 	.id		= UCLASS_VIDEO,
 	.name		= "video",
diff --git a/include/video.h b/include/video.h
index 3ce603384dc..3f5c8cbd45a 100644
--- a/include/video.h
+++ b/include/video.h
@@ -543,4 +543,14 @@  static inline bool video_is_visible(void)
 #endif
 }
 
+/**
+ * video_set_manual_sync() - Set manual-sync mode for video subsystem
+ *
+ * When manual-sync mode is enabled, automatic video sync operations are
+ * suppressed to allow the caller to control rendering timing.
+ *
+ * @enable: true to enable manual-sync mode, false to disable
+ */
+void video_set_manual_sync(bool enable);
+
 #endif
diff --git a/test/dm/video.c b/test/dm/video.c
index 7ada4c75bf7..cee9e528689 100644
--- a/test/dm/video.c
+++ b/test/dm/video.c
@@ -1293,3 +1293,55 @@  static int dm_test_video_images(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_video_images, UTF_SCAN_PDATA | UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test manual-sync mode suppresses auto-sync */
+static int dm_test_video_manual_sync(struct unit_test_state *uts)
+{
+	struct video_priv *priv;
+	struct udevice *dev, *con;
+
+	ut_assertok(select_vidconsole(uts, "vidconsole0"));
+	ut_assertok(video_get_nologo(uts, &dev));
+	ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+	priv = dev_get_uclass_priv(dev);
+
+	/* Write some text and verify it appears in the framebuffer */
+	vidconsole_put_string(con, "Test");
+	ut_asserteq(118, video_compress_fb(uts, dev, false));
+
+	/* Sync to copy buffer before enabling manual-sync mode */
+	ut_assertok(video_sync(dev, true));
+
+	/* Enable manual-sync mode - sync should be suppressed */
+	video_set_manual_sync(true);
+
+	/* Clear and write new text - auto-sync should not happen */
+	video_clear(dev);
+	vidconsole_put_string(con, "Manual Sync");
+
+	/* should do nothing in manual-sync mode */
+	ut_assertok(video_sync(dev, false));
+
+	/* The copy framebuffer should still show old content */
+	if (IS_ENABLED(CONFIG_VIDEO_COPY)) {
+		ut_assertf(memcmp(priv->fb, priv->copy_fb, priv->fb_size),
+			   "Copy fb should not match fb in manual-sync mode");
+	}
+
+	/*
+	 * video_sync() with force=true should still do nothing, except of
+	 * course that without a copy framebuffer the string will be present on
+	 * (only) framebuffer
+	 */
+	ut_assertok(video_sync(dev, true));
+	if (IS_ENABLED(CONFIG_VIDEO_COPY)) {
+		ut_asserteq(118, video_compress_fb(uts, dev, true));
+		ut_assertf(memcmp(priv->fb, priv->copy_fb, priv->fb_size),
+			   "Copy fb should not match fb in manual-sync mode");
+	} else {
+		ut_asserteq(183, video_compress_fb(uts, dev, true));
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_video_manual_sync, UTF_SCAN_PDATA | UTF_SCAN_FDT);