From 857d02d97c222b0b5c96fdcc90032453646c0f06 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:40:59 +0530 Subject: drivers: video: Kconfig: Add configs for enabling video at SPL Add Kconfigs which enable the video driver and splash screen at SPL stage only and not at u-boot proper. The existing Kconfigs from u-boot proper were not used to make SPL splash screen independent to them. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass Tested-by: Simon Glass --- drivers/video/Kconfig | 223 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 1 deletion(-) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 334d64c..9b7abd4 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -886,7 +886,7 @@ endif # SPLASH_SCREEN config VIDEO_BMP_GZIP bool "Gzip compressed BMP image support" - depends on CMD_BMP || SPLASH_SCREEN + depends on BMP || SPLASH_SCREEN help If this option is set, additionally to standard BMP images, gzipped BMP images can be displayed via the @@ -923,4 +923,225 @@ config BMP_32BPP endif # VIDEO +config SPL_VIDEO + bool "Enable driver model support for LCD/video" + depends on SPL_DM + help + The video subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use video drivers in + SPL, enable this option. It might provide a cleaner interface to + setting up video within SPL, and allows the same drivers to be + used as U-Boot proper. + +if SPL_VIDEO +source "drivers/video/tidss/Kconfig" + +config SPL_VIDEO_LOGO + bool "Show the U-Boot logo on the display at SPL" + default y if !SPL_SPLASH_SCREEN + select SPL_VIDEO_BMP_RLE8 + help + This enables showing the U-Boot logo on the display when a video + device is probed. It appears at the top right. The logo itself is at + tools/logos/u-boot_logo.bmp and looks best when the display has a + black background. + +config SPL_SPLASH_SCREEN + bool "Show a splash-screen image at SPL" + help + If this option is set, the environment is checked for a variable + "splashimage" at spl stage. + +config SPL_SYS_WHITE_ON_BLACK + bool "Display console as white on a black background at SPL" + help + Normally the display is black on a white background, Enable this + option to invert this, i.e. white on a black background at spl stage. + This can be better in low-light situations or to reduce eye strain in + some cases. + +config SPL_VIDEO_PCI_DEFAULT_FB_SIZE + hex "Default framebuffer size to use if no drivers request it at SPL" + default 0x1000000 if X86 && PCI + default 0 if !(X86 && PCI) + help + Generally, video drivers request the amount of memory they need for + the frame buffer when they are bound, by setting the size field in + struct video_uc_plat. That memory is then reserved for use after + relocation. But PCI drivers cannot be bound before relocation unless + they are mentioned in the devicetree. + + With this value set appropriately, it is possible for PCI video + devices to have a framebuffer allocated by U-Boot. + + Note: the framebuffer needs to be large enough to store all pixels at + maximum resolution. For example, at 1920 x 1200 with 32 bits per + pixel, 2560 * 1600 * 32 / 8 = 0xfa0000 bytes are needed. + +config SPL_CONSOLE_SCROLL_LINES + int "Number of lines to scroll the console by at SPL" + default 1 + help + When the console need to be scrolled, this is the number of + lines to scroll by. It defaults to 1. Increasing this makes the + console jump but can help speed up operation when scrolling + is slow. + +config SPL_CONSOLE_NORMAL + bool "Support a simple text console at SPL" + default y + help + Support drawing text on the frame buffer console so that it can be + used as a console. Rotation is not supported by this driver (see + CONFIG_CONSOLE_ROTATION for that). A built-in 8x16 font is used + for the display. + +config SPL_BACKLIGHT + bool "Enable panel backlight uclass support at SPL" + default y + help + This provides backlight uclass driver that enables basic panel + backlight support. + +config SPL_PANEL + bool "Enable panel uclass support at SPL" + default y + help + This provides panel uclass driver that enables basic panel support. + +config SPL_SIMPLE_PANEL + bool "Enable simple panel support at SPL" + depends on SPL_PANEL && SPL_BACKLIGHT && SPL_DM_GPIO + default y + help + This turns on a simple panel driver that enables a compatible + video panel. + +config SPL_SYS_WHITE_ON_BLACK + bool "Display console as white on a black background at SPL" + help + Normally the display is black on a white background, Enable this + option to invert this, i.e. white on a black background at spl stage. + This can be better in low-light situations or to reduce eye strain in + some cases. + +if SPL_SPLASH_SCREEN + +config SPL_SPLASH_SCREEN_ALIGN + bool "Allow positioning the splash image anywhere on the display at SPL" + help + If this option is set the splash image can be freely positioned + on the screen only at SPL. Environment variable "splashpos" specifies + the position as "x,y". If a positive number is given it is used as + number of pixel from left/top. If a negative number is given it + is used as number of pixel from right/bottom. + +config SPL_SPLASH_SOURCE + bool "Control the source of the splash image at SPL" + help + Use the splash_source.c library. This library provides facilities to + declare board specific splash image locations, routines for loading + splash image from supported locations, and a way of controlling the + selected splash location using the "splashsource" environment + variable. + + This CONFIG works as follows: + + - If splashsource is set to a supported location name as defined by + board code, use that splash location. + - If splashsource is undefined, use the first splash location as + default. + - If splashsource is set to an unsupported value, do not load a splash + screen. + + A splash source location can describe either storage with raw data, a + storage formatted with a file system or a FIT image. In case of a + filesystem, the splash screen data is loaded as a file. The name of + the splash screen file can be controlled with the environment variable + "splashfile". + + To enable loading the splash image from a FIT image, CONFIG_FIT must + be enabled. The FIT image has to start at the 'offset' field address + in the selected splash location. The name of splash image within the + FIT shall be specified by the environment variable "splashfile". + + In case the environment variable "splashfile" is not defined the + default name 'splash.bmp' will be used. + +endif # SPL_SPLASH_SCREEN + +config SPL_VIDEO_BMP_GZIP + bool "Gzip compressed BMP image support at SPL" + depends on SPL_SPLASH_SCREEN || SPL_BMP + help + If this option is set, additionally to standard BMP + images, gzipped BMP images can be displayed via the + splashscreen supportat SPL stage. + +config SPL_VIDEO_LOGO_MAX_SIZE + hex "Maximum size of the bitmap logo in bytes at SPL" + default 0x100000 + help + Sets the maximum uncompressed size of the logo. This is needed when + decompressing a BMP file using the gzip algorithm, since it cannot + read the size from the bitmap header. + +config SPL_VIDEO_BMP_RLE8 + bool "Run length encoded BMP image (RLE8) support at SPL" + help + If this option is set, the 8-bit RLE compressed BMP images + is supported. + +config SPL_BMP_16BPP + bool "16-bit-per-pixel BMP image support at SPL" + help + Support display of bitmaps file with 16-bit-per-pixel + +config SPL_BMP_24BPP + bool "24-bit-per-pixel BMP image support at SPL" + help + Support display of bitmaps file with 24-bit-per-pixel. + +config SPL_BMP_32BPP + bool "32-bit-per-pixel BMP image support at SPL" + help + Support display of bitmaps file with 32-bit-per-pixel. + +config SPL_VIDEO_BPP8 + bool "Support 8-bit-per-pixel displays at SPL" + default y + help + Support drawing text and bitmaps onto a 8-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config SPL_VIDEO_BPP16 + bool "Support 16-bit-per-pixel displays at SPL" + default y + help + Support drawing text and bitmaps onto a 16-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config SPL_VIDEO_BPP32 + bool "Support 32-bit-per-pixel displays at SPL" + default y + help + Support drawing text and bitmaps onto a 32-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config SPL_HIDE_LOGO_VERSION + bool "Hide the version information on the splash screen at SPL" + help + Normally the U-Boot version string is shown on the display when the + splash screen is enabled. This information is not otherwise visible + since video starts up after U-Boot has displayed the initial banner. + + Enable this option to hide this information. +endif + endmenu -- cgit v1.1 From 54929ecdd2141f23732d5bc8de7fa62d1d2b4d02 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:00 +0530 Subject: drivers: video: tidss: Kconfig: Configs to enable TIDSS at SPL To enable tidss display driver only at SPL stage, add necessary config, CONFIG_SPL_VIDEO_TIDSS. Signed-off-by: Nikhil M Jain Reviewed-by: Simon Glass Reviewed-by: Devarsh Thakkar --- drivers/video/tidss/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/video/tidss/Kconfig b/drivers/video/tidss/Kconfig index 2a5e56e..95086f3 100644 --- a/drivers/video/tidss/Kconfig +++ b/drivers/video/tidss/Kconfig @@ -16,3 +16,9 @@ menuconfig VIDEO_TIDSS DPI . This option enables these supports which can be used on devices which have OLDI or HDMI display connected. +config SPL_VIDEO_TIDSS + bool "Enable TIDSS video support in SPL Stage" + depends on SPL_VIDEO + help + This options enables tidss driver in SPL stage. If + you need to use tidss at SPL stage use this config. -- cgit v1.1 From e3357a9e036f92ca389f8c3cfa3ed335b2f798e1 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:01 +0530 Subject: drivers: Makefile: Add rule to compile video driver Compile video driver at SPL using CONFIG_SPL_VIDEO. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass Tested-by: Simon Glass # qemu-x86_64 --- drivers/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/Makefile b/drivers/Makefile index 58be410..928a8b0 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_SPL_USB_HOST) += usb/host/ obj-$(CONFIG_SPL_SATA) += ata/ scsi/ obj-$(CONFIG_SPL_LEGACY_BLOCK) += block/ obj-$(CONFIG_SPL_THERMAL) += thermal/ +obj-$(CONFIG_SPL_VIDEO) +=video/ endif endif -- cgit v1.1 From 35ca2e4fa21187b13e6ab323ac5782292f52a634 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:02 +0530 Subject: drivers: video: Makefile: Rule to compile necessary video driver files To enable video driver at SPL, need to compile video-uclass, vidconsole-uclass, backlight-uclass, panel-uclass, simple-panel, add rules to compile them at SPL and u-boot proper. To support splash_display at SPL, need to compile video-bmp, add rule to compile at SPL and u-boot proper. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass Tested-by: Simon Glass --- drivers/video/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 4d75771..868e834 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -4,12 +4,12 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. ifdef CONFIG_DM -obj-$(CONFIG_BACKLIGHT) += backlight-uclass.o +obj-$(CONFIG_$(SPL_TPL_)BACKLIGHT) += backlight-uclass.o obj-$(CONFIG_BACKLIGHT_GPIO) += backlight_gpio.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_backlight.o -obj-$(CONFIG_CONSOLE_NORMAL) += console_normal.o +obj-$(CONFIG_$(SPL_TPL_)CONSOLE_NORMAL) += console_normal.o obj-$(CONFIG_CONSOLE_ROTATION) += console_rotate.o -ifdef CONFIG_CONSOLE_NORMAL +ifdef CONFIG_$(SPL_TPL_)CONSOLE_NORMAL obj-y += console_core.o else ifdef CONFIG_CONSOLE_ROTATION obj-y += console_core.o @@ -18,11 +18,11 @@ obj-$(CONFIG_CONSOLE_ROTATION) += console_core.o obj-$(CONFIG_CONSOLE_TRUETYPE) += console_truetype.o fonts/ obj-$(CONFIG_DISPLAY) += display-uclass.o obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi-host-uclass.o -obj-$(CONFIG_VIDEO) += video-uclass.o vidconsole-uclass.o -obj-$(CONFIG_VIDEO) += video_bmp.o -obj-$(CONFIG_PANEL) += panel-uclass.o +obj-$(CONFIG_$(SPL_TPL_)VIDEO) += video-uclass.o vidconsole-uclass.o +obj-$(CONFIG_$(SPL_TPL_)VIDEO) += video_bmp.o +obj-$(CONFIG_$(SPL_TPL_)PANEL) += panel-uclass.o obj-$(CONFIG_PANEL_HX8238D) += hx8238d.o -obj-$(CONFIG_SIMPLE_PANEL) += simple_panel.o +obj-$(CONFIG_$(SPL_TPL_)SIMPLE_PANEL) += simple_panel.o obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.o -- cgit v1.1 From 2e72e4ec847bd81b4f30d6641efb70c3b422ac7b Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:03 +0530 Subject: drivers: video: tidss: Makefile: Add condition to compile TIDSS at SPL To enable TIDSS driver only at SPL stage add rule to compile the TIDSS video driver. CONFIG_$(SPL_)VIDEO_TIDSS will compile tidss_drv, at SPL only if CONFIG_SPL_VIDEO_TIDSS is defined and at u-boot proper if CONFIG_VIDEO_TIDSS is defined. Signed-off-by: Nikhil M Jain Reviewed-by: Simon Glass --- drivers/video/Makefile | 2 +- drivers/video/tidss/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 868e834..a609e35 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -32,7 +32,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_$(SPL_)VIDEO_TIDSS} += tidss/ obj-y += ti/ obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile index f4f8c6c..a29cee2 100644 --- a/drivers/video/tidss/Makefile +++ b/drivers/video/tidss/Makefile @@ -9,4 +9,4 @@ # Author: Tomi Valkeinen -obj-${CONFIG_VIDEO_TIDSS} = tidss_drv.o +obj-${CONFIG_$(SPL_)VIDEO_TIDSS} = tidss_drv.o -- cgit v1.1 From a8bca7ea61a6c492fbd6c32c6057168dd3f0b19b Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:04 +0530 Subject: common: Makefile: Add rule to compile splash and splash_source at SPL To enable splash screen and loading bmp from boot media, add rules to compile splash.c and splash_source.c at SPL stage only when CONFIG_SPL_SPLASH_SCREEN and CONFIG_SPL_SPLASH_SOURCE are defined. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass --- common/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/Makefile b/common/Makefile index a50302d..3a6ca33 100644 --- a/common/Makefile +++ b/common/Makefile @@ -56,6 +56,8 @@ obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += fdt_support.o obj-$(CONFIG_SPL_USB_HOST) += usb.o usb_hub.o obj-$(CONFIG_SPL_USB_STORAGE) += usb_storage.o obj-$(CONFIG_SPL_MUSB_NEW) += usb.o +obj-$(CONFIG_SPL_SPLASH_SCREEN) += splash.o +obj-$(CONFIG_SPL_SPLASH_SOURCE) += splash_source.o endif # CONFIG_SPL_BUILD #others -- cgit v1.1 From 072b0e16c482114d242580dd7a3197db5966705f Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:05 +0530 Subject: common: Kconfig: Add BMP configs Add CONFIG_BMP and CONFIG_SPL_BMP which enable displaying bmp images at u-boot proper and SPL. Signed-off-by: Nikhil M Jain Reviewed-by: Simon Glass Reviewed-by: Devarsh Thakkar --- common/Kconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/Kconfig b/common/Kconfig index f2783ee..7c72186 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1154,3 +1154,15 @@ config FDT_SIMPLEFB config IO_TRACE bool + +config BMP + bool "Enable bmp image display" + default y if CMD_BMP + help + Enable bmp functions to display bmp image and get bmp info. + +config SPL_BMP + bool "Enable bmp image display at SPL" + depends on SPL_VIDEO + help + Enable bmp functions to display bmp image and get bmp info at SPL. -- cgit v1.1 From 58182b2db9d80930b2d98bfe08a08d3437e8a302 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:06 +0530 Subject: cmd: bmp: Split bmp commands and functions To enable splash screen at SPL, need to compile cmd/bmp.c which also includes bmp commands, since SPL doesn't use commands split bmp.c into common/bmp.c which includes all bmp functions and cmd/bmp.c which only contains bmp commands. Add function delclaration for bmp_info in video.h. Signed-off-by: Nikhil M Jain Reviewed-by: Simon Glass --- cmd/bmp.c | 162 +------------------------------------------------------- common/bmp.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/video.h | 8 +++ 3 files changed, 163 insertions(+), 160 deletions(-) create mode 100644 common/bmp.c diff --git a/cmd/bmp.c b/cmd/bmp.c index 46d0d91..8f43a40 100644 --- a/cmd/bmp.c +++ b/cmd/bmp.c @@ -9,84 +9,12 @@ */ #include -#include #include -#include -#include #include -#include -#include #include #include #include -#include - -static int bmp_info (ulong addr); - -/* - * Allocate and decompress a BMP image using gunzip(). - * - * Returns a pointer to the decompressed image data. This pointer is - * aligned to 32-bit-aligned-address + 2. - * See doc/README.displaying-bmps for explanation. - * - * The allocation address is passed to 'alloc_addr' and must be freed - * by the caller after use. - * - * Returns NULL if decompression failed, or if the decompressed data - * didn't contain a valid BMP signature. - */ -#ifdef CONFIG_VIDEO_BMP_GZIP -struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, - void **alloc_addr) -{ - void *dst; - unsigned long len; - struct bmp_image *bmp; - - /* - * Decompress bmp image - */ - len = CONFIG_VIDEO_LOGO_MAX_SIZE; - /* allocate extra 3 bytes for 32-bit-aligned-address + 2 alignment */ - dst = malloc(CONFIG_VIDEO_LOGO_MAX_SIZE + 3); - if (!dst) { - puts("Error: malloc in gunzip failed!\n"); - return NULL; - } - - /* align to 32-bit-aligned-address + 2 */ - bmp = dst + 2; - - if (gunzip(bmp, CONFIG_VIDEO_LOGO_MAX_SIZE, map_sysmem(addr, 0), - &len)) { - free(dst); - return NULL; - } - if (len == CONFIG_VIDEO_LOGO_MAX_SIZE) - puts("Image could be truncated (increase CONFIG_VIDEO_LOGO_MAX_SIZE)!\n"); - - /* - * Check for bmp mark 'BM' - */ - if (!((bmp->header.signature[0] == 'B') && - (bmp->header.signature[1] == 'M'))) { - free(dst); - return NULL; - } - - debug("Gzipped BMP image detected!\n"); - - *alloc_addr = dst; - return bmp; -} -#else -struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, - void **alloc_addr) -{ - return NULL; -} -#endif +#include static int do_bmp_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) @@ -137,7 +65,7 @@ static int do_bmp_display(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_USAGE; } - return (bmp_display(addr, x, y)); + return (bmp_display(addr, x, y)); } static struct cmd_tbl cmd_bmp_sub[] = { @@ -145,22 +73,6 @@ static struct cmd_tbl cmd_bmp_sub[] = { U_BOOT_CMD_MKENT(display, 5, 0, do_bmp_display, "", ""), }; -#ifdef CONFIG_NEEDS_MANUAL_RELOC -void bmp_reloc(void) { - fixup_cmdtable(cmd_bmp_sub, ARRAY_SIZE(cmd_bmp_sub)); -} -#endif - -/* - * Subroutine: do_bmp - * - * Description: Handler for 'bmp' command.. - * - * Inputs: argv[1] contains the subcommand - * - * Return: None - * - */ static int do_bmp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct cmd_tbl *c; @@ -183,73 +95,3 @@ U_BOOT_CMD( "info - display image info\n" "bmp display [x y] - display image at x,y" ); - -/* - * Subroutine: bmp_info - * - * Description: Show information about bmp file in memory - * - * Inputs: addr address of the bmp file - * - * Return: None - * - */ -static int bmp_info(ulong addr) -{ - struct bmp_image *bmp = (struct bmp_image *)map_sysmem(addr, 0); - void *bmp_alloc_addr = NULL; - unsigned long len; - - if (!((bmp->header.signature[0]=='B') && - (bmp->header.signature[1]=='M'))) - bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); - - if (bmp == NULL) { - printf("There is no valid bmp file at the given address\n"); - return 1; - } - - printf("Image size : %d x %d\n", le32_to_cpu(bmp->header.width), - le32_to_cpu(bmp->header.height)); - printf("Bits per pixel: %d\n", le16_to_cpu(bmp->header.bit_count)); - printf("Compression : %d\n", le32_to_cpu(bmp->header.compression)); - - if (bmp_alloc_addr) - free(bmp_alloc_addr); - - return(0); -} - -int bmp_display(ulong addr, int x, int y) -{ - struct udevice *dev; - int ret; - struct bmp_image *bmp = map_sysmem(addr, 0); - void *bmp_alloc_addr = NULL; - unsigned long len; - - if (!((bmp->header.signature[0]=='B') && - (bmp->header.signature[1]=='M'))) - bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); - - if (!bmp) { - printf("There is no valid bmp file at the given address\n"); - return 1; - } - addr = map_to_sysmem(bmp); - - ret = uclass_first_device_err(UCLASS_VIDEO, &dev); - if (!ret) { - bool align = false; - - if (x == BMP_ALIGN_CENTER || y == BMP_ALIGN_CENTER) - align = true; - - ret = video_bmp_display(dev, addr, x, y, align); - } - - if (bmp_alloc_addr) - free(bmp_alloc_addr); - - return ret ? CMD_RET_FAILURE : 0; -} diff --git a/common/bmp.c b/common/bmp.c new file mode 100644 index 0000000..4dbcb84 --- /dev/null +++ b/common/bmp.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de. + */ + +/* + * BMP handling routines + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Allocate and decompress a BMP image using gunzip(). + * + * Returns a pointer to the decompressed image data. This pointer is + * aligned to 32-bit-aligned-address + 2. + * See doc/README.displaying-bmps for explanation. + * + * The allocation address is passed to 'alloc_addr' and must be freed + * by the caller after use. + * + * Returns NULL if decompression failed, or if the decompressed data + * didn't contain a valid BMP signature. + */ +#ifdef CONFIG_VIDEO_BMP_GZIP +struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr) +{ + void *dst; + unsigned long len; + struct bmp_image *bmp; + + /* + * Decompress bmp image + */ + len = CONFIG_VIDEO_LOGO_MAX_SIZE; + /* allocate extra 3 bytes for 32-bit-aligned-address + 2 alignment */ + dst = malloc(CONFIG_VIDEO_LOGO_MAX_SIZE + 3); + if (!dst) { + puts("Error: malloc in gunzip failed!\n"); + return NULL; + } + + /* align to 32-bit-aligned-address + 2 */ + bmp = dst + 2; + + if (gunzip(bmp, CONFIG_VIDEO_LOGO_MAX_SIZE, map_sysmem(addr, 0), + &len)) { + free(dst); + return NULL; + } + if (len == CONFIG_VIDEO_LOGO_MAX_SIZE) + puts("Image could be truncated (increase CONFIG_VIDEO_LOGO_MAX_SIZE)!\n"); + + /* + * Check for bmp mark 'BM' + */ + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + free(dst); + return NULL; + } + + debug("Gzipped BMP image detected!\n"); + + *alloc_addr = dst; + return bmp; +} +#else +struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr) +{ + return NULL; +} +#endif + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +void bmp_reloc(void) +{ + fixup_cmdtable(cmd_bmp_sub, ARRAY_SIZE(cmd_bmp_sub)); +} +#endif + +int bmp_info(ulong addr) +{ + struct bmp_image *bmp = (struct bmp_image *)map_sysmem(addr, 0); + void *bmp_alloc_addr = NULL; + unsigned long len; + + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) + bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); + + if (!bmp) { + printf("There is no valid bmp file at the given address\n"); + return 1; + } + + printf("Image size : %d x %d\n", le32_to_cpu(bmp->header.width), + le32_to_cpu(bmp->header.height)); + printf("Bits per pixel: %d\n", le16_to_cpu(bmp->header.bit_count)); + printf("Compression : %d\n", le32_to_cpu(bmp->header.compression)); + + if (bmp_alloc_addr) + free(bmp_alloc_addr); + + return 0; +} + +int bmp_display(ulong addr, int x, int y) +{ + struct udevice *dev; + int ret; + struct bmp_image *bmp = map_sysmem(addr, 0); + void *bmp_alloc_addr = NULL; + unsigned long len; + + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) + bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); + + if (!bmp) { + printf("There is no valid bmp file at the given address\n"); + return 1; + } + addr = map_to_sysmem(bmp); + + ret = uclass_first_device_err(UCLASS_VIDEO, &dev); + if (!ret) { + bool align = false; + + if (x == BMP_ALIGN_CENTER || y == BMP_ALIGN_CENTER) + align = true; + + ret = video_bmp_display(dev, addr, x, y, align); + } + + if (bmp_alloc_addr) + free(bmp_alloc_addr); + + return ret ? CMD_RET_FAILURE : 0; +} diff --git a/include/video.h b/include/video.h index 4d99e5d..29c4f51 100644 --- a/include/video.h +++ b/include/video.h @@ -357,4 +357,12 @@ void *video_get_u_boot_logo(void); */ int bmp_display(ulong addr, int x, int y); +/* + * bmp_info() - Show information about bmp file + * + * @addr: address of bmp file + * Returns: 0 if OK, else 1 if bmp image not found + */ +int bmp_info(ulong addr); + #endif -- cgit v1.1 From d45e1d99679c4d214ab12eebefeb68bf11ab58a3 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:07 +0530 Subject: common: Makefile: Rule to compile bmp.c Add rule to compile bmp.c at SPL and u-boot proper when CONFIG_SPL_BMP and CONFIG_BMP are defined. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass Tested-by: Simon Glass --- common/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/common/Makefile b/common/Makefile index 3a6ca33..c87bb2e 100644 --- a/common/Makefile +++ b/common/Makefile @@ -45,6 +45,7 @@ endif # !CONFIG_SPL_BUILD obj-$(CONFIG_$(SPL_TPL_)BOOTSTAGE) += bootstage.o obj-$(CONFIG_$(SPL_TPL_)BLOBLIST) += bloblist.o +obj-$(CONFIG_$(SPL_)BMP) += bmp.o ifdef CONFIG_SPL_BUILD ifdef CONFIG_SPL_DFU -- cgit v1.1 From 86fbee6020f2b394b8570d34c5367570776a3000 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:08 +0530 Subject: drivers: video: Enable necessary video functions at SPL To support video driver at SPL use CONFIG_IS_ENABLED and CONFIG_VAL, which checks for stage specific configs and thus enables video support at respective stage. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass Tested-by: Simon Glass --- drivers/video/console_core.c | 6 +++--- drivers/video/vidconsole-uclass.c | 2 +- drivers/video/video-uclass.c | 14 +++++++------- drivers/video/video_bmp.c | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c index d4f79c6..1f93b1b 100644 --- a/drivers/video/console_core.c +++ b/drivers/video/console_core.c @@ -46,11 +46,11 @@ static int console_set_font(struct udevice *dev, struct video_fontdata *fontdata int check_bpix_support(int bpix) { - if (bpix == VIDEO_BPP8 && IS_ENABLED(CONFIG_VIDEO_BPP8)) + if (bpix == VIDEO_BPP8 && CONFIG_IS_ENABLED(VIDEO_BPP8)) return 0; - else if (bpix == VIDEO_BPP16 && IS_ENABLED(CONFIG_VIDEO_BPP16)) + else if (bpix == VIDEO_BPP16 && CONFIG_IS_ENABLED(VIDEO_BPP16)) return 0; - else if (bpix == VIDEO_BPP32 && IS_ENABLED(CONFIG_VIDEO_BPP32)) + else if (bpix == VIDEO_BPP32 && CONFIG_IS_ENABLED(VIDEO_BPP32)) return 0; else return -ENOSYS; diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 1225de2..a21fde0 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -86,7 +86,7 @@ static void vidconsole_newline(struct udevice *dev) struct vidconsole_priv *priv = dev_get_uclass_priv(dev); struct udevice *vid_dev = dev->parent; struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev); - const int rows = CONFIG_CONSOLE_SCROLL_LINES; + const int rows = CONFIG_VAL(CONSOLE_SCROLL_LINES); int i, ret; priv->xcur_frac = priv->xstart_frac; diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index da89f43..8396bdf 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -132,7 +132,7 @@ int video_reserve(ulong *addrp) /* Allocate space for PCI video devices in case there were not bound */ if (*addrp == gd->video_top) - *addrp -= CONFIG_VIDEO_PCI_DEFAULT_FB_SIZE; + *addrp -= CONFIG_VAL(VIDEO_PCI_DEFAULT_FB_SIZE); gd->video_bottom = *addrp; gd->fb_base = *addrp; @@ -149,7 +149,7 @@ int video_fill(struct udevice *dev, u32 colour) switch (priv->bpix) { case VIDEO_BPP16: - if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { + if (CONFIG_IS_ENABLED(VIDEO_BPP16)) { u16 *ppix = priv->fb; u16 *end = priv->fb + priv->fb_size; @@ -158,7 +158,7 @@ int video_fill(struct udevice *dev, u32 colour) break; } case VIDEO_BPP32: - if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { + if (CONFIG_IS_ENABLED(VIDEO_BPP32)) { u32 *ppix = priv->fb; u32 *end = priv->fb + priv->fb_size; @@ -212,14 +212,14 @@ u32 video_index_to_colour(struct video_priv *priv, unsigned int idx) { switch (priv->bpix) { case VIDEO_BPP16: - if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { + if (CONFIG_IS_ENABLED(VIDEO_BPP16)) { return ((colours[idx].r >> 3) << 11) | ((colours[idx].g >> 2) << 5) | ((colours[idx].b >> 3) << 0); } break; case VIDEO_BPP32: - if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { + if (CONFIG_IS_ENABLED(VIDEO_BPP32)) { if (priv->format == VIDEO_X2R10G10B10) return (colours[idx].r << 22) | (colours[idx].g << 12) | @@ -513,8 +513,8 @@ static int video_post_probe(struct udevice *dev) return ret; } - if (IS_ENABLED(CONFIG_VIDEO_LOGO) && - !IS_ENABLED(CONFIG_SPLASH_SCREEN) && !plat->hide_logo) { + if (CONFIG_IS_ENABLED(VIDEO_LOGO) && + !CONFIG_IS_ENABLED(SPLASH_SCREEN) && !plat->hide_logo) { ret = show_splash(dev); if (ret) { log_debug("Cannot show splash screen\n"); diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index 6188a13..47e52c4 100644 --- a/drivers/video/video_bmp.c +++ b/drivers/video/video_bmp.c @@ -320,7 +320,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, switch (bmp_bpix) { case 1: case 8: - if (IS_ENABLED(CONFIG_VIDEO_BMP_RLE8)) { + if (CONFIG_IS_ENABLED(VIDEO_BMP_RLE8)) { u32 compression = get_unaligned_le32( &bmp->header.compression); debug("compressed %d %d\n", compression, BMP_BI_RLE8); @@ -348,7 +348,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, } break; case 16: - if (IS_ENABLED(CONFIG_BMP_16BPP)) { + if (CONFIG_IS_ENABLED(BMP_16BPP)) { for (i = 0; i < height; ++i) { schedule(); for (j = 0; j < width; j++) { @@ -361,7 +361,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, } break; case 24: - if (IS_ENABLED(CONFIG_BMP_24BPP)) { + if (CONFIG_IS_ENABLED(BMP_24BPP)) { for (i = 0; i < height; ++i) { for (j = 0; j < width; j++) { if (bpix == 16) { @@ -395,7 +395,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, } break; case 32: - if (IS_ENABLED(CONFIG_BMP_32BPP)) { + if (CONFIG_IS_ENABLED(BMP_32BPP)) { for (i = 0; i < height; ++i) { for (j = 0; j < width; j++) { if (eformat == VIDEO_X2R10G10B10) { -- cgit v1.1 From eb9217dc036c9c481da7d89cd934cfc23b894a82 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:09 +0530 Subject: common: Enable splash functions at SPL To support splash screen at both u-boot proper and SPL use CONFIG_IS_ENABLED and CONFIG_VAL to check for video related Kconfigs at respective stages. Replace CONFIG_CMD_BMP with CONFIG_BMP to enable splash_display function at u-boot proper and SPL. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass --- common/bmp.c | 10 +++++----- common/splash.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/common/bmp.c b/common/bmp.c index 4dbcb84..ad91351 100644 --- a/common/bmp.c +++ b/common/bmp.c @@ -33,7 +33,7 @@ * Returns NULL if decompression failed, or if the decompressed data * didn't contain a valid BMP signature. */ -#ifdef CONFIG_VIDEO_BMP_GZIP +#if CONFIG_IS_ENABLED(VIDEO_BMP_GZIP) struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, void **alloc_addr) { @@ -44,9 +44,9 @@ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, /* * Decompress bmp image */ - len = CONFIG_VIDEO_LOGO_MAX_SIZE; + len = CONFIG_VAL(VIDEO_LOGO_MAX_SIZE); /* allocate extra 3 bytes for 32-bit-aligned-address + 2 alignment */ - dst = malloc(CONFIG_VIDEO_LOGO_MAX_SIZE + 3); + dst = malloc(CONFIG_VAL(VIDEO_LOGO_MAX_SIZE) + 3); if (!dst) { puts("Error: malloc in gunzip failed!\n"); return NULL; @@ -55,12 +55,12 @@ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, /* align to 32-bit-aligned-address + 2 */ bmp = dst + 2; - if (gunzip(bmp, CONFIG_VIDEO_LOGO_MAX_SIZE, map_sysmem(addr, 0), + if (gunzip(bmp, CONFIG_VAL(VIDEO_LOGO_MAX_SIZE), map_sysmem(addr, 0), &len)) { free(dst); return NULL; } - if (len == CONFIG_VIDEO_LOGO_MAX_SIZE) + if (len == CONFIG_VAL(VIDEO_LOGO_MAX_SIZE)) puts("Image could be truncated (increase CONFIG_VIDEO_LOGO_MAX_SIZE)!\n"); /* diff --git a/common/splash.c b/common/splash.c index 4bc54b1..a4e68b7 100644 --- a/common/splash.c +++ b/common/splash.c @@ -89,14 +89,14 @@ static inline int splash_video_logo_load(void) { return -ENOSYS; } __weak int splash_screen_prepare(void) { - if (IS_ENABLED(CONFIG_SPLASH_SOURCE)) + if (CONFIG_IS_ENABLED(SPLASH_SOURCE)) return splash_source_load(default_splash_locations, ARRAY_SIZE(default_splash_locations)); return splash_video_logo_load(); } -#ifdef CONFIG_SPLASH_SCREEN_ALIGN +#if CONFIG_IS_ENABLED(SPLASH_SCREEN_ALIGN) void splash_get_pos(int *x, int *y) { char *s = env_get("splashpos"); @@ -119,7 +119,7 @@ void splash_get_pos(int *x, int *y) } #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ -#if defined(CONFIG_VIDEO) && !defined(CONFIG_HIDE_LOGO_VERSION) +#if CONFIG_IS_ENABLED(VIDEO) && !CONFIG_IS_ENABLED(HIDE_LOGO_VERSION) #ifdef CONFIG_VIDEO_LOGO #include @@ -159,7 +159,7 @@ void splash_display_banner(void) * Common function to show a splash image if env("splashimage") is set. * For additional details please refer to doc/README.splashprepare. */ -#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_BMP) +#if CONFIG_IS_ENABLED(SPLASH_SCREEN) && CONFIG_IS_ENABLED(BMP) int splash_display(void) { ulong addr; @@ -183,7 +183,7 @@ int splash_display(void) if (x || y) goto end; -#if defined(CONFIG_VIDEO) && !defined(CONFIG_HIDE_LOGO_VERSION) +#if CONFIG_IS_ENABLED(VIDEO) && !CONFIG_IS_ENABLED(HIDE_LOGO_VERSION) splash_display_banner(); #endif end: -- cgit v1.1 From 25127ea38841e28b6e9eae37d40e5aa26eab50ef Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:10 +0530 Subject: include: Enable video related global data variable and splash at SPL To include video related global data variables and splash functions at SPL and u-boot proper, use CONFIG_IS_ENABLED. Replace CONFIG_CMD_BMP with CONFIG_BMP to enable splash_display function at u-boot proper and SPL. Signed-off-by: Nikhil M Jain Reviewed-by: Devarsh Thakkar Reviewed-by: Simon Glass --- include/asm-generic/global_data.h | 4 ++-- include/splash.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 987fb66..65bf8df 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -68,7 +68,7 @@ struct global_data { * @mem_clk: memory clock rate in Hz */ unsigned long mem_clk; -#if defined(CONFIG_VIDEO) +#if CONFIG_IS_ENABLED(VIDEO) /** * @fb_base: base address of frame buffer memory */ @@ -359,7 +359,7 @@ struct global_data { */ struct membuff console_in; #endif -#ifdef CONFIG_VIDEO +#if CONFIG_IS_ENABLED(VIDEO) /** * @video_top: top of video frame buffer area */ diff --git a/include/splash.h b/include/splash.h index 33e45e6..b6a100f 100644 --- a/include/splash.h +++ b/include/splash.h @@ -49,7 +49,7 @@ struct splash_location { char *ubivol; /* UBI volume-name for ubifsmount */ }; -#ifdef CONFIG_SPLASH_SOURCE +#if CONFIG_IS_ENABLED(SPLASH_SOURCE) int splash_source_load(struct splash_location *locations, uint size); #else static inline int splash_source_load(struct splash_location *locations, @@ -61,13 +61,13 @@ static inline int splash_source_load(struct splash_location *locations, int splash_screen_prepare(void); -#ifdef CONFIG_SPLASH_SCREEN_ALIGN +#if CONFIG_IS_ENABLED(SPLASH_SCREEN_ALIGN) void splash_get_pos(int *x, int *y); #else static inline void splash_get_pos(int *x, int *y) { } #endif -#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_BMP) +#if CONFIG_IS_ENABLED(SPLASH_SCREEN) && CONFIG_IS_ENABLED(BMP) int splash_display(void); #else static inline int splash_display(void) -- cgit v1.1 From 53ae978af51df7926ffa2e2cd0ab2b5196a66ec8 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:11 +0530 Subject: board: ti: am62x: evm: OSPI support for splash screen Add ospi boot media support to load splash image from OSPI memory, add offset to read image from ospi and necessary flags defininig type of storage and storage device. Use CONFIG_IS_ENBALED to use the splash locations at SPL and u-boot proper. Signed-off-by: Nikhil M Jain Reviewed-by: Simon Glass Reviewed-by: Devarsh Thakkar --- board/ti/am62x/evm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/board/ti/am62x/evm.c b/board/ti/am62x/evm.c index 20b2a70..1b7b439 100644 --- a/board/ti/am62x/evm.c +++ b/board/ti/am62x/evm.c @@ -20,9 +20,15 @@ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_SPLASH_SCREEN +#if CONFIG_IS_ENABLED(SPLASH_SCREEN) static struct splash_location default_splash_locations[] = { { + .name = "sf", + .storage = SPLASH_STORAGE_SF, + .flags = SPLASH_STORAGE_RAW, + .offset = 0x700000, + }, + { .name = "mmc", .storage = SPLASH_STORAGE_MMC, .flags = SPLASH_STORAGE_FS, -- cgit v1.1 From 9eeb1a299d40a4587d5b3c2b87ae8022998cf766 Mon Sep 17 00:00:00 2001 From: Nikhil M Jain Date: Thu, 20 Apr 2023 17:41:12 +0530 Subject: common: Replace #ifdef and #if with if's Avoid using preprocessor compilation directives and instead use simple logical expressions for better readability since compiler will anyway optimize out the respective code block if condition is not satisfied. Signed-off-by: Nikhil M Jain Reviewed-by: Simon Glass Reviewed-by: Devarsh Thakkar --- common/bmp.c | 14 +++++--------- common/splash.c | 14 +++++++------- include/splash.h | 13 ------------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/common/bmp.c b/common/bmp.c index ad91351..57764f3 100644 --- a/common/bmp.c +++ b/common/bmp.c @@ -31,9 +31,9 @@ * by the caller after use. * * Returns NULL if decompression failed, or if the decompressed data - * didn't contain a valid BMP signature. + * didn't contain a valid BMP signature or decompression is not enabled in + * Kconfig. */ -#if CONFIG_IS_ENABLED(VIDEO_BMP_GZIP) struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, void **alloc_addr) { @@ -41,6 +41,9 @@ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, unsigned long len; struct bmp_image *bmp; + if (!CONFIG_IS_ENABLED(VIDEO_BMP_GZIP)) + return NULL; + /* * Decompress bmp image */ @@ -77,13 +80,6 @@ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, *alloc_addr = dst; return bmp; } -#else -struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, - void **alloc_addr) -{ - return NULL; -} -#endif #ifdef CONFIG_NEEDS_MANUAL_RELOC void bmp_reloc(void) diff --git a/common/splash.c b/common/splash.c index a4e68b7..6820db6 100644 --- a/common/splash.c +++ b/common/splash.c @@ -96,12 +96,11 @@ __weak int splash_screen_prepare(void) return splash_video_logo_load(); } -#if CONFIG_IS_ENABLED(SPLASH_SCREEN_ALIGN) void splash_get_pos(int *x, int *y) { char *s = env_get("splashpos"); - if (!s) + if (!CONFIG_IS_ENABLED(SPLASH_SCREEN_ALIGN) || !s) return; if (s[0] == 'm') @@ -117,7 +116,6 @@ void splash_get_pos(int *x, int *y) *y = simple_strtol(s + 1, NULL, 0); } } -#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ #if CONFIG_IS_ENABLED(VIDEO) && !CONFIG_IS_ENABLED(HIDE_LOGO_VERSION) @@ -159,13 +157,13 @@ void splash_display_banner(void) * Common function to show a splash image if env("splashimage") is set. * For additional details please refer to doc/README.splashprepare. */ -#if CONFIG_IS_ENABLED(SPLASH_SCREEN) && CONFIG_IS_ENABLED(BMP) int splash_display(void) { ulong addr; char *s; int x = 0, y = 0, ret; - + if (!CONFIG_IS_ENABLED(SPLASH_SCREEN)) + return -ENOSYS; s = env_get("splashimage"); if (!s) return -EINVAL; @@ -177,7 +175,10 @@ int splash_display(void) splash_get_pos(&x, &y); - ret = bmp_display(addr, x, y); + if (CONFIG_IS_ENABLED(BMP)) + ret = bmp_display(addr, x, y); + else + return -ENOSYS; /* Skip banner output on video console if the logo is not at 0,0 */ if (x || y) @@ -189,4 +190,3 @@ int splash_display(void) end: return ret; } -#endif diff --git a/include/splash.h b/include/splash.h index b6a100f..c392237 100644 --- a/include/splash.h +++ b/include/splash.h @@ -60,21 +60,8 @@ static inline int splash_source_load(struct splash_location *locations, #endif int splash_screen_prepare(void); - -#if CONFIG_IS_ENABLED(SPLASH_SCREEN_ALIGN) void splash_get_pos(int *x, int *y); -#else -static inline void splash_get_pos(int *x, int *y) { } -#endif - -#if CONFIG_IS_ENABLED(SPLASH_SCREEN) && CONFIG_IS_ENABLED(BMP) int splash_display(void); -#else -static inline int splash_display(void) -{ - return -ENOSYS; -} -#endif #define BMP_ALIGN_CENTER 0x7FFF -- cgit v1.1 From 060208370674cb95806491f231b373f824afc5db Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 25 Apr 2023 10:51:42 +0300 Subject: video: add lm3533 backlight driver This is basic lm3533 driver only with bank A and backlight cell support. Tested-by: Andreas Westman Dorcsak # LG P880 T30 Tested-by: Svyatoslav Ryhel # LG P895 T30 Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/Kconfig | 9 +++ drivers/video/Makefile | 1 + drivers/video/lm3533_backlight.c | 134 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 drivers/video/lm3533_backlight.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9b7abd4..2be73d5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -606,6 +606,15 @@ config ATMEL_HLCD help HLCDC supports video output to an attached LCD panel. +config BACKLIGHT_LM3533 + bool "Backlight Driver for LM3533" + depends on BACKLIGHT + select DM_I2C + help + Say Y to enable the backlight driver for National Semiconductor / TI + LM3533 Lighting Power chip. Only Bank A is supported as for now. + Supported backlight level range is from 2 to 255 with step of 1. + source "drivers/video/ti/Kconfig" source "drivers/video/exynos/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a609e35..0d28eff 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.o endif +obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ diff --git a/drivers/video/lm3533_backlight.c b/drivers/video/lm3533_backlight.c new file mode 100644 index 0000000..00297a0 --- /dev/null +++ b/drivers/video/lm3533_backlight.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LM3533_BL_MIN_BRIGHTNESS 0x02 +#define LM3533_BL_MAX_BRIGHTNESS 0xFF + +#define LM3533_SINK_OUTPUT_CONFIG_1 0x10 +#define LM3533_CONTROL_BANK_A_PWM 0x14 +#define LM3533_CONTROL_BANK_AB_BRIGHTNESS 0x1A +#define LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT 0x1F +#define LM3533_CONTROL_BANK_ENABLE 0x27 +#define LM3533_OVP_FREQUENCY_PWM_POLARITY 0x2C +#define LM3533_BRIGHTNESS_REGISTER_A 0x40 + +struct lm3533_backlight_priv { + struct gpio_desc enable_gpio; + u32 def_bl_lvl; +}; + +static int lm3533_backlight_enable(struct udevice *dev) +{ + struct lm3533_backlight_priv *priv = dev_get_priv(dev); + int ret; + + dm_gpio_set_value(&priv->enable_gpio, 1); + mdelay(5); + + /* HVLED 1 & 2 are controlled by Bank A */ + ret = dm_i2c_reg_write(dev, LM3533_SINK_OUTPUT_CONFIG_1, 0x00); + if (ret) + return ret; + + /* PWM input is disabled for CABC */ + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_PWM, 0x00); + if (ret) + return ret; + + /* Linear & Control Bank A is configured for register Current control */ + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, 0x02); + if (ret) + return ret; + + /* Full-Scale Current (20.2mA) */ + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT, 0x13); + if (ret) + return ret; + + /* Control Bank A is enable */ + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_ENABLE, 0x01); + if (ret) + return ret; + + ret = dm_i2c_reg_write(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, 0x0A); + if (ret) + return ret; + + return 0; +} + +static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct lm3533_backlight_priv *priv = dev_get_priv(dev); + int ret; + + if (percent == BACKLIGHT_DEFAULT) + percent = priv->def_bl_lvl; + + if (percent < LM3533_BL_MIN_BRIGHTNESS) + percent = LM3533_BL_MIN_BRIGHTNESS; + + if (percent > LM3533_BL_MAX_BRIGHTNESS) + percent = LM3533_BL_MAX_BRIGHTNESS; + + /* Set brightness level */ + ret = dm_i2c_reg_write(dev, LM3533_BRIGHTNESS_REGISTER_A, + percent); + if (ret) + return ret; + + return 0; +} + +static int lm3533_backlight_probe(struct udevice *dev) +{ + struct lm3533_backlight_priv *priv = dev_get_priv(dev); + int ret; + + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("Could not decode enable-gpios (%d)\n", ret); + return ret; + } + + priv->def_bl_lvl = dev_read_u32_default(dev, "default-brightness-level", + LM3533_BL_MAX_BRIGHTNESS); + + return 0; +} + +static const struct backlight_ops lm3533_backlight_ops = { + .enable = lm3533_backlight_enable, + .set_brightness = lm3533_backlight_set_brightness, +}; + +static const struct udevice_id lm3533_backlight_ids[] = { + { .compatible = "ti,lm3533" }, + { } +}; + +U_BOOT_DRIVER(lm3533_backlight) = { + .name = "lm3533_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = lm3533_backlight_ids, + .probe = lm3533_backlight_probe, + .ops = &lm3533_backlight_ops, + .priv_auto = sizeof(struct lm3533_backlight_priv), +}; -- cgit v1.1 From b0268244d321dc2e103338052f1c8ba9b892fece Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 25 Apr 2023 10:51:43 +0300 Subject: video: bridge: add Solomon SSD2825 DSI/LVDS driver SSD2825 is an innovative and cost-effective MIPI Bridge Chip solution targeting high resolution smartphones. It can convert 24bit RGB interface into 4-lane MIPI-DSI interface to drive extremely high resolution display modules of up to 800 x 1366, while supporting AMOLED, a-si LCD or LTPS advanced panel technologies for smartphone applications. Bridge is wrapped in panel uClass model for wider compatibility. Tested-by: Andreas Westman Dorcsak # LG P880 T30 Tested-by: Svyatoslav Ryhel # LG P895 T30 Signed-off-by: Svyatoslav Ryhel --- drivers/video/bridge/Kconfig | 7 + drivers/video/bridge/Makefile | 1 + drivers/video/bridge/ssd2825.c | 520 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 528 insertions(+) create mode 100644 drivers/video/bridge/ssd2825.c diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index 765f738..2311ca2 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -33,3 +33,10 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345 help The Analogix ANX6345 is RGB-to-DP converter. It enables an eDP LCD panel to be connected to an parallel LCD interface. + +config VIDEO_BRIDGE_SOLOMON_SSD2825 + bool "Solomon SSD2825 bridge driver" + depends on PANEL && DM_GPIO + select VIDEO_MIPI_DSI + help + Solomon SSD2824 SPI RGB-DSI bridge driver wrapped into panel uClass. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index 45e54ac..22625c8 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o +obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o diff --git a/drivers/video/bridge/ssd2825.c b/drivers/video/bridge/ssd2825.c new file mode 100644 index 0000000..cea20dc --- /dev/null +++ b/drivers/video/bridge/ssd2825.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSD2825_DEVICE_ID_REG 0xB0 +#define SSD2825_RGB_INTERFACE_CTRL_REG_1 0xB1 +#define SSD2825_RGB_INTERFACE_CTRL_REG_2 0xB2 +#define SSD2825_RGB_INTERFACE_CTRL_REG_3 0xB3 +#define SSD2825_RGB_INTERFACE_CTRL_REG_4 0xB4 +#define SSD2825_RGB_INTERFACE_CTRL_REG_5 0xB5 +#define SSD2825_RGB_INTERFACE_CTRL_REG_6 0xB6 +#define SSD2825_NON_BURST BIT(2) +#define SSD2825_BURST BIT(3) +#define SSD2825_PCKL_HIGH BIT(13) +#define SSD2825_HSYNC_HIGH BIT(14) +#define SSD2825_VSYNC_HIGH BIT(15) +#define SSD2825_CONFIGURATION_REG 0xB7 +#define SSD2825_CONF_REG_HS BIT(0) +#define SSD2825_CONF_REG_CKE BIT(1) +#define SSD2825_CONF_REG_SLP BIT(2) +#define SSD2825_CONF_REG_VEN BIT(3) +#define SSD2825_CONF_REG_HCLK BIT(4) +#define SSD2825_CONF_REG_CSS BIT(5) +#define SSD2825_CONF_REG_DCS BIT(6) +#define SSD2825_CONF_REG_REN BIT(7) +#define SSD2825_CONF_REG_ECD BIT(8) +#define SSD2825_CONF_REG_EOT BIT(9) +#define SSD2825_CONF_REG_LPE BIT(10) +#define SSD2825_VC_CTRL_REG 0xB8 +#define SSD2825_PLL_CTRL_REG 0xB9 +#define SSD2825_PLL_CONFIGURATION_REG 0xBA +#define SSD2825_CLOCK_CTRL_REG 0xBB +#define SSD2825_PACKET_SIZE_CTRL_REG_1 0xBC +#define SSD2825_PACKET_SIZE_CTRL_REG_2 0xBD +#define SSD2825_PACKET_SIZE_CTRL_REG_3 0xBE +#define SSD2825_PACKET_DROP_REG 0xBF +#define SSD2825_OPERATION_CTRL_REG 0xC0 +#define SSD2825_MAX_RETURN_SIZE_REG 0xC1 +#define SSD2825_RETURN_DATA_COUNT_REG 0xC2 +#define SSD2825_ACK_RESPONSE_REG 0xC3 +#define SSD2825_LINE_CTRL_REG 0xC4 +#define SSD2825_INTERRUPT_CTRL_REG 0xC5 +#define SSD2825_INTERRUPT_STATUS_REG 0xC6 +#define SSD2825_ERROR_STATUS_REG 0xC7 +#define SSD2825_DATA_FORMAT_REG 0xC8 +#define SSD2825_DELAY_ADJ_REG_1 0xC9 +#define SSD2825_DELAY_ADJ_REG_2 0xCA +#define SSD2825_DELAY_ADJ_REG_3 0xCB +#define SSD2825_DELAY_ADJ_REG_4 0xCC +#define SSD2825_DELAY_ADJ_REG_5 0xCD +#define SSD2825_DELAY_ADJ_REG_6 0xCE +#define SSD2825_HS_TX_TIMER_REG_1 0xCF +#define SSD2825_HS_TX_TIMER_REG_2 0xD0 +#define SSD2825_LP_RX_TIMER_REG_1 0xD1 +#define SSD2825_LP_RX_TIMER_REG_2 0xD2 +#define SSD2825_TE_STATUS_REG 0xD3 +#define SSD2825_SPI_READ_REG 0xD4 +#define SSD2825_PLL_LOCK_REG 0xD5 +#define SSD2825_TEST_REG 0xD6 +#define SSD2825_TE_COUNT_REG 0xD7 +#define SSD2825_ANALOG_CTRL_REG_1 0xD8 +#define SSD2825_ANALOG_CTRL_REG_2 0xD9 +#define SSD2825_ANALOG_CTRL_REG_3 0xDA +#define SSD2825_ANALOG_CTRL_REG_4 0xDB +#define SSD2825_INTERRUPT_OUT_CTRL_REG 0xDC +#define SSD2825_RGB_INTERFACE_CTRL_REG_7 0xDD +#define SSD2825_LANE_CONFIGURATION_REG 0xDE +#define SSD2825_DELAY_ADJ_REG_7 0xDF +#define SSD2825_INPUT_PIN_CTRL_REG_1 0xE0 +#define SSD2825_INPUT_PIN_CTRL_REG_2 0xE1 +#define SSD2825_BIDIR_PIN_CTRL_REG_1 0xE2 +#define SSD2825_BIDIR_PIN_CTRL_REG_2 0xE3 +#define SSD2825_BIDIR_PIN_CTRL_REG_3 0xE4 +#define SSD2825_BIDIR_PIN_CTRL_REG_4 0xE5 +#define SSD2825_BIDIR_PIN_CTRL_REG_5 0xE6 +#define SSD2825_BIDIR_PIN_CTRL_REG_6 0xE7 +#define SSD2825_BIDIR_PIN_CTRL_REG_7 0xE8 +#define SSD2825_CABC_BRIGHTNESS_CTRL_REG_1 0xE9 +#define SSD2825_CABC_BRIGHTNESS_CTRL_REG_2 0xEA +#define SSD2825_CABC_BRIGHTNESS_STATUS_REG 0xEB +#define SSD2825_READ_REG 0xFF +#define SSD2825_SPI_READ_REG_RESET 0xFA + +#define SSD2825_CMD_MASK 0x00 +#define SSD2825_DAT_MASK 0x01 + +#define SSD2825_CMD_SEND BIT(0) +#define SSD2825_DAT_SEND BIT(1) +#define SSD2825_DSI_SEND BIT(2) + +#define SSD2828_LP_CLOCK_DIVIDER(n) (((n) - 1) & 0x3F) +#define SSD2825_LP_MIN_CLK 5000 /* KHz */ +#define SSD2825_REF_MIN_CLK 2000 /* KHz */ + +struct ssd2825_bridge_priv { + struct mipi_dsi_host host; + struct mipi_dsi_device device; + + struct udevice *panel; + struct display_timing timing; + + struct gpio_desc power_gpio; + struct gpio_desc reset_gpio; + + struct clk *tx_clk; + + u32 pll_freq_kbps; /* PLL in kbps */ +}; + +static int ssd2825_spi_write(struct udevice *dev, int reg, + const void *buf, int flags) +{ + u8 command[2]; + + if (flags & SSD2825_CMD_SEND) { + command[0] = SSD2825_CMD_MASK; + command[1] = reg; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + } + + if (flags & SSD2825_DAT_SEND) { + u16 data = *(u16 *)buf; + u8 cmd1, cmd2; + + /* send low byte first and then high byte */ + cmd1 = (data & 0x00FF); + cmd2 = (data & 0xFF00) >> 8; + + command[0] = SSD2825_DAT_MASK; + command[1] = cmd1; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + + command[0] = SSD2825_DAT_MASK; + command[1] = cmd2; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + } + + if (flags & SSD2825_DSI_SEND) { + u16 data = *(u16 *)buf; + data &= 0x00FF; + + debug("%s: dsi command (0x%x)\n", + __func__, data); + + command[0] = SSD2825_DAT_MASK; + command[1] = data; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + } + + return 0; +} + +static int ssd2825_spi_read(struct udevice *dev, int reg, + void *data, int flags) +{ + u8 command[2]; + + command[0] = SSD2825_CMD_MASK; + command[1] = SSD2825_SPI_READ_REG; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + + command[0] = SSD2825_DAT_MASK; + command[1] = SSD2825_SPI_READ_REG_RESET; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + + command[0] = SSD2825_DAT_MASK; + command[1] = 0; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + + command[0] = SSD2825_CMD_MASK; + command[1] = reg; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + + command[0] = SSD2825_CMD_MASK; + command[1] = SSD2825_SPI_READ_REG_RESET; + dm_spi_xfer(dev, 9, &command, + NULL, SPI_XFER_ONCE); + + dm_spi_xfer(dev, 16, NULL, + (u8 *)data, SPI_XFER_ONCE); + + return 0; +} + +static void ssd2825_write_register(struct udevice *dev, u8 reg, + u16 command) +{ + ssd2825_spi_write(dev, reg, &command, + SSD2825_CMD_SEND | + SSD2825_DAT_SEND); +} + +static void ssd2825_write_dsi(struct udevice *dev, const u8 *command, + int len) +{ + int i; + + ssd2825_spi_write(dev, SSD2825_PACKET_SIZE_CTRL_REG_1, &len, + SSD2825_CMD_SEND | SSD2825_DAT_SEND); + + ssd2825_spi_write(dev, SSD2825_PACKET_DROP_REG, NULL, + SSD2825_CMD_SEND); + + for (i = 0; i < len; i++) + ssd2825_spi_write(dev, 0, &command[i], SSD2825_DSI_SEND); +} + +static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct udevice *dev = (struct udevice *)host->dev; + u8 buf = *(u8 *)msg->tx_buf; + u16 config; + int ret; + + ret = ssd2825_spi_read(dev, SSD2825_CONFIGURATION_REG, + &config, 0); + if (ret) + return ret; + + switch (msg->type) { + case MIPI_DSI_DCS_SHORT_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + case MIPI_DSI_DCS_LONG_WRITE: + config |= SSD2825_CONF_REG_DCS; + break; + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + case MIPI_DSI_GENERIC_LONG_WRITE: + config &= ~SSD2825_CONF_REG_DCS; + break; + default: + return 0; + } + + ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, config); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); + ssd2825_write_dsi(dev, msg->tx_buf, msg->tx_len); + + if (buf == MIPI_DCS_SET_DISPLAY_ON) { + ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, + SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | + SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | + SSD2825_CONF_REG_EOT); + ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); + } + + return 0; +} + +static const struct mipi_dsi_host_ops ssd2825_bridge_host_ops = { + .transfer = ssd2825_bridge_transfer, +}; + +/* + * PLL configuration register settings. + * + * See the "PLL Configuration Register Description" in the SSD2825 datasheet. + */ +static u16 construct_pll_config(struct ssd2825_bridge_priv *priv, + u32 desired_pll_freq_kbps, u32 reference_freq_khz) +{ + u32 div_factor = 1, mul_factor, fr = 0; + + while (reference_freq_khz / (div_factor + 1) >= SSD2825_REF_MIN_CLK) + div_factor++; + if (div_factor > 31) + div_factor = 31; + + mul_factor = DIV_ROUND_UP(desired_pll_freq_kbps * div_factor, + reference_freq_khz); + + priv->pll_freq_kbps = reference_freq_khz * mul_factor / div_factor; + + if (priv->pll_freq_kbps >= 501000) + fr = 3; + else if (priv->pll_freq_kbps >= 251000) + fr = 2; + else if (priv->pll_freq_kbps >= 126000) + fr = 1; + + return (fr << 14) | (div_factor << 8) | mul_factor; +} + +static void ssd2825_setup_pll(struct udevice *dev) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct display_timing *dt = &priv->timing; + u16 pll_config, lp_div; + u32 pclk_mult, tx_freq_khz, pd_lines; + + tx_freq_khz = clk_get_rate(priv->tx_clk) / 1000; + pd_lines = mipi_dsi_pixel_format_to_bpp(device->format); + pclk_mult = pd_lines / device->lanes + 1; + + pll_config = construct_pll_config(priv, pclk_mult * + dt->pixelclock.typ / 1000, + tx_freq_khz); + + lp_div = priv->pll_freq_kbps / (SSD2825_LP_MIN_CLK * 8); + + /* Disable PLL */ + ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0000); + ssd2825_write_register(dev, SSD2825_LINE_CTRL_REG, 0x0001); + + /* Set delays */ + ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, 0x2103); + + /* Set PLL coeficients */ + ssd2825_write_register(dev, SSD2825_PLL_CONFIGURATION_REG, pll_config); + + /* Clock Control Register */ + ssd2825_write_register(dev, SSD2825_CLOCK_CTRL_REG, + SSD2828_LP_CLOCK_DIVIDER(lp_div)); + + /* Enable PLL */ + ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); +} + +static int ssd2825_bridge_enable_panel(struct udevice *dev) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct display_timing *dt = &priv->timing; + int ret; + + ret = clk_prepare_enable(priv->tx_clk); + if (ret) { + log_err("error enabling tx_clk (%d)\n", ret); + return ret; + } + + ret = dm_gpio_set_value(&priv->power_gpio, 1); + if (ret) { + log_err("error changing power-gpios (%d)\n", ret); + return ret; + } + mdelay(10); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(10); + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(10); + + /* Perform panel HW setup */ + ret = panel_enable_backlight(priv->panel); + if (ret) + return ret; + + /* Perform SW reset */ + ssd2825_write_register(dev, SSD2825_OPERATION_CTRL_REG, 0x0100); + + /* Set panel timings */ + ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_1, + dt->vsync_len.typ << 8 | dt->hsync_len.typ); + ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_2, + (dt->vsync_len.typ + dt->vback_porch.typ) << 8 | + (dt->hsync_len.typ + dt->hback_porch.typ)); + ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_3, + dt->vfront_porch.typ << 8 | dt->hfront_porch.typ); + ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_4, + dt->hactive.typ); + ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_5, + dt->vactive.typ); + ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_6, + SSD2825_HSYNC_HIGH | SSD2825_VSYNC_HIGH | + SSD2825_PCKL_HIGH | SSD2825_NON_BURST | + (3 - device->format)); + ssd2825_write_register(dev, SSD2825_LANE_CONFIGURATION_REG, + device->lanes - 1); + ssd2825_write_register(dev, SSD2825_TEST_REG, 0x0004); + + /* Call PLL configuration */ + ssd2825_setup_pll(dev); + + mdelay(10); + + /* Initial DSI configuration register set */ + ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, + SSD2825_CONF_REG_CKE | SSD2825_CONF_REG_DCS | + SSD2825_CONF_REG_ECD | SSD2825_CONF_REG_EOT); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); + + /* Set up SW panel configuration */ + ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (ret) + return ret; + + return 0; +} + +static int ssd2825_bridge_set_panel(struct udevice *dev, int percent) +{ + return 0; +} + +static int ssd2825_bridge_panel_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +static int ssd2825_bridge_probe(struct udevice *dev) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + struct spi_slave *slave = dev_get_parent_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct mipi_dsi_panel_plat *mipi_plat; + int ret; + + ret = spi_claim_bus(slave); + if (ret) { + log_err("SPI bus allocation failed (%d)\n", ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, + "panel", &priv->panel); + if (ret) { + log_err("cannot get panel: ret=%d\n", ret); + return ret; + } + + panel_get_display_timing(priv->panel, &priv->timing); + + mipi_plat = dev_get_plat(priv->panel); + mipi_plat->device = device; + + priv->host.dev = (struct device *)dev; + priv->host.ops = &ssd2825_bridge_host_ops; + + device->host = &priv->host; + device->lanes = mipi_plat->lanes; + device->format = mipi_plat->format; + device->mode_flags = mipi_plat->mode_flags; + + /* get panel gpios */ + ret = gpio_request_by_name(dev, "power-gpios", 0, + &priv->power_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("could not decode power-gpios (%d)\n", ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("could not decode reset-gpios (%d)\n", ret); + return ret; + } + + /* get clk */ + priv->tx_clk = devm_clk_get(dev, "tx_clk"); + if (IS_ERR(priv->tx_clk)) { + log_err("cannot get tx_clk: %ld\n", PTR_ERR(priv->tx_clk)); + return PTR_ERR(priv->tx_clk); + } + + return 0; +} + +static const struct panel_ops ssd2825_bridge_ops = { + .enable_backlight = ssd2825_bridge_enable_panel, + .set_backlight = ssd2825_bridge_set_panel, + .get_display_timing = ssd2825_bridge_panel_timings, +}; + +static const struct udevice_id ssd2825_bridge_ids[] = { + { .compatible = "solomon,ssd2825" }, + { } +}; + +U_BOOT_DRIVER(ssd2825) = { + .name = "ssd2825", + .id = UCLASS_PANEL, + .of_match = ssd2825_bridge_ids, + .ops = &ssd2825_bridge_ops, + .probe = ssd2825_bridge_probe, + .priv_auto = sizeof(struct ssd2825_bridge_priv), +}; -- cgit v1.1 From dcee478a6f3947325b7a19780112aa0d5043273a Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 25 Apr 2023 10:51:44 +0300 Subject: video: panel: add Renesas R61307 MIPI DSI panel driver R61307 is liquid crystal driver for high-definition amorphous silicon (a-Si) panels and is ideal for tablets and smartphones. Supported compatibles are: - koe,tx13d100vm0eaa - hitachi,tx13d100vm0eaa Tested-by: Andreas Westman Dorcsak # LG P880 T30 Tested-by: Svyatoslav Ryhel # LG P895 T30 Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/Kconfig | 9 ++ drivers/video/Makefile | 1 + drivers/video/renesas-r61307.c | 302 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 drivers/video/renesas-r61307.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2be73d5..e75282b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -480,6 +480,15 @@ config VIDEO_LCD_RAYDIUM_RM68200 Say Y here if you want to enable support for Raydium RM68200 720x1280 DSI video mode panel. +config VIDEO_LCD_RENESAS_R61307 + tristate "Renesas R61307 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for KOE tx13d100vm0eaa + IPS-LCD module with Renesas R69328 IC. The panel has a 1024x768 + resolution and uses 24 bit RGB per pixel. + config VIDEO_LCD_SSD2828 bool "SSD2828 bridge chip" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0d28eff..4c016f1 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.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 obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o diff --git a/drivers/video/renesas-r61307.c b/drivers/video/renesas-r61307.c new file mode 100644 index 0000000..426fdc6 --- /dev/null +++ b/drivers/video/renesas-r61307.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas R61307 panel driver + * + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The datasheet is not publicly available, all values are + * taken from the downstream. If you have access to datasheets, + * corrections are welcome. + */ + +#define R61307_MACP 0xB0 /* Manufacturer CMD Protect */ + +#define R61307_INVERSION 0xC1 +#define R61307_GAMMA_SET_A 0xC8 /* Gamma Setting A */ +#define R61307_GAMMA_SET_B 0xC9 /* Gamma Setting B */ +#define R61307_GAMMA_SET_C 0xCA /* Gamma Setting C */ +#define R61307_CONTRAST_SET 0xCC + +struct renesas_r61307_priv { + struct udevice *vcc; + struct udevice *iovcc; + + struct udevice *backlight; + + struct gpio_desc reset_gpio; + + bool dig_cont_adj; + bool inversion; + u32 gamma; +}; + +static const u8 macp_on[] = { + R61307_MACP, 0x03 +}; + +static const u8 macp_off[] = { + R61307_MACP, 0x04 +}; + +static const u8 address_mode[] = { + MIPI_DCS_SET_ADDRESS_MODE +}; + +static const u8 contrast_setting[] = { + R61307_CONTRAST_SET, + 0xdc, 0xb4, 0xff +}; + +static const u8 column_inversion[] = { + R61307_INVERSION, + 0x00, 0x50, 0x03, 0x22, + 0x16, 0x06, 0x60, 0x11 +}; + +static const u8 line_inversion[] = { + R61307_INVERSION, + 0x00, 0x10, 0x03, 0x22, + 0x16, 0x06, 0x60, 0x01 +}; + +static const u8 gamma_setting[][25] = { + {}, + { + R61307_GAMMA_SET_A, + 0x00, 0x06, 0x0a, 0x0f, + 0x14, 0x1f, 0x1f, 0x17, + 0x12, 0x0c, 0x09, 0x06, + 0x00, 0x06, 0x0a, 0x0f, + 0x14, 0x1f, 0x1f, 0x17, + 0x12, 0x0c, 0x09, 0x06 + }, + { + R61307_GAMMA_SET_A, + 0x00, 0x05, 0x0b, 0x0f, + 0x11, 0x1d, 0x20, 0x18, + 0x18, 0x09, 0x07, 0x06, + 0x00, 0x05, 0x0b, 0x0f, + 0x11, 0x1d, 0x20, 0x18, + 0x18, 0x09, 0x07, 0x06 + }, + { + R61307_GAMMA_SET_A, + 0x0b, 0x0d, 0x10, 0x14, + 0x13, 0x1d, 0x20, 0x18, + 0x12, 0x09, 0x07, 0x06, + 0x0a, 0x0c, 0x10, 0x14, + 0x13, 0x1d, 0x20, 0x18, + 0x12, 0x09, 0x07, 0x06 + }, +}; + +static struct display_timing default_timing = { + .pixelclock.typ = 62000000, + .hactive.typ = 768, + .hfront_porch.typ = 116, + .hback_porch.typ = 81, + .hsync_len.typ = 5, + .vactive.typ = 1024, + .vfront_porch.typ = 24, + .vback_porch.typ = 8, + .vsync_len.typ = 2, +}; + +static int renesas_r61307_enable_backlight(struct udevice *dev) +{ + struct renesas_r61307_priv *priv = dev_get_priv(dev); + int ret; + + ret = regulator_set_enable_if_allowed(priv->vcc, 1); + if (ret) { + log_err("enabling vcc-supply failed (%d)\n", ret); + return ret; + } + mdelay(5); + + ret = regulator_set_enable_if_allowed(priv->iovcc, 1); + if (ret) { + log_err("enabling iovcc-supply failed (%d)\n", ret); + return ret; + } + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_err("changing reset-gpio failed (%d)\n", ret); + return ret; + } + mdelay(5); + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_err("changing reset-gpio failed (%d)\n", ret); + return ret; + } + + mdelay(5); + + return 0; +} + +static int renesas_r61307_set_backlight(struct udevice *dev, int percent) +{ + struct renesas_r61307_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_err("failed to exit sleep mode: %d\n", ret); + return ret; + } + + mdelay(80); + + mipi_dsi_dcs_write_buffer(dsi, address_mode, + sizeof(address_mode)); + + mdelay(20); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4); + if (ret < 0) { + log_err("failed to set pixel format: %d\n", ret); + return ret; + } + + /* MACP Off */ + mipi_dsi_generic_write(dsi, macp_off, sizeof(macp_off)); + + if (priv->dig_cont_adj) + mipi_dsi_generic_write(dsi, contrast_setting, + sizeof(contrast_setting)); + + if (priv->gamma) + mipi_dsi_generic_write(dsi, gamma_setting[priv->gamma], + sizeof(gamma_setting[priv->gamma])); + + if (priv->inversion) + mipi_dsi_generic_write(dsi, column_inversion, + sizeof(column_inversion)); + else + mipi_dsi_generic_write(dsi, line_inversion, + sizeof(line_inversion)); + + /* MACP On */ + mipi_dsi_generic_write(dsi, macp_on, sizeof(macp_on)); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_err("failed to set display on: %d\n", ret); + return ret; + } + + mdelay(50); + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + ret = backlight_set_brightness(priv->backlight, percent); + if (ret) + return ret; + + return 0; +} + +static int renesas_r61307_timings(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int renesas_r61307_of_to_plat(struct udevice *dev) +{ + struct renesas_r61307_priv *priv = dev_get_priv(dev); + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_err("Cannot get backlight: ret = %d\n", ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vcc-supply", &priv->vcc); + if (ret) { + log_err("Cannot get vcc-supply: ret = %d\n", ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "iovcc-supply", &priv->iovcc); + if (ret) { + log_err("Cannot get iovcc-supply: ret = %d\n", ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("Could not decode reser-gpios (%d)\n", ret); + return ret; + } + + priv->dig_cont_adj = dev_read_bool(dev, "renesas,contrast"); + priv->inversion = dev_read_bool(dev, "renesas,inversion"); + priv->gamma = dev_read_u32_default(dev, "renesas,gamma", 0); + + return 0; +} + +static int renesas_r61307_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 4; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO; + + return 0; +} + +static const struct panel_ops renesas_r61307_ops = { + .enable_backlight = renesas_r61307_enable_backlight, + .set_backlight = renesas_r61307_set_backlight, + .get_display_timing = renesas_r61307_timings, +}; + +static const struct udevice_id renesas_r61307_ids[] = { + { .compatible = "koe,tx13d100vm0eaa" }, + { .compatible = "hitachi,tx13d100vm0eaa" }, + { } +}; + +U_BOOT_DRIVER(renesas_r61307) = { + .name = "renesas_r61307", + .id = UCLASS_PANEL, + .of_match = renesas_r61307_ids, + .ops = &renesas_r61307_ops, + .of_to_plat = renesas_r61307_of_to_plat, + .probe = renesas_r61307_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct renesas_r61307_priv), +}; -- cgit v1.1 From 6d9b3a7e837d8fe4d3856780c35ee9f40865ff8d Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 25 Apr 2023 10:51:45 +0300 Subject: video: panel: add Renesas R69328 MIPI DSI panel driver Driver adds support for panels with Renesas R69328 IC Currently supported compatible is: - jdi,dx12d100vm0eaa Tested-by: Andreas Westman Dorcsak # LG P880 T30 Tested-by: Svyatoslav Ryhel # LG P895 T30 Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/Kconfig | 9 ++ drivers/video/Makefile | 1 + drivers/video/renesas-r69328.c | 238 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 drivers/video/renesas-r69328.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e75282b..ebbf3d7 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -489,6 +489,15 @@ config VIDEO_LCD_RENESAS_R61307 IPS-LCD module with Renesas R69328 IC. The panel has a 1024x768 resolution and uses 24 bit RGB per pixel. +config VIDEO_LCD_RENESAS_R69328 + tristate "Renesas R69328 720x1280 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for JDI dx12d100vm0eaa + IPS-LCD module with Renesas R69328 IC. The panel has a 720x1280 + resolution and uses 24 bit RGB per pixel. + config VIDEO_LCD_SSD2828 bool "SSD2828 bridge chip" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 4c016f1..f845a56 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.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 +obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o diff --git a/drivers/video/renesas-r69328.c b/drivers/video/renesas-r69328.c new file mode 100644 index 0000000..d2f7169 --- /dev/null +++ b/drivers/video/renesas-r69328.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas R69328 panel driver + * + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The datasheet is not publicly available, all values are + * taken from the downstream. If you have access to datasheets, + * corrections are welcome. + */ + +#define R69328_MACP 0xB0 /* Manufacturer Command Access Protect */ + +#define R69328_GAMMA_SET_A 0xC8 /* Gamma Setting A */ +#define R69328_GAMMA_SET_B 0xC9 /* Gamma Setting B */ +#define R69328_GAMMA_SET_C 0xCA /* Gamma Setting C */ + +#define R69328_POWER_SET 0xD1 + +struct renesas_r69328_priv { + struct udevice *backlight; + + struct gpio_desc enable_gpio; + struct gpio_desc reset_gpio; +}; + +static const u8 address_mode[] = { + MIPI_DCS_SET_ADDRESS_MODE +}; + +#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 = 68000000, + .hactive.typ = 720, + .hfront_porch.typ = 92, + .hback_porch.typ = 62, + .hsync_len.typ = 4, + .vactive.typ = 1280, + .vfront_porch.typ = 6, + .vback_porch.typ = 3, + .vsync_len.typ = 1, +}; + +static int renesas_r69328_enable_backlight(struct udevice *dev) +{ + struct renesas_r69328_priv *priv = dev_get_priv(dev); + int ret; + + ret = dm_gpio_set_value(&priv->enable_gpio, 1); + if (ret) { + log_err("error changing enable-gpios (%d)\n", ret); + return ret; + } + mdelay(5); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(5); + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + + mdelay(5); + + return 0; +} + +static int renesas_r69328_set_backlight(struct udevice *dev, int percent) +{ + struct renesas_r69328_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + mipi_dsi_dcs_write_buffer(dsi, address_mode, + sizeof(address_mode)); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4); + if (ret < 0) { + log_err("failed to set pixel format: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_err("failed to exit sleep mode: %d\n", ret); + return ret; + } + + mdelay(100); + + /* MACP Off */ + dsi_generic_write_seq(dsi, R69328_MACP, 0x04); + + dsi_generic_write_seq(dsi, R69328_POWER_SET, 0x14, + 0x1d, 0x21, 0x67, 0x11, 0x9a); + + dsi_generic_write_seq(dsi, R69328_GAMMA_SET_A, 0x00, + 0x1a, 0x20, 0x28, 0x25, 0x24, + 0x26, 0x15, 0x13, 0x11, 0x18, + 0x1e, 0x1c, 0x00, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, + 0x15, 0x13, 0x11, 0x18, 0x1e, + 0x1c, 0x00); + dsi_generic_write_seq(dsi, R69328_GAMMA_SET_B, 0x00, + 0x1a, 0x20, 0x28, 0x25, 0x24, + 0x26, 0x15, 0x13, 0x11, 0x18, + 0x1e, 0x1c, 0x00, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, + 0x15, 0x13, 0x11, 0x18, 0x1e, + 0x1c, 0x00); + dsi_generic_write_seq(dsi, R69328_GAMMA_SET_C, 0x00, + 0x1a, 0x20, 0x28, 0x25, 0x24, + 0x26, 0x15, 0x13, 0x11, 0x18, + 0x1e, 0x1c, 0x00, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, + 0x15, 0x13, 0x11, 0x18, 0x1e, + 0x1c, 0x00); + + /* MACP On */ + dsi_generic_write_seq(dsi, R69328_MACP, 0x03); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_err("failed to set display on: %d\n", ret); + return ret; + } + + mdelay(50); + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + ret = backlight_set_brightness(priv->backlight, percent); + if (ret) + return ret; + + return 0; +} + +static int renesas_r69328_timings(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int renesas_r69328_of_to_plat(struct udevice *dev) +{ + struct renesas_r69328_priv *priv = dev_get_priv(dev); + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_err("cannot get backlight: ret = %d\n", ret); + return ret; + } + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("could not decode enable-gpios (%d)\n", ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("could not decode reser-gpios (%d)\n", ret); + return ret; + } + + return 0; +} + +static int renesas_r69328_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 4; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO; + + return 0; +} + +static const struct panel_ops renesas_r69328_ops = { + .enable_backlight = renesas_r69328_enable_backlight, + .set_backlight = renesas_r69328_set_backlight, + .get_display_timing = renesas_r69328_timings, +}; + +static const struct udevice_id renesas_r69328_ids[] = { + { .compatible = "jdi,dx12d100vm0eaa" }, + { } +}; + +U_BOOT_DRIVER(renesas_r69328) = { + .name = "renesas_r69328", + .id = UCLASS_PANEL, + .of_match = renesas_r69328_ids, + .ops = &renesas_r69328_ops, + .of_to_plat = renesas_r69328_of_to_plat, + .probe = renesas_r69328_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct renesas_r69328_priv), +}; -- cgit v1.1 From d8cca91380c0bd7ed83d90a200b674b1fae8c544 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 25 Apr 2023 10:51:46 +0300 Subject: video: tegra: add DC based PWM backlight driver DC based PWM backlight is found on some T20 and T30 devices (HTC One X). This backlight is controlled by Tegra DC and is adjustable by the DC PM0 or PM1 signal. Tested-by: Andreas Westman Dorcsak # HTC One X T30 Tested-by: Svyatoslav Ryhel # HTC One X T30 Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/tegra20/Kconfig | 7 ++ drivers/video/tegra20/Makefile | 1 + drivers/video/tegra20/tegra-pwm-backlight.c | 156 ++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 drivers/video/tegra20/tegra-pwm-backlight.c diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig index 5b1dfbf..f5c4843 100644 --- a/drivers/video/tegra20/Kconfig +++ b/drivers/video/tegra20/Kconfig @@ -15,3 +15,10 @@ config VIDEO_DSI_TEGRA30 help T30 has native support for DSI panels. This option enables support for such panels which can be used on endeavoru and tf600t. + +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 index e82ee96..f0b534c 100644 --- a/drivers/video/tegra20/Makefile +++ b/drivers/video/tegra20/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o mipi-phy.o +obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra20/tegra-pwm-backlight.c new file mode 100644 index 0000000..bb677da --- /dev/null +++ b/drivers/video/tegra20/tegra-pwm-backlight.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TEGRA_DISPLAY_A_BASE 0x54200000 +#define TEGRA_DISPLAY_B_BASE 0x54240000 + +#define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 +#define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF + +#define TEGRA_PWM_BL_PERIOD 0xFF +#define TEGRA_PWM_BL_CLK_DIV 0x14 +#define TEGRA_PWM_BL_CLK_SELECT 0x00 + +#define PM_PERIOD_SHIFT 18 +#define PM_CLK_DIVIDER_SHIFT 4 + +#define TEGRA_PWM_PM0 0 +#define TEGRA_PWM_PM1 1 + +struct tegra_pwm_backlight_priv { + struct dc_ctlr *dc; /* Display controller regmap */ + + u32 pwm_source; + u32 period; + u32 clk_div; + u32 clk_select; + u32 dft_brightness; +}; + +static int tegra_pwm_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + struct dc_cmd_reg *cmd = &priv->dc->cmd; + struct dc_com_reg *com = &priv->dc->com; + unsigned int ctrl; + unsigned long out_sel; + unsigned long cmd_state; + + if (percent == BACKLIGHT_DEFAULT) + percent = priv->dft_brightness; + + if (percent < TEGRA_PWM_BL_MIN_BRIGHTNESS) + percent = TEGRA_PWM_BL_MIN_BRIGHTNESS; + + if (percent > TEGRA_PWM_BL_MAX_BRIGHTNESS) + percent = TEGRA_PWM_BL_MAX_BRIGHTNESS; + + ctrl = ((priv->period << PM_PERIOD_SHIFT) | + (priv->clk_div << PM_CLK_DIVIDER_SHIFT) | + priv->clk_select); + + /* The new value should be effected immediately */ + cmd_state = readl(&cmd->state_access); + writel((cmd_state | (1 << 2)), &cmd->state_access); + + switch (priv->pwm_source) { + case TEGRA_PWM_PM0: + /* Select the LM0 on PM0 */ + out_sel = readl(&com->pin_output_sel[5]); + out_sel &= ~(7 << 0); + out_sel |= (3 << 0); + writel(out_sel, &com->pin_output_sel[5]); + writel(ctrl, &com->pm0_ctrl); + writel(percent, &com->pm0_duty_cycle); + break; + case TEGRA_PWM_PM1: + /* Select the LM1 on PM1 */ + out_sel = readl(&com->pin_output_sel[5]); + out_sel &= ~(7 << 4); + out_sel |= (3 << 4); + writel(out_sel, &com->pin_output_sel[5]); + writel(ctrl, &com->pm1_ctrl); + writel(percent, &com->pm1_duty_cycle); + break; + default: + break; + } + + writel(cmd_state, &cmd->state_access); + return 0; +} + +static int tegra_pwm_backlight_enable(struct udevice *dev) +{ + struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + + return tegra_pwm_backlight_set_brightness(dev, priv->dft_brightness); +} + +static int tegra_pwm_backlight_probe(struct udevice *dev) +{ + struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + + if (dev_read_bool(dev, "nvidia,display-b-base")) + priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_B_BASE; + else + priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_A_BASE; + + if (!priv->dc) { + log_err("no display controller address\n"); + return -EINVAL; + } + + priv->pwm_source = + dev_read_u32_default(dev, "nvidia,pwm-source", + TEGRA_PWM_PM0); + priv->period = + dev_read_u32_default(dev, "nvidia,period", + TEGRA_PWM_BL_PERIOD); + priv->clk_div = + dev_read_u32_default(dev, "nvidia,clock-div", + TEGRA_PWM_BL_CLK_DIV); + priv->clk_select = + dev_read_u32_default(dev, "nvidia,clock-select", + TEGRA_PWM_BL_CLK_SELECT); + priv->dft_brightness = + dev_read_u32_default(dev, "nvidia,default-brightness", + TEGRA_PWM_BL_MAX_BRIGHTNESS); + + return 0; +} + +static const struct backlight_ops tegra_pwm_backlight_ops = { + .enable = tegra_pwm_backlight_enable, + .set_brightness = tegra_pwm_backlight_set_brightness, +}; + +static const struct udevice_id tegra_pwm_backlight_ids[] = { + { .compatible = "nvidia,tegra-pwm-backlight" }, + { } +}; + +U_BOOT_DRIVER(tegra_pwm_backlight) = { + .name = "tegra_pwm_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = tegra_pwm_backlight_ids, + .probe = tegra_pwm_backlight_probe, + .ops = &tegra_pwm_backlight_ops, + .priv_auto = sizeof(struct tegra_pwm_backlight_priv), +}; -- cgit v1.1 From def72d5c6265bde4e9eb7976db53a77fabc4808a Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 25 Apr 2023 10:51:47 +0300 Subject: video: panel: add generic endeavoru panel Family of panels used by HTC in One X. Though were used variants at least from 3 vendors, this driver provides generic support for all of them. Tested-by: Ion Agorria # HTC One X T30 Sony Tested-by: Svyatoslav Ryhel # HTC One X T30 Sharp Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/Kconfig | 11 ++ drivers/video/Makefile | 1 + drivers/video/endeavoru-panel.c | 252 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 drivers/video/endeavoru-panel.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ebbf3d7..fcc0e85 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -466,6 +466,17 @@ config VIDEO_BCM2835 that same resolution (or as near as possible) and 32bpp depth, so that U-Boot can access it with full colour depth. +config VIDEO_LCD_ENDEAVORU + tristate "Endeavoru 720x1280 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for the IPS-LCD panel + module for HTC One X. Driver supports a family of panels, + made at least by 3 vendors (Sharp, Sony and AUO), but set up + using the same DSI command sequence. The panel has a 720x1280 + resolution and uses 24 bit RGB per pixel. + config VIDEO_LCD_ORISETECH_OTM8009A bool "OTM8009A DSI LCD panel support" select VIDEO_MIPI_DSI diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f845a56..9a53cd1 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_VIDEO_EFI) += efi.o obj-$(CONFIG_VIDEO_IPUV3) += 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_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o diff --git a/drivers/video/endeavoru-panel.c b/drivers/video/endeavoru-panel.c new file mode 100644 index 0000000..79a2721 --- /dev/null +++ b/drivers/video/endeavoru-panel.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct endeavoru_panel_priv { + struct udevice *vdd; + struct udevice *vddio; + + struct udevice *backlight; + + struct gpio_desc reset_gpio; +}; + +static struct display_timing default_timing = { + .pixelclock.typ = 63200000, + .hactive.typ = 720, + .hfront_porch.typ = 55, + .hback_porch.typ = 29, + .hsync_len.typ = 16, + .vactive.typ = 1280, + .vfront_porch.typ = 2, + .vback_porch.typ = 1, + .vsync_len.typ = 1, +}; + +static void dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data) +{ + mipi_dsi_dcs_write(dsi, cmd, &data, 1); +} + +/* + * This panel is not able to auto-increment all cmd addresses so for some of + * them, we need to send them one by one... + */ +#define dcs_write_seq(dsi, cmd, seq...) \ +({ \ + static const u8 d[] = { seq }; \ + unsigned int i; \ + \ + for (i = 0; i < ARRAY_SIZE(d) ; i++) \ + dcs_write_one(dsi, cmd + i, d[i]); \ +}) + +static int endeavoru_panel_enable_backlight(struct udevice *dev) +{ + struct endeavoru_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(5); + + ret = regulator_set_enable_if_allowed(priv->vddio, 1); + if (ret) { + log_err("error enabling iovcc-supply (%d)\n", ret); + return ret; + } + mdelay(1); + + ret = regulator_set_enable_if_allowed(priv->vdd, 1); + if (ret) { + log_err("error enabling vcc-supply (%d)\n", ret); + return ret; + } + mdelay(20); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(2); + + /* Reset panel */ + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(1); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_err("error changing reset-gpios (%d)\n", ret); + return ret; + } + mdelay(25); + + return 0; +} + +static int endeavoru_panel_set_backlight(struct udevice *dev, int percent) +{ + struct endeavoru_panel_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + dcs_write_one(dsi, 0xc2, 0x08); + + /* color enhancement 2.2 */ + dcs_write_one(dsi, 0xff, 0x03); + dcs_write_one(dsi, 0xfe, 0x08); + dcs_write_one(dsi, 0x18, 0x00); + dcs_write_one(dsi, 0x19, 0x00); + dcs_write_one(dsi, 0x1a, 0x00); + dcs_write_one(dsi, 0x25, 0x26); + + dcs_write_seq(dsi, 0x00, 0x00, 0x05, 0x10, 0x17, + 0x22, 0x26, 0x29, 0x29, 0x26, 0x23, + 0x17, 0x12, 0x06, 0x02, 0x01, 0x00); + + dcs_write_one(dsi, 0xfb, 0x01); + dcs_write_one(dsi, 0xff, 0x00); + dcs_write_one(dsi, 0xfe, 0x01); + + mipi_dsi_dcs_exit_sleep_mode(dsi); + + mdelay(105); + + dcs_write_one(dsi, 0x35, 0x00); + + /* PWM frequency adjust */ + dcs_write_one(dsi, 0xff, 0x04); + dcs_write_one(dsi, 0x0a, 0x07); + dcs_write_one(dsi, 0x09, 0x20); + dcs_write_one(dsi, 0xff, 0x00); + + dcs_write_one(dsi, 0xff, 0xee); + dcs_write_one(dsi, 0x12, 0x50); + dcs_write_one(dsi, 0x13, 0x02); + dcs_write_one(dsi, 0x6a, 0x60); + dcs_write_one(dsi, 0xfb, 0x01); + dcs_write_one(dsi, 0xff, 0x00); + + mipi_dsi_dcs_set_display_on(dsi); + + mdelay(42); + + dcs_write_one(dsi, 0xba, 0x01); + + dcs_write_one(dsi, 0x53, 0x24); + dcs_write_one(dsi, 0x55, 0x80); + dcs_write_one(dsi, 0x5e, 0x06); + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + /* Set backlight */ + dcs_write_one(dsi, 0x51, 0x96); + + ret = backlight_set_brightness(priv->backlight, percent); + if (ret) + return ret; + + return 0; +} + +static int endeavoru_panel_timings(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int endeavoru_panel_of_to_plat(struct udevice *dev) +{ + struct endeavoru_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_err("cannot get backlight: ret = %d\n", ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vdd-supply", &priv->vdd); + if (ret) { + log_err("cannot get vdd-supply: ret = %d\n", ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vddio-supply", &priv->vddio); + if (ret) { + log_err("cannot get vddio-supply: ret = %d\n", ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("could not decode reser-gpios (%d)\n", ret); + return ret; + } + + return 0; +} + +static int endeavoru_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_VIDEO; + + return 0; +} + +static const struct panel_ops endeavoru_panel_ops = { + .enable_backlight = endeavoru_panel_enable_backlight, + .set_backlight = endeavoru_panel_set_backlight, + .get_display_timing = endeavoru_panel_timings, +}; + +static const struct udevice_id endeavoru_panel_ids[] = { + { .compatible = "htc,edge-panel" }, + { } +}; + +U_BOOT_DRIVER(endeavoru_panel) = { + .name = "endeavoru_panel", + .id = UCLASS_PANEL, + .of_match = endeavoru_panel_ids, + .ops = &endeavoru_panel_ops, + .of_to_plat = endeavoru_panel_of_to_plat, + .probe = endeavoru_panel_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct endeavoru_panel_priv), +}; -- cgit v1.1