video: Add support for copying to a hardware framebuffer

Some architectures use a cached framebuffer and flush the cache as needed
so that changes are visible. This is supported by U-Boot.

However x86 uses an uncached framebuffer with a 'write-combining' feature
to speed up writes.  Reads are permitted but they are extremely expensive.

Unfortunately, reading from the frame buffer is quite common, e.g. to
scroll it. This makes scrolling very slow.

Add a new feature which supports copying modified parts of the frame
buffer to the uncached hardware buffer. This speeds up scrolling by at
least 10x on x86 so the extra complexity cost seems worth it.

As a starting point, add the Kconfig, update the video structures to keep
track of the buffer and add a function to do the copy.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Anatolij Gustschin <agust@denx.de>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
Simon Glass 2020-07-02 21:12:20 -06:00 committed by Bin Meng
parent 5a6cea37c6
commit 9beac5daf7
3 changed files with 95 additions and 0 deletions

View file

@ -22,6 +22,18 @@ config BACKLIGHT
This provides backlight uclass driver that enables basic panel
backlight support.
config VIDEO_COPY
bool "Enable copying the frame buffer to a hardware copy"
depends on DM_VIDEO
help
On some machines (e.g. x86), reading from the frame buffer is very
slow because it is uncached. To improve performance, this feature
allows the frame buffer to be kept in cached memory (allocated by
U-Boot) and then copied to the hardware frame-buffer as needed.
To use this, your video driver must set @copy_base in
struct video_uc_platdata.
config BACKLIGHT_PWM
bool "Generic PWM based Backlight Driver"
depends on BACKLIGHT && DM_PWM

View file

@ -4,6 +4,7 @@
*/
#include <common.h>
#include <console.h>
#include <cpu_func.h>
#include <dm.h>
#include <log.h>
@ -201,6 +202,59 @@ int video_get_ysize(struct udevice *dev)
return priv->ysize;
}
#ifdef CONFIG_VIDEO_COPY
int video_sync_copy(struct udevice *dev, void *from, void *to)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
if (priv->copy_fb) {
long offset, size;
/* Find the offset of the first byte to copy */
if ((ulong)to > (ulong)from) {
size = to - from;
offset = from - priv->fb;
} else {
size = from - to;
offset = to - priv->fb;
}
/*
* Allow a bit of leeway for valid requests somewhere near the
* frame buffer
*/
if (offset < -priv->fb_size || offset > 2 * priv->fb_size) {
#ifdef DEBUG
char str[80];
snprintf(str, sizeof(str),
"[sync_copy fb=%p, from=%p, to=%p, offset=%lx]",
priv->fb, from, to, offset);
console_puts_select_stderr(true, str);
#endif
return -EFAULT;
}
/*
* Silently crop the memcpy. This allows callers to avoid doing
* this themselves. It is common for the end pointer to go a
* few lines after the end of the frame buffer, since most of
* the update algorithms terminate a line after their last write
*/
if (offset + size > priv->fb_size) {
size = priv->fb_size - offset;
} else if (offset < 0) {
size += offset;
offset = 0;
}
memcpy(priv->copy_fb + offset, priv->fb + offset, size);
}
return 0;
}
#endif
/* Set up the colour map */
static int video_pre_probe(struct udevice *dev)
{

View file

@ -30,11 +30,14 @@ struct udevice;
* buffer should start on. If 0, 1MB is assumed
* @size: Frame-buffer size, in bytes
* @base: Base address of frame buffer, 0 if not yet known
* @copy_base: Base address of a hardware copy of the frame buffer. See
* CONFIG_VIDEO_COPY.
*/
struct video_uc_platdata {
uint align;
uint size;
ulong base;
ulong copy_base;
};
enum video_polarity {
@ -75,6 +78,8 @@ enum video_log2_bpp {
* @font_size: Font size in pixels (0 to use a default value)
* @fb: Frame buffer
* @fb_size: Frame buffer size
* @copy_fb: Copy of the frame buffer to keep up to date; see struct
* video_uc_platdata
* @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
@ -101,6 +106,7 @@ struct video_priv {
*/
void *fb;
int fb_size;
void *copy_fb;
int line_length;
u32 colour_fg;
u32 colour_bg;
@ -214,6 +220,29 @@ void video_set_flush_dcache(struct udevice *dev, bool flush);
*/
void video_set_default_colors(struct udevice *dev, bool invert);
#ifdef CONFIG_VIDEO_COPY
/**
* vidconsole_sync_copy() - Sync back to the copy framebuffer
*
* This ensures that the copy framebuffer has the same data as the framebuffer
* for a particular region. It should be called after the framebuffer is updated
*
* @from and @to can be in either order. The region between them is synced.
*
* @dev: Vidconsole device being updated
* @from: Start/end address within the framebuffer (->fb)
* @to: Other address within the frame buffer
* @return 0 if OK, -EFAULT if the start address is before the start of the
* frame buffer start
*/
int video_sync_copy(struct udevice *dev, void *from, void *to);
#else
static inline int video_sync_copy(struct udevice *dev, void *from, void *to)
{
return 0;
}
#endif
#endif /* CONFIG_DM_VIDEO */
#ifndef CONFIG_DM_VIDEO