[PATCH 0/6] Add u-boot splash screen support for AM62x

Enable TI Display Subsystem(TIDSS) driver as UCLASS_VIDEO. Enable splash screen support, to display TI logo during boot-up. Tested on microtips panel (Model No: 13-101HIEBCAF0-S) supported by AM62x.
The changes in patch 4 depends on the patch sent for addition of am62x.env file to set environment variables for AM62x.
link: https://lore.kernel.org/u-boot/20230123063018.6061-1-n-jain1@ti.com/T/#u
Nikhil M Jain (6): drivers: core: ofnode: Add panel timing decode. drivers: video: simple_panel: make simple panel independent of backlight drivers: video: tidss: TIDSS video driver support for AM62x board: ti: am62x: am62x: Add splash screen env variables board: ti: am62x: evm: Add splash screen support tools: logos: Add TI logo files
MAINTAINERS | 1 + board/ti/am62x/am62x.env | 5 + board/ti/am62x/evm.c | 31 + drivers/core/ofnode.c | 53 ++ drivers/video/Kconfig | 2 + drivers/video/Makefile | 1 + drivers/video/simple_panel.c | 7 +- drivers/video/tidss/Kconfig | 18 + drivers/video/tidss/Makefile | 12 + drivers/video/tidss/tidss_drv.c | 960 +++++++++++++++++++++++++++++++ drivers/video/tidss/tidss_drv.h | 152 +++++ drivers/video/tidss/tidss_regs.h | 292 ++++++++++ include/dm/ofnode.h | 12 + tools/logos/ti.bmp | Bin 0 -> 447258 bytes tools/logos/ti.gz | Bin 0 -> 19604 bytes 15 files changed, 1544 insertions(+), 2 deletions(-) create mode 100644 drivers/video/tidss/Kconfig create mode 100644 drivers/video/tidss/Makefile create mode 100644 drivers/video/tidss/tidss_drv.c create mode 100644 drivers/video/tidss/tidss_drv.h create mode 100644 drivers/video/tidss/tidss_regs.h create mode 100644 tools/logos/ti.bmp create mode 100644 tools/logos/ti.gz

ofnode_decode_display_timing supports reading timing parameters from subnode of display-timings node, for displays supporting multiple resolution, in case if a display supports single resolution, it fails reading directly from display-timings node, to support it ofnode_decode_panel_timing is added.
Signed-off-by: Nikhil M Jain n-jain1@ti.com --- drivers/core/ofnode.c | 53 +++++++++++++++++++++++++++++++++++++++++++ include/dm/ofnode.h | 12 ++++++++++ 2 files changed, 65 insertions(+)
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 4d56b1a767..d06b947516 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -991,6 +991,59 @@ int ofnode_decode_display_timing(ofnode parent, int index, return ret; }
+int ofnode_decode_panel_timing(ofnode parent, + struct display_timing *dt) +{ + ofnode timings; + u32 val = 0; + int ret = 0; + + timings = ofnode_find_subnode(parent, "panel-timings"); + + if (!ofnode_valid(timings)) + return -EINVAL; + memset(dt, 0, sizeof(*dt)); + ret |= decode_timing_property(timings, "hback-porch", &dt->hback_porch); + ret |= decode_timing_property(timings, "hfront-porch", &dt->hfront_porch); + ret |= decode_timing_property(timings, "hactive", &dt->hactive); + ret |= decode_timing_property(timings, "hsync-len", &dt->hsync_len); + ret |= decode_timing_property(timings, "vback-porch", &dt->vback_porch); + ret |= decode_timing_property(timings, "vfront-porch", &dt->vfront_porch); + ret |= decode_timing_property(timings, "vactive", &dt->vactive); + ret |= decode_timing_property(timings, "vsync-len", &dt->vsync_len); + ret |= decode_timing_property(timings, "clock-frequency", &dt->pixelclock); + dt->flags = 0; + val = ofnode_read_u32_default(timings, "vsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : + DISPLAY_FLAGS_VSYNC_LOW; + } + val = ofnode_read_u32_default(timings, "hsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : + DISPLAY_FLAGS_HSYNC_LOW; + } + val = ofnode_read_u32_default(timings, "de-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + } + val = ofnode_read_u32_default(timings, "pixelclk-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + } + + if (ofnode_read_bool(timings, "interlaced")) + dt->flags |= DISPLAY_FLAGS_INTERLACED; + if (ofnode_read_bool(timings, "doublescan")) + dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; + if (ofnode_read_bool(timings, "doubleclk")) + dt->flags |= DISPLAY_FLAGS_DOUBLECLK; + + return ret; +} + const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) { if (ofnode_is_np(node)) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index fa9865602d..3f6b0843c5 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -974,6 +974,18 @@ struct display_timing; int ofnode_decode_display_timing(ofnode node, int index, struct display_timing *config);
+/** + * ofnode_decode_panel_timing() - decode display timings + * + * Decode panel timings from the supplied 'panel-timings' node. + * + * @node: 'display-timing' node containing the timing subnodes + * @config: Place to put timings + * Return: 0 if OK, -FDT_ERR_NOTFOUND if not found + */ +int ofnode_decode_panel_timing(ofnode node, + struct display_timing *config); + /** * ofnode_get_property() - get a pointer to the value of a node property *

Hi Nikhil,
On Mon, 23 Jan 2023 at 01:07, Nikhil M Jain n-jain1@ti.com wrote:
ofnode_decode_display_timing supports reading timing parameters from subnode of display-timings node, for displays supporting multiple resolution, in case if a display supports single resolution, it fails reading directly from display-timings node, to support it ofnode_decode_panel_timing is added.
Signed-off-by: Nikhil M Jain n-jain1@ti.com
drivers/core/ofnode.c | 53 +++++++++++++++++++++++++++++++++++++++++++ include/dm/ofnode.h | 12 ++++++++++ 2 files changed, 65 insertions(+)
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 4d56b1a767..d06b947516 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -991,6 +991,59 @@ int ofnode_decode_display_timing(ofnode parent, int index, return ret; }
+int ofnode_decode_panel_timing(ofnode parent,
struct display_timing *dt)
+{
ofnode timings;
u32 val = 0;
int ret = 0;
timings = ofnode_find_subnode(parent, "panel-timings");
if (!ofnode_valid(timings))
return -EINVAL;
memset(dt, 0, sizeof(*dt));
ret |= decode_timing_property(timings, "hback-porch", &dt->hback_porch);
ret |= decode_timing_property(timings, "hfront-porch", &dt->hfront_porch);
ret |= decode_timing_property(timings, "hactive", &dt->hactive);
ret |= decode_timing_property(timings, "hsync-len", &dt->hsync_len);
ret |= decode_timing_property(timings, "vback-porch", &dt->vback_porch);
ret |= decode_timing_property(timings, "vfront-porch", &dt->vfront_porch);
ret |= decode_timing_property(timings, "vactive", &dt->vactive);
ret |= decode_timing_property(timings, "vsync-len", &dt->vsync_len);
ret |= decode_timing_property(timings, "clock-frequency", &dt->pixelclock);
dt->flags = 0;
val = ofnode_read_u32_default(timings, "vsync-active", -1);
if (val != -1) {
if (!ofnode_read_u32(timings, "vsync-active", &val)) {
Please fix below also
dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH :
DISPLAY_FLAGS_VSYNC_LOW;
}
val = ofnode_read_u32_default(timings, "hsync-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH :
DISPLAY_FLAGS_HSYNC_LOW;
}
val = ofnode_read_u32_default(timings, "de-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH :
DISPLAY_FLAGS_DE_LOW;
}
val = ofnode_read_u32_default(timings, "pixelclk-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
}
if (ofnode_read_bool(timings, "interlaced"))
dt->flags |= DISPLAY_FLAGS_INTERLACED;
if (ofnode_read_bool(timings, "doublescan"))
dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
if (ofnode_read_bool(timings, "doubleclk"))
dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
return ret;
+}
const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) { if (ofnode_is_np(node)) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index fa9865602d..3f6b0843c5 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -974,6 +974,18 @@ struct display_timing; int ofnode_decode_display_timing(ofnode node, int index, struct display_timing *config);
+/**
- ofnode_decode_panel_timing() - decode display timings
- Decode panel timings from the supplied 'panel-timings' node.
- @node: 'display-timing' node containing the timing subnodes
- @config: Place to put timings
- Return: 0 if OK, -FDT_ERR_NOTFOUND if not found
- */
+int ofnode_decode_panel_timing(ofnode node,
struct display_timing *config);
/**
- ofnode_get_property() - get a pointer to the value of a node property
-- 2.17.1
Please add a test to test/dm/ofnode.c
Regards, Simon

Hi Simon,
On 24/01/23 00:12, Simon Glass wrote:
Hi Nikhil,
On Mon, 23 Jan 2023 at 01:07, Nikhil M Jain n-jain1@ti.com wrote:
ofnode_decode_display_timing supports reading timing parameters from subnode of display-timings node, for displays supporting multiple resolution, in case if a display supports single resolution, it fails reading directly from display-timings node, to support it ofnode_decode_panel_timing is added.
Signed-off-by: Nikhil M Jain n-jain1@ti.com
drivers/core/ofnode.c | 53 +++++++++++++++++++++++++++++++++++++++++++ include/dm/ofnode.h | 12 ++++++++++ 2 files changed, 65 insertions(+)
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 4d56b1a767..d06b947516 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -991,6 +991,59 @@ int ofnode_decode_display_timing(ofnode parent, int index, return ret; }
+int ofnode_decode_panel_timing(ofnode parent,
struct display_timing *dt)
+{
ofnode timings;
u32 val = 0;
int ret = 0;
timings = ofnode_find_subnode(parent, "panel-timings");
if (!ofnode_valid(timings))
return -EINVAL;
memset(dt, 0, sizeof(*dt));
ret |= decode_timing_property(timings, "hback-porch", &dt->hback_porch);
ret |= decode_timing_property(timings, "hfront-porch", &dt->hfront_porch);
ret |= decode_timing_property(timings, "hactive", &dt->hactive);
ret |= decode_timing_property(timings, "hsync-len", &dt->hsync_len);
ret |= decode_timing_property(timings, "vback-porch", &dt->vback_porch);
ret |= decode_timing_property(timings, "vfront-porch", &dt->vfront_porch);
ret |= decode_timing_property(timings, "vactive", &dt->vactive);
ret |= decode_timing_property(timings, "vsync-len", &dt->vsync_len);
ret |= decode_timing_property(timings, "clock-frequency", &dt->pixelclock);
dt->flags = 0;
val = ofnode_read_u32_default(timings, "vsync-active", -1);
if (val != -1) {
if (!ofnode_read_u32(timings, "vsync-active", &val)) {
Please fix below also
dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH :
DISPLAY_FLAGS_VSYNC_LOW;
}
val = ofnode_read_u32_default(timings, "hsync-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH :
DISPLAY_FLAGS_HSYNC_LOW;
}
val = ofnode_read_u32_default(timings, "de-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH :
DISPLAY_FLAGS_DE_LOW;
}
val = ofnode_read_u32_default(timings, "pixelclk-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
}
if (ofnode_read_bool(timings, "interlaced"))
dt->flags |= DISPLAY_FLAGS_INTERLACED;
if (ofnode_read_bool(timings, "doublescan"))
dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
if (ofnode_read_bool(timings, "doubleclk"))
dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
return ret;
+}
const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) { if (ofnode_is_np(node)) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index fa9865602d..3f6b0843c5 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -974,6 +974,18 @@ struct display_timing; int ofnode_decode_display_timing(ofnode node, int index, struct display_timing *config);
+/**
- ofnode_decode_panel_timing() - decode display timings
- Decode panel timings from the supplied 'panel-timings' node.
- @node: 'display-timing' node containing the timing subnodes
- @config: Place to put timings
- Return: 0 if OK, -FDT_ERR_NOTFOUND if not found
- */
+int ofnode_decode_panel_timing(ofnode node,
struct display_timing *config);
/**
- ofnode_get_property() - get a pointer to the value of a node property
-- 2.17.1
Please add a test to test/dm/ofnode.c
Wanted to confirm whether I need to add a test for ofnode_get_property or for the ofnode_decode_panel_timings.
Regards, Simon
Thanks

Hi Nikhl,
On Tue, 24 Jan 2023 at 22:14, Nikhl M Jain n-jain1@ti.com wrote:
Hi Simon,
On 24/01/23 00:12, Simon Glass wrote:
Hi Nikhil,
On Mon, 23 Jan 2023 at 01:07, Nikhil M Jain n-jain1@ti.com wrote:
ofnode_decode_display_timing supports reading timing parameters from subnode of display-timings node, for displays supporting multiple resolution, in case if a display supports single resolution, it fails reading directly from display-timings node, to support it ofnode_decode_panel_timing is added.
Signed-off-by: Nikhil M Jain n-jain1@ti.com
drivers/core/ofnode.c | 53 +++++++++++++++++++++++++++++++++++++++++++ include/dm/ofnode.h | 12 ++++++++++ 2 files changed, 65 insertions(+)
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 4d56b1a767..d06b947516 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -991,6 +991,59 @@ int ofnode_decode_display_timing(ofnode parent, int index, return ret; }
+int ofnode_decode_panel_timing(ofnode parent,
struct display_timing *dt)
+{
ofnode timings;
u32 val = 0;
int ret = 0;
timings = ofnode_find_subnode(parent, "panel-timings");
if (!ofnode_valid(timings))
return -EINVAL;
memset(dt, 0, sizeof(*dt));
ret |= decode_timing_property(timings, "hback-porch", &dt->hback_porch);
ret |= decode_timing_property(timings, "hfront-porch", &dt->hfront_porch);
ret |= decode_timing_property(timings, "hactive", &dt->hactive);
ret |= decode_timing_property(timings, "hsync-len", &dt->hsync_len);
ret |= decode_timing_property(timings, "vback-porch", &dt->vback_porch);
ret |= decode_timing_property(timings, "vfront-porch", &dt->vfront_porch);
ret |= decode_timing_property(timings, "vactive", &dt->vactive);
ret |= decode_timing_property(timings, "vsync-len", &dt->vsync_len);
ret |= decode_timing_property(timings, "clock-frequency", &dt->pixelclock);
dt->flags = 0;
val = ofnode_read_u32_default(timings, "vsync-active", -1);
if (val != -1) {
if (!ofnode_read_u32(timings, "vsync-active", &val)) {
Please fix below also
dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH :
DISPLAY_FLAGS_VSYNC_LOW;
}
val = ofnode_read_u32_default(timings, "hsync-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH :
DISPLAY_FLAGS_HSYNC_LOW;
}
val = ofnode_read_u32_default(timings, "de-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH :
DISPLAY_FLAGS_DE_LOW;
}
val = ofnode_read_u32_default(timings, "pixelclk-active", -1);
if (val != -1) {
dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
}
if (ofnode_read_bool(timings, "interlaced"))
dt->flags |= DISPLAY_FLAGS_INTERLACED;
if (ofnode_read_bool(timings, "doublescan"))
dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
if (ofnode_read_bool(timings, "doubleclk"))
dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
return ret;
+}
const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) { if (ofnode_is_np(node)) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index fa9865602d..3f6b0843c5 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -974,6 +974,18 @@ struct display_timing; int ofnode_decode_display_timing(ofnode node, int index, struct display_timing *config);
+/**
- ofnode_decode_panel_timing() - decode display timings
- Decode panel timings from the supplied 'panel-timings' node.
- @node: 'display-timing' node containing the timing subnodes
- @config: Place to put timings
- Return: 0 if OK, -FDT_ERR_NOTFOUND if not found
- */
+int ofnode_decode_panel_timing(ofnode node,
struct display_timing *config);
/**
- ofnode_get_property() - get a pointer to the value of a node property
-- 2.17.1
Please add a test to test/dm/ofnode.c
Wanted to confirm whether I need to add a test for ofnode_get_property or for the ofnode_decode_panel_timings.
I mean for ofnode_decode_panel_timing().
You could certainly add a separate test for ofnode_get_property() too. It is somewhat implicitly tested by things like dm_test_read_int(), but more direct tests are always useful.
Regards, Simon

To support boards which don't need backlight control for displays, uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, "backlight", &priv->backlight); returns ENOENT which can be overlooked.
Signed-off-by: Nikhil M Jain n-jain1@ti.com --- drivers/video/simple_panel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c index c8f7022ea6..91c91ee75d 100644 --- a/drivers/video/simple_panel.c +++ b/drivers/video/simple_panel.c @@ -63,12 +63,15 @@ static int simple_panel_of_to_plat(struct udevice *dev) return ret; } } + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, - "backlight", &priv->backlight); + "backlight", &priv->backlight); if (ret) { debug("%s: Cannot get backlight: ret=%d\n", __func__, ret); - return log_ret(ret); + if (ret != -ENOENT) + return log_ret(ret); } + ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, GPIOD_IS_OUT); if (ret) {

On Mon, 23 Jan 2023 at 01:08, Nikhil M Jain n-jain1@ti.com wrote:
To support boards which don't need backlight control for displays, uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, "backlight", &priv->backlight); returns ENOENT which can be overlooked.
Signed-off-by: Nikhil M Jain n-jain1@ti.com
drivers/video/simple_panel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org
but please see below

Added tidss video driver support which enables display on oldi panel using AM62x, it creates a simple pipeline framebuffer==>vidl1==>ovr1==>vp1==>oldi_panel and calculates clock rates for panel from panel node in device tree.
To compile TIDSS when user sets CONFIG_VIDEO_TIDSS add rule in Makefile. Include tidss folder location in Kconfig.
TIDSS is ported from linux kernel version 5.10.145
Signed-off-by: Nikhil M Jain n-jain1@ti.com --- MAINTAINERS | 1 + drivers/video/Kconfig | 2 + drivers/video/Makefile | 1 + drivers/video/tidss/Kconfig | 18 + drivers/video/tidss/Makefile | 12 + drivers/video/tidss/tidss_drv.c | 960 +++++++++++++++++++++++++++++++ drivers/video/tidss/tidss_drv.h | 152 +++++ drivers/video/tidss/tidss_regs.h | 292 ++++++++++ 8 files changed, 1438 insertions(+) create mode 100644 drivers/video/tidss/Kconfig create mode 100644 drivers/video/tidss/Makefile create mode 100644 drivers/video/tidss/tidss_drv.c create mode 100644 drivers/video/tidss/tidss_drv.h create mode 100644 drivers/video/tidss/tidss_regs.h
diff --git a/MAINTAINERS b/MAINTAINERS index b2de50ccfc..a577f3a1a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -644,6 +644,7 @@ F: drivers/soc/ti/ F: drivers/sysreset/sysreset-ti-sci.c F: drivers/thermal/ti-bandgap.c F: drivers/timer/omap-timer.c +F: drivers/video/tidss/ F: drivers/watchdog/omap_wdt.c F: include/linux/pruss_driver.h F: include/linux/soc/ti/ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 440b161b84..40465ebea7 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -634,6 +634,8 @@ config VIDEO_SANDBOX_SDL
source "drivers/video/stm32/Kconfig"
+source "drivers/video/tidss/Kconfig" + config VIDEO_TEGRA20 bool "Enable LCD support on Tegra20" depends on OF_CONTROL diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 40a871d638..4c14cc2663 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -26,6 +26,7 @@ obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ +obj-${CONFIG_VIDEO_TIDSS} += tidss/
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o diff --git a/drivers/video/tidss/Kconfig b/drivers/video/tidss/Kconfig new file mode 100644 index 0000000000..2a5e56ea4e --- /dev/null +++ b/drivers/video/tidss/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ +# Nikhil M Jain, n-jain1@ti.com +# +# based on the linux tidss driver, which is +# +# (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/ +# Author: Tomi Valkeinen tomi.valkeinen@ti.com + +menuconfig VIDEO_TIDSS + bool "Enable TIDSS video support" + depends on VIDEO + help + TIDSS supports video output options LVDS and + DPI . This option enables these supports which can be used on + devices which have OLDI or HDMI display connected. + diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile new file mode 100644 index 0000000000..f4f8c6c470 --- /dev/null +++ b/drivers/video/tidss/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ +# Nikhil M Jain, n-jain1@ti.com +# +# based on the linux tidss driver, which is +# +# (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/ +# Author: Tomi Valkeinen tomi.valkeinen@ti.com + + +obj-${CONFIG_VIDEO_TIDSS} = tidss_drv.o diff --git a/drivers/video/tidss/tidss_drv.c b/drivers/video/tidss/tidss_drv.c new file mode 100644 index 0000000000..5caa438edd --- /dev/null +++ b/drivers/video/tidss/tidss_drv.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ + * Nikhil M Jain, n-jain1@ti.com + * + * based on the linux tidss driver, which is + * + * (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Tomi Valkeinen tomi.valkeinen@ti.com + */ + +#include <dm.h> +#include <clk.h> +#include <log.h> +#include <video.h> +#include <errno.h> +#include <panel.h> +#include <reset.h> +#include <malloc.h> +#include <fdtdec.h> +#include <common.h> +#include <syscon.h> +#include <regmap.h> +#include <linux/iopoll.h> +#include <cpu_func.h> +#include <media_bus_format.h> + +#include <asm/io.h> +#include <asm/cache.h> +#include <asm/utils.h> +#include <asm/bitops.h> + +#include <linux/bug.h> + +#include <dm/devres.h> +#include <dm/of_access.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> + +#include <linux/delay.h> +#include <linux/err.h> + +#include "tidss_drv.h" +#include "tidss_regs.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* Panel parameters */ +enum { + LCD_MAX_WIDTH = 1920, + LCD_MAX_HEIGHT = 1200, + LCD_MAX_LOG2_BPP = VIDEO_BPP32, +}; + +static const u16 *dss_common_regmap; + +static const u16 tidss_am62x_common_regs[DSS_COMMON_REG_TABLE_LEN] = { + [DSS_REVISION_OFF] = 0x4, + [DSS_SYSCONFIG_OFF] = 0x8, + [DSS_SYSSTATUS_OFF] = 0x20, + [DSS_IRQ_EOI_OFF] = 0x24, + [DSS_IRQSTATUS_RAW_OFF] = 0x28, + [DSS_IRQSTATUS_OFF] = 0x2c, + [DSS_IRQENABLE_SET_OFF] = 0x30, + [DSS_IRQENABLE_CLR_OFF] = 0x40, + [DSS_VID_IRQENABLE_OFF] = 0x44, + [DSS_VID_IRQSTATUS_OFF] = 0x58, + [DSS_VP_IRQENABLE_OFF] = 0x70, + [DSS_VP_IRQSTATUS_OFF] = 0x7c, + + [WB_IRQENABLE_OFF] = 0x88, + [WB_IRQSTATUS_OFF] = 0x8c, + + [DSS_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x90, + [DSS_GLOBAL_OUTPUT_ENABLE_OFF] = 0x94, + [DSS_GLOBAL_BUFFER_OFF] = 0x98, + [DSS_CBA_CFG_OFF] = 0x9c, + [DSS_DBG_CONTROL_OFF] = 0xa0, + [DSS_DBG_STATUS_OFF] = 0xa4, + [DSS_CLKGATING_DISABLE_OFF] = 0xa8, + [DSS_SECURE_DISABLE_OFF] = 0xac, +}; + +/* TIDSS AM62x Features */ +const struct dss_features dss_am625_feats = { + .max_pclk_khz = { + [DSS_VP_DPI] = 165000, + [DSS_VP_OLDI] = 165000, + }, + + .subrev = DSS_AM625, + + .common = "common", + .common_regs = tidss_am62x_common_regs, + + .num_vps = 2, + .vp_name = { "vp1", "vp2" }, + .ovr_name = { "ovr1", "ovr2" }, + .vpclk_name = { "vp1", "vp2" }, + .vp_bus_type = { DSS_VP_OLDI, DSS_VP_DPI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_planes = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ + .vid_name = { "vidl1", "vid1" }, + .vid_lite = { true, false }, + .vid_order = { 1, 0 }, +}; + +/* Wrapper functions to write and read TI_DSS registers */ +static void dss_write(struct tidss_drv_priv *priv, u16 reg, u32 val) +{ + writel(val, priv->base_common + reg); +} + +static u32 dss_read(struct tidss_drv_priv *priv, u16 reg) +{ + return readl(priv->base_common + reg); +} + +static void dss_vid_write(struct tidss_drv_priv *priv, u32 hw_plane, u16 reg, u32 val) +{ + void __iomem *base = priv->base_vid[hw_plane]; + + writel(val, base + reg); +} + +static u32 dss_vid_read(struct tidss_drv_priv *priv, u32 hw_plane, u16 reg) +{ + void __iomem *base = priv->base_vid[hw_plane]; + + return readl(base + reg); +} + +static void dss_ovr_write(struct tidss_drv_priv *priv, u32 hw_videoport, + u16 reg, u32 val) +{ + void __iomem *base = priv->base_ovr[hw_videoport]; + + writel(val, base + reg); +} + +static u32 dss_ovr_read(struct tidss_drv_priv *priv, u32 hw_videoport, u16 reg) +{ + void __iomem *base = priv->base_ovr[hw_videoport]; + + return readl(base + reg); +} + +static void dss_vp_write(struct tidss_drv_priv *priv, u32 hw_videoport, + u16 reg, u32 val) +{ + void __iomem *base = priv->base_vp[hw_videoport]; + + writel(val, base + reg); +} + +static u32 dss_vp_read(struct tidss_drv_priv *priv, u32 hw_videoport, u16 reg) +{ + void __iomem *base = priv->base_vp[hw_videoport]; + + return readl(base + reg); +} + +static u32 FLD_MASK(u32 start, u32 end) +{ + return ((1 << (start - end + 1)) - 1) << end; +} + +static u32 FLD_VAL(u32 val, u32 start, u32 end) +{ + return (val << end) & FLD_MASK(start, end); +} + +static u32 FLD_GET(u32 val, u32 start, u32 end) +{ + return (val & FLD_MASK(start, end)) >> end; +} + +static u32 FLD_MOD(u32 orig, u32 val, u32 start, u32 end) +{ + return (orig & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end); +} + +__maybe_unused +static u32 REG_GET(struct tidss_drv_priv *priv, u32 idx, u32 start, u32 end) +{ + return FLD_GET(dss_read(priv, idx), start, end); +} + +static void REG_FLD_MOD(struct tidss_drv_priv *priv, u32 idx, u32 val, + u32 start, u32 end) +{ + dss_write(priv, idx, FLD_MOD(dss_read(priv, idx), val, + start, end)); +} + +static u32 VID_REG_GET(struct tidss_drv_priv *priv, u32 hw_plane, u32 idx, + u32 start, u32 end) +{ + return FLD_GET(dss_vid_read(priv, hw_plane, idx), start, end); +} + +static void VID_REG_FLD_MOD(struct tidss_drv_priv *priv, u32 hw_plane, u32 idx, + u32 val, u32 start, u32 end) +{ + dss_vid_write(priv, hw_plane, idx, + FLD_MOD(dss_vid_read(priv, hw_plane, idx), + val, start, end)); +} + +__maybe_unused +static u32 VP_REG_GET(struct tidss_drv_priv *priv, u32 vp, u32 idx, + u32 start, u32 end) +{ + return FLD_GET(dss_vp_read(priv, vp, idx), start, end); +} + +static void VP_REG_FLD_MOD(struct tidss_drv_priv *priv, u32 vp, u32 idx, u32 val, + u32 start, u32 end) +{ + dss_vp_write(priv, vp, idx, FLD_MOD(dss_vp_read(priv, vp, idx), + val, start, end)); +} + +__maybe_unused +static u32 OVR_REG_GET(struct tidss_drv_priv *priv, u32 ovr, u32 idx, + u32 start, u32 end) +{ + return FLD_GET(dss_ovr_read(priv, ovr, idx), start, end); +} + +static void OVR_REG_FLD_MOD(struct tidss_drv_priv *priv, u32 ovr, u32 idx, + u32 val, u32 start, u32 end) +{ + dss_ovr_write(priv, ovr, idx, FLD_MOD(dss_ovr_read(priv, ovr, idx), + val, start, end)); +} + +static void dss_oldi_tx_power(struct tidss_drv_priv *priv, bool power) +{ + u32 val; + + if (WARN_ON(!priv->oldi_io_ctrl)) + return; + + if (priv->feat->subrev == DSS_AM625) { + if (power) { + switch (priv->oldi_mode) { + case OLDI_SINGLE_LINK_SINGLE_MODE: + /* Power down OLDI TX 1 */ + val = OLDI1_PWRDN_TX; + break; + case OLDI_DUAL_LINK: + /* No Power down */ + val = 0; + break; + default: + /* Power down both the OLDI TXes */ + val = OLDI_BANDGAP_PWR | OLDI0_PWRDN_TX | OLDI1_PWRDN_TX; + break; + } + } else { + val = OLDI_BANDGAP_PWR | OLDI0_PWRDN_TX | OLDI1_PWRDN_TX; + } + regmap_update_bits(priv->oldi_io_ctrl, OLDI_PD_CTRL, + OLDI_BANDGAP_PWR | OLDI0_PWRDN_TX | OLDI1_PWRDN_TX, val); + } +} + +static void dss_set_num_datalines(struct tidss_drv_priv *priv, + u32 hw_videoport) +{ + int v; + u32 num_lines = priv->bus_format->data_width; + + switch (num_lines) { + case 12: + v = 0; break; + case 16: + v = 1; break; + case 18: + v = 2; break; + case 24: + v = 3; break; + case 30: + v = 4; break; + case 36: + v = 5; break; + default: + WARN_ON(1); + v = 3; + } + + VP_REG_FLD_MOD(priv, hw_videoport, DSS_VP_CONTROL, v, 10, 8); +} + +static void dss_enable_oldi(struct tidss_drv_priv *priv, u32 hw_videoport) +{ + u32 oldi_cfg = 0; + u32 oldi_reset_bit = BIT(5 + hw_videoport); + int count = 0; + + /* + * For the moment MASTERSLAVE, and SRC bits of DSS_VP_DSS_OLDI_CFG are + * set statically to 0. + */ + + if (priv->bus_format->data_width == 24) + oldi_cfg |= BIT(8); /* MSB */ + else if (priv->bus_format->data_width != 18) + dev_warn(priv->dev, "%s: %d port width not supported\n", + __func__, priv->bus_format->data_width); + + oldi_cfg |= BIT(7); /* DEPOL */ + + oldi_cfg = FLD_MOD(oldi_cfg, priv->bus_format->oldi_mode_reg_val, 3, 1); + + oldi_cfg |= BIT(12); /* SOFTRST */ + + oldi_cfg |= BIT(0); /* ENABLE */ + + switch (priv->oldi_mode) { + case OLDI_MODE_OFF: + oldi_cfg &= ~BIT(0); /* DISABLE */ + break; + + case OLDI_SINGLE_LINK_SINGLE_MODE: + /* All configuration is done for this mode. */ + break; + + case OLDI_SINGLE_LINK_DUPLICATE_MODE: + oldi_cfg |= BIT(5); /* DUPLICATE MODE */ + break; + + case OLDI_DUAL_LINK: + oldi_cfg |= BIT(11); /* DUALMODESYNC */ + oldi_cfg |= BIT(3); /* data-mapping field also indicates dual-link mode */ + break; + + default: + dev_warn(priv->dev, "%s: Incorrect oldi mode. Returning.\n", + __func__); + return; + } + + dss_vp_write(priv, hw_videoport, DSS_VP_DSS_OLDI_CFG, oldi_cfg); + + while (!(oldi_reset_bit & dss_read(priv, DSS_SYSSTATUS)) && + count < 10000) + count++; + + if (!(oldi_reset_bit & dss_read(priv, DSS_SYSSTATUS))) + dev_warn(priv->dev, "%s: timeout waiting OLDI reset done\n", + __func__); +} + +static const struct dss_color_lut dss_vp_gamma_default_lut[] = { + { .red = 0, .green = 0, .blue = 0, }, + { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, +}; + +static void dss_vp_write_gamma_table(struct tidss_drv_priv *priv, + u32 hw_videoport) +{ + u32 *table = priv->vp_data[hw_videoport].gamma_table; + u32 hwlen = priv->feat->vp_feat.color.gamma_size; + unsigned int i; + + dev_dbg(priv->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); + + for (i = 0; i < hwlen; ++i) { + u32 v = table[i]; + + v |= i << 24; + + dss_vp_write(priv, hw_videoport, DSS_VP_GAMMA_TABLE, v); + } +} + +static void dss_vp_set_gamma(struct tidss_drv_priv *priv, + u32 hw_videoport, const struct dss_color_lut *lut, + unsigned int length) +{ + u32 *table = priv->vp_data[hw_videoport].gamma_table; + u32 hwlen = priv->feat->vp_feat.color.gamma_size; + u32 hwbits; + unsigned int i; + + dev_dbg(priv->dev, "%s: hw_videoport %d, lut len %u, hw len %u\n", + __func__, hw_videoport, length, hwlen); + + if (priv->feat->vp_feat.color.gamma_type == TIDSS_GAMMA_10BIT) + hwbits = 10; + else + hwbits = 8; + + lut = dss_vp_gamma_default_lut; + length = ARRAY_SIZE(dss_vp_gamma_default_lut); + + for (i = 0; i < length - 1; ++i) { + unsigned int first = i * (hwlen - 1) / (length - 1); + unsigned int last = (i + 1) * (hwlen - 1) / (length - 1); + unsigned int w = last - first; + u16 r, g, b; + unsigned int j; + + if (w == 0) + continue; + + for (j = 0; j <= w; j++) { + r = (lut[i].red * (w - j) + lut[i + 1].red * j) / w; + g = (lut[i].green * (w - j) + lut[i + 1].green * j) / w; + b = (lut[i].blue * (w - j) + lut[i + 1].blue * j) / w; + + r >>= 16 - hwbits; + g >>= 16 - hwbits; + b >>= 16 - hwbits; + + table[first + j] = (r << (hwbits * 2)) | + (g << hwbits) | b; + } + } + + dss_vp_write_gamma_table(priv, hw_videoport); +} + +void dss_vp_enable(struct tidss_drv_priv *priv, u32 hw_videoport, struct display_timing *timing) +{ + bool align, onoff, rf, ieo, ipc, ihs, ivs; + u32 hsw, hfp, hbp, vsw, vfp, vbp; + + dss_set_num_datalines(priv, hw_videoport); + + hfp = timing->hfront_porch.typ; + hsw = timing->hsync_len.typ; + hbp = timing->hback_porch.typ; + vfp = timing->vfront_porch.typ; + vsw = timing->vsync_len.typ; + vbp = timing->vback_porch.typ; + + dss_vp_write(priv, hw_videoport, DSS_VP_TIMING_H, + FLD_VAL(hsw - 1, 7, 0) | + FLD_VAL(hfp - 1, 19, 8) | FLD_VAL(hbp - 1, 31, 20)); + + dss_vp_write(priv, hw_videoport, DSS_VP_TIMING_V, + FLD_VAL(vsw - 1, 7, 0) | + FLD_VAL(vfp, 19, 8) | FLD_VAL(vbp, 31, 20)); + + ivs = !!(timing->flags & (1 << 3)); + + ihs = !!(timing->flags & (1 << 1)); + + ieo = 0; + + ipc = 0; + + /* always use the 'rf' setting */ + onoff = true; + + rf = true; + + /* always use aligned syncs */ + align = true; + + /* always use DE_HIGH for OLDI */ + if (priv->feat->vp_bus_type[hw_videoport] == DSS_VP_OLDI) + ieo = false; + + dss_vp_write(priv, hw_videoport, DSS_VP_POL_FREQ, + FLD_VAL(align, 18, 18) | + FLD_VAL(onoff, 17, 17) | + FLD_VAL(rf, 16, 16) | + FLD_VAL(ieo, 15, 15) | + FLD_VAL(ipc, 14, 14) | + FLD_VAL(ihs, 13, 13) | + FLD_VAL(ivs, 12, 12)); + + dss_vp_write(priv, hw_videoport, DSS_VP_SIZE_SCREEN, + FLD_VAL(timing->hactive.typ - 1, 11, 0) | + FLD_VAL(timing->vactive.typ - 1, 27, 16)); + + VP_REG_FLD_MOD(priv, hw_videoport, DSS_VP_CONTROL, 1, 0, 0); +} + +enum c8_to_c12_mode { C8_TO_C12_REPLICATE, C8_TO_C12_MAX, C8_TO_C12_MIN }; + +static u16 c8_to_c12(u8 c8, enum c8_to_c12_mode mode) +{ + u16 c12; + + c12 = c8 << 4; + + switch (mode) { + case C8_TO_C12_REPLICATE: + /* Copy c8 4 MSB to 4 LSB for full scale c12 */ + c12 |= c8 >> 4; + break; + case C8_TO_C12_MAX: + c12 |= 0xF; + break; + default: + case C8_TO_C12_MIN: + break; + } + + return c12; +} + +static u64 argb8888_to_argb12121212(u32 argb8888, enum c8_to_c12_mode m) +{ + u8 a, r, g, b; + u64 v; + + a = (argb8888 >> 24) & 0xff; + r = (argb8888 >> 16) & 0xff; + g = (argb8888 >> 8) & 0xff; + b = (argb8888 >> 0) & 0xff; + + v = ((u64)c8_to_c12(a, m) << 36) | ((u64)c8_to_c12(r, m) << 24) | + ((u64)c8_to_c12(g, m) << 12) | (u64)c8_to_c12(b, m); + + return v; +} + +static void dss_vp_set_default_color(struct tidss_drv_priv *priv, + u32 hw_videoport, u32 default_color) +{ + u64 v; + + v = argb8888_to_argb12121212(default_color, C8_TO_C12_REPLICATE); + dss_ovr_write(priv, hw_videoport, + DSS_OVR_DEFAULT_COLOR, v & 0xffffffff); + dss_ovr_write(priv, hw_videoport, + DSS_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff); +} + +int dss_vp_enable_clk(struct tidss_drv_priv *priv, u32 hw_videoport) +{ + int ret = clk_prepare_enable(&priv->vp_clk[hw_videoport]); + + if (ret) + dev_dbg(priv->dev, "%s: enabling clk failed: %d\n", __func__, + ret); + + return ret; +} + +void dss_vp_prepare(struct tidss_drv_priv *priv, u32 hw_videoport) +{ + dss_vp_set_gamma(priv, hw_videoport, NULL, 0); + dss_vp_set_default_color(priv, 0, 0); + + if (priv->feat->vp_bus_type[hw_videoport] == DSS_VP_OLDI) { + dss_oldi_tx_power(priv, true); + dss_enable_oldi(priv, hw_videoport); + } +} + +static +unsigned int dss_pclk_diff(unsigned long rate, unsigned long real_rate) +{ + int r = rate / 100, rr = real_rate / 100; + + return (unsigned int)(abs(((rr - r) * 100) / r)); +} + +int dss_vp_set_clk_rate(struct tidss_drv_priv *priv, u32 hw_videoport, + unsigned long rate) +{ + int r; + unsigned long new_rate; + + /* + * For AM625 OLDI video ports, the requested pixel clock needs to take into account the + * serial clock required for the serialization of DPI signals into LVDS signals. The + * incoming pixel clock on the OLDI video port gets divided by 7 whenever OLDI enable bit + * gets set. + */ + if (priv->feat->vp_bus_type[hw_videoport] == DSS_VP_OLDI && + priv->feat->subrev == DSS_AM625) + rate *= 7; + + r = clk_set_rate(&priv->vp_clk[hw_videoport], rate); + + new_rate = clk_get_rate(&priv->vp_clk[hw_videoport]); + + if (dss_pclk_diff(rate, new_rate) > 5) + dev_warn(priv->dev, + "vp%d: Clock rate %lu differs over 5%% from requested %lu\n", + hw_videoport, new_rate, rate); + + dev_dbg(priv->dev, "vp%d: new rate %lu Hz (requested %lu Hz)\n", + hw_videoport, clk_get_rate(&priv->vp_clk[hw_videoport]), rate); + return 0; +} + +static void dss_ovr_set_plane(struct tidss_drv_priv *priv, + u32 hw_plane, u32 hw_ovr, + u32 x, u32 y, u32 layer) +{ + OVR_REG_FLD_MOD(priv, hw_ovr, DSS_OVR_ATTRIBUTES(layer), + 0x1, 4, 1); + OVR_REG_FLD_MOD(priv, hw_ovr, DSS_OVR_ATTRIBUTES(layer), + x, 17, 6); + OVR_REG_FLD_MOD(priv, hw_ovr, DSS_OVR_ATTRIBUTES(layer), + y, 30, 19); +} + +void dss_ovr_enable_layer(struct tidss_drv_priv *priv, + u32 hw_ovr, u32 layer, bool enable) +{ + OVR_REG_FLD_MOD(priv, hw_ovr, DSS_OVR_ATTRIBUTES(layer), + !!enable, 0, 0); +} + +static void dss_vid_csc_enable(struct tidss_drv_priv *priv, u32 hw_plane, + bool enable) +{ + VID_REG_FLD_MOD(priv, hw_plane, DSS_VID_ATTRIBUTES, !!enable, 9, 9); +} + +int dss_plane_setup(struct tidss_drv_priv *priv, u32 hw_plane, u32 hw_videoport) +{ + VID_REG_FLD_MOD(priv, hw_plane, DSS_VID_ATTRIBUTES, + priv->pixel_format, 6, 1); + + dss_vid_write(priv, hw_plane, DSS_VID_PICTURE_SIZE, + ((LCD_MAX_WIDTH - 1) | ((LCD_MAX_HEIGHT - 1) << 16))); + + dss_vid_csc_enable(priv, hw_plane, false); + + dss_vid_write(priv, hw_plane, DSS_VID_GLOBAL_ALPHA, 0xFF); + + VID_REG_FLD_MOD(priv, hw_plane, DSS_VID_ATTRIBUTES, 1, 28, 28); + return 0; +} + +int dss_plane_enable(struct tidss_drv_priv *priv, u32 hw_plane, bool enable) +{ + VID_REG_FLD_MOD(priv, hw_plane, DSS_VID_ATTRIBUTES, !!enable, 0, 0); + + return 0; +} + +static u32 dss_vid_get_fifo_size(struct tidss_drv_priv *priv, u32 hw_plane) +{ + return VID_REG_GET(priv, hw_plane, DSS_VID_BUF_SIZE_STATUS, 15, 0); +} + +static void dss_vid_set_mflag_threshold(struct tidss_drv_priv *priv, + u32 hw_plane, u32 low, u32 high) +{ + dss_vid_write(priv, hw_plane, DSS_VID_MFLAG_THRESHOLD, + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); +} + +static +void dss_vid_set_buf_threshold(struct tidss_drv_priv *priv, + u32 hw_plane, u32 low, u32 high) +{ + dss_vid_write(priv, hw_plane, DSS_VID_BUF_THRESHOLD, + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); +} + +static void dss_plane_init(struct tidss_drv_priv *priv) +{ + unsigned int hw_plane; + u32 cba_lo_pri = 1; + u32 cba_hi_pri = 0; + + REG_FLD_MOD(priv, DSS_CBA_CFG, cba_lo_pri, 2, 0); + REG_FLD_MOD(priv, DSS_CBA_CFG, cba_hi_pri, 5, 3); + + /* MFLAG_CTRL = ENABLED */ + REG_FLD_MOD(priv, DSS_GLOBAL_MFLAG_ATTRIBUTE, 2, 1, 0); + /* MFLAG_START = MFLAGNORMALSTARTMODE */ + REG_FLD_MOD(priv, DSS_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); + + for (hw_plane = 0; hw_plane < priv->feat->num_planes; hw_plane++) { + u32 size = dss_vid_get_fifo_size(priv, hw_plane); + u32 thr_low, thr_high; + u32 mflag_low, mflag_high; + u32 preload; + + thr_high = size - 1; + thr_low = size / 2; + + mflag_high = size * 2 / 3; + mflag_low = size / 3; + + preload = thr_low; + + dev_dbg(priv->dev, + "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", + priv->feat->vid_name[hw_plane], + size, + thr_high, thr_low, + mflag_high, mflag_low, + preload); + + dss_vid_set_buf_threshold(priv, hw_plane, + thr_low, thr_high); + dss_vid_set_mflag_threshold(priv, hw_plane, + mflag_low, mflag_high); + + dss_vid_write(priv, hw_plane, DSS_VID_PRELOAD, preload); + + /* Prefech up to PRELOAD value */ + VID_REG_FLD_MOD(priv, hw_plane, DSS_VID_ATTRIBUTES, 0, + 19, 19); + } +} + +static void dss_vp_init(struct tidss_drv_priv *priv) +{ + unsigned int i; + + /* Enable the gamma Shadow bit-field for all VPs*/ + for (i = 0; i < priv->feat->num_vps; i++) + VP_REG_FLD_MOD(priv, i, DSS_VP_CONFIG, 1, 2, 2); +} + +static int dss_init_am65x_oldi_io_ctrl(struct udevice *dev, + struct tidss_drv_priv *priv) +{ + struct udevice *syscon; + struct regmap *regmap; + int ret = 0; + + ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "ti,am65x-oldi-io-ctrl", + &syscon); + if (ret) { + debug("unable to find ti,am65x-oldi-io-ctrl syscon device (%d)\n", ret); + return ret; + } + + /* get grf-reg base address */ + regmap = syscon_get_regmap(syscon); + if (!regmap) { + debug("unable to find rockchip grf regmap\n"); + return -ENODEV; + } + priv->oldi_io_ctrl = regmap; + return 0; +} + +int tidss_drv_init(struct tidss_drv_priv *priv) +{ + dss_plane_init(priv); + dss_vp_init(priv); + return 0; +} + +static int tidss_drv_probe(struct udevice *dev) +{ + struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct tidss_drv_priv *priv = dev_get_priv(dev); + struct udevice *panel = NULL; + struct display_timing timings; + unsigned int i; + int ret = 0; + const char *mode; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + + priv->feat = &dss_am625_feats; + + /* + * set your plane format based on your bmp image + * Supported 24bpp and 32bpp bmpimages + */ + + priv->pixel_format = DSS_FORMAT_XRGB8888; + + dss_common_regmap = priv->feat->common_regs; + + ret = uclass_first_device_err(UCLASS_PANEL, &panel); + if (ret) { + if (ret != -ENODEV) + dev_err(dev, "panel device error %d\n", ret); + return ret; + } + + ret = panel_get_display_timing(panel, &timings); + if (ret) { + ret = ofnode_decode_panel_timing(dev_ofnode(panel), + &timings); + if (ret) { + dev_err(dev, "decode display timing error %d\n", ret); + return ret; + } + } + + mode = ofnode_read_string(dev_ofnode(panel), "data-mapping"); + if (!mode) { + debug("%s: Could not read mode property\n", dev->name); + return -EINVAL; + } + + uc_priv->bpix = VIDEO_BPP32; + + if (!strcmp(mode, "vesa-24")) + priv->bus_format = &dss_bus_formats[7]; + else + priv->bus_format = &dss_bus_formats[8]; + + /* Common address */ + priv->base_common = dev_remap_addr_index(dev, 0); + if (!priv->base_common) + return -EINVAL; + + /* plane address setup and enable */ + for (i = 0; i < priv->feat->num_planes; i++) { + priv->base_vid[i] = dev_remap_addr_index(dev, i + 2); + if (!priv->base_vid[i]) + return -EINVAL; + } + + dss_vid_write(priv, 0, DSS_VID_BA_0, uc_plat->base & 0xffffffff); + dss_vid_write(priv, 0, DSS_VID_BA_EXT_0, (u64)uc_plat->base >> 32); + dss_vid_write(priv, 0, DSS_VID_BA_1, uc_plat->base & 0xffffffff); + dss_vid_write(priv, 0, DSS_VID_BA_EXT_1, (u64)uc_plat->base >> 32); + + ret = dss_plane_setup(priv, 0, 0); + if (ret) { + dss_plane_enable(priv, 0, false); + return ret; + } + + dss_plane_enable(priv, 0, true); + dss_plane_init(priv); + + /* video port address clocks and enable */ + for (i = 0; i < priv->feat->num_vps; i++) { + priv->base_ovr[i] = dev_remap_addr_index(dev, i + 4); + priv->base_vp[i] = dev_remap_addr_index(dev, i + 6); + } + + ret = clk_get_by_name(dev, "vp1", &priv->vp_clk[0]); + if (ret) { + dev_err(dev, "video port %d clock enable error %d\n", i, ret); + return ret; + } + + dss_ovr_set_plane(priv, 1, 0, 0, 0, 0); + dss_ovr_enable_layer(priv, 0, 0, true); + + /* Video Port cloks */ + dss_vp_enable_clk(priv, 0); + + dss_vp_set_clk_rate(priv, 0, timings.pixelclock.typ * 1000); + + priv->oldi_mode = OLDI_MODE_OFF; + uc_priv->xsize = timings.hactive.typ; + uc_priv->ysize = timings.vactive.typ; + if (priv->feat->subrev == DSS_AM65X || priv->feat->subrev == DSS_AM625) { + priv->oldi_mode = OLDI_DUAL_LINK; + if (priv->oldi_mode) { + ret = dss_init_am65x_oldi_io_ctrl(dev, priv); + if (ret) + return ret; + } + } + + dss_vp_prepare(priv, 0); + dss_vp_enable(priv, 0, &timings); + dss_vp_init(priv); + + ret = clk_get_by_name(dev, "fck", &priv->fclk); + if (ret) { + dev_err(dev, "peripheral clock get error %d\n", ret); + return ret; + } + + ret = clk_enable(&priv->fclk); + if (ret) { + dev_err(dev, "peripheral clock enable error %d\n", ret); + return ret; + } + + if (IS_ERR(&priv->fclk)) { + dev_err(dev, "%s: Failed to get fclk: %ld\n", + __func__, PTR_ERR(&priv->fclk)); + return PTR_ERR(&priv->fclk); + } + + dev_dbg(dev, "DSS fclk %lu Hz\n", clk_get_rate(&priv->fclk)); + + video_set_flush_dcache(dev, true); + return 0; +} + +static int tidss_drv_remove(struct udevice *dev) +{ + u32 val; + int ret; + struct tidss_drv_priv *priv = dev_get_priv(dev); + + priv->base_common = dev_remap_addr_index(dev, 0); + REG_FLD_MOD(priv, DSS_SYSCONFIG, 1, 1, 1); + /* Wait for reset to complete */ + ret = readl_poll_timeout(priv->base_common + DSS_SYSSTATUS, + val, val & 1, 5000); + if (ret) + dev_warn(priv->dev, "failed to reset priv\n"); + return ret; + return 0; +} + +static int tidss_drv_bind(struct udevice *dev) +{ + struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); + + uc_plat->size = ((LCD_MAX_WIDTH * LCD_MAX_HEIGHT * + (1 << LCD_MAX_LOG2_BPP)) >> 3) + 0x20; + return 0; +} + +static const struct udevice_id tidss_drv_ids[] = { + { .compatible = "ti,am625-dss" }, + { } +}; + +static int tidss_ofdata_to_platdata(struct udevice *dev) +{ + ofnode node; + + node = ofnode_by_compatible(ofnode_null(), "ti,am625-dss"); + + if (!ofnode_valid(node)) { + dev_err(dev, "missing 'ti,am625-dss' node\n"); + return -ENXIO; + } + + return 0; +} + +U_BOOT_DRIVER(tidss_drv) = { + .name = "tidss_drv", + .id = UCLASS_VIDEO, + .of_match = tidss_drv_ids, + .bind = tidss_drv_bind, + .of_to_plat = tidss_ofdata_to_platdata, + .probe = tidss_drv_probe, + .remove = tidss_drv_remove, + .priv_auto = sizeof(struct tidss_drv_priv), + .flags = DM_FLAG_OS_PREPARE, +}; diff --git a/drivers/video/tidss/tidss_drv.h b/drivers/video/tidss/tidss_drv.h new file mode 100644 index 0000000000..669c6d642e --- /dev/null +++ b/drivers/video/tidss/tidss_drv.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ + * Nikhil M Jain, n-jain1@ti.com + * + * based on the linux tidss driver, which is + * + * (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Tomi Valkeinen tomi.valkeinen@ti.com + */ + +#ifndef __TIDSS_DRV_H__ +#define __TIDSS_DRV_H__ + +#include <media_bus_format.h> + +#define TIDSS_MAX_PORTS 4 +#define TIDSS_MAX_PLANES 4 + +enum dss_vp_bus_type { + DSS_VP_DPI, /* DPI output */ + DSS_VP_OLDI, /* OLDI (LVDS) output */ + DSS_VP_INTERNAL, /* SoC internal routing */ + DSS_VP_MAX_BUS_TYPE, +}; + +enum dss_oldi_modes { + OLDI_MODE_OFF, /* OLDI turned off / tied off in IP. */ + OLDI_SINGLE_LINK_SINGLE_MODE, /* Single Output over OLDI 0. */ + OLDI_SINGLE_LINK_DUPLICATE_MODE, /* Duplicate Output over OLDI 0 and 1. */ + OLDI_DUAL_LINK, /* Combined Output over OLDI 0 and 1. */ +}; + +struct dss_features_scaling { + u32 in_width_max_5tap_rgb; + u32 in_width_max_3tap_rgb; + u32 in_width_max_5tap_yuv; + u32 in_width_max_3tap_yuv; + u32 upscale_limit; + u32 downscale_limit_5tap; + u32 downscale_limit_3tap; + u32 xinc_max; +}; + +enum tidss_gamma_type { TIDSS_GAMMA_8BIT, TIDSS_GAMMA_10BIT }; + +enum dss_subrevision { + DSS_K2G, + DSS_AM65X, + DSS_J721E, + DSS_AM625, +}; + +struct tidss_vp_feat { + struct tidss_vp_color_feat { + u32 gamma_size; + enum tidss_gamma_type gamma_type; + bool has_ctm; + } color; +}; + +struct tidss_scale_coefs { + s16 c2[16]; + s16 c1[16]; + u16 c0[9]; +}; + +struct dss_color_lut { + /* + * Data is U0.16 fixed point format. + */ + __u16 red; + __u16 green; + __u16 blue; + __u16 reserved; +}; + +struct dss_vp_data { + u32 *gamma_table; +}; + +struct dss_features { + int min_pclk_khz; + int max_pclk_khz[DSS_VP_MAX_BUS_TYPE]; + + struct dss_features_scaling scaling; + + enum dss_subrevision subrev; + + const char *common; + const u16 *common_regs; + u32 num_vps; + const char *vp_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */ + const char *ovr_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */ + const char *vpclk_name[TIDSS_MAX_PORTS]; /* Should match dt clk names */ + const enum dss_vp_bus_type vp_bus_type[TIDSS_MAX_PORTS]; + struct tidss_vp_feat vp_feat; + u32 num_planes; + const char *vid_name[TIDSS_MAX_PLANES]; /* Should match dt reg names */ + bool vid_lite[TIDSS_MAX_PLANES]; + u32 vid_order[TIDSS_MAX_PLANES]; +}; + +enum dss_oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 }; + +struct dss_bus_format { + u32 bus_fmt; + u32 data_width; + bool is_oldi_fmt; + enum dss_oldi_mode_reg_val oldi_mode_reg_val; +}; + +static struct dss_bus_format dss_bus_formats[] = { + { MEDIA_BUS_FMT_RGB444_1X12, 12, false, 0 }, + { MEDIA_BUS_FMT_RGB565_1X16, 16, false, 0 }, + { MEDIA_BUS_FMT_RGB666_1X18, 18, false, 0 }, + { MEDIA_BUS_FMT_RGB888_1X24, 24, false, 0 }, + { MEDIA_BUS_FMT_RGB101010_1X30, 30, false, 0 }, + { MEDIA_BUS_FMT_RGB121212_1X36, 36, false, 0 }, + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, true, SPWG_18 }, + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, true, SPWG_24 }, + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, true, JEIDA_24 }, +}; + +struct tidss_drv_priv { + struct udevice *dev; + void __iomem *base_common; + void __iomem *base_vid[TIDSS_MAX_PLANES]; + void __iomem *base_ovr[TIDSS_MAX_PORTS]; + void __iomem *base_vp[TIDSS_MAX_PORTS]; + struct regmap *oldi_io_ctrl; + + struct clk vp_clk[TIDSS_MAX_PORTS]; + + const struct dss_features *feat; + + struct clk fclk; + + bool is_enabled; + + struct dss_vp_data vp_data[TIDSS_MAX_PORTS]; + + enum dss_oldi_modes oldi_mode; + + struct dss_bus_format *bus_format; + + u32 pixel_format; + + u32 memory_bandwidth_limit; +}; + +#endif diff --git a/drivers/video/tidss/tidss_regs.h b/drivers/video/tidss/tidss_regs.h new file mode 100644 index 0000000000..440db8d1c7 --- /dev/null +++ b/drivers/video/tidss/tidss_regs.h @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ + * Nikhil M Jain, n-jain1@ti.com + * + * based on the linux tidss driver, which is + * + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Jyri Sarha jsarha@ti.com + */ + +#ifndef __TIDSS_REGS_H +#define __TIDSS_REGS_H + +enum dss_common_regs { + NOT_APPLICABLE_OFF = 0, + DSS_REVISION_OFF, + DSS_SYSCONFIG_OFF, + DSS_SYSSTATUS_OFF, + DSS_IRQ_EOI_OFF, + DSS_IRQSTATUS_RAW_OFF, + DSS_IRQSTATUS_OFF, + DSS_IRQENABLE_SET_OFF, + DSS_IRQENABLE_CLR_OFF, + DSS_VID_IRQENABLE_OFF, + DSS_VID_IRQSTATUS_OFF, + DSS_VP_IRQENABLE_OFF, + DSS_VP_IRQSTATUS_OFF, + WB_IRQENABLE_OFF, + WB_IRQSTATUS_OFF, + DSS_GLOBAL_MFLAG_ATTRIBUTE_OFF, + DSS_GLOBAL_OUTPUT_ENABLE_OFF, + DSS_GLOBAL_BUFFER_OFF, + DSS_CBA_CFG_OFF, + DSS_DBG_CONTROL_OFF, + DSS_DBG_STATUS_OFF, + DSS_CLKGATING_DISABLE_OFF, + DSS_SECURE_DISABLE_OFF, + FBDC_REVISION_1_OFF, + FBDC_REVISION_2_OFF, + FBDC_REVISION_3_OFF, + FBDC_REVISION_4_OFF, + FBDC_REVISION_5_OFF, + FBDC_REVISION_6_OFF, + FBDC_COMMON_CONTROL_OFF, + FBDC_CONSTANT_COLOR_0_OFF, + FBDC_CONSTANT_COLOR_1_OFF, + DSS_CONNECTIONS_OFF, + DSS_MSS_VP1_OFF, + DSS_MSS_VP3_OFF, + DSS_COMMON_REG_TABLE_LEN, +}; + +/* + * dss_common_regmap should be defined as const u16 * and pointing + * to a valid dss common register map for the platform, before the + * macros bellow can be used. + */ + +#define REG(r) (dss_common_regmap[r ## _OFF]) + +#define DSS_REVISION REG(DSS_REVISION) +#define DSS_SYSCONFIG REG(DSS_SYSCONFIG) +#define DSS_SYSSTATUS REG(DSS_SYSSTATUS) +#define DSS_IRQ_EOI REG(DSS_IRQ_EOI) +#define DSS_IRQSTATUS_RAW REG(DSS_IRQSTATUS_RAW) +#define DSS_IRQSTATUS REG(DSS_IRQSTATUS) +#define DSS_IRQENABLE_SET REG(DSS_IRQENABLE_SET) +#define DSS_IRQENABLE_CLR REG(DSS_IRQENABLE_CLR) +#define DSS_VID_IRQENABLE(n) (REG(DSS_VID_IRQENABLE) + (n) * 4) +#define DSS_VID_IRQSTATUS(n) (REG(DSS_VID_IRQSTATUS) + (n) * 4) +#define DSS_VP_IRQENABLE(n) (REG(DSS_VP_IRQENABLE) + (n) * 4) +#define DSS_VP_IRQSTATUS(n) (REG(DSS_VP_IRQSTATUS) + (n) * 4) +#define WB_IRQENABLE REG(WB_IRQENABLE) +#define WB_IRQSTATUS REG(WB_IRQSTATUS) + +#define DSS_GLOBAL_MFLAG_ATTRIBUTE REG(DSS_GLOBAL_MFLAG_ATTRIBUTE) +#define DSS_GLOBAL_OUTPUT_ENABLE REG(DSS_GLOBAL_OUTPUT_ENABLE) +#define DSS_GLOBAL_BUFFER REG(DSS_GLOBAL_BUFFER) +#define DSS_CBA_CFG REG(DSS_CBA_CFG) +#define DSS_DBG_CONTROL REG(DSS_DBG_CONTROL) +#define DSS_DBG_STATUS REG(DSS_DBG_STATUS) +#define DSS_CLKGATING_DISABLE REG(DSS_CLKGATING_DISABLE) +#define DSS_SECURE_DISABLE REG(DSS_SECURE_DISABLE) + +#define FBDC_REVISION_1 REG(FBDC_REVISION_1) +#define FBDC_REVISION_2 REG(FBDC_REVISION_2) +#define FBDC_REVISION_3 REG(FBDC_REVISION_3) +#define FBDC_REVISION_4 REG(FBDC_REVISION_4) +#define FBDC_REVISION_5 REG(FBDC_REVISION_5) +#define FBDC_REVISION_6 REG(FBDC_REVISION_6) +#define FBDC_COMMON_CONTROL REG(FBDC_COMMON_CONTROL) +#define FBDC_CONSTANT_COLOR_0 REG(FBDC_CONSTANT_COLOR_0) +#define FBDC_CONSTANT_COLOR_1 REG(FBDC_CONSTANT_COLOR_1) +#define DSS_CONNECTIONS REG(DSS_CONNECTIONS) +#define DSS_MSS_VP1 REG(DSS_MSS_VP1) +#define DSS_MSS_VP3 REG(DSS_MSS_VP3) + +/* VID */ + +#define DSS_VID_ACCUH_0 0x0 +#define DSS_VID_ACCUH_1 0x4 +#define DSS_VID_ACCUH2_0 0x8 +#define DSS_VID_ACCUH2_1 0xc +#define DSS_VID_ACCUV_0 0x10 +#define DSS_VID_ACCUV_1 0x14 +#define DSS_VID_ACCUV2_0 0x18 +#define DSS_VID_ACCUV2_1 0x1c +#define DSS_VID_ATTRIBUTES 0x20 +#define DSS_VID_ATTRIBUTES2 0x24 +#define DSS_VID_BA_0 0x28 +#define DSS_VID_BA_1 0x2c +#define DSS_VID_BA_UV_0 0x30 +#define DSS_VID_BA_UV_1 0x34 +#define DSS_VID_BUF_SIZE_STATUS 0x38 +#define DSS_VID_BUF_THRESHOLD 0x3c +#define DSS_VID_CSC_COEF(n) (0x40 + (n) * 4) + +#define DSS_VID_FIRH 0x5c +#define DSS_VID_FIRH2 0x60 +#define DSS_VID_FIRV 0x64 +#define DSS_VID_FIRV2 0x68 + +#define DSS_VID_FIR_COEFS_H0 0x6c +#define DSS_VID_FIR_COEF_H0(phase) (0x6c + (phase) * 4) +#define DSS_VID_FIR_COEFS_H0_C 0x90 +#define DSS_VID_FIR_COEF_H0_C(phase) (0x90 + (phase) * 4) + +#define DSS_VID_FIR_COEFS_H12 0xb4 +#define DSS_VID_FIR_COEF_H12(phase) (0xb4 + (phase) * 4) +#define DSS_VID_FIR_COEFS_H12_C 0xf4 +#define DSS_VID_FIR_COEF_H12_C(phase) (0xf4 + (phase) * 4) + +#define DSS_VID_FIR_COEFS_V0 0x134 +#define DSS_VID_FIR_COEF_V0(phase) (0x134 + (phase) * 4) +#define DSS_VID_FIR_COEFS_V0_C 0x158 +#define DSS_VID_FIR_COEF_V0_C(phase) (0x158 + (phase) * 4) + +#define DSS_VID_FIR_COEFS_V12 0x17c +#define DSS_VID_FIR_COEF_V12(phase) (0x17c + (phase) * 4) +#define DSS_VID_FIR_COEFS_V12_C 0x1bc +#define DSS_VID_FIR_COEF_V12_C(phase) (0x1bc + (phase) * 4) + +#define DSS_VID_GLOBAL_ALPHA 0x1fc +#define DSS_VID_K2G_IRQENABLE 0x200 /* K2G */ +#define DSS_VID_K2G_IRQSTATUS 0x204 /* K2G */ +#define DSS_VID_MFLAG_THRESHOLD 0x208 +#define DSS_VID_PICTURE_SIZE 0x20c +#define DSS_VID_PIXEL_INC 0x210 +#define DSS_VID_K2G_POSITION 0x214 /* K2G */ +#define DSS_VID_PRELOAD 0x218 +#define DSS_VID_ROW_INC 0x21c +#define DSS_VID_SIZE 0x220 +#define DSS_VID_BA_EXT_0 0x22c +#define DSS_VID_BA_EXT_1 0x230 +#define DSS_VID_BA_UV_EXT_0 0x234 +#define DSS_VID_BA_UV_EXT_1 0x238 +#define DSS_VID_CSC_COEF7 0x23c +#define DSS_VID_ROW_INC_UV 0x248 +#define DSS_VID_CLUT 0x260 +#define DSS_VID_SAFETY_ATTRIBUTES 0x2a0 +#define DSS_VID_SAFETY_CAPT_SIGNATURE 0x2a4 +#define DSS_VID_SAFETY_POSITION 0x2a8 +#define DSS_VID_SAFETY_REF_SIGNATURE 0x2ac +#define DSS_VID_SAFETY_SIZE 0x2b0 +#define DSS_VID_SAFETY_LFSR_SEED 0x2b4 +#define DSS_VID_LUMAKEY 0x2b8 +#define DSS_VID_DMA_BUFSIZE 0x2bc /* J721E */ + +/* OVR */ + +#define DSS_OVR_CONFIG 0x0 +#define DSS_OVR_VIRTVP 0x4 /* J721E */ +#define DSS_OVR_DEFAULT_COLOR 0x8 +#define DSS_OVR_DEFAULT_COLOR2 0xc +#define DSS_OVR_TRANS_COLOR_MAX 0x10 +#define DSS_OVR_TRANS_COLOR_MAX2 0x14 +#define DSS_OVR_TRANS_COLOR_MIN 0x18 +#define DSS_OVR_TRANS_COLOR_MIN2 0x1c +#define DSS_OVR_ATTRIBUTES(n) (0x20 + (n) * 4) +#define DSS_OVR_ATTRIBUTES2(n) (0x34 + (n) * 4) /* J721E */ +/* VP */ + +#define DSS_VP_CONFIG 0x0 +#define DSS_VP_CONTROL 0x4 +#define DSS_VP_CSC_COEF0 0x8 +#define DSS_VP_CSC_COEF1 0xc +#define DSS_VP_CSC_COEF2 0x10 +#define DSS_VP_DATA_CYCLE_0 0x14 +#define DSS_VP_DATA_CYCLE_1 0x18 +#define DSS_VP_K2G_GAMMA_TABLE 0x20 /* K2G */ +#define DSS_VP_K2G_IRQENABLE 0x3c /* K2G */ +#define DSS_VP_K2G_IRQSTATUS 0x40 /* K2G */ +#define DSS_VP_DATA_CYCLE_2 0x1c +#define DSS_VP_LINE_NUMBER 0x44 +#define DSS_VP_POL_FREQ 0x4c +#define DSS_VP_SIZE_SCREEN 0x50 +#define DSS_VP_TIMING_H 0x54 +#define DSS_VP_TIMING_V 0x58 +#define DSS_VP_CSC_COEF3 0x5c +#define DSS_VP_CSC_COEF4 0x60 +#define DSS_VP_CSC_COEF5 0x64 +#define DSS_VP_CSC_COEF6 0x68 +#define DSS_VP_CSC_COEF7 0x6c +#define DSS_VP_SAFETY_ATTRIBUTES_0 0x70 +#define DSS_VP_SAFETY_ATTRIBUTES_1 0x74 +#define DSS_VP_SAFETY_ATTRIBUTES_2 0x78 +#define DSS_VP_SAFETY_ATTRIBUTES_3 0x7c +#define DSS_VP_SAFETY_CAPT_SIGNATURE_0 0x90 +#define DSS_VP_SAFETY_CAPT_SIGNATURE_1 0x94 +#define DSS_VP_SAFETY_CAPT_SIGNATURE_2 0x98 +#define DSS_VP_SAFETY_CAPT_SIGNATURE_3 0x9c +#define DSS_VP_SAFETY_POSITION_0 0xb0 +#define DSS_VP_SAFETY_POSITION_1 0xb4 +#define DSS_VP_SAFETY_POSITION_2 0xb8 +#define DSS_VP_SAFETY_POSITION_3 0xbc +#define DSS_VP_SAFETY_REF_SIGNATURE_0 0xd0 +#define DSS_VP_SAFETY_REF_SIGNATURE_1 0xd4 +#define DSS_VP_SAFETY_REF_SIGNATURE_2 0xd8 +#define DSS_VP_SAFETY_REF_SIGNATURE_3 0xdc +#define DSS_VP_SAFETY_SIZE_0 0xf0 +#define DSS_VP_SAFETY_SIZE_1 0xf4 +#define DSS_VP_SAFETY_SIZE_2 0xf8 +#define DSS_VP_SAFETY_SIZE_3 0xfc +#define DSS_VP_SAFETY_LFSR_SEED 0x110 +#define DSS_VP_GAMMA_TABLE 0x120 +#define DSS_VP_DSS_OLDI_CFG 0x160 +#define DSS_VP_DSS_OLDI_STATUS 0x164 +#define DSS_VP_DSS_OLDI_LB 0x168 +#define DSS_VP_DSS_MERGE_SPLIT 0x16c /* J721E */ +#define DSS_VP_DSS_DMA_THREADSIZE 0x170 /* J721E */ +#define DSS_VP_DSS_DMA_THREADSIZE_STATUS 0x174 /* J721E */ + +/* + * OLDI IO_CTRL register offsets. On AM654 the registers are found + * from CTRL_MMR0, there the syscon regmap should map 0x14 bytes from + * CTRLMMR0P1_OLDI_DAT0_IO_CTRL to CTRLMMR0P1_OLDI_CLK_IO_CTRL + * register range. + */ +#define OLDI_DAT0_IO_CTRL 0x00 +#define OLDI_DAT1_IO_CTRL 0x04 +#define OLDI_DAT2_IO_CTRL 0x08 +#define OLDI_DAT3_IO_CTRL 0x0C +#define OLDI_CLK_IO_CTRL 0x10 + +/* Only for AM625 OLDI TX */ +#define OLDI_PD_CTRL 0x100 +#define OLDI_LB_CTRL 0x104 + +#define OLDI_BANDGAP_PWR BIT(8) +#define OLDI_PWRDN_TX BIT(8) +#define OLDI0_PWRDN_TX BIT(0) +#define OLDI1_PWRDN_TX BIT(1) + +/* Supported plane formats */ +#define DSS_FORMAT_ARGB4444 0x0 +#define DSS_FORMAT_ABGR4444 0x1 +#define DSS_FORMAT_RGBA4444 0x2 + +#define DSS_FORMAT_RGB565 0x3 +#define DSS_FORMAT_BGR565 0x4 + +#define DSS_FORMAT_ARGB1555 0x5 +#define DSS_FORMAT_ABGR1555 0x6 + +#define DSS_FORMAT_ARGB8888 0x7 +#define DSS_FORMAT_ABGR8888 0x8 +#define DSS_FORMAT_RGBA8888 0x9 +#define DSS_FORMAT_BGRA8888 0xa + +#define DSS_FORMAT_RGB888 0xb +#define DSS_FORMAT_BGR888 0xc + +#define DSS_FORMAT_ARGB2101010 0xe +#define DSS_FORMAT_ABGR2101010 0xf + +#define DSS_FORMAT_XRGB4444 0x20 +#define DSS_FORMAT_XBGR4444 0x21 +#define DSS_FORMAT_RGBX4444 0x22 + +#define DSS_FORMAT_XRGB1555 0x25 +#define DSS_FORMAT_XBGR1555 0x26 + +#define DSS_FORMAT_XRGB8888 0x27 +#define DSS_FORMAT_XBGR8888 0x28 +#define DSS_FORMAT_RGBX8888 0x29 +#define DSS_FORMAT_BGRX8888 0x2a + +#define DSS_FORMAT_XRGB2101010 0x2e, +#define DSS_FORMAT_XBGR2101010 0x2f, + +#endif /* __TIDSS_DSS_REGS_H */

() aHi Nikhil,
On Mon, 23 Jan 2023 at 01:08, Nikhil M Jain n-jain1@ti.com wrote:
Added tidss video driver support which enables display on oldi panel using AM62x, it creates a simple pipeline framebuffer==>vidl1==>ovr1==>vp1==>oldi_panel and calculates clock rates for panel from panel node in device tree.
To compile TIDSS when user sets CONFIG_VIDEO_TIDSS add rule in Makefile. Include tidss folder location in Kconfig.
TIDSS is ported from linux kernel version 5.10.145
Signed-off-by: Nikhil M Jain n-jain1@ti.com
MAINTAINERS | 1 + drivers/video/Kconfig | 2 + drivers/video/Makefile | 1 + drivers/video/tidss/Kconfig | 18 + drivers/video/tidss/Makefile | 12 + drivers/video/tidss/tidss_drv.c | 960 +++++++++++++++++++++++++++++++ drivers/video/tidss/tidss_drv.h | 152 +++++ drivers/video/tidss/tidss_regs.h | 292 ++++++++++ 8 files changed, 1438 insertions(+) create mode 100644 drivers/video/tidss/Kconfig create mode 100644 drivers/video/tidss/Makefile create mode 100644 drivers/video/tidss/tidss_drv.c create mode 100644 drivers/video/tidss/tidss_drv.h create mode 100644 drivers/video/tidss/tidss_regs.h
diff --git a/MAINTAINERS b/MAINTAINERS index b2de50ccfc..a577f3a1a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -644,6 +644,7 @@ F: drivers/soc/ti/ F: drivers/sysreset/sysreset-ti-sci.c F: drivers/thermal/ti-bandgap.c F: drivers/timer/omap-timer.c +F: drivers/video/tidss/ F: drivers/watchdog/omap_wdt.c F: include/linux/pruss_driver.h F: include/linux/soc/ti/ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 440b161b84..40465ebea7 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -634,6 +634,8 @@ config VIDEO_SANDBOX_SDL
source "drivers/video/stm32/Kconfig"
+source "drivers/video/tidss/Kconfig"
config VIDEO_TEGRA20 bool "Enable LCD support on Tegra20" depends on OF_CONTROL diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 40a871d638..4c14cc2663 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -26,6 +26,7 @@ obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ +obj-${CONFIG_VIDEO_TIDSS} += tidss/
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o diff --git a/drivers/video/tidss/Kconfig b/drivers/video/tidss/Kconfig new file mode 100644 index 0000000000..2a5e56ea4e --- /dev/null +++ b/drivers/video/tidss/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ +# Nikhil M Jain, n-jain1@ti.com +# +# based on the linux tidss driver, which is +# +# (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/ +# Author: Tomi Valkeinen tomi.valkeinen@ti.com
+menuconfig VIDEO_TIDSS
bool "Enable TIDSS video support"
depends on VIDEO
help
TIDSS supports video output options LVDS and
DPI . This option enables these supports which can be used on
devices which have OLDI or HDMI display connected.
diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile new file mode 100644 index 0000000000..f4f8c6c470 --- /dev/null +++ b/drivers/video/tidss/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/ +# Nikhil M Jain, n-jain1@ti.com +# +# based on the linux tidss driver, which is +# +# (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/ +# Author: Tomi Valkeinen tomi.valkeinen@ti.com
+obj-${CONFIG_VIDEO_TIDSS} = tidss_drv.o diff --git a/drivers/video/tidss/tidss_drv.c b/drivers/video/tidss/tidss_drv.c new file mode 100644 index 0000000000..5caa438edd --- /dev/null +++ b/drivers/video/tidss/tidss_drv.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/
- Nikhil M Jain, n-jain1@ti.com
- based on the linux tidss driver, which is
- (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/
- Author: Tomi Valkeinen tomi.valkeinen@ti.com
- */
+#include <dm.h> +#include <clk.h> +#include <log.h> +#include <video.h> +#include <errno.h> +#include <panel.h> +#include <reset.h> +#include <malloc.h> +#include <fdtdec.h> +#include <common.h> +#include <syscon.h> +#include <regmap.h> +#include <linux/iopoll.h> +#include <cpu_func.h> +#include <media_bus_format.h>
Please sort those and put the linux one below
+#include <asm/io.h> +#include <asm/cache.h> +#include <asm/utils.h> +#include <asm/bitops.h>
+#include <linux/bug.h>
+#include <dm/devres.h> +#include <dm/of_access.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h>
+#include <linux/delay.h> +#include <linux/err.h>
+#include "tidss_drv.h" +#include "tidss_regs.h"
+DECLARE_GLOBAL_DATA_PTR;
+/* Panel parameters */ +enum {
LCD_MAX_WIDTH = 1920,
LCD_MAX_HEIGHT = 1200,
LCD_MAX_LOG2_BPP = VIDEO_BPP32,
+};
+static const u16 *dss_common_regmap;
+static const u16 tidss_am62x_common_regs[DSS_COMMON_REG_TABLE_LEN] = {
[DSS_REVISION_OFF] = 0x4,
[DSS_SYSCONFIG_OFF] = 0x8,
[DSS_SYSSTATUS_OFF] = 0x20,
[DSS_IRQ_EOI_OFF] = 0x24,
[DSS_IRQSTATUS_RAW_OFF] = 0x28,
[DSS_IRQSTATUS_OFF] = 0x2c,
[DSS_IRQENABLE_SET_OFF] = 0x30,
[DSS_IRQENABLE_CLR_OFF] = 0x40,
[DSS_VID_IRQENABLE_OFF] = 0x44,
[DSS_VID_IRQSTATUS_OFF] = 0x58,
[DSS_VP_IRQENABLE_OFF] = 0x70,
[DSS_VP_IRQSTATUS_OFF] = 0x7c,
[WB_IRQENABLE_OFF] = 0x88,
[WB_IRQSTATUS_OFF] = 0x8c,
[DSS_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x90,
[DSS_GLOBAL_OUTPUT_ENABLE_OFF] = 0x94,
[DSS_GLOBAL_BUFFER_OFF] = 0x98,
[DSS_CBA_CFG_OFF] = 0x9c,
[DSS_DBG_CONTROL_OFF] = 0xa0,
[DSS_DBG_STATUS_OFF] = 0xa4,
[DSS_CLKGATING_DISABLE_OFF] = 0xa8,
[DSS_SECURE_DISABLE_OFF] = 0xac,
+};
[..]
+static u32 FLD_MASK(u32 start, u32 end) +{
return ((1 << (start - end + 1)) - 1) << end;
+}
Is this like GENMASK etc.? Please use lower case for function names and add comments as to what on earth these do :-)
+static u32 FLD_VAL(u32 val, u32 start, u32 end) +{
return (val << end) & FLD_MASK(start, end);
+}
+static u32 FLD_GET(u32 val, u32 start, u32 end) +{
return (val & FLD_MASK(start, end)) >> end;
+}
+static u32 FLD_MOD(u32 orig, u32 val, u32 start, u32 end) +{
return (orig & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end);
+}
+__maybe_unused +static u32 REG_GET(struct tidss_drv_priv *priv, u32 idx, u32 start, u32 end) +{
return FLD_GET(dss_read(priv, idx), start, end);
+}
+static void REG_FLD_MOD(struct tidss_drv_priv *priv, u32 idx, u32 val,
u32 start, u32 end)
+{
dss_write(priv, idx, FLD_MOD(dss_read(priv, idx), val,
start, end));
+}
+static u32 VID_REG_GET(struct tidss_drv_priv *priv, u32 hw_plane, u32 idx,
u32 start, u32 end)
+{
return FLD_GET(dss_vid_read(priv, hw_plane, idx), start, end);
+}
+static void VID_REG_FLD_MOD(struct tidss_drv_priv *priv, u32 hw_plane, u32 idx,
u32 val, u32 start, u32 end)
+{
dss_vid_write(priv, hw_plane, idx,
FLD_MOD(dss_vid_read(priv, hw_plane, idx),
val, start, end));
+}
+__maybe_unused +static u32 VP_REG_GET(struct tidss_drv_priv *priv, u32 vp, u32 idx,
u32 start, u32 end)
+{
return FLD_GET(dss_vp_read(priv, vp, idx), start, end);
+}
+static void VP_REG_FLD_MOD(struct tidss_drv_priv *priv, u32 vp, u32 idx, u32 val,
u32 start, u32 end)
+{
dss_vp_write(priv, vp, idx, FLD_MOD(dss_vp_read(priv, vp, idx),
val, start, end));
+}
+__maybe_unused +static u32 OVR_REG_GET(struct tidss_drv_priv *priv, u32 ovr, u32 idx,
u32 start, u32 end)
+{
return FLD_GET(dss_ovr_read(priv, ovr, idx), start, end);
+}
+static void OVR_REG_FLD_MOD(struct tidss_drv_priv *priv, u32 ovr, u32 idx,
u32 val, u32 start, u32 end)
Why all the u32? You should be able to use natural types like uint
+{
dss_ovr_write(priv, ovr, idx, FLD_MOD(dss_ovr_read(priv, ovr, idx),
val, start, end));
+}
[..]
+int tidss_drv_init(struct tidss_drv_priv *priv)
Should not export functions. What is this for?
+{
dss_plane_init(priv);
dss_vp_init(priv);
return 0;
+}
+static int tidss_drv_probe(struct udevice *dev) +{
struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct tidss_drv_priv *priv = dev_get_priv(dev);
struct udevice *panel = NULL;
struct display_timing timings;
unsigned int i;
int ret = 0;
const char *mode;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
It is already allocated by driver model - see dev_get_priv() above.
priv->dev = dev;
priv->feat = &dss_am625_feats;
- /*
* set your plane format based on your bmp image
* Supported 24bpp and 32bpp bmpimages
*/
priv->pixel_format = DSS_FORMAT_XRGB8888;
[..]
+static int tidss_ofdata_to_platdata(struct udevice *dev) +{
ofnode node;
node = ofnode_by_compatible(ofnode_null(), "ti,am625-dss");
What is going on here? Drivers should be discovered by having a driver. Please drop this code.
if (!ofnode_valid(node)) {
dev_err(dev, "missing 'ti,am625-dss' node\n");
return -ENXIO;
}
return 0;
+}
+U_BOOT_DRIVER(tidss_drv) = {
.name = "tidss_drv",
.id = UCLASS_VIDEO,
.of_match = tidss_drv_ids,
.bind = tidss_drv_bind,
.of_to_plat = tidss_ofdata_to_platdata,
.probe = tidss_drv_probe,
.remove = tidss_drv_remove,
.priv_auto = sizeof(struct tidss_drv_priv),
.flags = DM_FLAG_OS_PREPARE,
+}; diff --git a/drivers/video/tidss/tidss_drv.h b/drivers/video/tidss/tidss_drv.h new file mode 100644 index 0000000000..669c6d642e --- /dev/null +++ b/drivers/video/tidss/tidss_drv.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2023 Texas Instruments Incorporated - https://www.ti.com/
- Nikhil M Jain, n-jain1@ti.com
- based on the linux tidss driver, which is
- (C) Copyright 2018 Texas Instruments Incorporated - https://www.ti.com/
- Author: Tomi Valkeinen tomi.valkeinen@ti.com
- */
+#ifndef __TIDSS_DRV_H__ +#define __TIDSS_DRV_H__
+#include <media_bus_format.h>
+#define TIDSS_MAX_PORTS 4 +#define TIDSS_MAX_PLANES 4
+enum dss_vp_bus_type {
DSS_VP_DPI, /* DPI output */
DSS_VP_OLDI, /* OLDI (LVDS) output */
DSS_VP_INTERNAL, /* SoC internal routing */
DSS_VP_MAX_BUS_TYPE,
+};
+enum dss_oldi_modes {
OLDI_MODE_OFF, /* OLDI turned off / tied off in IP. */
OLDI_SINGLE_LINK_SINGLE_MODE, /* Single Output over OLDI 0. */
OLDI_SINGLE_LINK_DUPLICATE_MODE, /* Duplicate Output over OLDI 0 and 1. */
OLDI_DUAL_LINK, /* Combined Output over OLDI 0 and 1. */
+};
+struct dss_features_scaling {
u32 in_width_max_5tap_rgb;
u32 in_width_max_3tap_rgb;
u32 in_width_max_5tap_yuv;
u32 in_width_max_3tap_yuv;
u32 upscale_limit;
u32 downscale_limit_5tap;
u32 downscale_limit_3tap;
u32 xinc_max;
+};
+enum tidss_gamma_type { TIDSS_GAMMA_8BIT, TIDSS_GAMMA_10BIT };
+enum dss_subrevision {
DSS_K2G,
DSS_AM65X,
DSS_J721E,
DSS_AM625,
+};
+struct tidss_vp_feat {
struct tidss_vp_color_feat {
u32 gamma_size;
enum tidss_gamma_type gamma_type;
bool has_ctm;
} color;
+};
+struct tidss_scale_coefs {
s16 c2[16];
s16 c1[16];
u16 c0[9];
+};
+struct dss_color_lut {
/*
* Data is U0.16 fixed point format.
*/
__u16 red;
__u16 green;
__u16 blue;
__u16 reserved;
+};
+struct dss_vp_data {
u32 *gamma_table;
+};
+struct dss_features {
int min_pclk_khz;
int max_pclk_khz[DSS_VP_MAX_BUS_TYPE];
struct dss_features_scaling scaling;
enum dss_subrevision subrev;
const char *common;
const u16 *common_regs;
u32 num_vps;
const char *vp_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */
const char *ovr_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */
const char *vpclk_name[TIDSS_MAX_PORTS]; /* Should match dt clk names */
const enum dss_vp_bus_type vp_bus_type[TIDSS_MAX_PORTS];
struct tidss_vp_feat vp_feat;
u32 num_planes;
const char *vid_name[TIDSS_MAX_PLANES]; /* Should match dt reg names */
bool vid_lite[TIDSS_MAX_PLANES];
u32 vid_order[TIDSS_MAX_PLANES];
+};
+enum dss_oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 };
+struct dss_bus_format {
u32 bus_fmt;
u32 data_width;
bool is_oldi_fmt;
enum dss_oldi_mode_reg_val oldi_mode_reg_val;
+};
+static struct dss_bus_format dss_bus_formats[] = {
{ MEDIA_BUS_FMT_RGB444_1X12, 12, false, 0 },
{ MEDIA_BUS_FMT_RGB565_1X16, 16, false, 0 },
{ MEDIA_BUS_FMT_RGB666_1X18, 18, false, 0 },
{ MEDIA_BUS_FMT_RGB888_1X24, 24, false, 0 },
{ MEDIA_BUS_FMT_RGB101010_1X30, 30, false, 0 },
{ MEDIA_BUS_FMT_RGB121212_1X36, 36, false, 0 },
{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, true, SPWG_18 },
{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, true, SPWG_24 },
{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, true, JEIDA_24 },
+};
+struct tidss_drv_priv {
struct udevice *dev;
void __iomem *base_common;
void __iomem *base_vid[TIDSS_MAX_PLANES];
void __iomem *base_ovr[TIDSS_MAX_PORTS];
void __iomem *base_vp[TIDSS_MAX_PORTS];
struct regmap *oldi_io_ctrl;
struct clk vp_clk[TIDSS_MAX_PORTS];
const struct dss_features *feat;
struct clk fclk;
bool is_enabled;
struct dss_vp_data vp_data[TIDSS_MAX_PORTS];
enum dss_oldi_modes oldi_mode;
struct dss_bus_format *bus_format;
u32 pixel_format;
u32 memory_bandwidth_limit;
+};
Please comment this structure and drop the extra blank lines. This is not Linux :-)
+#endif

Set splash screen related env variables. Default splash source is set to mmc where user is expected to keep bmp in compressed format with name ti.gz on first partition of mmc.
Splash file will be uncompressed to DDR at address 0x82000000 and splash position is set to middle of screen.
Signed-off-by: Nikhil M Jain n-jain1@ti.com --- board/ti/am62x/am62x.env | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/board/ti/am62x/am62x.env b/board/ti/am62x/am62x.env index 3d4ab84fa3..128191f621 100644 --- a/board/ti/am62x/am62x.env +++ b/board/ti/am62x/am62x.env @@ -103,3 +103,8 @@ get_kern_mmc=load mmc ${bootpart} ${loadaddr} get_fit_mmc=load mmc ${bootpart} ${addr_fit} ${bootdir}/${name_fit} partitions=name=rootfs,start=0,size=-,uuid=${uuid_gpt_rootfs} + +splashfile=ti.gz +splashimage=0x82000000 +splashpos=m,m +splashsource=mmc

Splash screen function needs splash source information to load image and display it, splash_location provides the necessary info, Set default_splash_location to MMC at partition 1:1. Probe DSS for splash screen display.
Signed-off-by: Nikhil M Jain n-jain1@ti.com --- board/ti/am62x/evm.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/board/ti/am62x/evm.c b/board/ti/am62x/evm.c index d65ee1d696..a75c5365c8 100644 --- a/board/ti/am62x/evm.c +++ b/board/ti/am62x/evm.c @@ -10,6 +10,8 @@ #include <asm/io.h> #include <spl.h> #include <dm/uclass.h> +#include <video.h> +#include <splash.h> #include <k3-ddrss.h> #include <fdt_support.h> #include <asm/arch/hardware.h> @@ -18,8 +20,37 @@
DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_SPLASH_SCREEN + static struct splash_location default_splash_locations[] = { + { + .name = "mmc", + .storage = SPLASH_STORAGE_MMC, + .flags = SPLASH_STORAGE_FS, + .devpart = "1:1", + }, + }; + +int splash_screen_prepare(void) +{ + return splash_source_load(default_splash_locations, + ARRAY_SIZE(default_splash_locations)); +} +#endif + int board_init(void) { + if (IS_ENABLED(CONFIG_VIDEO_TIDSS)) { + int ret; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_VIDEO, "dss@30200000", &dev); + if (ret) { + debug("%s: Error device dss@30200000 tree not node added %d\n" + , __func__, ret); + if (ret != -ENODEV) + panic("Cannot get DSS device: %d\n", ret); + } + } return 0; }

Hi Nikhil,
On Mon, 23 Jan 2023 at 01:07, Nikhil M Jain n-jain1@ti.com wrote:
Splash screen function needs splash source information to load image and display it, splash_location provides the necessary info, Set default_splash_location to MMC at partition 1:1. Probe DSS for splash screen display.
Signed-off-by: Nikhil M Jain n-jain1@ti.com
board/ti/am62x/evm.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/board/ti/am62x/evm.c b/board/ti/am62x/evm.c index d65ee1d696..a75c5365c8 100644 --- a/board/ti/am62x/evm.c +++ b/board/ti/am62x/evm.c @@ -10,6 +10,8 @@ #include <asm/io.h> #include <spl.h> #include <dm/uclass.h> +#include <video.h> +#include <splash.h> #include <k3-ddrss.h> #include <fdt_support.h> #include <asm/arch/hardware.h> @@ -18,8 +20,37 @@
DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_SPLASH_SCREEN
static struct splash_location default_splash_locations[] = {
{
.name = "mmc",
.storage = SPLASH_STORAGE_MMC,
.flags = SPLASH_STORAGE_FS,
.devpart = "1:1",
},
};
+int splash_screen_prepare(void) +{
return splash_source_load(default_splash_locations,
ARRAY_SIZE(default_splash_locations));
+} +#endif
int board_init(void) {
if (IS_ENABLED(CONFIG_VIDEO_TIDSS)) {
int ret;
struct udevice *dev;
ret = uclass_get_device_by_name(UCLASS_VIDEO, "dss@30200000", &dev);
This is bound by stdio so you should not need this. What is going wrong? Does the display not appear?
Also, please drop getting devices by name...it is really not nice.
if (ret) {
debug("%s: Error device dss@30200000 tree not node added %d\n"
, __func__, ret);
if (ret != -ENODEV)
panic("Cannot get DSS device: %d\n", ret);
}
} return 0;
}
-- 2.17.1
Regards, Simon

The default splashfile name saved is ti.gz. User can use these logos to test splash screen.
Signed-off-by: Nikhil M Jain n-jain1@ti.com --- tools/logos/ti.bmp | Bin 0 -> 447258 bytes tools/logos/ti.gz | Bin 0 -> 19604 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tools/logos/ti.bmp create mode 100644 tools/logos/ti.gz
diff --git a/tools/logos/ti.bmp b/tools/logos/ti.bmp new file mode 100644 index 0000000000000000000000000000000000000000..46b194eb0bf7715903bccfac21aa4ed7b24de376 GIT binary patch literal 447258 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
literal 0 HcmV?d00001
diff --git a/tools/logos/ti.gz b/tools/logos/ti.gz new file mode 100644 index 0000000000000000000000000000000000000000..cea23b532b09ce0b30395bc138b609af1aa7b7f6 GIT binary patch literal 19604 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	H7 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
literal 0 HcmV?d00001

Hi Nikhil,
On Mon, 23 Jan 2023 at 01:08, Nikhil M Jain n-jain1@ti.com wrote:
The default splashfile name saved is ti.gz. User can use these logos to test splash screen.
Signed-off-by: Nikhil M Jain n-jain1@ti.com
tools/logos/ti.bmp | Bin 0 -> 447258 bytes tools/logos/ti.gz | Bin 0 -> 19604 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tools/logos/ti.bmp create mode 100644 tools/logos/ti.gz
diff --git a/tools/logos/ti.bmp b/tools/logos/ti.bmp new file mode 100644 index 0000000000000000000000000000000000000000..46b194eb0bf7715903bccfac21aa4ed7b24de376 GIT binary patch literal 447258
That seems quite large. Could you use compression, reduce the size, or should we add PNG8 support?
Regards, Simon
participants (3)
-
Nikhil M Jain
-
Nikhl M Jain
-
Simon Glass