From patchwork Fri Oct 3 16:54:56 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 484 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1759510567; bh=5roTVs/P2ovZBBj4fKg7Mp+936Ym/henE08PPDg2e5g=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=sbxhCejpkrhjj/Y1O4Z9u1doQzGOXZ+tM9m7BtN4Tqo80KTt01UyidxENaNyb/b6t u6H7bEzaWoZB7AsVTycX78dBUUZistNbbn0+eojH87FQoZICGlNauefA+wGl2Cw63B +uYbu2fibA6du9avnHaWS0DhgKROm7o3AFkHog1xayKl2+FcJDAT1CKQdTVOL+tpP/ PplRCZVGcGmV37wQav9VAiUZ9Vkn9S0kMq03D0xSHNHjUhc5Yn+B6qcTtAMn+/xToS iwat4WgUA69x2E2hJbCittmqoVOy4f2g3EaIFTVfB3z25OqlBsVIl6C29DwNb6nPGo HFGYGLztCcGXA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D4EDB67ECA for ; Fri, 3 Oct 2025 10:56:07 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id vX_l2UVQJzRP for ; Fri, 3 Oct 2025 10:56:07 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1759510565; bh=5roTVs/P2ovZBBj4fKg7Mp+936Ym/henE08PPDg2e5g=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=SgOjqyKnRSKsLhOZHfXJSWwFS2mtBRkPorAUH1sQ7DAPAwv+UzbnQNkpI14Wt9jo3 LwYxk159SaURLJ4iridTxa6vERtHWk4ltFT7AaNUxMyzU+0TsbJk9W8mm2n+45C2M+ uwZniNlo8vazwyu/sazlovCDpYcOQGTBF+x6Zquo9bvkfvYamoETRddQGFf7pxBYUK yE+frAxD6YNah0v/tCxylgw580BB8HY3eeT6XT3hCS4gn8QTX8qFcDyzZ6LdR4cThS TJcu1YMi7xIqffxtN20Plaw9WGnQZUvCZO7A7DKAlB7LeCz/zvnaq6y31I53zDsDkc 2PBZECSiSXw0g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D31F567E85 for ; Fri, 3 Oct 2025 10:56:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1759510562; bh=fMx1IspmnxXzntZH/8lrHzQJDdryZ0ciXkOROJ21xfk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I9fH4uLkXx2P5QrphBodwknCtGeVbZD5YBChAFQzrdFjoG5Cuz8B08fpRHrxIZtkG YAZuYoHD8rGmXk5c+SBLzJJhorIshn31aGNA7Qmn3aCYSCNR3vV1a1VVrO+WxNEMxW iQSSDMZu4MY9F87aOS67SdDc/z3EabP8HSNhKUlP5VPYmGYRi4S5CtswUuXt0FoYe+ HnOE2uIh5N7kqfuMlUbi38UWBsoiSNGi/c/9LGlk1rIuaW0+zkXluqf8UMPxx5WSPS eIpLO57vosthKtNI9yybEJ6F/hMvAhAOJwvmVYjDhl4eyzzgObna4ANkZ7GLylyrwR +valcKdsuBs+A== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C4F8167E7C; Fri, 3 Oct 2025 10:56:02 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id Qs5S0k8Gm_MG; Fri, 3 Oct 2025 10:56:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1759510561; bh=G+gd8+Qcxv8tqHmGdXfR5ZO14FxCIK+B3aldvFTOrIM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LTBVGKOhHyk3LHFoj+82uKTi5WdQmh9CdjSlK213dg5RgwzSYPeCl96u+HgXsMHKu bMVEDZaVlVAXToUeW8y8FPiY2qTC293UiHsJbYNYYWinO/9cvGOvlTytRIJ/6yychi U6/vpuDObWlw+hu5UpyuYnfZ5juzF7EIHDjyRcS9cGsML1tkvV/5VW/Cwn35Y/aSOi 5r8Cz5VMU3so7USdWvomUXjshtpp7UD+90YMp5dkgcfcTuhGxcPeGeNqVxc0Rz812n GiWLyFgW+oxHtqOZx9bX6aSCDyLCHpPB9JOaWkksF5ifev2W1Bbtz1V+yiNx3lUKdk u1p/GcHMuSctw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id AAAE967F30; Fri, 3 Oct 2025 10:56:00 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Oct 2025 10:54:56 -0600 Message-ID: <20251003165525.440173-4-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251003165525.440173-1-sjg@u-boot.org> References: <20251003165525.440173-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: FC6IWB6A7RN7CG7UXREQNMYU7XWW6N5B X-Message-ID-Hash: FC6IWB6A7RN7CG7UXREQNMYU7XWW6N5B X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 03/22] sandbox: Add --video_frames option to capture test frames List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add a new --video_frames option to sandbox which accepts a directory path. When set, every video test assertion writes a BMP file (frame0.bmp, frame1.bmp, etc.) to the specified directory, allowing visual inspection of the framebuffer at each test step. A new video_write_bmp() function writes 16/32bpp framebuffer contents as Windows BMP files. On startup, any existing frame*.bmp files in the directory are removed to ensure a clean state. Usage example: ./u-boot --video_frames /tmp/frames -c "ut dm video_text" Co-developed-by: Claude Signed-off-by: Simon Glass --- arch/sandbox/cpu/start.c | 22 ++++++++ arch/sandbox/include/asm/state.h | 2 + doc/arch/sandbox/sandbox.rst | 3 ++ test/dm/video.c | 86 ++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 559c11a2dbb..30d4f83b6ee 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -375,6 +375,15 @@ static int sandbox_cmdline_cb_video_test(struct sandbox_state *state, SANDBOX_CMDLINE_OPT_SHORT(video_test, 'V', 1, "Enable video test mode (ms delay between asserts)"); +static int sandbox_cmdline_cb_video_frames(struct sandbox_state *state, + const char *arg) +{ + state->video_frames_dir = arg; + + return 0; +} +SANDBOX_CMDLINE_OPT(video_frames, 1, "Directory to write video frames"); + static const char *term_args[STATE_TERM_COUNT] = { "raw-with-sigs", "raw", @@ -655,6 +664,19 @@ int sandbox_init(int argc, char *argv[], struct global_data *data) if (os_parse_args(state, argc, argv)) return 1; + /* Remove old frame*.bmp files if video_frames_dir is set */ + if (state->video_frames_dir) { + char pattern[256]; + int i; + + for (i = 0; i < 1000; i++) { + snprintf(pattern, sizeof(pattern), "%s/frame%d.bmp", + state->video_frames_dir, i); + if (os_unlink(pattern)) + break; + } + } + /* Detect if serial console is connected to a terminal */ state->serial_is_tty = os_isatty(1) && state->term_raw != STATE_TERM_COOKED; diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index fc3c39cde27..6a89f0ca5ef 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -178,6 +178,8 @@ struct sandbox_state { bool pager_bypass; /* Enable pager-bypass mode */ bool no_term_present; /* Assume no terminal present */ int video_test; /* ms to wait before next assert */ + const char *video_frames_dir; /* Directory to write video frames */ + int video_frame_count; /* Number of frames written */ /* Pointer to information for each SPI bus/cs */ struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS] diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index bfc0ee70834..fc2b7c482f4 100644 --- a/doc/arch/sandbox/sandbox.rst +++ b/doc/arch/sandbox/sandbox.rst @@ -252,6 +252,9 @@ available options. Some of these are described below: -v, --verbose Show console output from tests. Normally this is suppressed. +--video_frames + Write video frames to the specified directory for debugging purposes. + -V, --video_test Enable video test mode with a delay (in milliseconds) between assertions. This allows time to examine the display during testing. diff --git a/test/dm/video.c b/test/dm/video.c index f8c126dba5c..56e63a6f5cf 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -4,6 +4,7 @@ * Written by Simon Glass */ +#include #include #include #include @@ -47,6 +48,79 @@ static int dm_test_video_base(struct unit_test_state *uts) } DM_TEST(dm_test_video_base, UTF_SCAN_PDATA | UTF_SCAN_FDT); +/** + * video_write_bmp() - Write framebuffer to BMP file + * + * This writes the current framebuffer contents to a BMP file on the host + * filesystem. Useful for debugging video tests. + * + * @uts: Test state + * @dev: Video device + * @fname: Filename to write to + * Return: 0 if OK, -ve on error + */ +static int video_write_bmp(struct unit_test_state *uts, struct udevice *dev, + const char *fname) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + struct bmp_image *bmp; + u32 width = priv->xsize; + u32 height = priv->ysize; + u32 row_bytes, bmp_size, bpp, bytes_per_pixel; + void *bmp_data; + int ret, y; + + /* Support 16bpp and 32bpp */ + switch (priv->bpix) { + case VIDEO_BPP16: + bpp = 16; + bytes_per_pixel = 2; + break; + case VIDEO_BPP32: + bpp = 32; + bytes_per_pixel = 4; + break; + default: + return -ENOSYS; + } + + /* BMP rows are padded to 4-byte boundary */ + row_bytes = ALIGN(width * bytes_per_pixel, BMP_DATA_ALIGN); + bmp_size = sizeof(struct bmp_header) + row_bytes * height; + + bmp = malloc(bmp_size); + if (!bmp) + return -ENOMEM; + + memset(bmp, 0, bmp_size); + + /* Fill in BMP header */ + bmp->header.signature[0] = 'B'; + bmp->header.signature[1] = 'M'; + bmp->header.file_size = cpu_to_le32(bmp_size); + bmp->header.data_offset = cpu_to_le32(sizeof(struct bmp_header)); + bmp->header.size = cpu_to_le32(40); + bmp->header.width = cpu_to_le32(width); + bmp->header.height = cpu_to_le32(height); + bmp->header.planes = cpu_to_le16(1); + bmp->header.bit_count = cpu_to_le16(bpp); + bmp->header.compression = cpu_to_le32(BMP_BI_RGB); + + /* Copy framebuffer data (BMP is bottom-up) */ + bmp_data = (void *)bmp + sizeof(struct bmp_header); + for (y = 0; y < height; y++) { + void *src = priv->fb + (height - 1 - y) * priv->line_length; + void *dst = bmp_data + y * row_bytes; + + memcpy(dst, src, width * bytes_per_pixel); + } + + ret = os_write_file(fname, bmp, bmp_size); + free(bmp); + + return ret; +} + int video_compress_fb(struct unit_test_state *uts, struct udevice *dev, bool use_copy) { @@ -71,6 +145,18 @@ int video_compress_fb(struct unit_test_state *uts, struct udevice *dev, if (ret) return ret; + /* Write frame to file if --video-frames option is set */ + if (state->video_frames_dir) { + char filename[256]; + + snprintf(filename, sizeof(filename), "%s/frame%d.bmp", + state->video_frames_dir, state->video_frame_count++); + ret = video_write_bmp(uts, dev, filename); + if (ret) + printf("Failed to write frame to %s: %d\n", filename, + ret); + } + /* provide a useful delay if -V flag is used or LOG_DEBUG is set */ if (state->video_test) mdelay(state->video_test);