- enable video support in SPL

- support splash screen for TI am62x
  - replace #ifdef and #if with if's in bmp/splash
  - add lm3533 backlight driver
  - add Solomon SSD2825 DSI/LVDS bridge driver
  - add Renesas R61307 and R69328 MIPI DSI panel drivers
  - add tegra DC based PWM backlight driver
  - add generic endeavoru (HTC One X) panel driver
 -----BEGIN PGP SIGNATURE-----
 
 iGwEABECACwWIQSC4hxrSoIUVfFO0kRM6ATMmsalXAUCZFS44Q4cYWd1c3RAZGVu
 eC5kZQAKCRBM6ATMmsalXPcDAJ9JjtVNvOUbS8mW51dHvmCYIP/tFwCfbfYTNVmq
 jvfn5+wAnkARJxcU6G8=
 =zXBt
 -----END PGP SIGNATURE-----

Merge tag 'video-for-v2023.07-rc2' of https://source.denx.de/u-boot/custodians/u-boot-video

 - enable video support in SPL
 - support splash screen for TI am62x
 - replace #ifdef and #if with if's in bmp/splash
 - add lm3533 backlight driver
 - add Solomon SSD2825 DSI/LVDS bridge driver
 - add Renesas R61307 and R69328 MIPI DSI panel drivers
 - add tegra DC based PWM backlight driver
 - add generic endeavoru (HTC One X) panel driver
This commit is contained in:
Tom Rini 2023-05-05 09:36:08 -04:00
commit ab75996ba4
28 changed files with 2107 additions and 212 deletions

View File

@ -19,8 +19,14 @@
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,

162
cmd/bmp.c
View File

@ -9,84 +9,12 @@
*/
#include <common.h>
#include <bmp_layout.h>
#include <command.h>
#include <dm.h>
#include <gzip.h>
#include <image.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
#include <splash.h>
#include <video.h>
#include <asm/byteorder.h>
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 <stdlib.h>
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 <imageAddr> - display image info\n"
"bmp display <imageAddr> [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;
}

View File

@ -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.

View File

@ -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
@ -56,6 +57,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

149
common/bmp.c Normal file
View File

@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002
* Detlev Zundel, DENX Software Engineering, dzu@denx.de.
*/
/*
* BMP handling routines
*/
#include <common.h>
#include <bmp_layout.h>
#include <command.h>
#include <dm.h>
#include <gzip.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
#include <splash.h>
#include <video.h>
#include <asm/byteorder.h>
/*
* 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 or decompression is not enabled in
* Kconfig.
*/
struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp,
void **alloc_addr)
{
void *dst;
unsigned long len;
struct bmp_image *bmp;
if (!CONFIG_IS_ENABLED(VIDEO_BMP_GZIP))
return NULL;
/*
* Decompress bmp image
*/
len = CONFIG_VAL(VIDEO_LOGO_MAX_SIZE);
/* allocate extra 3 bytes for 32-bit-aligned-address + 2 alignment */
dst = malloc(CONFIG_VAL(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_VAL(VIDEO_LOGO_MAX_SIZE), map_sysmem(addr, 0),
&len)) {
free(dst);
return NULL;
}
if (len == CONFIG_VAL(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;
}
#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;
}

View File

@ -89,19 +89,18 @@ 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
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,9 +116,8 @@ void splash_get_pos(int *x, int *y)
*y = simple_strtol(s + 1, NULL, 0);
}
}
#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 <bmp_logo.h>
@ -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 defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_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,16 +175,18 @@ 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)
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:
return ret;
}
#endif

View File

@ -62,6 +62,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

View File

@ -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
@ -480,6 +491,24 @@ 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_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---
@ -606,6 +635,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"
@ -886,7 +924,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 +961,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

View File

@ -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,21 +18,22 @@ 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
endif
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o
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
@ -52,9 +53,12 @@ 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
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

View File

@ -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.

View File

@ -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

View File

@ -0,0 +1,520 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <log.h>
#include <misc.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <backlight.h>
#include <panel.h>
#include <spi.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <asm/gpio.h>
#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),
};

View File

@ -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;

View File

@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <common.h>
#include <backlight.h>
#include <dm.h>
#include <panel.h>
#include <log.h>
#include <misc.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <asm/gpio.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <power/regulator.h>
struct 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),
};

View File

@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
#include <backlight.h>
#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <asm/gpio.h>
#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),
};

View File

@ -0,0 +1,302 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Renesas R61307 panel driver
*
* Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <common.h>
#include <backlight.h>
#include <dm.h>
#include <panel.h>
#include <log.h>
#include <misc.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <asm/gpio.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <power/regulator.h>
/*
* 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),
};

View File

@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Renesas R69328 panel driver
*
* Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <common.h>
#include <backlight.h>
#include <dm.h>
#include <panel.h>
#include <log.h>
#include <misc.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <asm/gpio.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <power/regulator.h>
/*
* 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),
};

View File

@ -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.

View File

@ -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

View File

@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
#include <backlight.h>
#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/arch/display.h>
#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),
};

View File

@ -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.

View File

@ -9,4 +9,4 @@
# Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
obj-${CONFIG_VIDEO_TIDSS} = tidss_drv.o
obj-${CONFIG_$(SPL_)VIDEO_TIDSS} = tidss_drv.o

View File

@ -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;

View File

@ -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");

View File

@ -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) {

View File

@ -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
*/

View File

@ -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,
@ -60,21 +60,8 @@ static inline int splash_source_load(struct splash_location *locations,
#endif
int splash_screen_prepare(void);
#ifdef CONFIG_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)
int splash_display(void);
#else
static inline int splash_display(void)
{
return -ENOSYS;
}
#endif
#define BMP_ALIGN_CENTER 0x7FFF

View File

@ -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