Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5963d6b645 | ||
|
|
4762ee1c42 | ||
|
|
141ec258ef | ||
|
|
44e01aa9a5 | ||
|
|
ae68be7090 | ||
|
|
742694c77e | ||
|
|
c4cb9899b8 | ||
|
|
b987829750 | ||
|
|
0133a7125e | ||
|
|
279adef5aa | ||
|
|
ff9d219391 | ||
|
|
5900eb7138 | ||
|
|
dbf7444385 | ||
|
|
5f7ce069fc | ||
|
|
7106c6d3eb | ||
|
|
93a92d0a2e | ||
|
|
dd56b0bdc9 | ||
|
|
370f81d3e2 | ||
|
|
af6f2bc251 | ||
|
|
d2c58d046f | ||
|
|
d41ae1c9d6 | ||
|
|
37bfc07a14 |
@@ -6,11 +6,13 @@
|
||||
#include <errno.h>
|
||||
#include <mouse.h>
|
||||
#include <unistd.h>
|
||||
#include <video_defs.h>
|
||||
#include <stdbool.h>
|
||||
#include <sysreset.h>
|
||||
#include <linux/input.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <asm/state.h>
|
||||
#include <video_defs.h>
|
||||
|
||||
/**
|
||||
* struct buf_info - a data buffer holding audio data
|
||||
@@ -232,17 +234,46 @@ int sandbox_sdl_init_display(int width, int height, int log2_bpp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_to_texture(void *lcd_base)
|
||||
static int copy_to_texture(void *lcd_base, const struct video_bbox *damage)
|
||||
{
|
||||
char *dest;
|
||||
int pitch, x, y;
|
||||
int src_pitch;
|
||||
void *pixels;
|
||||
char *src;
|
||||
SDL_Rect rect, *rectp = NULL;
|
||||
int ret;
|
||||
int x0, y0, x1, y1;
|
||||
|
||||
/* Set up damage region if provided */
|
||||
if (damage && damage->x1 > damage->x0 && damage->y1 > damage->y0) {
|
||||
rect.x = damage->x0;
|
||||
rect.y = damage->y0;
|
||||
rect.w = damage->x1 - damage->x0;
|
||||
rect.h = damage->y1 - damage->y0;
|
||||
rectp = ▭
|
||||
x0 = damage->x0;
|
||||
y0 = damage->y0;
|
||||
x1 = damage->x1;
|
||||
y1 = damage->y1;
|
||||
} else {
|
||||
x0 = 0;
|
||||
y0 = 0;
|
||||
x1 = sdl.width;
|
||||
y1 = sdl.height;
|
||||
}
|
||||
|
||||
if (sdl.src_depth == sdl.depth) {
|
||||
SDL_UpdateTexture(sdl.texture, NULL, lcd_base, sdl.pitch);
|
||||
if (rectp) {
|
||||
/* Update only the damaged region */
|
||||
src_pitch = sdl.width * sdl.src_depth / 8;
|
||||
src = lcd_base + y0 * src_pitch +
|
||||
x0 * sdl.src_depth / 8;
|
||||
SDL_UpdateTexture(sdl.texture, rectp, src, src_pitch);
|
||||
} else {
|
||||
SDL_UpdateTexture(sdl.texture, NULL, lcd_base,
|
||||
sdl.pitch);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -255,7 +286,7 @@ static int copy_to_texture(void *lcd_base)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = SDL_LockTexture(sdl.texture, NULL, &pixels, &pitch);
|
||||
ret = SDL_LockTexture(sdl.texture, rectp, &pixels, &pitch);
|
||||
if (ret) {
|
||||
printf("SDL lock %d: %s\n", ret, SDL_GetError());
|
||||
return ret;
|
||||
@@ -263,12 +294,12 @@ static int copy_to_texture(void *lcd_base)
|
||||
|
||||
/* Copy the pixels one by one */
|
||||
src_pitch = sdl.width * sdl.src_depth / 8;
|
||||
for (y = 0; y < sdl.height; y++) {
|
||||
for (y = y0; y < y1; y++) {
|
||||
char val;
|
||||
|
||||
dest = pixels + y * pitch;
|
||||
src = lcd_base + src_pitch * y;
|
||||
for (x = 0; x < sdl.width; x++, dest += 4) {
|
||||
dest = pixels + (y - y0) * pitch;
|
||||
src = lcd_base + src_pitch * y + x0;
|
||||
for (x = x0; x < x1; x++, dest += 4) {
|
||||
val = *src++;
|
||||
dest[0] = val;
|
||||
dest[1] = val;
|
||||
@@ -281,7 +312,7 @@ static int copy_to_texture(void *lcd_base)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_sdl_sync(void *lcd_base)
|
||||
int sandbox_sdl_sync(void *lcd_base, const struct video_bbox *damage)
|
||||
{
|
||||
struct SDL_Rect rect;
|
||||
int ret;
|
||||
@@ -289,7 +320,7 @@ int sandbox_sdl_sync(void *lcd_base)
|
||||
if (!sdl.texture)
|
||||
return 0;
|
||||
SDL_RenderClear(sdl.renderer);
|
||||
ret = copy_to_texture(lcd_base);
|
||||
ret = copy_to_texture(lcd_base, damage);
|
||||
if (ret) {
|
||||
printf("copy_to_texture: %d: %s\n", ret, SDL_GetError());
|
||||
return -EIO;
|
||||
|
||||
@@ -365,6 +365,25 @@ static int sandbox_cmdline_cb_double_lcd(struct sandbox_state *state,
|
||||
SANDBOX_CMDLINE_OPT_SHORT(double_lcd, 'K', 0,
|
||||
"Double the LCD display size in each direction");
|
||||
|
||||
static int sandbox_cmdline_cb_video_test(struct sandbox_state *state,
|
||||
const char *arg)
|
||||
{
|
||||
state->video_test = simple_strtol(arg, NULL, 10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
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",
|
||||
@@ -645,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;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <video.h>
|
||||
|
||||
struct mouse_event;
|
||||
struct video_bbox;
|
||||
|
||||
#ifdef CONFIG_SANDBOX_SDL
|
||||
|
||||
@@ -42,9 +43,10 @@ int sandbox_sdl_remove_display(void);
|
||||
* user can see it.
|
||||
*
|
||||
* @lcd_base: Base of frame buffer
|
||||
* @damage: Optional damage rectangle to limit the update region (may be NULL)
|
||||
* Return: 0 if screen was updated, -ENODEV is there is no screen.
|
||||
*/
|
||||
int sandbox_sdl_sync(void *lcd_base);
|
||||
int sandbox_sdl_sync(void *lcd_base, const struct video_bbox *damage);
|
||||
|
||||
/**
|
||||
* sandbox_sdl_scan_keys() - scan for pressed keys
|
||||
@@ -106,6 +108,15 @@ int sandbox_sdl_sound_init(int rate, int channels);
|
||||
*/
|
||||
int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp);
|
||||
|
||||
/**
|
||||
* sandbox_sdl_get_mouse_event() - Read a mouse event from SDL
|
||||
*
|
||||
* If a mouse event has been recorded since the last call, this marked the event
|
||||
* as used and then returns its.
|
||||
*
|
||||
* @evt: Mouse event
|
||||
* ReturnL 0 if OK, -EAGAIN if there is no event available
|
||||
*/
|
||||
int sandbox_sdl_get_mouse_event(struct mouse_event *evt);
|
||||
|
||||
#else
|
||||
@@ -120,7 +131,8 @@ static inline int sandbox_sdl_remove_display(void)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int sandbox_sdl_sync(void *lcd_base)
|
||||
static inline int sandbox_sdl_sync(void *lcd_base,
|
||||
const struct video_bbox *damage)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -177,6 +177,9 @@ struct sandbox_state {
|
||||
bool soft_fail; /* Continue on failure */
|
||||
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]
|
||||
|
||||
@@ -103,6 +103,9 @@ available options. Some of these are described below:
|
||||
even if CONFIG_AUTOBOOT_KEYED is enabled, since it interfers with tests and
|
||||
normal usage
|
||||
|
||||
-A, --no_term_present
|
||||
Assume no terminal is present. This is used for pager testing.
|
||||
|
||||
-b. boot
|
||||
The distro boot feature doesn't run by default on sandbox, since it normally
|
||||
not vert useful. For the distro_bootcmds to succeed, quite a bit of setup is
|
||||
@@ -162,7 +165,10 @@ available options. Some of these are described below:
|
||||
writes an elf file containing the extracted portion, then execs it. This
|
||||
argument provides the filename, so it can be removed before U-Boot exits.
|
||||
|
||||
-k, --double_lcd
|
||||
-k, --select_unittests <arg>
|
||||
Select specific unit tests to run. This is only used with SPL.
|
||||
|
||||
-K, --double_lcd
|
||||
Doubles the size of the emulated LCD, so that it appears bigger. This can be
|
||||
useful on large or high-resolution displays.
|
||||
|
||||
@@ -199,6 +205,9 @@ available options. Some of these are described below:
|
||||
each program is extracted from the original image and executed (see -j), this
|
||||
is the only way that subsequent phases can locate the full image.
|
||||
|
||||
-P, --pager_bypass
|
||||
Enable pager bypass mode for testing.
|
||||
|
||||
-r, --read
|
||||
Read driver state from a dtb file. In conjunction with `-w`, this allows
|
||||
sandbox to save and restore emulated hardware state (such as a TPM) across
|
||||
@@ -243,11 +252,21 @@ available options. Some of these are described below:
|
||||
-v, --verbose
|
||||
Show console output from tests. Normally this is suppressed.
|
||||
|
||||
--video_frames <dir>
|
||||
Write video frames to the specified directory for debugging purposes.
|
||||
|
||||
-V, --video_test <ms>
|
||||
Enable video test mode with a delay (in milliseconds) between assertions. This
|
||||
allows time to examine the display during testing.
|
||||
|
||||
-w, --write
|
||||
Write driver state to state file on exit. In conjunction with `-r`, this allows
|
||||
sandbox to save and restore emulated hardware state (such as a TPM) across
|
||||
each U-Boot phase.
|
||||
|
||||
-W, --title <title>
|
||||
Set the window title for the sandbox display.
|
||||
|
||||
|
||||
Environment Variables
|
||||
---------------------
|
||||
|
||||
@@ -43,7 +43,7 @@ struct efi_video_priv {
|
||||
bool use_blit;
|
||||
};
|
||||
|
||||
static int efi_video_sync(struct udevice *dev)
|
||||
static int efi_video_sync(struct udevice *dev, uint flags)
|
||||
{
|
||||
struct video_priv *vid_priv = dev_get_uclass_priv(dev);
|
||||
struct efi_video_priv *priv = dev_get_priv(dev);
|
||||
@@ -291,7 +291,7 @@ static int efi_video_bind(struct udevice *dev)
|
||||
}
|
||||
|
||||
const static struct video_ops efi_video_ops = {
|
||||
.video_sync = efi_video_sync,
|
||||
.sync = efi_video_sync,
|
||||
};
|
||||
|
||||
static const struct udevice_id efi_video_ids[] = {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
# Copyright 2025 Simon Glass <sjg@chromium.org>
|
||||
|
||||
obj-$(CONFIG_VIDEO_LOGO) += u_boot.o
|
||||
ifdef CONFIG_MOUSE
|
||||
obj-$(CONFIG_EXPO) += riscos_arrow.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_$(PHASE_)GENERATE_ACPI_TABLE
|
||||
obj-y += bgrt.o
|
||||
|
||||
BIN
drivers/video/images/riscos_arrow.bmp
Normal file
BIN
drivers/video/images/riscos_arrow.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -94,7 +94,7 @@ static int mcde_simple_probe(struct udevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcde_simple_video_sync(struct udevice *dev)
|
||||
static int mcde_simple_video_sync(struct udevice *dev, uint flags)
|
||||
{
|
||||
struct mcde_simple_priv *priv = dev_get_priv(dev);
|
||||
unsigned int val;
|
||||
@@ -122,7 +122,7 @@ static int mcde_simple_video_sync(struct udevice *dev)
|
||||
}
|
||||
|
||||
static struct video_ops mcde_simple_ops = {
|
||||
.video_sync = mcde_simple_video_sync,
|
||||
.sync = mcde_simple_video_sync,
|
||||
};
|
||||
|
||||
static const struct udevice_id mcde_simple_ids[] = {
|
||||
|
||||
@@ -127,6 +127,41 @@ static int sandbox_sdl_bind(struct udevice *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sandbox_sdl_video_sync(struct udevice *vid, uint flags)
|
||||
{
|
||||
struct sandbox_sdl_plat *plat = dev_get_plat(vid);
|
||||
struct video_priv *uc_priv = dev_get_uclass_priv(vid);
|
||||
const struct video_bbox *damage = NULL;
|
||||
|
||||
if (!(flags & VIDSYNC_FLUSH))
|
||||
return 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_VIDEO_DAMAGE))
|
||||
damage = &uc_priv->damage;
|
||||
|
||||
/* Record the damage box for testing */
|
||||
if (damage)
|
||||
plat->last_sync_damage = *damage;
|
||||
else
|
||||
memset(&plat->last_sync_damage, '\0',
|
||||
sizeof(plat->last_sync_damage));
|
||||
|
||||
return sandbox_sdl_sync(uc_priv->fb, damage);
|
||||
}
|
||||
|
||||
static const struct video_ops sandbox_sdl_ops = {
|
||||
.sync = sandbox_sdl_video_sync,
|
||||
};
|
||||
|
||||
int sandbox_sdl_get_sync_damage(struct udevice *dev, struct video_bbox *damage)
|
||||
{
|
||||
struct sandbox_sdl_plat *plat = dev_get_plat(dev);
|
||||
|
||||
*damage = plat->last_sync_damage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id sandbox_sdl_ids[] = {
|
||||
{ .compatible = "sandbox,lcd-sdl" },
|
||||
{ }
|
||||
@@ -139,5 +174,6 @@ U_BOOT_DRIVER(sandbox_lcd_sdl) = {
|
||||
.bind = sandbox_sdl_bind,
|
||||
.probe = sandbox_sdl_probe,
|
||||
.remove = sandbox_sdl_remove,
|
||||
.ops = &sandbox_sdl_ops,
|
||||
.plat_auto = sizeof(struct sandbox_sdl_plat),
|
||||
};
|
||||
|
||||
@@ -221,7 +221,7 @@ static int seps525_spi_startup(struct udevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seps525_sync(struct udevice *vid)
|
||||
static int seps525_sync(struct udevice *vid, uint flags)
|
||||
{
|
||||
struct video_priv *uc_priv = dev_get_uclass_priv(vid);
|
||||
struct seps525_priv *priv = dev_get_priv(vid);
|
||||
@@ -306,7 +306,7 @@ static int seps525_bind(struct udevice *dev)
|
||||
}
|
||||
|
||||
static const struct video_ops seps525_ops = {
|
||||
.video_sync = seps525_sync,
|
||||
.sync = seps525_sync,
|
||||
};
|
||||
|
||||
static const struct udevice_id seps525_ids[] = {
|
||||
|
||||
@@ -64,11 +64,14 @@ struct cyclic_info;
|
||||
* available address to use for a device's framebuffer. It starts at
|
||||
* 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;
|
||||
};
|
||||
|
||||
@@ -410,6 +413,7 @@ void video_set_default_colors(struct udevice *dev, bool invert)
|
||||
void video_damage(struct udevice *vid, int x, int y, int width, int height)
|
||||
{
|
||||
struct video_priv *priv = dev_get_uclass_priv(vid);
|
||||
struct video_bbox *damage = &priv->damage;
|
||||
int xend = x + width;
|
||||
int yend = y + height;
|
||||
|
||||
@@ -426,10 +430,10 @@ void video_damage(struct udevice *vid, int x, int y, int width, int height)
|
||||
yend = priv->ysize;
|
||||
|
||||
/* Span a rectangle across all old and new damage */
|
||||
priv->damage.xstart = min(x, priv->damage.xstart);
|
||||
priv->damage.ystart = min(y, priv->damage.ystart);
|
||||
priv->damage.xend = max(xend, priv->damage.xend);
|
||||
priv->damage.yend = max(yend, priv->damage.yend);
|
||||
damage->x0 = min(x, damage->x0);
|
||||
damage->y0 = min(y, damage->y0);
|
||||
damage->x1 = max(xend, damage->x1);
|
||||
damage->y1 = max(yend, damage->y1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -456,12 +460,12 @@ static void video_flush_dcache(struct udevice *vid, bool use_copy)
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->damage.xend && priv->damage.yend) {
|
||||
int lstart = priv->damage.xstart * VNBYTES(priv->bpix);
|
||||
int lend = priv->damage.xend * VNBYTES(priv->bpix);
|
||||
if (priv->damage.x1 && priv->damage.y1) {
|
||||
int lstart = priv->damage.x0 * VNBYTES(priv->bpix);
|
||||
int lend = priv->damage.x1 * VNBYTES(priv->bpix);
|
||||
int y;
|
||||
|
||||
for (y = priv->damage.ystart; y < priv->damage.yend; y++) {
|
||||
for (y = priv->damage.y0; y < priv->damage.y1; y++) {
|
||||
ulong start = fb + (y * priv->line_length) + lstart;
|
||||
ulong end = start + lend - lstart;
|
||||
|
||||
@@ -476,16 +480,17 @@ static void video_flush_dcache(struct udevice *vid, bool use_copy)
|
||||
static void video_flush_copy(struct udevice *vid)
|
||||
{
|
||||
struct video_priv *priv = dev_get_uclass_priv(vid);
|
||||
struct video_bbox *damage = &priv->damage;
|
||||
|
||||
if (!priv->copy_fb)
|
||||
return;
|
||||
|
||||
if (priv->damage.xend && priv->damage.yend) {
|
||||
int lstart = priv->damage.xstart * VNBYTES(priv->bpix);
|
||||
int lend = priv->damage.xend * VNBYTES(priv->bpix);
|
||||
if (damage->x1 && damage->y1) {
|
||||
int lstart = damage->x0 * VNBYTES(priv->bpix);
|
||||
int lend = damage->x1 * VNBYTES(priv->bpix);
|
||||
int y;
|
||||
|
||||
for (y = priv->damage.ystart; y < priv->damage.yend; y++) {
|
||||
for (y = damage->y0; y < damage->y1; y++) {
|
||||
ulong offset = (y * priv->line_length) + lstart;
|
||||
ulong len = lend - lstart;
|
||||
|
||||
@@ -494,47 +499,68 @@ static void video_flush_copy(struct udevice *vid)
|
||||
}
|
||||
}
|
||||
|
||||
/* Flush video activity to the caches */
|
||||
int video_sync(struct udevice *vid, bool force)
|
||||
int video_manual_sync(struct udevice *vid, uint flags)
|
||||
{
|
||||
struct video_priv *priv = dev_get_uclass_priv(vid);
|
||||
struct video_ops *ops = video_get_ops(vid);
|
||||
int ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_VIDEO_COPY))
|
||||
if (IS_ENABLED(CONFIG_VIDEO_COPY) && (flags & VIDSYNC_COPY))
|
||||
video_flush_copy(vid);
|
||||
|
||||
if (ops && ops->video_sync) {
|
||||
ret = ops->video_sync(vid);
|
||||
if (ops && ops->sync) {
|
||||
ret = ops->sync(vid, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (CONFIG_IS_ENABLED(CYCLIC) && !force &&
|
||||
get_timer(priv->last_sync) < CONFIG_VIDEO_SYNC_MS)
|
||||
if (!(flags & VIDSYNC_FLUSH))
|
||||
return 0;
|
||||
|
||||
video_flush_dcache(vid, false);
|
||||
|
||||
if (IS_ENABLED(CONFIG_VIDEO_COPY))
|
||||
if (IS_ENABLED(CONFIG_VIDEO_COPY) && (flags & VIDSYNC_COPY))
|
||||
video_flush_dcache(vid, true);
|
||||
|
||||
#if defined(CONFIG_VIDEO_SANDBOX_SDL)
|
||||
/* to see the copy framebuffer, use priv->copy_fb */
|
||||
sandbox_sdl_sync(priv->fb);
|
||||
#endif
|
||||
priv->last_sync = get_timer(0);
|
||||
|
||||
if (IS_ENABLED(CONFIG_VIDEO_DAMAGE)) {
|
||||
priv->damage.xstart = priv->xsize;
|
||||
priv->damage.ystart = priv->ysize;
|
||||
priv->damage.xend = 0;
|
||||
priv->damage.yend = 0;
|
||||
struct video_bbox *damage = &priv->damage;
|
||||
|
||||
damage->x0 = priv->xsize;
|
||||
damage->y0 = priv->ysize;
|
||||
damage->x1 = 0;
|
||||
damage->y1 = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flush video activity to the caches */
|
||||
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);
|
||||
uint flags = 0;
|
||||
|
||||
/* Skip sync if manual-sync mode is active */
|
||||
if (uc_priv->manual_sync)
|
||||
return 0;
|
||||
|
||||
if (force)
|
||||
flags |= VIDSYNC_FORCE;
|
||||
|
||||
/* Check if sync should do full flush */
|
||||
if (!CONFIG_IS_ENABLED(CYCLIC) || force ||
|
||||
get_timer(priv->last_sync) >= CONFIG_VIDEO_SYNC_MS)
|
||||
flags |= VIDSYNC_FLUSH;
|
||||
|
||||
if (IS_ENABLED(CONFIG_VIDEO_COPY))
|
||||
flags |= VIDSYNC_COPY;
|
||||
|
||||
return video_manual_sync(vid, flags);
|
||||
}
|
||||
|
||||
void video_sync_all(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
@@ -617,6 +643,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;
|
||||
@@ -804,6 +844,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",
|
||||
|
||||
@@ -53,6 +53,20 @@ static u32 get_bmp_col_rgba8888(struct bmp_color_table_entry *cte)
|
||||
(cte->blue << 16U) | 0xff << 24U);
|
||||
}
|
||||
|
||||
/**
|
||||
* matches_alpha() - Check if a palette entry matches the alpha color
|
||||
*
|
||||
* @ent: BMP palette entry
|
||||
* @col: Alpha color to compare (RGB888 format)
|
||||
* Return: true if the palette entry matches the alpha color
|
||||
*/
|
||||
static bool matches_alpha(struct bmp_color_table_entry *ent, u32 col)
|
||||
{
|
||||
u32 colour = (ent->red << 16) | (ent->green << 8) | ent->blue;
|
||||
|
||||
return colour == col;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_pix8() - Write a pixel from a BMP image into the framebuffer
|
||||
*
|
||||
@@ -64,18 +78,25 @@ static u32 get_bmp_col_rgba8888(struct bmp_color_table_entry *cte)
|
||||
* @bmap: Pointer to BMP bitmap position to write. This contains a single byte
|
||||
* which is either written directly (bpix == 8) or used to look up the
|
||||
* palette to get a colour to write
|
||||
* @alpha: Enable alpha transparency
|
||||
* @acol: Alpha color (RGB888 format)
|
||||
*/
|
||||
static void write_pix8(u8 *fb, uint bpix, enum video_format eformat,
|
||||
struct bmp_color_table_entry *palette, u8 *bmap)
|
||||
struct bmp_color_table_entry *palette, u8 *bmap,
|
||||
bool alpha, u32 acol)
|
||||
{
|
||||
struct bmp_color_table_entry *cte = &palette[*bmap];
|
||||
|
||||
/* Check for transparent pixel */
|
||||
if (alpha && matches_alpha(cte, acol))
|
||||
return;
|
||||
|
||||
if (bpix == 8) {
|
||||
*fb++ = *bmap;
|
||||
} else if (bpix == 16) {
|
||||
*(u16 *)fb = get_bmp_col_16bpp(palette[*bmap]);
|
||||
*(u16 *)fb = get_bmp_col_16bpp(*cte);
|
||||
} else {
|
||||
/* Only support big endian */
|
||||
struct bmp_color_table_entry *cte = &palette[*bmap];
|
||||
|
||||
if (bpix == 24) {
|
||||
*fb++ = cte->red;
|
||||
*fb++ = cte->green;
|
||||
@@ -96,12 +117,12 @@ static void write_pix8(u8 *fb, uint bpix, enum video_format eformat,
|
||||
static void draw_unencoded_bitmap(u8 **fbp, uint bpix,
|
||||
enum video_format eformat, uchar *bmap,
|
||||
struct bmp_color_table_entry *palette,
|
||||
int cnt)
|
||||
int cnt, bool alpha, u32 acolour)
|
||||
{
|
||||
u8 *fb = *fbp;
|
||||
|
||||
while (cnt > 0) {
|
||||
write_pix8(fb, bpix, eformat, palette, bmap++);
|
||||
write_pix8(fb, bpix, eformat, palette, bmap++, alpha, acolour);
|
||||
fb += bpix / 8;
|
||||
cnt--;
|
||||
}
|
||||
@@ -110,12 +131,12 @@ static void draw_unencoded_bitmap(u8 **fbp, uint bpix,
|
||||
|
||||
static void draw_encoded_bitmap(u8 **fbp, uint bpix, enum video_format eformat,
|
||||
struct bmp_color_table_entry *palette, u8 *bmap,
|
||||
int cnt)
|
||||
int cnt, bool alpha, u32 acolour)
|
||||
{
|
||||
u8 *fb = *fbp;
|
||||
|
||||
while (cnt > 0) {
|
||||
write_pix8(fb, bpix, eformat, palette, bmap);
|
||||
write_pix8(fb, bpix, eformat, palette, bmap, alpha, acolour);
|
||||
fb += bpix / 8;
|
||||
cnt--;
|
||||
}
|
||||
@@ -126,7 +147,8 @@ static void video_display_rle8_bitmap(struct udevice *dev,
|
||||
struct bmp_image *bmp, uint bpix,
|
||||
struct bmp_color_table_entry *palette,
|
||||
uchar *fb, int x_off, int y_off,
|
||||
ulong width, ulong height)
|
||||
ulong width, ulong height, bool alpha,
|
||||
u32 acolour)
|
||||
{
|
||||
struct video_priv *priv = dev_get_uclass_priv(dev);
|
||||
uchar *bmap;
|
||||
@@ -178,7 +200,8 @@ static void video_display_rle8_bitmap(struct udevice *dev,
|
||||
cnt = runlen;
|
||||
draw_unencoded_bitmap(
|
||||
&fb, bpix, eformat,
|
||||
bmap, palette, cnt);
|
||||
bmap, palette, cnt,
|
||||
alpha, acolour);
|
||||
}
|
||||
x += runlen;
|
||||
}
|
||||
@@ -204,7 +227,8 @@ static void video_display_rle8_bitmap(struct udevice *dev,
|
||||
cnt = runlen;
|
||||
draw_encoded_bitmap(&fb, bpix, eformat,
|
||||
palette, &bmap[1],
|
||||
cnt);
|
||||
cnt, alpha,
|
||||
acolour);
|
||||
}
|
||||
x += runlen;
|
||||
}
|
||||
@@ -252,8 +276,8 @@ void video_bmp_get_info(const void *bmp_image, ulong *widthp, ulong *heightp,
|
||||
*bpixp = get_unaligned_le16(&bmp->header.bit_count);
|
||||
}
|
||||
|
||||
int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
bool align)
|
||||
static int draw_bmp(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
bool align, bool alpha, u32 acolour)
|
||||
{
|
||||
struct video_priv *priv = dev_get_uclass_priv(dev);
|
||||
int i, j;
|
||||
@@ -337,8 +361,10 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
&bmp->header.compression);
|
||||
debug("compressed %d %d\n", compression, BMP_BI_RLE8);
|
||||
if (compression == BMP_BI_RLE8) {
|
||||
video_display_rle8_bitmap(dev, bmp, bpix, palette, fb,
|
||||
x, y, width, height);
|
||||
video_display_rle8_bitmap(dev, bmp, bpix,
|
||||
palette, fb, x, y,
|
||||
width, height, alpha,
|
||||
acolour);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -351,9 +377,10 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
for (i = 0; i < height; ++i) {
|
||||
schedule();
|
||||
for (j = 0; j < width; j++) {
|
||||
write_pix8(fb, bpix, eformat, palette, bmap);
|
||||
bmap++;
|
||||
write_pix8(fb, bpix, eformat, palette, bmap,
|
||||
alpha, acolour);
|
||||
fb += bpix / 8;
|
||||
bmap++;
|
||||
}
|
||||
bmap += (padded_width - width);
|
||||
fb -= byte_width + priv->line_length;
|
||||
@@ -376,20 +403,32 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
if (CONFIG_IS_ENABLED(BMP_24BPP)) {
|
||||
for (i = 0; i < height; ++i) {
|
||||
for (j = 0; j < width; j++) {
|
||||
u8 red = bmap[0];
|
||||
u8 green = bmap[1];
|
||||
u8 blue = bmap[2];
|
||||
u32 pixel_color = (red << 16) |
|
||||
(green << 8) | blue;
|
||||
|
||||
bmap += 3;
|
||||
/* Skip transparent pixels if alpha enabled */
|
||||
if (alpha && pixel_color == acolour) {
|
||||
fb += bpix / 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bpix == 16) {
|
||||
/* 16bit 565RGB format */
|
||||
*(u16 *)fb = ((bmap[2] >> 3)
|
||||
<< 11) |
|
||||
((bmap[1] >> 2) << 5) |
|
||||
(bmap[0] >> 3);
|
||||
bmap += 3;
|
||||
*(u16 *)fb =
|
||||
((blue >> 3) << 11) |
|
||||
((green >> 2) << 5) |
|
||||
(red >> 3);
|
||||
fb += 2;
|
||||
} else if (eformat == VIDEO_X2R10G10B10) {
|
||||
u32 pix;
|
||||
|
||||
pix = *bmap++ << 2U;
|
||||
pix |= *bmap++ << 12U;
|
||||
pix |= *bmap++ << 22U;
|
||||
pix = blue << 2U;
|
||||
pix |= green << 12U;
|
||||
pix |= red << 22U;
|
||||
*fb++ = pix & 0xff;
|
||||
*fb++ = (pix >> 8) & 0xff;
|
||||
*fb++ = (pix >> 16) & 0xff;
|
||||
@@ -397,18 +436,18 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
} else if (eformat == VIDEO_RGBA8888) {
|
||||
u32 pix;
|
||||
|
||||
pix = *bmap++ << 8U; /* blue */
|
||||
pix |= *bmap++ << 16U; /* green */
|
||||
pix |= *bmap++ << 24U; /* red */
|
||||
|
||||
*fb++ = (pix >> 24) & 0xff;
|
||||
*fb++ = (pix >> 16) & 0xff;
|
||||
pix = red << 24U;
|
||||
pix |= green << 16U;
|
||||
pix |= blue << 8U;
|
||||
pix |= 0xff;
|
||||
*fb++ = pix & 0xff;
|
||||
*fb++ = (pix >> 8) & 0xff;
|
||||
*fb++ = 0xff;
|
||||
*fb++ = (pix >> 16) & 0xff;
|
||||
*fb++ = pix >> 24;
|
||||
} else {
|
||||
*fb++ = *bmap++;
|
||||
*fb++ = *bmap++;
|
||||
*fb++ = *bmap++;
|
||||
*fb++ = red;
|
||||
*fb++ = green;
|
||||
*fb++ = blue;
|
||||
*fb++ = 0;
|
||||
}
|
||||
}
|
||||
@@ -462,3 +501,15 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
|
||||
return video_sync(dev, false);
|
||||
}
|
||||
|
||||
int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
bool align)
|
||||
{
|
||||
return draw_bmp(dev, bmp_image, x, y, align, false, 0);
|
||||
}
|
||||
|
||||
int video_bmp_displaya(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
bool align, bool alpha, u32 acolour)
|
||||
{
|
||||
return draw_bmp(dev, bmp_image, x, y, align, alpha, acolour);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define __DM_TEST_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <video_defs.h>
|
||||
|
||||
struct udevice;
|
||||
|
||||
@@ -157,6 +158,7 @@ extern struct unit_test_state global_dm_test_state;
|
||||
* 2=upside down, 3=90 degree counterclockwise)
|
||||
* @vidconsole_drv_name: Name of video console driver (set by tests)
|
||||
* @font_size: Console font size to select (set by tests)
|
||||
* @last_sync_damage: Last damage rectangle passed to sync() method (for testing)
|
||||
*/
|
||||
struct sandbox_sdl_plat {
|
||||
int xres;
|
||||
@@ -165,6 +167,7 @@ struct sandbox_sdl_plat {
|
||||
int rot;
|
||||
const char *vidconsole_drv_name;
|
||||
int font_size;
|
||||
struct video_bbox last_sync_damage;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -232,4 +235,16 @@ void dm_leak_check_start(struct unit_test_state *uts);
|
||||
* @dms: Overall test state
|
||||
*/int dm_leak_check_end(struct unit_test_state *uts);
|
||||
|
||||
/**
|
||||
* sandbox_sdl_get_sync_damage() - Get the last damage rect passed to sync()
|
||||
*
|
||||
* This is used for testing to verify that the correct damage rectangle was
|
||||
* passed to the driver's sync() method.
|
||||
*
|
||||
* @dev: Video device
|
||||
* @damage: Returns the last damage rectangle
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
int sandbox_sdl_get_sync_damage(struct udevice *dev, struct video_bbox *damage);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linker_lists.h>
|
||||
#include <stdio_dev.h>
|
||||
#include <video_defs.h>
|
||||
#include <linux/bitops.h>
|
||||
#ifdef CONFIG_SANDBOX
|
||||
#include <asm/state.h>
|
||||
#endif
|
||||
@@ -74,6 +75,19 @@ enum video_format {
|
||||
VIDEO_X2R10G10B10,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum video_sync_flags - Flags for video_sync() operations
|
||||
*
|
||||
* @VIDSYNC_FORCE: Force sync even if recently synced or in manual-sync mode
|
||||
* @VIDSYNC_FLUSH: Flush dcache and perform full sync operations
|
||||
* @VIDSYNC_COPY: Flush framebuffer to copy buffer
|
||||
*/
|
||||
enum video_sync_flags {
|
||||
VIDSYNC_FORCE = BIT(0),
|
||||
VIDSYNC_FLUSH = BIT(1),
|
||||
VIDSYNC_COPY = BIT(2),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct video_priv - Device information used by the video uclass
|
||||
*
|
||||
@@ -90,11 +104,7 @@ enum video_format {
|
||||
* @fb_size: Frame buffer size
|
||||
* @copy_fb: Copy of the frame buffer to keep up to date; see struct
|
||||
* video_uc_plat
|
||||
* @damage: A bounding box of framebuffer regions updated since last sync
|
||||
* @damage.xstart: X start position in pixels from the left
|
||||
* @damage.ystart: Y start position in pixels from the top
|
||||
* @damage.xend: X end position in pixels from the left
|
||||
* @damage.xend: Y end position in pixels from the top
|
||||
* @damage: Bounding box of framebuffer regions updated since last sync
|
||||
* @line_length: Length of each frame buffer line, in bytes. This can be
|
||||
* set by the driver, but if not, the uclass will set it after
|
||||
* probing
|
||||
@@ -124,12 +134,7 @@ struct video_priv {
|
||||
void *fb;
|
||||
int fb_size;
|
||||
void *copy_fb;
|
||||
struct {
|
||||
int xstart;
|
||||
int ystart;
|
||||
int xend;
|
||||
int yend;
|
||||
} damage;
|
||||
struct video_bbox damage;
|
||||
int line_length;
|
||||
u32 colour_fg;
|
||||
u32 colour_bg;
|
||||
@@ -142,14 +147,23 @@ struct video_priv {
|
||||
|
||||
/**
|
||||
* struct video_ops - structure for keeping video operations
|
||||
* @video_sync: Synchronize FB with device. Some device like SPI based LCD
|
||||
* displays needs synchronization when data in an FB is available.
|
||||
* For these devices implement video_sync hook to call a sync
|
||||
* function. vid is pointer to video device udevice. Function
|
||||
* should return 0 on success video_sync and error code otherwise
|
||||
*/
|
||||
struct video_ops {
|
||||
int (*video_sync)(struct udevice *vid);
|
||||
/**
|
||||
* @sync() - Synchronize FB with device
|
||||
*
|
||||
* Some devices like SPI-based LCD displays needs synchronization when
|
||||
* data in a framebuffer is available. These devices can implement this
|
||||
* method which is called whenever a video device is synced.
|
||||
*
|
||||
* Note that if CONFIG_VIDEO_DAMAGE is enabled, the driver can use this
|
||||
* to optimise the region to redraw.
|
||||
*
|
||||
* @dev: Video device
|
||||
* @flags: Flags for the sync operation (enum video_sync_flags)
|
||||
* Return 0 on success, or -ve error code
|
||||
*/
|
||||
int (*sync)(struct udevice *dev, uint flags);
|
||||
};
|
||||
|
||||
#define video_get_ops(dev) ((struct video_ops *)(dev)->driver->ops)
|
||||
@@ -329,6 +343,20 @@ int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
|
||||
int video_draw_box(struct udevice *dev, int x0, int y0, int x1, int y1,
|
||||
int width, u32 colour, bool fill);
|
||||
|
||||
/**
|
||||
* video_manual_sync() - Manually sync a device's frame buffer with its hardware
|
||||
*
|
||||
* @vid: Device to sync
|
||||
* @flags: Flags for the sync (enum video_sync_flags)
|
||||
*
|
||||
* @return: 0 on success, error code otherwise
|
||||
*
|
||||
* Performs the actual sync operation with the provided flags. This is called
|
||||
* by video_sync() after determining the appropriate flags, but can also be
|
||||
* called directly when manual-sync mode is enabled.
|
||||
*/
|
||||
int video_manual_sync(struct udevice *vid, uint flags);
|
||||
|
||||
/**
|
||||
* video_sync() - Sync a device's frame buffer with its hardware
|
||||
*
|
||||
@@ -383,6 +411,22 @@ void video_bmp_get_info(const void *bmp_image, ulong *widthp, ulong *heightp,
|
||||
int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
bool align);
|
||||
|
||||
/**
|
||||
* video_bmp_displaya() - Display a BMP image with alpha transparency
|
||||
*
|
||||
* @dev: Device to use
|
||||
* @bmp_image: Address of BMP image
|
||||
* @x: X position to draw image
|
||||
* @y: Y position to draw image
|
||||
* @align: true to adjust the coordinates to centre the image (see
|
||||
* video_bmp_display() for details)
|
||||
* @alpha: true to enable alpha transparency
|
||||
* @acolour: Color to treat as transparent (RGB888 format: 0xRRGGBB)
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
int video_bmp_displaya(struct udevice *dev, ulong bmp_image, int x, int y,
|
||||
bool align, bool alpha, u32 acolour);
|
||||
|
||||
/**
|
||||
* video_get_xsize() - Get the width of the display in pixels
|
||||
*
|
||||
@@ -528,4 +572,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
|
||||
|
||||
@@ -12,4 +12,41 @@
|
||||
/* Maximum length of an embedded image name */
|
||||
#define VIDEO_IMAGE_NAMELEN 16
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* struct video_bbox - Represents a bounding box for video operations
|
||||
*
|
||||
* The bounding box is only valid if x1 > x0 and y1 > y0. An invalid bounding
|
||||
* box (where x1 <= x0 or y1 <= y0) indicates that there is no area to process.
|
||||
*
|
||||
* @x0: X start position in pixels from the left
|
||||
* @y0: Y start position in pixels from the top
|
||||
* @x1: X end position in pixels from the left
|
||||
* @y1: Y end position in pixels from the top
|
||||
*/
|
||||
struct video_bbox {
|
||||
int x0;
|
||||
int y0;
|
||||
int x1;
|
||||
int y1;
|
||||
};
|
||||
|
||||
/**
|
||||
* video_bbox_valid() - Check if a bounding box is valid
|
||||
*
|
||||
* A valid bounding box has x1 > x0 and y1 > y0. An invalid/inverted bounding
|
||||
* box (where x1 <= x0 or y1 <= y0) indicates that there is no area to process.
|
||||
*
|
||||
* @bbox: Bounding box to check
|
||||
* Return: true if valid, false if invalid/inverted
|
||||
*/
|
||||
static inline bool video_bbox_valid(const struct video_bbox *bbox)
|
||||
{
|
||||
return bbox->x1 > bbox->x0 && bbox->y1 > bbox->y0;
|
||||
}
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* __VIDEO_DEFS_H */
|
||||
|
||||
376
test/dm/video.c
376
test/dm/video.c
@@ -4,6 +4,7 @@
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <bmp_layout.h>
|
||||
#include <bzlib.h>
|
||||
#include <dm.h>
|
||||
#include <gzip.h>
|
||||
@@ -47,9 +48,83 @@ 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)
|
||||
{
|
||||
struct sandbox_state *state = state_get_current();
|
||||
struct video_priv *priv = dev_get_uclass_priv(dev);
|
||||
uint destlen;
|
||||
void *dest;
|
||||
@@ -70,8 +145,22 @@ int video_compress_fb(struct unit_test_state *uts, struct udevice *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* provide a useful delay if #define LOG_DEBUG at the top of file */
|
||||
if (_DEBUG)
|
||||
/* 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);
|
||||
else if (_DEBUG)
|
||||
mdelay(300);
|
||||
|
||||
return destlen;
|
||||
@@ -578,6 +667,77 @@ static int dm_test_video_comp_bmp8(struct unit_test_state *uts)
|
||||
}
|
||||
DM_TEST(dm_test_video_comp_bmp8, UTF_SCAN_PDATA | UTF_SCAN_FDT);
|
||||
|
||||
/**
|
||||
* check_bmp_alpha() - Test drawing the riscos pointer with transparency
|
||||
*
|
||||
* Draws the riscos pointer BMP in various positions with and without
|
||||
* transparency to verify alpha blending works correctly
|
||||
*
|
||||
* @uts: Test state
|
||||
* @dev: Video device
|
||||
* @first: Expected compression after first pointer draw
|
||||
* @second: Expected compression after second pointer draw
|
||||
* @trans: Expected compression after drawing with transparency
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
static int check_bmp_alpha(struct unit_test_state *uts, struct udevice *dev,
|
||||
int first, int second, int trans)
|
||||
{
|
||||
struct video_priv *priv = dev_get_uclass_priv(dev);
|
||||
ulong addr;
|
||||
|
||||
addr = map_to_sysmem(video_image_getptr(riscos_arrow));
|
||||
|
||||
/* Draw a black rectangle first */
|
||||
video_draw_box(dev, 100, 100, 200, 200, 0,
|
||||
video_index_to_colour(priv, VID_BLACK), true);
|
||||
|
||||
/* Draw the pointer on top of the black rectangle */
|
||||
ut_assertok(video_bmp_display(dev, addr, 110, 110, false));
|
||||
ut_asserteq(first, video_compress_fb(uts, dev, false));
|
||||
ut_assertok(video_check_copy_fb(uts, dev));
|
||||
|
||||
/* Draw the pointer on top of the white background */
|
||||
ut_assertok(video_bmp_display(dev, addr, 350, 110, false));
|
||||
ut_asserteq(second, video_compress_fb(uts, dev, false));
|
||||
|
||||
/* Draw the pointer with white (0xffffff) as transparent */
|
||||
ut_assertok(video_bmp_displaya(dev, addr, 110, 160, false, true,
|
||||
0xffffff));
|
||||
ut_assertok(video_bmp_displaya(dev, addr, 350, 160, false, true,
|
||||
0xffffff));
|
||||
ut_asserteq(trans, video_compress_fb(uts, dev, false));
|
||||
ut_assertok(video_check_copy_fb(uts, dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test drawing the riscos pointer on a 16bpp display */
|
||||
static int dm_test_video_bmp_alpha16(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
ut_assertok(video_get_nologo(uts, &dev));
|
||||
ut_assertok(check_bmp_alpha(uts, dev, 174, 249, 358));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_video_bmp_alpha16, UTF_SCAN_PDATA | UTF_SCAN_FDT);
|
||||
|
||||
/* Test drawing the riscos pointer on 32bpp display */
|
||||
static int dm_test_video_bmp_alpha32(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
|
||||
ut_assertnonnull(dev);
|
||||
ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32));
|
||||
ut_assertok(check_bmp_alpha(uts, dev, 641, 710, 869));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_video_bmp_alpha32, UTF_SCAN_PDATA | UTF_SCAN_FDT);
|
||||
|
||||
/* Test TrueType console */
|
||||
static int dm_test_video_truetype(struct unit_test_state *uts)
|
||||
{
|
||||
@@ -743,32 +903,34 @@ static int dm_test_video_damage(struct unit_test_state *uts)
|
||||
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
|
||||
priv = dev_get_uclass_priv(dev);
|
||||
|
||||
struct video_bbox *damage = &priv->damage;
|
||||
|
||||
vidconsole_position_cursor(con, 14, 10);
|
||||
vidconsole_put_string(con, test_string_2);
|
||||
ut_asserteq(449, priv->damage.xstart);
|
||||
ut_asserteq(325, priv->damage.ystart);
|
||||
ut_asserteq(661, priv->damage.xend);
|
||||
ut_asserteq(350, priv->damage.yend);
|
||||
ut_asserteq(449, damage->x0);
|
||||
ut_asserteq(325, damage->y0);
|
||||
ut_asserteq(661, damage->x1);
|
||||
ut_asserteq(350, damage->y1);
|
||||
|
||||
vidconsole_position_cursor(con, 7, 5);
|
||||
vidconsole_put_string(con, test_string_1);
|
||||
ut_asserteq(225, priv->damage.xstart);
|
||||
ut_asserteq(164, priv->damage.ystart);
|
||||
ut_asserteq(661, priv->damage.xend);
|
||||
ut_asserteq(350, priv->damage.yend);
|
||||
ut_asserteq(225, damage->x0);
|
||||
ut_asserteq(164, damage->y0);
|
||||
ut_asserteq(661, damage->x1);
|
||||
ut_asserteq(350, damage->y1);
|
||||
|
||||
vidconsole_position_cursor(con, 21, 15);
|
||||
vidconsole_put_string(con, test_string_3);
|
||||
ut_asserteq(225, priv->damage.xstart);
|
||||
ut_asserteq(164, priv->damage.ystart);
|
||||
ut_asserteq(1280, priv->damage.xend);
|
||||
ut_asserteq(510, priv->damage.yend);
|
||||
ut_asserteq(225, damage->x0);
|
||||
ut_asserteq(164, damage->y0);
|
||||
ut_asserteq(1280, damage->x1);
|
||||
ut_asserteq(510, damage->y1);
|
||||
|
||||
video_sync(dev, true);
|
||||
ut_asserteq(priv->xsize, priv->damage.xstart);
|
||||
ut_asserteq(priv->ysize, priv->damage.ystart);
|
||||
ut_asserteq(0, priv->damage.xend);
|
||||
ut_asserteq(0, priv->damage.yend);
|
||||
ut_asserteq(priv->xsize, damage->x0);
|
||||
ut_asserteq(priv->ysize, damage->y0);
|
||||
ut_asserteq(0, damage->x1);
|
||||
ut_asserteq(0, damage->y1);
|
||||
|
||||
ut_asserteq(7335, video_compress_fb(uts, dev, false));
|
||||
ut_assertok(video_check_copy_fb(uts, dev));
|
||||
@@ -1122,11 +1284,187 @@ static int dm_test_video_images(struct unit_test_state *uts)
|
||||
ut_assert_nextline("Name Size");
|
||||
ut_assert_nextline("-------------------- ----------");
|
||||
ut_assert_nextline("bgrt 43926");
|
||||
ut_assert_nextline("riscos_arrow 3798");
|
||||
ut_assert_nextline("u_boot 6932");
|
||||
ut_assert_skip_to_line("");
|
||||
ut_assert_nextline("Total images: 2");
|
||||
ut_assert_nextline("Total images: 3");
|
||||
ut_assert_console_end();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/* Now test video_manual_sync() directly with VIDSYNC_FORCE and COPY */
|
||||
ut_assertok(video_manual_sync(dev, VIDSYNC_FORCE | VIDSYNC_FLUSH |
|
||||
VIDSYNC_COPY));
|
||||
ut_asserteq(183, video_compress_fb(uts, dev, false));
|
||||
|
||||
/* The copy framebuffer should now match since we forced the sync */
|
||||
ut_assertok(video_check_copy_fb(uts, dev));
|
||||
|
||||
/* Write new text again */
|
||||
vidconsole_put_string(con, "Test2");
|
||||
|
||||
/* without VIDSYNC_FLUSH or COPY - should do nothing */
|
||||
ut_assertok(video_manual_sync(dev, 0));
|
||||
|
||||
/* Copy fb should not match since neither flush nor copy occurred */
|
||||
if (IS_ENABLED(CONFIG_VIDEO_COPY)) {
|
||||
ut_assertf(memcmp(priv->fb, priv->copy_fb, priv->fb_size),
|
||||
"Copy fb shouldn't match fb w/o VIDSYNC_FLUSH/COPY");
|
||||
}
|
||||
|
||||
/* video_manual_sync() with full flags - should perform full sync */
|
||||
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
|
||||
ut_assertok(video_check_copy_fb(uts, dev));
|
||||
|
||||
/* Disable manual-sync mode */
|
||||
video_set_manual_sync(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_video_manual_sync, UTF_SCAN_PDATA | UTF_SCAN_FDT);
|
||||
|
||||
/* Test that sync() receives the correct damage rectangle */
|
||||
static int dm_test_video_sync_damage(struct unit_test_state *uts)
|
||||
{
|
||||
struct video_bbox damage;
|
||||
struct udevice *dev, *con;
|
||||
struct video_priv *priv;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_VIDEO_DAMAGE))
|
||||
return -EAGAIN;
|
||||
|
||||
ut_assertok(select_vidconsole(uts, "vidconsole0"));
|
||||
ut_assertok(video_get_nologo(uts, &dev));
|
||||
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
|
||||
ut_assertok(vidconsole_select_font(con, "8x16", 0));
|
||||
priv = dev_get_uclass_priv(dev);
|
||||
|
||||
/* Use manual sync to prevent interference with the test */
|
||||
video_set_manual_sync(true);
|
||||
|
||||
/* Clear the display - this creates a full-screen damage and syncs */
|
||||
video_clear(dev);
|
||||
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
|
||||
ut_asserteq(46, video_compress_fb(uts, dev, false));
|
||||
|
||||
/* Get the damage rectangle that was passed to sync() */
|
||||
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
|
||||
|
||||
/* Should be the full screen */
|
||||
ut_assert(video_bbox_valid(&damage));
|
||||
ut_asserteq(0, damage.x0);
|
||||
ut_asserteq(0, damage.y0);
|
||||
ut_asserteq(priv->xsize, damage.x1);
|
||||
ut_asserteq(priv->ysize, damage.y1);
|
||||
|
||||
/* Sync again with no changes - should have empty damage */
|
||||
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
|
||||
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
|
||||
ut_assert(!video_bbox_valid(&damage));
|
||||
|
||||
/* Check that priv->damage is still reset to empty */
|
||||
ut_assert(!video_bbox_valid(&priv->damage));
|
||||
|
||||
/* Write a small piece of text at a specific position */
|
||||
vidconsole_putc_xy(con, VID_TO_POS(400), 67, 'T');
|
||||
|
||||
/* Check priv->damage before sync - should have text damage */
|
||||
ut_assert(video_bbox_valid(&priv->damage));
|
||||
ut_asserteq(400, priv->damage.x0);
|
||||
ut_asserteq(67, priv->damage.y0);
|
||||
ut_asserteq(400 + 8, priv->damage.x1); /* 8x16 font */
|
||||
ut_asserteq(67 + 16, priv->damage.y1);
|
||||
|
||||
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
|
||||
|
||||
/* Get the damage rectangle that was passed to sync() */
|
||||
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
|
||||
|
||||
/* The damage should cover just the character */
|
||||
ut_assert(video_bbox_valid(&damage));
|
||||
ut_asserteq(400, damage.x0);
|
||||
ut_asserteq(67, damage.y0);
|
||||
ut_asserteq(400 + 8, damage.x1);
|
||||
ut_asserteq(67 + 16, damage.y1);
|
||||
|
||||
/* Check priv->damage after sync - should be reset to empty */
|
||||
ut_assert(!video_bbox_valid(&priv->damage));
|
||||
|
||||
/* Draw a filled box at a different position */
|
||||
ut_assertok(video_draw_box(dev, 200, 300, 250, 340, 1, 0xffffff, true));
|
||||
|
||||
/* Check priv->damage before sync - should have box damage */
|
||||
ut_assert(video_bbox_valid(&priv->damage));
|
||||
ut_asserteq(200, priv->damage.x0);
|
||||
ut_asserteq(300, priv->damage.y0);
|
||||
ut_asserteq(250, priv->damage.x1);
|
||||
ut_asserteq(340, priv->damage.y1);
|
||||
|
||||
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
|
||||
|
||||
/* Get the damage rectangle for the box */
|
||||
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
|
||||
|
||||
/* The damage should cover the box area */
|
||||
ut_assert(video_bbox_valid(&damage));
|
||||
ut_asserteq(200, damage.x0);
|
||||
ut_asserteq(300, damage.y0);
|
||||
ut_asserteq(250, damage.x1);
|
||||
ut_asserteq(340, damage.y1);
|
||||
|
||||
/* Check priv->damage after sync - should be reset to inverted/empty */
|
||||
ut_assert(!video_bbox_valid(&priv->damage));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_video_sync_damage, UTF_SCAN_PDATA | UTF_SCAN_FDT);
|
||||
|
||||
Reference in New Issue
Block a user