summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Kconfig54
-rw-r--r--drivers/video/Makefile9
-rw-r--r--drivers/video/console_normal.c27
-rw-r--r--drivers/video/console_rotate.c94
-rw-r--r--drivers/video/console_truetype.c43
-rw-r--r--drivers/video/exynos/Kconfig1
-rw-r--r--drivers/video/imx/Kconfig10
-rw-r--r--drivers/video/imx/Makefile4
-rw-r--r--drivers/video/imx/lcdif.c314
-rw-r--r--drivers/video/imx/ldb.c251
-rw-r--r--drivers/video/lm3532_backlight.c380
-rw-r--r--drivers/video/meson/Kconfig1
-rw-r--r--drivers/video/mot-panel.c308
-rw-r--r--drivers/video/rockchip/Kconfig1
-rw-r--r--drivers/video/stm32/Kconfig1
-rw-r--r--drivers/video/tegra/Kconfig52
-rw-r--r--drivers/video/tegra/Makefile9
-rw-r--r--drivers/video/tegra/TODO5
-rw-r--r--drivers/video/tegra/dc-pwm-backlight.c (renamed from drivers/video/tegra20/tegra-pwm-backlight.c)2
-rw-r--r--drivers/video/tegra/dc.c (renamed from drivers/video/tegra20/tegra-dc.c)2
-rw-r--r--drivers/video/tegra/dc.h (renamed from drivers/video/tegra20/tegra-dc.h)0
-rw-r--r--drivers/video/tegra/dsi.c (renamed from drivers/video/tegra20/tegra-dsi.c)5
-rw-r--r--drivers/video/tegra/dsi.h (renamed from drivers/video/tegra20/tegra-dsi.h)0
-rw-r--r--drivers/video/tegra/hdmi.c (renamed from drivers/video/tegra20/tegra-hdmi.c)4
-rw-r--r--drivers/video/tegra/hdmi.h (renamed from drivers/video/tegra20/tegra-hdmi.h)0
-rw-r--r--drivers/video/tegra/host1x.c (renamed from drivers/video/tegra20/tegra-host1x.c)0
-rw-r--r--drivers/video/tegra/mipi-phy.c (renamed from drivers/video/tegra20/mipi-phy.c)0
-rw-r--r--drivers/video/tegra/mipi-phy.h (renamed from drivers/video/tegra20/mipi-phy.h)0
-rw-r--r--drivers/video/tegra/mipi.c (renamed from drivers/video/tegra20/tegra-mipi.c)0
-rw-r--r--drivers/video/tegra/tegra124/Makefile (renamed from drivers/video/tegra124/Makefile)0
-rw-r--r--drivers/video/tegra/tegra124/display.c (renamed from drivers/video/tegra124/display.c)0
-rw-r--r--drivers/video/tegra/tegra124/displayport.h (renamed from drivers/video/tegra124/displayport.h)0
-rw-r--r--drivers/video/tegra/tegra124/dp.c (renamed from drivers/video/tegra124/dp.c)0
-rw-r--r--drivers/video/tegra/tegra124/sor.c (renamed from drivers/video/tegra124/sor.c)0
-rw-r--r--drivers/video/tegra/tegra124/sor.h (renamed from drivers/video/tegra124/sor.h)0
-rw-r--r--drivers/video/tegra20/Kconfig38
-rw-r--r--drivers/video/tegra20/Makefile7
-rw-r--r--drivers/video/tidss/Kconfig1
-rw-r--r--drivers/video/tidss/Makefile2
-rw-r--r--drivers/video/vidconsole-uclass.c16
-rw-r--r--drivers/video/video-uclass.c193
-rw-r--r--drivers/video/video_bmp.c7
42 files changed, 1608 insertions, 233 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index df607303616..80508fb24df 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -89,15 +89,33 @@ config VIDEO_PCI_DEFAULT_FB_SIZE
config VIDEO_COPY
bool "Enable copying the frame buffer to a hardware copy"
+ select VIDEO_DAMAGE
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.
+ It uses the VIDEO_DAMAGE feature to keep track of regions to copy
+ and will only copy actually touched regions.
To use this, your video driver must set @copy_base in
struct video_uc_plat.
+config VIDEO_DAMAGE
+ bool "Enable damage tracking of frame buffer regions"
+ help
+ On some machines (most ARM), the display frame buffer resides in
+ RAM. To make the display controller pick up screen updates, we
+ have to flush frame buffer contents from CPU caches into RAM which
+ can be a slow operation.
+
+ This feature adds damage tracking to collect information about regions
+ that received updates. When we want to sync, we then only flush
+ regions of the frame buffer that were modified before, speeding up
+ screen refreshes significantly.
+
+ It is also used by VIDEO_COPY to identify which regions changed.
+
config BACKLIGHT_PWM
bool "Generic PWM based Backlight Driver"
depends on BACKLIGHT && DM_PWM
@@ -495,6 +513,7 @@ config VIDEO_LCD_ANX9804
config ATMEL_LCD
bool "Atmel LCD panel support"
+ imply VIDEO_DAMAGE
depends on ARCH_AT91
config ATMEL_LCD_BGR555
@@ -504,6 +523,7 @@ config ATMEL_LCD_BGR555
config VIDEO_BCM2835
bool "Display support for BCM2835"
+ imply VIDEO_DAMAGE
help
The graphics processor already sets up the display so this driver
simply checks the resolution and then sets up the frame buffer with
@@ -529,6 +549,16 @@ config VIDEO_LCD_HIMAX_HX8394
Say Y here if you want to enable support for Himax HX8394
dsi 4dl panel.
+config VIDEO_LCD_MOT
+ tristate "Atrix 4G and Droid X2 540x960 DSI video mode panel"
+ depends on PANEL && BACKLIGHT
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for the LCD panel module for
+ Motorola Atrix 4G or Droid X2. Exact panel vendor and model are
+ unknown. The panel has a 540x960 resolution and uses 24 bit RGB per
+ pixel.
+
config VIDEO_LCD_ORISETECH_OTM8009A
bool "OTM8009A DSI LCD panel support"
select VIDEO_MIPI_DSI
@@ -685,6 +715,7 @@ source "drivers/video/meson/Kconfig"
config VIDEO_MVEBU
bool "Armada XP LCD controller"
+ imply VIDEO_DAMAGE
---help---
Support for the LCD controller integrated in the Marvell
Armada XP SoC.
@@ -725,9 +756,19 @@ config NXP_TDA19988
config ATMEL_HLCD
bool "Enable ATMEL video support using HLCDC"
+ imply VIDEO_DAMAGE
help
HLCDC supports video output to an attached LCD panel.
+config BACKLIGHT_LM3532
+ bool "Backlight Driver for LM3532"
+ depends on BACKLIGHT
+ select DM_I2C
+ help
+ Say Y to enable the backlight driver for National Semiconductor / TI
+ LM3532 Lighting Power chip. Only backlight functions is supported as
+ for now. Supported backlight level range is from 1 to 255.
+
config BACKLIGHT_LM3533
bool "Backlight Driver for LM3533"
depends on BACKLIGHT
@@ -809,22 +850,15 @@ source "drivers/video/stm32/Kconfig"
source "drivers/video/tidss/Kconfig"
-config VIDEO_TEGRA124
- bool "Enable video support on Tegra124"
- help
- Tegra124 supports many video output options including eDP and
- HDMI. At present only eDP is supported by U-Boot. This option
- enables this support which can be used on devices which
- have an eDP display connected.
-
source "drivers/video/bridge/Kconfig"
-source "drivers/video/tegra20/Kconfig"
+source "drivers/video/tegra/Kconfig"
source "drivers/video/imx/Kconfig"
config VIDEO_MXS
bool "Enable video support on i.MX28/i.MX6UL/i.MX7 SoCs"
+ imply VIDEO_DAMAGE
help
Enable framebuffer driver for i.MX28/i.MX6UL/i.MX7 processors
@@ -887,6 +921,7 @@ config VIDEO_DW_MIPI_DSI
config VIDEO_SIMPLE
bool "Simple display driver for preconfigured display"
+ imply VIDEO_DAMAGE
help
Enables a simple generic display driver which utilizes the
simple-framebuffer devicetree bindings.
@@ -905,6 +940,7 @@ config VIDEO_DT_SIMPLEFB
config VIDEO_MCDE_SIMPLE
bool "Simple driver for ST-Ericsson MCDE with preconfigured display"
+ imply VIDEO_DAMAGE
help
Enables a simple display driver for ST-Ericsson MCDE
(Multichannel Display Engine), which reads the configuration from
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 6073bc5234a..fbdb058647a 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -29,13 +29,13 @@ obj-$(CONFIG_$(PHASE_)BMP) += bmp.o
endif
+obj-$(CONFIG_BACKLIGHT_LM3532) += lm3532_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o
obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o
obj-${CONFIG_EXYNOS_FB} += exynos/
obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
obj-${CONFIG_VIDEO_STM32} += stm32/
-obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
-obj-${CONFIG_$(XPL_)VIDEO_TIDSS} += tidss/
+obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} += tidss/
obj-y += ti/
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
@@ -53,13 +53,14 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o
obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o
obj-$(CONFIG_VIDEO_EFI) += efi.o
-obj-$(CONFIG_VIDEO_IPUV3) += imx/
+obj-y += imx/
obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o
obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o
obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
obj-$(CONFIG_VIDEO_LCD_LG_LD070WX3) += lg-ld070wx3.o
+obj-$(CONFIG_VIDEO_LCD_MOT) += mot-panel.o
obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o
obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o
@@ -85,4 +86,4 @@ obj-$(CONFIG_VIDEO_ZYNQMP_DPSUB) += zynqmp/
obj-y += bridge/
obj-y += sunxi/
-obj-y += tegra20/
+obj-y += tegra/
diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c
index 6f4194a1814..07db613ac53 100644
--- a/drivers/video/console_normal.c
+++ b/drivers/video/console_normal.c
@@ -35,9 +35,11 @@ static int console_set_row(struct udevice *dev, uint row, int clr)
fill_pixel_and_goto_next(&dst, clr, pbytes, pbytes);
end = dst;
- ret = vidconsole_sync_copy(dev, line, end);
- if (ret)
- return ret;
+ video_damage(dev->parent,
+ 0,
+ fontdata->height * row,
+ vid_priv->xsize,
+ fontdata->height);
return 0;
}
@@ -51,14 +53,17 @@ static int console_move_rows(struct udevice *dev, uint rowdst,
void *dst;
void *src;
int size;
- int ret;
dst = vid_priv->fb + rowdst * fontdata->height * vid_priv->line_length;
src = vid_priv->fb + rowsrc * fontdata->height * vid_priv->line_length;
size = fontdata->height * vid_priv->line_length * count;
- ret = vidconsole_memmove(dev, dst, src, size);
- if (ret)
- return ret;
+ memmove(dst, src, size);
+
+ video_damage(dev->parent,
+ 0,
+ fontdata->height * rowdst,
+ vid_priv->xsize,
+ fontdata->height * count);
return 0;
}
@@ -91,9 +96,11 @@ static int console_putc_xy(struct udevice *dev, uint x_frac, uint y, int cp)
if (ret)
return ret;
- ret = vidconsole_sync_copy(dev, start, line);
- if (ret)
- return ret;
+ video_damage(dev->parent,
+ x,
+ y,
+ fontdata->width,
+ fontdata->height);
return VID_TO_POS(fontdata->width);
}
diff --git a/drivers/video/console_rotate.c b/drivers/video/console_rotate.c
index a3f8c6352f8..886b25dcfaf 100644
--- a/drivers/video/console_rotate.c
+++ b/drivers/video/console_rotate.c
@@ -21,7 +21,6 @@ static int console_set_row_1(struct udevice *dev, uint row, int clr)
int pbytes = VNBYTES(vid_priv->bpix);
void *start, *dst, *line;
int i, j;
- int ret;
start = vid_priv->fb + vid_priv->line_length -
(row + 1) * fontdata->height * pbytes;
@@ -32,9 +31,12 @@ static int console_set_row_1(struct udevice *dev, uint row, int clr)
fill_pixel_and_goto_next(&dst, clr, pbytes, pbytes);
line += vid_priv->line_length;
}
- ret = vidconsole_sync_copy(dev, start, line);
- if (ret)
- return ret;
+
+ video_damage(dev->parent,
+ vid_priv->xsize - ((row + 1) * fontdata->height),
+ 0,
+ fontdata->height,
+ vid_priv->ysize);
return 0;
}
@@ -48,7 +50,7 @@ static int console_move_rows_1(struct udevice *dev, uint rowdst, uint rowsrc,
int pbytes = VNBYTES(vid_priv->bpix);
void *dst;
void *src;
- int j, ret;
+ int j;
dst = vid_priv->fb + vid_priv->line_length -
(rowdst + count) * fontdata->height * pbytes;
@@ -56,14 +58,17 @@ static int console_move_rows_1(struct udevice *dev, uint rowdst, uint rowsrc,
(rowsrc + count) * fontdata->height * pbytes;
for (j = 0; j < vid_priv->ysize; j++) {
- ret = vidconsole_memmove(dev, dst, src,
- fontdata->height * pbytes * count);
- if (ret)
- return ret;
+ memmove(dst, src, fontdata->height * pbytes * count);
src += vid_priv->line_length;
dst += vid_priv->line_length;
}
+ video_damage(dev->parent,
+ vid_priv->xsize - ((rowdst + count) * fontdata->height),
+ 0,
+ count * fontdata->height,
+ vid_priv->ysize);
+
return 0;
}
@@ -93,9 +98,11 @@ static int console_putc_xy_1(struct udevice *dev, uint x_frac, uint y, int cp)
return ret;
/* We draw backwards from 'start, so account for the first line */
- ret = vidconsole_sync_copy(dev, start - vid_priv->line_length, line);
- if (ret)
- return ret;
+ video_damage(dev->parent,
+ vid_priv->xsize - y - fontdata->height,
+ linenum - 1,
+ fontdata->height,
+ fontdata->width);
return VID_TO_POS(fontdata->width);
}
@@ -107,7 +114,7 @@ static int console_set_row_2(struct udevice *dev, uint row, int clr)
struct video_fontdata *fontdata = priv->fontdata;
void *start, *line, *dst, *end;
int pixels = fontdata->height * vid_priv->xsize;
- int i, ret;
+ int i;
int pbytes = VNBYTES(vid_priv->bpix);
start = vid_priv->fb + vid_priv->ysize * vid_priv->line_length -
@@ -117,9 +124,12 @@ static int console_set_row_2(struct udevice *dev, uint row, int clr)
for (i = 0; i < pixels; i++)
fill_pixel_and_goto_next(&dst, clr, pbytes, pbytes);
end = dst;
- ret = vidconsole_sync_copy(dev, start, end);
- if (ret)
- return ret;
+
+ video_damage(dev->parent,
+ 0,
+ vid_priv->ysize - (row + 1) * fontdata->height,
+ vid_priv->xsize,
+ fontdata->height);
return 0;
}
@@ -139,8 +149,13 @@ static int console_move_rows_2(struct udevice *dev, uint rowdst, uint rowsrc,
vid_priv->line_length;
src = end - (rowsrc + count) * fontdata->height *
vid_priv->line_length;
- vidconsole_memmove(dev, dst, src,
- fontdata->height * vid_priv->line_length * count);
+ memmove(dst, src, fontdata->height * vid_priv->line_length * count);
+
+ video_damage(dev->parent,
+ 0,
+ vid_priv->ysize - (rowdst + count) * fontdata->height,
+ vid_priv->xsize,
+ count * fontdata->height);
return 0;
}
@@ -170,10 +185,11 @@ static int console_putc_xy_2(struct udevice *dev, uint x_frac, uint y, int cp)
if (ret)
return ret;
- /* Add 4 bytes to allow for the first pixel writen */
- ret = vidconsole_sync_copy(dev, start + 4, line);
- if (ret)
- return ret;
+ video_damage(dev->parent,
+ x - fontdata->width + 1,
+ linenum - fontdata->height + 1,
+ fontdata->width,
+ fontdata->height);
return VID_TO_POS(fontdata->width);
}
@@ -185,7 +201,7 @@ static int console_set_row_3(struct udevice *dev, uint row, int clr)
struct video_fontdata *fontdata = priv->fontdata;
int pbytes = VNBYTES(vid_priv->bpix);
void *start, *dst, *line;
- int i, j, ret;
+ int i, j;
start = vid_priv->fb + row * fontdata->height * pbytes;
line = start;
@@ -195,9 +211,12 @@ static int console_set_row_3(struct udevice *dev, uint row, int clr)
fill_pixel_and_goto_next(&dst, clr, pbytes, pbytes);
line += vid_priv->line_length;
}
- ret = vidconsole_sync_copy(dev, start, line);
- if (ret)
- return ret;
+
+ video_damage(dev->parent,
+ row * fontdata->height,
+ 0,
+ fontdata->height,
+ vid_priv->ysize);
return 0;
}
@@ -211,20 +230,23 @@ static int console_move_rows_3(struct udevice *dev, uint rowdst, uint rowsrc,
int pbytes = VNBYTES(vid_priv->bpix);
void *dst;
void *src;
- int j, ret;
+ int j;
dst = vid_priv->fb + rowdst * fontdata->height * pbytes;
src = vid_priv->fb + rowsrc * fontdata->height * pbytes;
for (j = 0; j < vid_priv->ysize; j++) {
- ret = vidconsole_memmove(dev, dst, src,
- fontdata->height * pbytes * count);
- if (ret)
- return ret;
+ memmove(dst, src, fontdata->height * pbytes * count);
src += vid_priv->line_length;
dst += vid_priv->line_length;
}
+ video_damage(dev->parent,
+ rowdst * fontdata->height,
+ 0,
+ count * fontdata->height,
+ vid_priv->ysize);
+
return 0;
}
@@ -252,10 +274,12 @@ static int console_putc_xy_3(struct udevice *dev, uint x_frac, uint y, int cp)
ret = fill_char_horizontally(pfont, &line, vid_priv, fontdata, NORMAL_DIRECTION);
if (ret)
return ret;
- /* Add a line to allow for the first pixels writen */
- ret = vidconsole_sync_copy(dev, start + vid_priv->line_length, line);
- if (ret)
- return ret;
+
+ video_damage(dev->parent,
+ y,
+ linenum - fontdata->width + 1,
+ fontdata->height,
+ fontdata->width);
return VID_TO_POS(fontdata->width);
}
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c
index 17a29817664..980baee83cf 100644
--- a/drivers/video/console_truetype.c
+++ b/drivers/video/console_truetype.c
@@ -190,10 +190,10 @@ struct console_tt_store {
static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_tt_priv *priv = dev_get_priv(dev);
struct console_tt_metrics *met = priv->cur_met;
void *end, *line;
- int ret;
line = vid_priv->fb + row * met->font_size * vid_priv->line_length;
end = line + met->font_size * vid_priv->line_length;
@@ -229,9 +229,12 @@ static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
default:
return -ENOSYS;
}
- ret = vidconsole_sync_copy(dev, line, end);
- if (ret)
- return ret;
+
+ video_damage(dev->parent,
+ 0,
+ vc_priv->y_charsize * row,
+ vid_priv->xsize,
+ vc_priv->y_charsize);
return 0;
}
@@ -240,24 +243,28 @@ static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
uint rowsrc, uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_tt_priv *priv = dev_get_priv(dev);
struct console_tt_metrics *met = priv->cur_met;
void *dst;
void *src;
- int i, diff, ret;
+ int i, diff;
dst = vid_priv->fb + rowdst * met->font_size * vid_priv->line_length;
src = vid_priv->fb + rowsrc * met->font_size * vid_priv->line_length;
- ret = vidconsole_memmove(dev, dst, src, met->font_size *
- vid_priv->line_length * count);
- if (ret)
- return ret;
+ memmove(dst, src, met->font_size * vid_priv->line_length * count);
/* Scroll up our position history */
diff = (rowsrc - rowdst) * met->font_size;
for (i = 0; i < priv->pos_ptr; i++)
priv->pos[i].ypos -= diff;
+ video_damage(dev->parent,
+ 0,
+ vc_priv->y_charsize * rowdst,
+ vid_priv->xsize,
+ vc_priv->y_charsize * count);
+
return 0;
}
@@ -278,7 +285,7 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
u8 *bits, *data;
int advance;
void *start, *end, *line;
- int row, ret;
+ int row;
/* First get some basic metrics about this character */
stbtt_GetCodepointHMetrics(font, cp, &advance, &lsb);
@@ -418,9 +425,13 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
line += vid_priv->line_length;
}
- ret = vidconsole_sync_copy(dev, start, line);
- if (ret)
- return ret;
+
+ video_damage(dev->parent,
+ VID_TO_PIXEL(x) + xoff,
+ y + met->baseline + yoff,
+ width,
+ height);
+
free(data);
return width_frac;
@@ -851,7 +862,6 @@ static int truetype_set_cursor_visible(struct udevice *dev, bool visible,
uint row, width, height, xoff;
void *start, *line;
uint out, val;
- int ret;
if (xpl_phase() <= PHASE_SPL)
return -ENOSYS;
@@ -941,9 +951,8 @@ static int truetype_set_cursor_visible(struct udevice *dev, bool visible,
line += vid_priv->line_length;
}
- ret = vidconsole_sync_copy(dev, start, line);
- if (ret)
- return ret;
+
+ video_damage(dev->parent, x, y, width, height);
return video_sync(vid, true);
}
diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig
index 599d19d5ecc..a2cf752aac0 100644
--- a/drivers/video/exynos/Kconfig
+++ b/drivers/video/exynos/Kconfig
@@ -12,6 +12,7 @@ config EXYNOS_DP
config EXYNOS_FB
bool "Exynos FIMD support"
+ imply VIDEO_DAMAGE
config EXYNOS_MIPI_DSIM
bool "Exynos MIPI DSI support"
diff --git a/drivers/video/imx/Kconfig b/drivers/video/imx/Kconfig
index 34e8b640595..4a1927c66d7 100644
--- a/drivers/video/imx/Kconfig
+++ b/drivers/video/imx/Kconfig
@@ -2,6 +2,7 @@
config VIDEO_IPUV3
bool "i.MX IPUv3 Core video support"
depends on VIDEO && (MX5 || MX6)
+ imply VIDEO_DAMAGE
help
This enables framebuffer driver for i.MX processors working
on the IPUv3(Image Processing Unit) internal graphic processor.
@@ -13,3 +14,12 @@ config IMX_VIDEO_SKIP
config IMX_HDMI
bool "Enable HDMI support in IPUv3"
depends on VIDEO_IPUV3
+
+config IMX_LDB
+ bool "Freescale i.MX8MP LDB bridge"
+ depends on VIDEO_BRIDGE
+ help
+ Support for i.MX8MP DPI-to-LVDS on-SoC encoder.
+
+config IMX_LCDIF
+ bool "i.MX LCDIFv3 LCD controller"
diff --git a/drivers/video/imx/Makefile b/drivers/video/imx/Makefile
index 179ea651fe8..1edf5a6bdf0 100644
--- a/drivers/video/imx/Makefile
+++ b/drivers/video/imx/Makefile
@@ -3,4 +3,6 @@
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-obj-y += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o
+obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o
+obj-$(CONFIG_IMX_LDB) += ldb.o
+obj-$(CONFIG_IMX_LCDIF) += lcdif.o
diff --git a/drivers/video/imx/lcdif.c b/drivers/video/imx/lcdif.c
new file mode 100644
index 00000000000..9f4fc7f5152
--- /dev/null
+++ b/drivers/video/imx/lcdif.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * i.MX8 LCD interface driver inspired from the Linux driver
+ * Copyright 2019 NXP
+ * Copyright 2024 Bootlin
+ * Adapted by Miquel Raynal <miquel.raynal@bootlin.com>
+ */
+
+#include <asm/io.h>
+#include <asm/mach-imx/dma.h>
+#include <clk.h>
+#include <dm.h>
+#include <panel.h>
+#include <power-domain.h>
+#include <video.h>
+#include <video_bridge.h>
+#include <linux/delay.h>
+
+#include "../videomodes.h"
+
+#define LCDIFV3_CTRL 0x0
+#define LCDIFV3_CTRL_SET 0x4
+#define LCDIFV3_CTRL_CLR 0x8
+#define CTRL_INV_HS BIT(0)
+#define CTRL_INV_VS BIT(1)
+#define CTRL_INV_DE BIT(2)
+#define CTRL_INV_PXCK BIT(3)
+#define CTRL_CLK_GATE BIT(30)
+#define CTRL_SW_RESET BIT(31)
+
+#define LCDIFV3_DISP_PARA 0x10
+#define DISP_PARA_DISP_MODE_NORMAL 0
+#define DISP_PARA_LINE_PATTERN_RGB_YUV 0
+#define DISP_PARA_DISP_ON BIT(31)
+
+#define LCDIFV3_DISP_SIZE 0x14
+#define DISP_SIZE_DELTA_X(x) ((x) & 0xffff)
+#define DISP_SIZE_DELTA_Y(x) ((x) << 16)
+
+#define LCDIFV3_HSYN_PARA 0x18
+#define HSYN_PARA_FP_H(x) ((x) & 0xffff)
+#define HSYN_PARA_BP_H(x) ((x) << 16)
+
+#define LCDIFV3_VSYN_PARA 0x1C
+#define VSYN_PARA_FP_V(x) ((x) & 0xffff)
+#define VSYN_PARA_BP_V(x) ((x) << 16)
+
+#define LCDIFV3_VSYN_HSYN_WIDTH 0x20
+#define VSYN_HSYN_PW_H(x) ((x) & 0xffff)
+#define VSYN_HSYN_PW_V(x) ((x) << 16)
+
+#define LCDIFV3_CTRLDESCL0_1 0x200
+#define CTRLDESCL0_1_WIDTH(x) ((x) & 0xffff)
+#define CTRLDESCL0_1_HEIGHT(x) ((x) << 16)
+
+#define LCDIFV3_CTRLDESCL0_3 0x208
+#define CTRLDESCL0_3_PITCH(x) ((x) & 0xFFFF)
+
+#define LCDIFV3_CTRLDESCL_LOW0_4 0x20C
+#define LCDIFV3_CTRLDESCL_HIGH0_4 0x210
+
+#define LCDIFV3_CTRLDESCL0_5 0x214
+#define CTRLDESCL0_5_YUV_FORMAT(x) (((x) & 0x3) << 14)
+#define CTRLDESCL0_5_BPP(x) (((x) & 0xf) << 24)
+#define BPP32_ARGB8888 0x9
+#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30)
+#define CTRLDESCL0_5_EN BIT(31)
+
+struct lcdifv3_priv {
+ void __iomem *base;
+ struct clk pix_clk;
+ struct power_domain pd;
+ struct udevice *panel;
+ struct udevice *bridge;
+};
+
+static void lcdifv3_set_mode(struct lcdifv3_priv *priv,
+ struct display_timing *timings)
+{
+ u32 reg;
+
+ writel(DISP_SIZE_DELTA_X(timings->hactive.typ) |
+ DISP_SIZE_DELTA_Y(timings->vactive.typ),
+ priv->base + LCDIFV3_DISP_SIZE);
+
+ writel(HSYN_PARA_FP_H(timings->hfront_porch.typ) |
+ HSYN_PARA_BP_H(timings->hback_porch.typ),
+ priv->base + LCDIFV3_HSYN_PARA);
+
+ writel(VSYN_PARA_BP_V(timings->vback_porch.typ) |
+ VSYN_PARA_FP_V(timings->vfront_porch.typ),
+ priv->base + LCDIFV3_VSYN_PARA);
+
+ writel(VSYN_HSYN_PW_H(timings->hsync_len.typ) |
+ VSYN_HSYN_PW_V(timings->vsync_len.typ),
+ priv->base + LCDIFV3_VSYN_HSYN_WIDTH);
+
+ writel(CTRLDESCL0_1_WIDTH(timings->hactive.typ) |
+ CTRLDESCL0_1_HEIGHT(timings->vactive.typ),
+ priv->base + LCDIFV3_CTRLDESCL0_1);
+
+ if (timings->flags & DISPLAY_FLAGS_HSYNC_LOW)
+ writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_SET);
+ else
+ writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_CLR);
+
+ if (timings->flags & DISPLAY_FLAGS_VSYNC_LOW)
+ writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_SET);
+ else
+ writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_CLR);
+
+ if (timings->flags & DISPLAY_FLAGS_DE_LOW)
+ writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_SET);
+ else
+ writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_CLR);
+
+ if (timings->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+ writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_SET);
+ else
+ writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_CLR);
+
+ writel(0, priv->base + LCDIFV3_DISP_PARA);
+
+ reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5);
+ reg &= ~(CTRLDESCL0_5_BPP(0xf) | CTRLDESCL0_5_YUV_FORMAT(0x3));
+ reg |= CTRLDESCL0_5_BPP(BPP32_ARGB8888);
+ writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5);
+}
+
+static void lcdifv3_enable_controller(struct lcdifv3_priv *priv)
+{
+ u32 reg;
+
+ reg = readl(priv->base + LCDIFV3_DISP_PARA);
+ reg |= DISP_PARA_DISP_ON;
+ writel(reg, priv->base + LCDIFV3_DISP_PARA);
+
+ reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5);
+ reg |= CTRLDESCL0_5_EN;
+ writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5);
+}
+
+static int lcdifv3_video_sync(struct udevice *dev)
+{
+ struct lcdifv3_priv *priv = dev_get_priv(dev);
+ u32 reg;
+
+ reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5);
+ reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
+ writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5);
+
+ return 0;
+}
+
+static void lcdifv3_init(struct udevice *dev, struct display_timing *timings)
+{
+ struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+ struct lcdifv3_priv *priv = dev_get_priv(dev);
+
+ clk_set_rate(&priv->pix_clk, timings->pixelclock.typ);
+
+ writel(CTRL_SW_RESET | CTRL_CLK_GATE, priv->base + LCDIFV3_CTRL_CLR);
+ udelay(10);
+
+ lcdifv3_set_mode(priv, timings);
+
+ writel(plat->base & 0xFFFFFFFF, priv->base + LCDIFV3_CTRLDESCL_LOW0_4);
+ writel(plat->base >> 32, priv->base + LCDIFV3_CTRLDESCL_HIGH0_4);
+
+ writel(CTRLDESCL0_3_PITCH(timings->hactive.typ * 4), /* 32bpp */
+ priv->base + LCDIFV3_CTRLDESCL0_3);
+
+ lcdifv3_enable_controller(priv);
+}
+
+static int lcdifv3_video_probe(struct udevice *dev)
+{
+ struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct lcdifv3_priv *priv = dev_get_priv(dev);
+ struct clk axi_clk, disp_axi_clk;
+ struct display_timing timings;
+ u32 fb_start, fb_end;
+ int ret;
+
+ ret = power_domain_get(dev, &priv->pd);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(dev, "pix", &priv->pix_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(dev, "axi", &axi_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(dev, "disp_axi", &disp_axi_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = power_domain_on(&priv->pd);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&priv->pix_clk);
+ if (ret)
+ goto dis_pd;
+
+ ret = clk_enable(&axi_clk);
+ if (ret)
+ goto dis_pix_clk;
+
+ ret = clk_enable(&disp_axi_clk);
+ if (ret)
+ goto dis_axi_clk;
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base) {
+ ret = -EINVAL;
+ goto dis_clks;
+ }
+
+ /* Attach bridge */
+ ret = uclass_get_device_by_endpoint(UCLASS_VIDEO_BRIDGE, dev,
+ -1, -1, &priv->bridge);
+ if (ret)
+ goto dis_clks;
+
+ ret = video_bridge_attach(priv->bridge);
+ if (ret)
+ goto dis_clks;
+
+ ret = video_bridge_set_backlight(priv->bridge, 80);
+ if (ret)
+ goto dis_clks;
+
+ /* Attach panels */
+ ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge,
+ 1, -1, &priv->panel);
+ if (ret) {
+ ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge,
+ 2, -1, &priv->panel);
+ if (ret)
+ goto dis_clks;
+ }
+
+ ret = panel_get_display_timing(priv->panel, &timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(priv->panel),
+ 0, &timings);
+ if (ret) {
+ printf("Cannot decode panel timings (%d)\n", ret);
+ goto dis_clks;
+ }
+ }
+
+ lcdifv3_init(dev, &timings);
+
+ /* Only support 32bpp for now */
+ uc_priv->bpix = VIDEO_BPP32;
+ uc_priv->xsize = timings.hactive.typ;
+ uc_priv->ysize = timings.vactive.typ;
+
+ /* Enable dcache for the frame buffer */
+ fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
+ fb_end = ALIGN(plat->base + plat->size, 1 << MMU_SECTION_SHIFT);
+ mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
+ DCACHE_WRITEBACK);
+ video_set_flush_dcache(dev, true);
+
+ return 0;
+
+dis_clks:
+ clk_disable(&disp_axi_clk);
+dis_axi_clk:
+ clk_disable(&axi_clk);
+dis_pix_clk:
+ clk_disable(&priv->pix_clk);
+dis_pd:
+ power_domain_off(&priv->pd);
+
+ return ret;
+}
+
+static int lcdifv3_video_bind(struct udevice *dev)
+{
+ struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ /* Max size supported by LCDIF */
+ plat->size = 1920 * 1080 * VNBYTES(VIDEO_BPP32);
+
+ return 0;
+}
+
+static const struct udevice_id lcdifv3_video_ids[] = {
+ { .compatible = "fsl,imx8mp-lcdif" },
+ { }
+};
+
+static struct video_ops lcdifv3_video_ops = {
+ .video_sync = lcdifv3_video_sync,
+};
+
+U_BOOT_DRIVER(lcdifv3_video) = {
+ .name = "lcdif",
+ .id = UCLASS_VIDEO,
+ .of_match = lcdifv3_video_ids,
+ .bind = lcdifv3_video_bind,
+ .ops = &lcdifv3_video_ops,
+ .probe = lcdifv3_video_probe,
+ .priv_auto = sizeof(struct lcdifv3_priv),
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
+};
diff --git a/drivers/video/imx/ldb.c b/drivers/video/imx/ldb.c
new file mode 100644
index 00000000000..e918341c0a3
--- /dev/null
+++ b/drivers/video/imx/ldb.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Derived work from:
+ * Philippe Cornu <philippe.cornu@st.com>
+ * Yannick Fertre <yannick.fertre@st.com>
+ * Adapted by Miquel Raynal <miquel.raynal@bootlin.com>
+ */
+
+#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE
+
+#include <clk.h>
+#include <dm.h>
+#include <log.h>
+#include <panel.h>
+#include <video_bridge.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#define LDB_CTRL_CH0_ENABLE BIT(0)
+#define LDB_CTRL_CH1_ENABLE BIT(2)
+#define LDB_CTRL_CH0_DATA_WIDTH BIT(5)
+#define LDB_CTRL_CH0_BIT_MAPPING BIT(6)
+#define LDB_CTRL_CH1_DATA_WIDTH BIT(7)
+#define LDB_CTRL_CH1_BIT_MAPPING BIT(8)
+#define LDB_CTRL_DI0_VSYNC_POLARITY BIT(9)
+#define LDB_CTRL_DI1_VSYNC_POLARITY BIT(10)
+
+#define LVDS_CTRL_CH0_EN BIT(0)
+#define LVDS_CTRL_CH1_EN BIT(1)
+#define LVDS_CTRL_VBG_EN BIT(2)
+#define LVDS_CTRL_PRE_EMPH_EN BIT(4)
+#define LVDS_CTRL_PRE_EMPH_ADJ(n) (((n) & 0x7) << 5)
+#define LVDS_CTRL_CC_ADJ(n) (((n) & 0x7) << 11)
+
+struct imx_ldb_priv {
+ struct clk ldb_clk;
+ void __iomem *ldb_ctrl;
+ void __iomem *lvds_ctrl;
+ struct udevice *lvds1;
+ struct udevice *lvds2;
+};
+
+static int imx_ldb_set_backlight(struct udevice *dev, int percent)
+{
+ struct imx_ldb_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (priv->lvds1) {
+ ret = panel_enable_backlight(priv->lvds1);
+ if (ret) {
+ debug("ldb: Cannot enable lvds1 backlight\n");
+ return ret;
+ }
+
+ ret = panel_set_backlight(priv->lvds1, percent);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->lvds2) {
+ ret = panel_enable_backlight(priv->lvds2);
+ if (ret) {
+ debug("ldb: Cannot enable lvds2 backlight\n");
+ return ret;
+ }
+
+ ret = panel_set_backlight(priv->lvds2, percent);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_ldb_of_to_plat(struct udevice *dev)
+{
+ struct imx_ldb_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 1, -1, &priv->lvds1);
+ uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 2, -1, &priv->lvds2);
+ if (!priv->lvds1 && !priv->lvds2) {
+ debug("ldb: No remote panel for '%s' (ret=%d)\n",
+ dev_read_name(dev), ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* The block has a mysterious x7 internal divisor (x3.5 in dual configuration) */
+#define IMX_LDB_INTERNAL_DIVISOR(x) (((x) * 70) / 10)
+#define IMX_LDB_INTERNAL_DIVISOR_DUAL(x) (((x) * 35) / 10)
+
+static ulong imx_ldb_input_rate(struct imx_ldb_priv *priv,
+ struct display_timing *timings)
+{
+ ulong target_rate = timings->pixelclock.typ;
+
+ if (priv->lvds1 && priv->lvds2)
+ return IMX_LDB_INTERNAL_DIVISOR_DUAL(target_rate);
+
+ return IMX_LDB_INTERNAL_DIVISOR(target_rate);
+}
+
+static int imx_ldb_attach(struct udevice *dev)
+{
+ struct imx_ldb_priv *priv = dev_get_priv(dev);
+ struct display_timing timings;
+ bool format_jeida = false;
+ bool format_24bpp = true;
+ u32 ldb_ctrl = 0, lvds_ctrl;
+ ulong ldb_rate;
+ int ret;
+
+ /* TODO: update the 24bpp/jeida booleans with proper checks when they
+ * will be supported.
+ */
+ if (priv->lvds1) {
+ ret = panel_get_display_timing(priv->lvds1, &timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds1),
+ 0, &timings);
+ if (ret) {
+ printf("Cannot decode lvds1 timings (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ldb_ctrl |= LDB_CTRL_CH0_ENABLE;
+ if (format_24bpp)
+ ldb_ctrl |= LDB_CTRL_CH0_DATA_WIDTH;
+ if (format_jeida)
+ ldb_ctrl |= LDB_CTRL_CH0_BIT_MAPPING;
+ if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ ldb_ctrl |= LDB_CTRL_DI0_VSYNC_POLARITY;
+ }
+
+ if (priv->lvds2) {
+ ret = panel_get_display_timing(priv->lvds2, &timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds2),
+ 0, &timings);
+ if (ret) {
+ printf("Cannot decode lvds2 timings (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ldb_ctrl |= LDB_CTRL_CH1_ENABLE;
+ if (format_24bpp)
+ ldb_ctrl |= LDB_CTRL_CH1_DATA_WIDTH;
+ if (format_jeida)
+ ldb_ctrl |= LDB_CTRL_CH1_BIT_MAPPING;
+ if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ ldb_ctrl |= LDB_CTRL_DI1_VSYNC_POLARITY;
+ }
+
+ /*
+ * Not all pixel clocks will work, as the final rate (after internal
+ * integer division) should be identical to the LCDIF clock, otherwise
+ * the rendering will appear resized/shimmering.
+ */
+ ldb_rate = imx_ldb_input_rate(priv, &timings);
+ clk_set_rate(&priv->ldb_clk, ldb_rate);
+
+ writel(ldb_ctrl, priv->ldb_ctrl);
+
+ lvds_ctrl = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN |
+ LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN;
+ writel(lvds_ctrl, priv->lvds_ctrl);
+
+ /* Wait for VBG to stabilize. */
+ udelay(15);
+
+ if (priv->lvds1)
+ lvds_ctrl |= LVDS_CTRL_CH0_EN;
+ if (priv->lvds2)
+ lvds_ctrl |= LVDS_CTRL_CH1_EN;
+
+ writel(lvds_ctrl, priv->lvds_ctrl);
+
+ return 0;
+}
+
+static int imx_ldb_probe(struct udevice *dev)
+{
+ struct imx_ldb_priv *priv = dev_get_priv(dev);
+ struct udevice *parent = dev_get_parent(dev);
+ fdt_addr_t parent_addr, child_addr;
+ int ret;
+
+ ret = clk_get_by_name(dev, "ldb", &priv->ldb_clk);
+ if (ret < 0)
+ return ret;
+
+ parent_addr = dev_read_addr(parent);
+ if (parent_addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ child_addr = dev_read_addr_name(dev, "ldb");
+ if (child_addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->ldb_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE);
+ if (!priv->ldb_ctrl)
+ return -EINVAL;
+
+ child_addr = dev_read_addr_name(dev, "lvds");
+ if (child_addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->lvds_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE);
+ if (!priv->lvds_ctrl)
+ return -EINVAL;
+
+ ret = clk_enable(&priv->ldb_clk);
+ if (ret)
+ return ret;
+
+ ret = video_bridge_set_active(dev, true);
+ if (ret)
+ goto dis_clk;
+
+ return 0;
+
+dis_clk:
+ clk_disable(&priv->ldb_clk);
+
+ return ret;
+}
+
+struct video_bridge_ops imx_ldb_ops = {
+ .attach = imx_ldb_attach,
+ .set_backlight = imx_ldb_set_backlight,
+};
+
+static const struct udevice_id imx_ldb_ids[] = {
+ { .compatible = "fsl,imx8mp-ldb"},
+ { }
+};
+
+U_BOOT_DRIVER(imx_ldb) = {
+ .name = "imx-lvds-display-bridge",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = imx_ldb_ids,
+ .probe = imx_ldb_probe,
+ .of_to_plat = imx_ldb_of_to_plat,
+ .ops = &imx_ldb_ops,
+ .priv_auto = sizeof(struct imx_ldb_priv),
+};
diff --git a/drivers/video/lm3532_backlight.c b/drivers/video/lm3532_backlight.c
new file mode 100644
index 00000000000..81b3b910196
--- /dev/null
+++ b/drivers/video/lm3532_backlight.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI LM3532 LED driver
+ *
+ * Copyright (c) 2019 Texas Instruments Incorporated
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
+
+#include <backlight.h>
+#include <dm.h>
+#include <dm/ofnode.h>
+#include <i2c.h>
+#include <log.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <asm/gpio.h>
+#include <power/regulator.h>
+
+#define LM3532_BL_MODE_MANUAL 0x00
+#define LM3532_BL_MODE_ALS 0x01
+
+#define LM3532_REG_OUTPUT_CFG 0x10
+#define LM3532_REG_STARTSHUT_RAMP 0x11
+#define LM3532_REG_RT_RAMP 0x12
+#define LM3532_REG_PWM_A_CFG 0x13
+#define LM3532_REG_PWM_B_CFG 0x14
+#define LM3532_REG_PWM_C_CFG 0x15
+#define LM3532_REG_ZONE_CFG_A 0x16
+#define LM3532_REG_CTRL_A_FS_CURR 0x17
+#define LM3532_REG_ZONE_CFG_B 0x18
+#define LM3532_REG_CTRL_B_FS_CURR 0x19
+#define LM3532_REG_ZONE_CFG_C 0x1a
+#define LM3532_REG_CTRL_C_FS_CURR 0x1b
+#define LM3532_REG_ENABLE 0x1d
+#define LM3532_ALS_CONFIG 0x23
+#define LM3532_REG_ZN_0_HI 0x60
+#define LM3532_REG_ZN_0_LO 0x61
+#define LM3532_REG_ZN_1_HI 0x62
+#define LM3532_REG_ZN_1_LO 0x63
+#define LM3532_REG_ZN_2_HI 0x64
+#define LM3532_REG_ZN_2_LO 0x65
+#define LM3532_REG_ZN_3_HI 0x66
+#define LM3532_REG_ZN_3_LO 0x67
+#define LM3532_REG_ZONE_TRGT_A 0x70
+#define LM3532_REG_ZONE_TRGT_B 0x75
+#define LM3532_REG_ZONE_TRGT_C 0x7a
+#define LM3532_REG_MAX 0x7e
+
+/* Control Enable */
+#define LM3532_CTRL_A_ENABLE BIT(0)
+#define LM3532_CTRL_B_ENABLE BIT(1)
+#define LM3532_CTRL_C_ENABLE BIT(2)
+
+/* PWM Zone Control */
+#define LM3532_PWM_ZONE_MASK 0x7c
+#define LM3532_PWM_ZONE_0_EN BIT(2)
+#define LM3532_PWM_ZONE_1_EN BIT(3)
+#define LM3532_PWM_ZONE_2_EN BIT(4)
+#define LM3532_PWM_ZONE_3_EN BIT(5)
+#define LM3532_PWM_ZONE_4_EN BIT(6)
+
+/* Brightness Configuration */
+#define LM3532_I2C_CTRL BIT(0)
+#define LM3532_ALS_CTRL 0
+#define LM3532_LINEAR_MAP BIT(1)
+#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4))
+#define LM3532_ZONE_0 0
+#define LM3532_ZONE_1 BIT(2)
+#define LM3532_ZONE_2 BIT(3)
+#define LM3532_ZONE_3 (BIT(2) | BIT(3))
+#define LM3532_ZONE_4 BIT(4)
+
+#define LM3532_ENABLE_ALS BIT(3)
+#define LM3532_ALS_SEL_SHIFT 6
+
+/* Zone Boundary Register */
+#define LM3532_ALS_WINDOW_mV 2000
+#define LM3532_ALS_ZB_MAX 4
+#define LM3532_ALS_OFFSET_mV 2
+
+#define LM3532_CONTROL_A 0
+#define LM3532_CONTROL_B 1
+#define LM3532_CONTROL_C 2
+#define LM3532_MAX_CONTROL_BANKS 3
+#define LM3532_MAX_LED_STRINGS 3
+
+#define LM3532_OUTPUT_CFG_MASK 0x3
+#define LM3532_BRT_VAL_ADJUST 8
+#define LM3532_RAMP_DOWN_SHIFT 3
+
+#define LM3532_NUM_RAMP_VALS 8
+#define LM3532_NUM_AVG_VALS 8
+#define LM3532_NUM_IMP_VALS 32
+
+#define LM3532_FS_CURR_MIN 5000
+#define LM3532_FS_CURR_MAX 29800
+#define LM3532_FS_CURR_STEP 800
+
+struct lm3532_bank_data {
+ int control_bank;
+ int mode;
+ int ctrl_brt_pointer;
+ int num_leds;
+ int full_scale_current;
+ u32 present:1;
+ u32 led_strings[LM3532_MAX_CONTROL_BANKS];
+};
+
+struct lm3532_backlight_priv {
+ struct gpio_desc enable_gpio;
+ struct udevice *regulator;
+
+ u32 runtime_ramp_up;
+ u32 runtime_ramp_down;
+
+ struct lm3532_bank_data bank[LM3532_MAX_CONTROL_BANKS];
+};
+
+/* This device does not like i2c md so use this instead */
+static void __maybe_unused dump_i2c(struct udevice *dev)
+{
+ int i, c;
+
+ for (i = 0; i < 0x10; i++) {
+ printf("00%02x: %02x", i * 0x10, dm_i2c_reg_read(dev, i * 0x10));
+ for (c = 1; c < 0xf; c++)
+ printf(" %02x", dm_i2c_reg_read(dev, i * 0x10 + c));
+ printf(" %02x\n", dm_i2c_reg_read(dev, i * 0x10 + 0xf));
+ }
+}
+
+static int lm3532_backlight_enable(struct udevice *dev)
+{
+ struct lm3532_backlight_priv *priv = dev_get_priv(dev);
+ int ret, i;
+
+ for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) {
+ if (priv->bank[i].present) {
+ u32 ctrl_en_val = BIT(priv->bank[i].control_bank);
+
+ ret = dm_i2c_reg_clrset(dev, LM3532_REG_ENABLE,
+ ctrl_en_val, ctrl_en_val);
+ if (ret) {
+ log_debug("%s: failed to set ctrl: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ }
+
+ regulator_set_enable_if_allowed(priv->regulator, 1);
+
+ return 0;
+}
+
+static int lm3532_backlight_set_brightness(struct udevice *dev, int percent)
+{
+ struct lm3532_backlight_priv *priv = dev_get_priv(dev);
+ struct lm3532_bank_data *bank;
+ int ret, i;
+
+ for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) {
+ if (priv->bank[i].present) {
+ bank = &priv->bank[i];
+ u32 brightness_reg = LM3532_REG_ZONE_TRGT_A +
+ bank->control_bank * 5 +
+ (bank->ctrl_brt_pointer >> 2);
+
+ ret = dm_i2c_reg_write(dev, brightness_reg, percent);
+ if (ret) {
+ log_debug("%s: failed to set brightness: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
+ 16384, 32768, 65536 };
+static int lm3532_get_ramp_index(int ramp_time)
+{
+ int i;
+
+ if (ramp_time <= ramp_table[0])
+ return 0;
+
+ if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
+ return LM3532_NUM_RAMP_VALS - 1;
+
+ for (i = 1; i < LM3532_NUM_RAMP_VALS; i++) {
+ if (ramp_time == ramp_table[i])
+ return i;
+
+ /* Find an approximate index by looking up the table */
+ if (ramp_time > ramp_table[i - 1] &&
+ ramp_time < ramp_table[i]) {
+ if (ramp_time - ramp_table[i - 1] < ramp_table[i] - ramp_time)
+ return i - 1;
+ else
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static int lm3532_backlight_of_to_plat(struct udevice *dev)
+{
+ struct lm3532_backlight_priv *priv = dev_get_priv(dev);
+ u32 ramp_time, reg;
+ ofnode child;
+ int ret;
+
+ ret = gpio_request_by_name(dev, "enable-gpios", 0,
+ &priv->enable_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: could not decode enable-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vin-supply", &priv->regulator);
+ if (ret) {
+ log_debug("%s: vin regulator not defined: %d\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ ramp_time = dev_read_u32_default(dev, "ramp-up-us", 0);
+ priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
+
+ ramp_time = dev_read_u32_default(dev, "ramp-down-us", 0);
+ priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
+
+ /* Backlight is one of children but has no dedicated driver */
+ ofnode_for_each_subnode(child, dev_ofnode(dev)) {
+ ret = ofnode_read_u32(child, "reg", &reg);
+ if (ret || reg > LM3532_CONTROL_C) {
+ log_debug("%s: control bank invalid %d\n", __func__, reg);
+ continue;
+ }
+
+ struct lm3532_bank_data *bank = &priv->bank[reg];
+
+ bank->control_bank = reg;
+ bank->present = 1;
+ bank->mode = ofnode_read_u32_default(child, "ti,led-mode",
+ LM3532_BL_MODE_MANUAL);
+ bank->mode = LM3532_BL_MODE_ALS ? LM3532_ALS_CTRL : LM3532_I2C_CTRL;
+
+ if (ofnode_read_bool(child, "ti,linear-mapping-mode"))
+ bank->mode |= LM3532_LINEAR_MAP;
+
+ bank->num_leds = ofnode_read_size(child, "led-sources");
+ bank->num_leds /= sizeof(u32);
+ if (bank->num_leds > LM3532_MAX_LED_STRINGS) {
+ log_debug("%s: too many LED string defined %d\n",
+ __func__, bank->num_leds);
+ continue;
+ }
+
+ ret = ofnode_read_u32_array(child, "led-sources",
+ bank->led_strings,
+ bank->num_leds);
+ if (ret) {
+ log_debug("%s: led-sources property missing %d\n",
+ __func__, ret);
+ continue;
+ }
+
+ ret = ofnode_read_u32(child, "led-max-microamp",
+ &bank->full_scale_current);
+ if (ret)
+ log_debug("%s: failed getting led-max-microamp %d\n",
+ __func__, ret);
+ else
+ bank->full_scale_current = min(bank->full_scale_current,
+ LM3532_FS_CURR_MAX);
+ }
+
+ return 0;
+}
+
+static int lm3532_backlight_init_registers(struct udevice *dev,
+ struct lm3532_bank_data *bank)
+{
+ struct lm3532_backlight_priv *priv = dev_get_priv(dev);
+ u32 brightness_config_val, runtime_ramp_val;
+ u32 output_cfg_val = 0, output_cfg_shift = 0, output_cfg_mask = 0;
+ int fs_current_reg, fs_current_val;
+ int ret, i;
+
+ if (!bank->present)
+ return 0;
+
+ u32 brightness_config_reg = LM3532_REG_ZONE_CFG_A + bank->control_bank * 2;
+ /*
+ * This could be hard coded to the default value but the control
+ * brightness register may have changed during boot.
+ */
+ ret = dm_i2c_reg_read(dev, brightness_config_reg);
+ if (ret < 0)
+ return ret;
+
+ bank->ctrl_brt_pointer = ret & ~LM3532_ZONE_MASK;
+ brightness_config_val = bank->ctrl_brt_pointer | bank->mode;
+
+ ret = dm_i2c_reg_write(dev, brightness_config_reg, brightness_config_val);
+ if (ret)
+ return ret;
+
+ if (bank->full_scale_current) {
+ fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + bank->control_bank * 2;
+ fs_current_val = (bank->full_scale_current - LM3532_FS_CURR_MIN) /
+ LM3532_FS_CURR_STEP;
+
+ ret = dm_i2c_reg_write(dev, fs_current_reg, fs_current_val);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < bank->num_leds; i++) {
+ output_cfg_shift = bank->led_strings[i] * 2;
+ output_cfg_val |= (bank->control_bank << output_cfg_shift);
+ output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
+ }
+
+ ret = dm_i2c_reg_clrset(dev, LM3532_REG_OUTPUT_CFG, output_cfg_mask,
+ output_cfg_val);
+ if (ret)
+ return ret;
+
+ runtime_ramp_val = priv->runtime_ramp_up |
+ (priv->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
+
+ return dm_i2c_reg_write(dev, LM3532_REG_RT_RAMP, runtime_ramp_val);
+}
+
+static int lm3532_backlight_probe(struct udevice *dev)
+{
+ struct lm3532_backlight_priv *priv = dev_get_priv(dev);
+ int ret, i;
+
+ if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
+ return -EPROTONOSUPPORT;
+
+ dm_gpio_set_value(&priv->enable_gpio, 1);
+
+ for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) {
+ ret = lm3532_backlight_init_registers(dev, &priv->bank[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct backlight_ops lm3532_backlight_ops = {
+ .enable = lm3532_backlight_enable,
+ .set_brightness = lm3532_backlight_set_brightness,
+};
+
+static const struct udevice_id lm3532_backlight_ids[] = {
+ { .compatible = "ti,lm3532" },
+ { }
+};
+
+U_BOOT_DRIVER(lm3532_backlight) = {
+ .name = "lm3532_backlight",
+ .id = UCLASS_PANEL_BACKLIGHT,
+ .of_match = lm3532_backlight_ids,
+ .of_to_plat = lm3532_backlight_of_to_plat,
+ .probe = lm3532_backlight_probe,
+ .ops = &lm3532_backlight_ops,
+ .priv_auto = sizeof(struct lm3532_backlight_priv),
+};
diff --git a/drivers/video/meson/Kconfig b/drivers/video/meson/Kconfig
index 3c2d72d019b..fcf486ca0a3 100644
--- a/drivers/video/meson/Kconfig
+++ b/drivers/video/meson/Kconfig
@@ -8,5 +8,6 @@ config VIDEO_MESON
bool "Enable Amlogic Meson video support"
depends on VIDEO
select DISPLAY
+ imply VIDEO_DAMAGE
help
Enable Amlogic Meson Video Processing Unit video support.
diff --git a/drivers/video/mot-panel.c b/drivers/video/mot-panel.c
new file mode 100644
index 00000000000..a9114957867
--- /dev/null
+++ b/drivers/video/mot-panel.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Motorola ATRIX 4G and DROID X2 DSI panel driver
+ * Exact manufacturer and model unknown
+ *
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <log.h>
+#include <misc.h>
+#include <mipi_display.h>
+#include <mipi_dsi.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <power/regulator.h>
+
+struct mot_panel_priv {
+ struct udevice *vdd;
+ struct udevice *vddio;
+
+ struct udevice *backlight;
+
+ struct gpio_desc reset_gpio;
+};
+
+#define dsi_generic_write_seq(dsi, cmd, seq...) do { \
+ static const u8 b[] = { cmd, seq }; \
+ int ret; \
+ ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+static struct display_timing default_timing = {
+ .pixelclock.typ = 38250000,
+ .hactive.typ = 540,
+ .hfront_porch.typ = 32,
+ .hback_porch.typ = 32,
+ .hsync_len.typ = 16,
+ .vactive.typ = 960,
+ .vfront_porch.typ = 12,
+ .vback_porch.typ = 12,
+ .vsync_len.typ = 8,
+};
+
+static int mot_es2(struct mipi_dsi_device *dsi)
+{
+ int ret;
+
+ dsi_generic_write_seq(dsi, 0x55, 0x01);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret);
+ return ret;
+ }
+ mdelay(120);
+
+ dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49,
+ 0x74, 0x29, 0x12, 0x15, 0x2f, 0x2f, 0x04);
+ dsi_generic_write_seq(dsi, 0xf8, 0x4b, 0x04, 0x10, 0x1a, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x14, 0x12);
+
+ dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x00, 0x80, 0xc7, 0x00);
+ dsi_generic_write_seq(dsi, 0xb7, 0x66, 0xf6, 0x46, 0x9f, 0x90, 0x99,
+ 0xff, 0x80, 0x6d, 0x01);
+
+ /* Gamma R */
+ dsi_generic_write_seq(dsi, 0xf9, 0x04);
+ dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c,
+ 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f);
+ dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c,
+ 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f);
+
+ /* Gamma G */
+ dsi_generic_write_seq(dsi, 0xf9, 0x02);
+ dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11,
+ 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f);
+ dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11,
+ 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f);
+
+ /* Gamma B */
+ dsi_generic_write_seq(dsi, 0xf9, 0x01);
+ dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15,
+ 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b);
+ dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15,
+ 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b);
+
+ /* Gamma W */
+ dsi_generic_write_seq(dsi, 0xf9, 0x20);
+ dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11,
+ 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08);
+ dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11,
+ 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08);
+
+ dsi_generic_write_seq(dsi, 0x53, 0x2c);
+ dsi_generic_write_seq(dsi, 0x35, 0x00);
+
+ return 0;
+}
+
+static int __maybe_unused mot_es4(struct mipi_dsi_device *dsi)
+{
+ int ret;
+
+ dsi_generic_write_seq(dsi, 0xd2, 0x04, 0x53);
+ dsi_generic_write_seq(dsi, 0xd2, 0x05, 0x53);
+ dsi_generic_write_seq(dsi, 0x55, 0x01);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret);
+ return ret;
+ }
+ mdelay(120);
+
+ dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x0a, 0x80, 0xff, 0x00);
+ dsi_generic_write_seq(dsi, 0xb7, 0x7a, 0xf7, 0x4d, 0x91, 0x90, 0xb3,
+ 0xff, 0x80, 0x6d, 0x01);
+ dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49,
+ 0x74, 0x29, 0x12, 0x15, 0x37, 0x37, 0x04);
+ dsi_generic_write_seq(dsi, 0xf8, 0x0a, 0x04, 0x10, 0x2a, 0x35, 0x35,
+ 0x35, 0x35, 0x21, 0x1a);
+
+ /* Gamma R */
+ dsi_generic_write_seq(dsi, 0xf9, 0x04);
+ dsi_generic_write_seq(dsi, 0xfa, 0x08, 0x1c, 0x1b, 0x0f, 0x0f, 0x0a,
+ 0x1e, 0x22, 0x27, 0x26, 0x07, 0x0d);
+ dsi_generic_write_seq(dsi, 0xfb, 0x08, 0x3c, 0x27, 0x0f, 0x0f, 0x0a,
+ 0x1e, 0x26, 0x31, 0x2f, 0x07, 0x0b);
+
+ /* Gamma G */
+ dsi_generic_write_seq(dsi, 0xf9, 0x02);
+ dsi_generic_write_seq(dsi, 0xfa, 0x30, 0x14, 0x0f, 0x00, 0x06, 0x02,
+ 0x1e, 0x22, 0x27, 0x27, 0x08, 0x10);
+ dsi_generic_write_seq(dsi, 0xfb, 0x30, 0x35, 0x0f, 0x00, 0x0a, 0x02,
+ 0x1c, 0x23, 0x31, 0x2f, 0x08, 0x0e);
+
+ /* Gamma B */
+ dsi_generic_write_seq(dsi, 0xf9, 0x01);
+ dsi_generic_write_seq(dsi, 0xfa, 0x12, 0x1b, 0x26, 0x0e, 0x12, 0x0b,
+ 0x1e, 0x22, 0x27, 0x27, 0x06, 0x0c);
+ dsi_generic_write_seq(dsi, 0xfb, 0x12, 0x3b, 0x2c, 0x12, 0x12, 0x0e,
+ 0x1e, 0x26, 0x31, 0x2f, 0x06, 0x0d);
+
+ /* Gamma W */
+ dsi_generic_write_seq(dsi, 0xf9, 0x20);
+ dsi_generic_write_seq(dsi, 0xfa, 0x37, 0x1b, 0x09, 0x01, 0x06, 0x04,
+ 0x19, 0x19, 0x22, 0x24, 0x04, 0x15);
+ dsi_generic_write_seq(dsi, 0xfb, 0x37, 0x3b, 0x17, 0x01, 0x0a, 0x04,
+ 0x19, 0x1d, 0x2c, 0x2c, 0x04, 0x13);
+
+ dsi_generic_write_seq(dsi, 0x53, 0x2c);
+ dsi_generic_write_seq(dsi, 0x35, 0x00);
+ dsi_generic_write_seq(dsi, 0xc3, 0x01, 0x4e);
+
+ return 0;
+}
+
+static int mot_panel_enable_backlight(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ struct mipi_dsi_device *dsi = plat->device;
+ int ret;
+
+ dsi_generic_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ dsi_generic_write_seq(dsi, 0xf1, 0x5a, 0x5a);
+ dsi_generic_write_seq(dsi, 0xd0, 0x8e);
+
+ ret = mot_es2(dsi);
+ if (ret)
+ return ret;
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to set display on: %d\n", __func__, ret);
+ return ret;
+ }
+ mdelay(20);
+
+ return 0;
+}
+
+static int mot_panel_set_backlight(struct udevice *dev, int percent)
+{
+ struct mot_panel_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret)
+ return ret;
+
+ mdelay(5);
+
+ return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int mot_panel_timings(struct udevice *dev, struct display_timing *timing)
+{
+ memcpy(timing, &default_timing, sizeof(*timing));
+ return 0;
+}
+
+static int mot_panel_of_to_plat(struct udevice *dev)
+{
+ struct mot_panel_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ log_debug("%s: cannot get backlight: ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd);
+ if (ret) {
+ log_debug("%s: cannot get vdd-supply: ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio);
+ if (ret) {
+ log_debug("%s: cannot get vddio-supply: ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: could not decode reser-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mot_panel_hw_init(struct udevice *dev)
+{
+ struct mot_panel_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = regulator_set_enable_if_allowed(priv->vddio, 1);
+ if (ret) {
+ log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->vdd, 1);
+ if (ret) {
+ log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error entering reset (%d)\n", __func__, ret);
+ return ret;
+ }
+ mdelay(50);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error exiting reset (%d)\n", __func__, ret);
+ return ret;
+ }
+ mdelay(10);
+
+ return 0;
+}
+
+static int mot_panel_probe(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ /* fill characteristics of DSI data link */
+ plat->lanes = 2;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_LPM;
+
+ return mot_panel_hw_init(dev);
+}
+
+static const struct panel_ops mot_panel_ops = {
+ .enable_backlight = mot_panel_enable_backlight,
+ .set_backlight = mot_panel_set_backlight,
+ .get_display_timing = mot_panel_timings,
+};
+
+static const struct udevice_id mot_panel_ids[] = {
+ { .compatible = "motorola,mot-panel" },
+ { }
+};
+
+U_BOOT_DRIVER(mot_panel) = {
+ .name = "mot_panel",
+ .id = UCLASS_PANEL,
+ .of_match = mot_panel_ids,
+ .ops = &mot_panel_ops,
+ .of_to_plat = mot_panel_of_to_plat,
+ .probe = mot_panel_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct mot_panel_priv),
+};
diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig
index 01804dcb1cc..0f4550a29e3 100644
--- a/drivers/video/rockchip/Kconfig
+++ b/drivers/video/rockchip/Kconfig
@@ -11,6 +11,7 @@
menuconfig VIDEO_ROCKCHIP
bool "Enable Rockchip Video Support"
depends on VIDEO
+ imply VIDEO_DAMAGE
help
Rockchip SoCs provide video output capabilities for High-Definition
Multimedia Interface (HDMI), Low-voltage Differential Signalling
diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
index 48066063e4c..c354c402c28 100644
--- a/drivers/video/stm32/Kconfig
+++ b/drivers/video/stm32/Kconfig
@@ -8,6 +8,7 @@
menuconfig VIDEO_STM32
bool "Enable STM32 video support"
depends on VIDEO
+ imply VIDEO_DAMAGE
help
STM32 supports many video output options including RGB and
DSI. This option enables these supports which can be used on
diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig
new file mode 100644
index 00000000000..1a328407b13
--- /dev/null
+++ b/drivers/video/tegra/Kconfig
@@ -0,0 +1,52 @@
+config HOST1X_TEGRA
+ bool "NVIDIA Tegra host1x BUS support"
+ depends on SIMPLE_BUS
+
+config VIDEO_TEGRA
+ bool "Enable Display Controller support on Tegra devices"
+ depends on OF_CONTROL
+ select HOST1X_TEGRA
+ help
+ Enable support for Display Controller found in Tegra SoC. The
+ Display Controller Complex integrates two independent display
+ controllers. Each display controller is capable of interfacing
+ to an external display device, which can be a parallel interface
+ or SPI LCD, DVI, an HDMI HDTV, RGB monitor or a MIPI DSI LCD.
+ Direct interface is supported directly to most LCD displays with
+ TFT or TFT-like interface.
+
+config VIDEO_DSI_TEGRA
+ bool "Enable DSI controller support on Tegra devices"
+ depends on VIDEO_BRIDGE && PANEL && DM_GPIO
+ select VIDEO_TEGRA
+ select VIDEO_MIPI_DSI
+ help
+ Enable support for the Display Serial Interface (DSI) found in
+ Tegra SoC. It is a MIPI standard serial bitstream, intended to
+ provide a low pin count interface to a display panel.
+
+config VIDEO_HDMI_TEGRA
+ bool "Enable HDMI support on Tegra devices"
+ depends on VIDEO_BRIDGE && DM_I2C
+ select I2C_EDID
+ select VIDEO_TEGRA
+ help
+ Enable support for the High-Definition Multimedia Interface (HDMI)
+ found in Tegra SoC.
+
+config TEGRA_BACKLIGHT_PWM
+ bool "Enable Tegra DC PWM backlight support"
+ depends on BACKLIGHT
+ select VIDEO_TEGRA
+ help
+ Enable support for the Display Controller dependent PWM backlight
+ found in the Tegra SoC and usually used with DSI panels.
+
+config VIDEO_TEGRA124
+ bool "Enable video support on Tegra124"
+ imply VIDEO_DAMAGE
+ help
+ Tegra124 supports many video output options including eDP and
+ HDMI. At present only eDP is supported by U-Boot. This option
+ enables this support which can be used on devices which
+ have an eDP display connected.
diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile
new file mode 100644
index 00000000000..3c50a0ba3c3
--- /dev/null
+++ b/drivers/video/tegra/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_HOST1X_TEGRA) += host1x.o
+obj-$(CONFIG_VIDEO_TEGRA) += dc.o
+obj-$(CONFIG_VIDEO_DSI_TEGRA) += dsi.o mipi.o mipi-phy.o
+obj-$(CONFIG_VIDEO_HDMI_TEGRA) += hdmi.o
+obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += dc-pwm-backlight.o
+
+obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
diff --git a/drivers/video/tegra/TODO b/drivers/video/tegra/TODO
new file mode 100644
index 00000000000..1c8c2389a18
--- /dev/null
+++ b/drivers/video/tegra/TODO
@@ -0,0 +1,5 @@
+Existence of separate Tegra124 video implementations is not an ideal solution
+since generic video setup for Tegra already has Tegra124 support of some degree.
+It is not possible at the time of this note is written to integrate T124 SOR
+and DP without possible regressions. Tegra124 setup for SOR and DP should be
+incorporated into existing setup once such opportunity occurs.
diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra/dc-pwm-backlight.c
index 998f0df1991..eff10b5563e 100644
--- a/drivers/video/tegra20/tegra-pwm-backlight.c
+++ b/drivers/video/tegra/dc-pwm-backlight.c
@@ -15,7 +15,7 @@
#include <asm/io.h>
#include <asm/gpio.h>
-#include "tegra-dc.h"
+#include "dc.h"
#define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10
#define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra/dc.c
index 1f43153ff27..f0e3d2c993f 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra/dc.c
@@ -19,7 +19,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/powergate.h>
-#include "tegra-dc.h"
+#include "dc.h"
/* Holder of Tegra per-SOC DC differences */
struct tegra_dc_soc_info {
diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra/dc.h
index 2a4013b3355..2a4013b3355 100644
--- a/drivers/video/tegra20/tegra-dc.h
+++ b/drivers/video/tegra/dc.h
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra/dsi.c
index a2a22fa0fe2..bc308869f4e 100644
--- a/drivers/video/tegra20/tegra-dsi.c
+++ b/drivers/video/tegra/dsi.c
@@ -24,8 +24,8 @@
#include <asm/arch/clock.h>
#include <asm/arch-tegra/clk_rst.h>
-#include "tegra-dc.h"
-#include "tegra-dsi.h"
+#include "dc.h"
+#include "dsi.h"
#include "mipi-phy.h"
/* List of supported DSI bridges */
@@ -1129,6 +1129,7 @@ static const struct video_bridge_ops tegra_dsi_bridge_ops = {
};
static const struct udevice_id tegra_dsi_bridge_ids[] = {
+ { .compatible = "nvidia,tegra20-dsi", .data = DSI_V0 },
{ .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 },
{ .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 },
{ .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 },
diff --git a/drivers/video/tegra20/tegra-dsi.h b/drivers/video/tegra/dsi.h
index 683c5e31a34..683c5e31a34 100644
--- a/drivers/video/tegra20/tegra-dsi.h
+++ b/drivers/video/tegra/dsi.h
diff --git a/drivers/video/tegra20/tegra-hdmi.c b/drivers/video/tegra/hdmi.c
index bda69919d92..bfb48b25187 100644
--- a/drivers/video/tegra20/tegra-hdmi.c
+++ b/drivers/video/tegra/hdmi.c
@@ -22,8 +22,8 @@
#include <asm/io.h>
#include <asm/arch/clock.h>
-#include "tegra-dc.h"
-#include "tegra-hdmi.h"
+#include "dc.h"
+#include "hdmi.h"
#define DDCCI_ENTRY_ADDR 0x37
#define DDCCI_SOURSE_ADDR 0x51
diff --git a/drivers/video/tegra20/tegra-hdmi.h b/drivers/video/tegra/hdmi.h
index d17655973e3..d17655973e3 100644
--- a/drivers/video/tegra20/tegra-hdmi.h
+++ b/drivers/video/tegra/hdmi.h
diff --git a/drivers/video/tegra20/tegra-host1x.c b/drivers/video/tegra/host1x.c
index 58ab871a3b4..58ab871a3b4 100644
--- a/drivers/video/tegra20/tegra-host1x.c
+++ b/drivers/video/tegra/host1x.c
diff --git a/drivers/video/tegra20/mipi-phy.c b/drivers/video/tegra/mipi-phy.c
index 576262e405d..576262e405d 100644
--- a/drivers/video/tegra20/mipi-phy.c
+++ b/drivers/video/tegra/mipi-phy.c
diff --git a/drivers/video/tegra20/mipi-phy.h b/drivers/video/tegra/mipi-phy.h
index 41889a75035..41889a75035 100644
--- a/drivers/video/tegra20/mipi-phy.h
+++ b/drivers/video/tegra/mipi-phy.h
diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra/mipi.c
index a4f4343d008..a4f4343d008 100644
--- a/drivers/video/tegra20/tegra-mipi.c
+++ b/drivers/video/tegra/mipi.c
diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra/tegra124/Makefile
index a378382628c..a378382628c 100644
--- a/drivers/video/tegra124/Makefile
+++ b/drivers/video/tegra/tegra124/Makefile
diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra/tegra124/display.c
index abe31e27d84..abe31e27d84 100644
--- a/drivers/video/tegra124/display.c
+++ b/drivers/video/tegra/tegra124/display.c
diff --git a/drivers/video/tegra124/displayport.h b/drivers/video/tegra/tegra124/displayport.h
index a3044475aeb..a3044475aeb 100644
--- a/drivers/video/tegra124/displayport.h
+++ b/drivers/video/tegra/tegra124/displayport.h
diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra/tegra124/dp.c
index b95b14da77d..b95b14da77d 100644
--- a/drivers/video/tegra124/dp.c
+++ b/drivers/video/tegra/tegra124/dp.c
diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra/tegra124/sor.c
index 1ce5330c6bc..1ce5330c6bc 100644
--- a/drivers/video/tegra124/sor.c
+++ b/drivers/video/tegra/tegra124/sor.c
diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra/tegra124/sor.h
index 2fc9a38267d..2fc9a38267d 100644
--- a/drivers/video/tegra124/sor.h
+++ b/drivers/video/tegra/tegra124/sor.h
diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig
deleted file mode 100644
index 598f9ea1f21..00000000000
--- a/drivers/video/tegra20/Kconfig
+++ /dev/null
@@ -1,38 +0,0 @@
-config HOST1X_TEGRA
- bool "NVIDIA Tegra host1x BUS support"
- depends on SIMPLE_BUS
-
-config VIDEO_TEGRA20
- bool "Enable Display Controller support on Tegra20 and Tegra 30"
- depends on OF_CONTROL
- select HOST1X_TEGRA
- help
- T20/T30 support video output to an attached LCD panel as well as
- other options such as HDMI. Only the LCD is supported in U-Boot.
- This option enables this support which can be used on devices which
- have an LCD display connected.
-
-config VIDEO_DSI_TEGRA30
- bool "Enable Tegra 30 DSI support"
- depends on VIDEO_BRIDGE && PANEL && DM_GPIO
- select VIDEO_TEGRA20
- select VIDEO_MIPI_DSI
- help
- T30 has native support for DSI panels. This option enables support
- for such panels which can be used on endeavoru and tf600t.
-
-config VIDEO_HDMI_TEGRA
- bool "Enable Tegra HDMI support"
- depends on VIDEO_BRIDGE && DM_I2C
- select I2C_EDID
- select VIDEO_TEGRA20
- help
- Tegra has native support for HDMI. This option enables support
- for such connection and can be used for any supported device.
-
-config TEGRA_BACKLIGHT_PWM
- bool "Enable Tegra DC PWM backlight support"
- depends on BACKLIGHT
- select VIDEO_TEGRA20
- help
- Tegra DC dependent backlight.
diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile
deleted file mode 100644
index 78521405749..00000000000
--- a/drivers/video/tegra20/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0+
-
-obj-$(CONFIG_HOST1X_TEGRA) += tegra-host1x.o
-obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o
-obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o
-obj-$(CONFIG_VIDEO_HDMI_TEGRA) += tegra-hdmi.o
-obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o
diff --git a/drivers/video/tidss/Kconfig b/drivers/video/tidss/Kconfig
index 95086f3a5d6..3291b3ceb8d 100644
--- a/drivers/video/tidss/Kconfig
+++ b/drivers/video/tidss/Kconfig
@@ -11,6 +11,7 @@
menuconfig VIDEO_TIDSS
bool "Enable TIDSS video support"
depends on VIDEO
+ imply VIDEO_DAMAGE
help
TIDSS supports video output options LVDS and
DPI . This option enables these supports which can be used on
diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile
index f0cbe1d4ed1..3381a5fec57 100644
--- a/drivers/video/tidss/Makefile
+++ b/drivers/video/tidss/Makefile
@@ -9,4 +9,4 @@
# Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
-obj-${CONFIG_$(XPL_)VIDEO_TIDSS} = tidss_drv.o
+obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} = tidss_drv.o
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index ebe96bf0c2f..a1dfd35b7b8 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -761,22 +761,6 @@ UCLASS_DRIVER(vidconsole) = {
.per_device_auto = sizeof(struct vidconsole_priv),
};
-#ifdef CONFIG_VIDEO_COPY
-int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
-{
- struct udevice *vid = dev_get_parent(dev);
-
- return video_sync_copy(vid, from, to);
-}
-
-int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
- int size)
-{
- memmove(dst, src, size);
- return vidconsole_sync_copy(dev, dst, dst + size);
-}
-#endif
-
int vidconsole_clear_and_reset(struct udevice *dev)
{
int ret;
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index c684c994b61..503cdb9f025 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -171,7 +171,7 @@ int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
struct video_priv *priv = dev_get_uclass_priv(dev);
void *start, *line;
int pixels = xend - xstart;
- int row, i, ret;
+ int row, i;
start = priv->fb + ystart * priv->line_length;
start += xstart * VNBYTES(priv->bpix);
@@ -210,9 +210,8 @@ int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
}
line += priv->line_length;
}
- ret = video_sync_copy(dev, start, line);
- if (ret)
- return ret;
+
+ video_damage(dev, xstart, ystart, xend - xstart, yend - ystart);
return 0;
}
@@ -233,7 +232,6 @@ int video_reserve_from_bloblist(struct video_handoff *ho)
int video_fill(struct udevice *dev, u32 colour)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
- int ret;
switch (priv->bpix) {
case VIDEO_BPP16:
@@ -260,9 +258,8 @@ int video_fill(struct udevice *dev, u32 colour)
memset(priv->fb, colour, priv->fb_size);
break;
}
- ret = video_sync_copy(dev, priv->fb, priv->fb + priv->fb_size);
- if (ret)
- return ret;
+
+ video_damage(dev, 0, 0, priv->xsize, priv->ysize);
return video_sync(dev, false);
}
@@ -369,6 +366,95 @@ void video_set_default_colors(struct udevice *dev, bool invert)
priv->colour_bg = video_index_to_colour(priv, back);
}
+/* Notify about changes in the frame buffer */
+#ifdef CONFIG_VIDEO_DAMAGE
+void video_damage(struct udevice *vid, int x, int y, int width, int height)
+{
+ struct video_priv *priv = dev_get_uclass_priv(vid);
+ int xend = x + width;
+ int yend = y + height;
+
+ if (x > priv->xsize)
+ return;
+
+ if (y > priv->ysize)
+ return;
+
+ if (xend > priv->xsize)
+ xend = priv->xsize;
+
+ if (yend > priv->ysize)
+ 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);
+}
+#endif
+
+static void video_flush_dcache(struct udevice *vid, bool use_copy)
+{
+ struct video_priv *priv = dev_get_uclass_priv(vid);
+ ulong fb = use_copy ? (ulong)priv->copy_fb : (ulong)priv->fb;
+ uint cacheline_size = 32;
+
+#ifdef CONFIG_SYS_CACHELINE_SIZE
+ cacheline_size = CONFIG_SYS_CACHELINE_SIZE;
+#endif
+
+ if (CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
+ return;
+
+ if (!priv->flush_dcache)
+ return;
+
+ if (!IS_ENABLED(CONFIG_VIDEO_DAMAGE)) {
+ flush_dcache_range(fb, ALIGN(fb + priv->fb_size,
+ cacheline_size));
+
+ return;
+ }
+
+ if (priv->damage.xend && priv->damage.yend) {
+ int lstart = priv->damage.xstart * VNBYTES(priv->bpix);
+ int lend = priv->damage.xend * VNBYTES(priv->bpix);
+ int y;
+
+ for (y = priv->damage.ystart; y < priv->damage.yend; y++) {
+ ulong start = fb + (y * priv->line_length) + lstart;
+ ulong end = start + lend - lstart;
+
+ start = ALIGN_DOWN(start, cacheline_size);
+ end = ALIGN(end, cacheline_size);
+
+ flush_dcache_range(start, end);
+ }
+ }
+}
+
+static void video_flush_copy(struct udevice *vid)
+{
+ struct video_priv *priv = dev_get_uclass_priv(vid);
+
+ 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);
+ int y;
+
+ for (y = priv->damage.ystart; y < priv->damage.yend; y++) {
+ ulong offset = (y * priv->line_length) + lstart;
+ ulong len = lend - lstart;
+
+ memcpy(priv->copy_fb + offset, priv->fb + offset, len);
+ }
+ }
+}
+
/* Flush video activity to the caches */
int video_sync(struct udevice *vid, bool force)
{
@@ -376,6 +462,9 @@ int video_sync(struct udevice *vid, bool force)
struct video_ops *ops = video_get_ops(vid);
int ret;
+ if (IS_ENABLED(CONFIG_VIDEO_COPY))
+ video_flush_copy(vid);
+
if (ops && ops->video_sync) {
ret = ops->video_sync(vid);
if (ret)
@@ -386,22 +475,23 @@ int video_sync(struct udevice *vid, bool force)
get_timer(priv->last_sync) < CONFIG_VIDEO_SYNC_MS)
return 0;
- /*
- * flush_dcache_range() is declared in common.h but it seems that some
- * architectures do not actually implement it. Is there a way to find
- * out whether it exists? For now, ARM is safe.
- */
-#if defined(CONFIG_ARM) && !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
- if (priv->flush_dcache) {
- flush_dcache_range((ulong)priv->fb,
- ALIGN((ulong)priv->fb + priv->fb_size,
- CONFIG_SYS_CACHELINE_SIZE));
- }
-#elif defined(CONFIG_VIDEO_SANDBOX_SDL)
+ video_flush_dcache(vid, false);
+
+ if (IS_ENABLED(CONFIG_VIDEO_COPY))
+ video_flush_dcache(vid, true);
+
+#if defined(CONFIG_VIDEO_SANDBOX_SDL)
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;
+ }
+
return 0;
}
@@ -453,69 +543,6 @@ 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[120];
-
- snprintf(str, sizeof(str),
- "[** FAULT 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;
-}
-
-int video_sync_copy_all(struct udevice *dev)
-{
- struct video_priv *priv = dev_get_uclass_priv(dev);
-
- video_sync_copy(dev, priv->fb, priv->fb + priv->fb_size);
-
- return 0;
-}
-
-#endif
-
#define SPLASH_DECL(_name) \
extern u8 __splash_ ## _name ## _begin[]; \
extern u8 __splash_ ## _name ## _end[]
diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c
index ad512d99a1b..1f267d45812 100644
--- a/drivers/video/video_bmp.c
+++ b/drivers/video/video_bmp.c
@@ -267,7 +267,6 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
enum video_format eformat;
struct bmp_color_table_entry *palette;
int hdr_size;
- int ret;
if (!bmp || !(bmp->header.signature[0] == 'B' &&
bmp->header.signature[1] == 'M')) {
@@ -459,11 +458,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
break;
};
- /* Find the position of the top left of the image in the framebuffer */
- fb = (uchar *)(priv->fb + y * priv->line_length + x * bpix / 8);
- ret = video_sync_copy(dev, start, fb);
- if (ret)
- return log_ret(ret);
+ video_damage(dev, x, y, width, height);
return video_sync(dev, false);
}