GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,766 @@
#
# Video configuration
#
menu "Graphics support"
config DM_VIDEO
bool "Enable driver model support for LCD/video"
depends on DM
help
This enables driver model for LCD and video devices. These support
a bitmap display of various sizes and depths which can be drawn on
to display a command-line console or splash screen. Enabling this
option compiles in the video uclass and routes all LCD/video access
through this.
config BACKLIGHT_PWM
bool "Generic PWM based Backlight Driver"
depends on DM_VIDEO && DM_PWM
default y
help
If you have a LCD backlight adjustable by PWM, say Y to enable
this driver.
This driver can be use with "simple-panel" and
it understands the standard device tree
(leds/backlight/pwm-backlight.txt)
config BACKLIGHT_GPIO
bool "Generic GPIO based Backlight Driver"
depends on DM_VIDEO
help
If you have a LCD backlight adjustable by GPIO, say Y to enable
this driver.
This driver can be used with "simple-panel" and
it understands the standard device tree
(leds/backlight/gpio-backlight.txt)
config VIDEO_BPP8
bool "Support 8-bit-per-pixel displays"
depends on DM_VIDEO
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 VIDEO_BPP16
bool "Support 16-bit-per-pixel displays"
depends on DM_VIDEO
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 VIDEO_BPP32
bool "Support 32-bit-per-pixel displays"
depends on DM_VIDEO
default y if X86
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 VIDEO_ANSI
bool "Support ANSI escape sequences in video console"
depends on DM_VIDEO
help
Enable ANSI escape sequence decoding for a more fully functional
console.
config VIDEO_MIPI_DSI
bool "Support MIPI DSI interface"
depends on DM_VIDEO
help
Support MIPI DSI interface for driving a MIPI compatible device.
The MIPI Display Serial Interface (MIPI DSI) defines a high-speed
serial interface between a host processor and a display module.
config CONSOLE_NORMAL
bool "Support a simple text console"
depends on DM_VIDEO
default y if DM_VIDEO
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 CONSOLE_ROTATION
bool "Support rotated displays"
depends on DM_VIDEO
help
Sometimes, for example if the display is mounted in portrait
mode or even if it's mounted landscape but rotated by 180degree,
we need to rotate our content of the display relative to the
framebuffer, so that user can read the messages which are
printed out. Enable this option to include a text driver which can
support this. The rotation is set by the 'rot' parameter in
struct video_priv: 0=unrotated, 1=90 degrees clockwise, 2=180
degrees, 3=270 degrees.
config CONSOLE_TRUETYPE
bool "Support a console that uses TrueType fonts"
depends on DM_VIDEO
help
TrueTrype fonts can provide outline-drawing capability rather than
needing to provide a bitmap for each font and size that is needed.
With this option you can adjust the text size and use a variety of
fonts. Note that this is noticeably slower than with normal console.
config CONSOLE_TRUETYPE_SIZE
int "TrueType font size"
depends on CONSOLE_TRUETYPE
default 18
help
This sets the font size for the console. The size is measured in
pixels and is the nominal height of a character. Note that fonts
are commonly measured in 'points', being 1/72 inch (about 3.52mm).
However that measurement depends on the size of your display and
there is no standard display density. At present there is not a
method to select the display's physical size, which would allow
U-Boot to calculate the correct font size.
config SYS_WHITE_ON_BLACK
bool "Display console as white on a black background"
default y if ARCH_AT91 || ARCH_EXYNOS || ARCH_ROCKCHIP || TEGRA || X86 || ARCH_SUNXI
help
Normally the display is black on a white background, Enable this
option to invert this, i.e. white on a black background. This can be
better in low-light situations or to reduce eye strain in some
cases.
config NO_FB_CLEAR
bool "Skip framebuffer clear"
help
If firmware (whatever loads u-boot) has already put a splash image
on screen, you might want to preserve it until whatever u-boot
loads takes over the screen. This, for example, can be used to
keep splash image on screen until grub graphical boot menu starts.
source "drivers/video/fonts/Kconfig"
config VIDCONSOLE_AS_LCD
bool "Use 'vidconsole' when 'lcd' is seen in stdout"
depends on DM_VIDEO
help
This is a work-around for boards which have 'lcd' in their stdout
environment variable, but have moved to use driver model for video.
In this case the console will no-longer work. While it is possible
to update the environment, the breakage may be confusing for users.
This option will be removed around the end of 2016.
config VIDEO_COREBOOT
bool "Enable coreboot framebuffer driver support"
depends on X86 && SYS_COREBOOT
help
Turn on this option to enable a framebuffer driver when U-Boot is
loaded by coreboot where the graphics device is configured by
coreboot already. This can in principle be used with any platform
that coreboot supports.
config VIDEO_EFI
bool "Enable EFI framebuffer driver support"
depends on EFI_STUB
help
Turn on this option to enable a framebuffeer driver when U-Boot is
loaded as a payload (see README.u-boot_on_efi) by an EFI BIOS where
the graphics device is configured by the EFI BIOS already. This can
in principle be used with any platform that has an EFI BIOS.
config VIDEO_VESA
bool "Enable VESA video driver support"
default n
help
Turn on this option to enable a very simple driver which uses vesa
to discover the video mode and then provides a frame buffer for use
by U-Boot. This can in principle be used with any platform that
supports PCI and video cards that support VESA BIOS Extension (VBE).
config FRAMEBUFFER_SET_VESA_MODE
bool "Set framebuffer graphics resolution"
depends on VIDEO_VESA || VIDEO_BROADWELL_IGD
help
Set VESA/native framebuffer mode (needed for bootsplash and graphical
framebuffer console)
choice
prompt "framebuffer graphics resolution"
default FRAMEBUFFER_VESA_MODE_118
depends on FRAMEBUFFER_SET_VESA_MODE
help
This option sets the resolution used for the U-Boot framebuffer (and
bootsplash screen).
config FRAMEBUFFER_VESA_MODE_100
bool "640x400 256-color"
config FRAMEBUFFER_VESA_MODE_101
bool "640x480 256-color"
config FRAMEBUFFER_VESA_MODE_102
bool "800x600 16-color"
config FRAMEBUFFER_VESA_MODE_103
bool "800x600 256-color"
config FRAMEBUFFER_VESA_MODE_104
bool "1024x768 16-color"
config FRAMEBUFFER_VESA_MODE_105
bool "1024x768 256-color"
config FRAMEBUFFER_VESA_MODE_106
bool "1280x1024 16-color"
config FRAMEBUFFER_VESA_MODE_107
bool "1280x1024 256-color"
config FRAMEBUFFER_VESA_MODE_108
bool "80x60 text"
config FRAMEBUFFER_VESA_MODE_109
bool "132x25 text"
config FRAMEBUFFER_VESA_MODE_10A
bool "132x43 text"
config FRAMEBUFFER_VESA_MODE_10B
bool "132x50 text"
config FRAMEBUFFER_VESA_MODE_10C
bool "132x60 text"
config FRAMEBUFFER_VESA_MODE_10D
bool "320x200 32k-color (1:5:5:5)"
config FRAMEBUFFER_VESA_MODE_10E
bool "320x200 64k-color (5:6:5)"
config FRAMEBUFFER_VESA_MODE_10F
bool "320x200 16.8M-color (8:8:8)"
config FRAMEBUFFER_VESA_MODE_110
bool "640x480 32k-color (1:5:5:5)"
config FRAMEBUFFER_VESA_MODE_111
bool "640x480 64k-color (5:6:5)"
config FRAMEBUFFER_VESA_MODE_112
bool "640x480 16.8M-color (8:8:8)"
config FRAMEBUFFER_VESA_MODE_113
bool "800x600 32k-color (1:5:5:5)"
config FRAMEBUFFER_VESA_MODE_114
bool "800x600 64k-color (5:6:5)"
config FRAMEBUFFER_VESA_MODE_115
bool "800x600 16.8M-color (8:8:8)"
config FRAMEBUFFER_VESA_MODE_116
bool "1024x768 32k-color (1:5:5:5)"
config FRAMEBUFFER_VESA_MODE_117
bool "1024x768 64k-color (5:6:5)"
config FRAMEBUFFER_VESA_MODE_118
bool "1024x768 16.8M-color (8:8:8)"
config FRAMEBUFFER_VESA_MODE_119
bool "1280x1024 32k-color (1:5:5:5)"
config FRAMEBUFFER_VESA_MODE_11A
bool "1280x1024 64k-color (5:6:5)"
config FRAMEBUFFER_VESA_MODE_11B
bool "1280x1024 16.8M-color (8:8:8)"
config FRAMEBUFFER_VESA_MODE_USER
bool "Manually select VESA mode"
endchoice
# Map the config names to an integer (KB).
config FRAMEBUFFER_VESA_MODE
prompt "VESA mode" if FRAMEBUFFER_VESA_MODE_USER
hex
default 0x100 if FRAMEBUFFER_VESA_MODE_100
default 0x101 if FRAMEBUFFER_VESA_MODE_101
default 0x102 if FRAMEBUFFER_VESA_MODE_102
default 0x103 if FRAMEBUFFER_VESA_MODE_103
default 0x104 if FRAMEBUFFER_VESA_MODE_104
default 0x105 if FRAMEBUFFER_VESA_MODE_105
default 0x106 if FRAMEBUFFER_VESA_MODE_106
default 0x107 if FRAMEBUFFER_VESA_MODE_107
default 0x108 if FRAMEBUFFER_VESA_MODE_108
default 0x109 if FRAMEBUFFER_VESA_MODE_109
default 0x10A if FRAMEBUFFER_VESA_MODE_10A
default 0x10B if FRAMEBUFFER_VESA_MODE_10B
default 0x10C if FRAMEBUFFER_VESA_MODE_10C
default 0x10D if FRAMEBUFFER_VESA_MODE_10D
default 0x10E if FRAMEBUFFER_VESA_MODE_10E
default 0x10F if FRAMEBUFFER_VESA_MODE_10F
default 0x110 if FRAMEBUFFER_VESA_MODE_110
default 0x111 if FRAMEBUFFER_VESA_MODE_111
default 0x112 if FRAMEBUFFER_VESA_MODE_112
default 0x113 if FRAMEBUFFER_VESA_MODE_113
default 0x114 if FRAMEBUFFER_VESA_MODE_114
default 0x115 if FRAMEBUFFER_VESA_MODE_115
default 0x116 if FRAMEBUFFER_VESA_MODE_116
default 0x117 if FRAMEBUFFER_VESA_MODE_117
default 0x118 if FRAMEBUFFER_VESA_MODE_118
default 0x119 if FRAMEBUFFER_VESA_MODE_119
default 0x11A if FRAMEBUFFER_VESA_MODE_11A
default 0x11B if FRAMEBUFFER_VESA_MODE_11B
default 0x117 if FRAMEBUFFER_VESA_MODE_USER
config VIDEO_LCD_ANX9804
bool "ANX9804 bridge chip"
default n
---help---
Support for the ANX9804 bridge chip, which can take pixel data coming
from a parallel LCD interface and translate it on the fy into a DP
interface for driving eDP TFT displays. It uses I2C for configuration.
config VIDEO_LCD_ORISETECH_OTM8009A
bool "OTM8009A DSI LCD panel support"
depends on DM_VIDEO
select VIDEO_MIPI_DSI
default n
help
Say Y here if you want to enable support for Orise Technology
otm8009a 480x800 dsi 2dl panel.
config VIDEO_LCD_RAYDIUM_RM68200
bool "RM68200 DSI LCD panel support"
depends on DM_VIDEO
select VIDEO_MIPI_DSI
default n
help
Say Y here if you want to enable support for Raydium RM68200
720x1280 DSI video mode panel.
config VIDEO_LCD_SSD2828
bool "SSD2828 bridge chip"
default n
---help---
Support for the SSD2828 bridge chip, which can take pixel data coming
from a parallel LCD interface and translate it on the fly into MIPI DSI
interface for driving a MIPI compatible LCD panel. It uses SPI for
configuration.
config VIDEO_LCD_SSD2828_TX_CLK
int "SSD2828 TX_CLK frequency (in MHz)"
depends on VIDEO_LCD_SSD2828
default 0
---help---
The frequency of the crystal, which is clocking SSD2828. It may be
anything in the 8MHz-30MHz range and the exact value should be
retrieved from the board schematics. Or in the case of Allwinner
hardware, it can be usually found as 'lcd_xtal_freq' variable in
FEX files. It can be also set to 0 for selecting PCLK from the
parallel LCD interface instead of TX_CLK as the PLL clock source.
config VIDEO_LCD_SSD2828_RESET
string "RESET pin of SSD2828"
depends on VIDEO_LCD_SSD2828
default ""
---help---
The reset pin of SSD2828 chip. This takes a string in the format
understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H.
config VIDEO_LCD_HITACHI_TX18D42VM
bool "Hitachi tx18d42vm LVDS LCD panel support"
depends on VIDEO
default n
---help---
Support for Hitachi tx18d42vm LVDS LCD panels, these panels have a
lcd controller which needs to be initialized over SPI, once that is
done they work like a regular LVDS panel.
config VIDEO_LCD_SPI_CS
string "SPI CS pin for LCD related config job"
depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM
default ""
---help---
This is one of the SPI communication pins, involved in setting up a
working LCD configuration. The exact role of SPI may differ for
different hardware setups. The option takes a string in the format
understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H.
config VIDEO_LCD_SPI_SCLK
string "SPI SCLK pin for LCD related config job"
depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM
default ""
---help---
This is one of the SPI communication pins, involved in setting up a
working LCD configuration. The exact role of SPI may differ for
different hardware setups. The option takes a string in the format
understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H.
config VIDEO_LCD_SPI_MOSI
string "SPI MOSI pin for LCD related config job"
depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM
default ""
---help---
This is one of the SPI communication pins, involved in setting up a
working LCD configuration. The exact role of SPI may differ for
different hardware setups. The option takes a string in the format
understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H.
config VIDEO_LCD_SPI_MISO
string "SPI MISO pin for LCD related config job (optional)"
depends on VIDEO_LCD_SSD2828
default ""
---help---
This is one of the SPI communication pins, involved in setting up a
working LCD configuration. The exact role of SPI may differ for
different hardware setups. If wired up, this pin may provide additional
useful functionality. Such as bi-directional communication with the
hardware and LCD panel id retrieval (if the panel can report it). The
option takes a string in the format understood by 'name_to_gpio'
function, e.g. PH1 for pin 1 of port H.
source "drivers/video/meson/Kconfig"
config VIDEO_MVEBU
bool "Armada XP LCD controller"
default n
---help---
Support for the LCD controller integrated in the Marvell
Armada XP SoC.
config VIDEO_OMAP3
bool "Enable OMAP3+ DSS Support"
depends on ARCH_OMAP2PLUS
help
This enables the Display subsystem (DSS) on OMAP3+ boards.
config I2C_EDID
bool "Enable EDID library"
default n
help
This enables library for accessing EDID data from an LCD panel.
config DISPLAY
bool "Enable Display support"
depends on DM
default n
select I2C_EDID
help
This supports drivers that provide a display, such as eDP (Embedded
DisplayPort) and HDMI (High Definition Multimedia Interface).
The devices provide a simple interface to start up the display,
read display information and enable it.
config NXP_TDA19988
bool "Enable NXP TDA19988 support"
depends on DISPLAY
default n
help
This enables support for the NXP TDA19988 HDMI encoder. This encoder
will convert RGB data streams into HDMI-encoded signals.
config ATMEL_HLCD
bool "Enable ATMEL video support using HLCDC"
depends on DM_VIDEO
help
HLCDC supports video output to an attached LCD panel.
config LOGICORE_DP_TX
bool "Enable Logicore DP TX driver"
depends on DISPLAY
help
Enable the driver for the transmitter part of the Xilinx LogiCORE
DisplayPort, a IP core for Xilinx FPGAs that implements a DisplayPort
video interface as defined by VESA DisplayPort v1.2.
Note that this is a pure transmitter device, and has no display
capabilities by itself.
config VIDEO_BROADWELL_IGD
bool "Enable Intel Broadwell integrated graphics device"
depends on X86
help
This enables support for integrated graphics on Intel broadwell
devices. Initialisation is mostly performed by a VGA boot ROM, with
some setup handled by U-Boot itself. The graphics adaptor works as
a VESA device and supports LCD panels, eDP and LVDS outputs.
Configuration of most aspects of device operation is performed using
a special tool which configures the VGA ROM, but the graphics
resolution can be selected in U-Boot.
config VIDEO_IVYBRIDGE_IGD
bool "Enable Intel Ivybridge integration graphics support"
depends on X86
help
This enables support for integrated graphics on Intel ivybridge
devices. Initialisation is mostly performed by a VGA boot ROM, with
some setup handled by U-Boot itself. The graphics adaptor works as
a VESA device and supports LCD panels, eDP and LVDS outputs.
Configuration of most aspects of device operation is performed using
a special tool which configures the VGA ROM, but the graphics
resolution can be selected in U-Boot.
config VIDEO_FSL_DCU_FB
bool "Enable Freescale Display Control Unit"
depends on VIDEO || DM_VIDEO
help
This enables support for Freescale Display Control Unit (DCU4)
module found on Freescale Vybrid and QorIQ family of SoCs.
config VIDEO_FSL_DCU_MAX_FB_SIZE_MB
int "Freescale DCU framebuffer size"
depends on VIDEO_FSL_DCU_FB
default 4194304
help
Set maximum framebuffer size to be used for Freescale Display
Controller Unit (DCU4).
source "drivers/video/rockchip/Kconfig"
config VIDEO_ARM_MALIDP
bool "Enable Arm Mali Display Processor support"
depends on DM_VIDEO && OF_CONTROL
select VEXPRESS_CLK
help
This enables support for Arm Ltd Mali Display Processors from
the DP500, DP550 and DP650 family.
config VIDEO_SANDBOX_SDL
bool "Enable sandbox video console using SDL"
depends on SANDBOX
help
When using sandbox you can enable an emulated LCD display which
appears as an SDL (Simple DirectMedia Layer) window. This is a
console device and can display stdout output. Within U-Boot is is
a normal bitmap display and can display images as well as text.
source "drivers/video/stm32/Kconfig"
config VIDEO_TEGRA20
bool "Enable LCD support on Tegra20"
depends on OF_CONTROL
help
Tegra20 supports video output to an attached LCD panel as well as
other options such as HDMI. Only the LCD is supported in U-Boot.
This option enables this support which can be used on devices which
have an LCD display connected.
config VIDEO_TEGRA124
bool "Enable video support on Tegra124"
depends on DM_VIDEO
help
Tegra124 supports many video output options including eDP and
HDMI. At present only eDP is supported by U-Boot. This option
enables this support which can be used on devices which
have an eDP display connected.
source "drivers/video/bridge/Kconfig"
source "drivers/video/imx/Kconfig"
config VIDEO
bool "Enable legacy video support"
depends on !DM_VIDEO
help
Define this for video support, without using driver model. Some
drivers use this because they are not yet converted to driver
model. Video drivers typically provide a colour text console and
cursor.
config CFB_CONSOLE
bool "Enable colour frame buffer console"
depends on VIDEO
default y if VIDEO
help
Enables the colour frame buffer driver. This supports colour
output on a bitmap display from an in-memory frame buffer.
Several colour devices are supported along with various options to
adjust the supported features. The driver is implemented in
cfb_console.c
The following defines are needed (cf. smiLynxEM, i8042)
VIDEO_FB_LITTLE_ENDIAN graphic memory organisation
(default big endian)
VIDEO_HW_RECTFILL graphic chip supports
rectangle fill (cf. smiLynxEM)
VIDEO_HW_BITBLT graphic chip supports
bit-blit (cf. smiLynxEM)
VIDEO_VISIBLE_COLS visible pixel columns (cols=pitch)
VIDEO_VISIBLE_ROWS visible pixel rows
VIDEO_PIXEL_SIZE bytes per pixel
VIDEO_DATA_FORMAT graphic data format
(0-5, cf. cfb_console.c)
VIDEO_FB_ADRS framebuffer address
VIDEO_KBD_INIT_FCT keyboard int fct (i.e. rx51_kp_init())
VIDEO_TSTC_FCT test char fct (i.e. rx51_kp_tstc)
VIDEO_GETC_FCT get char fct (i.e. rx51_kp_getc)
CONFIG_VIDEO_LOGO display Linux logo in upper left corner
CONFIG_VIDEO_BMP_LOGO use bmp_logo.h instead of linux_logo.h
for logo. Requires CONFIG_VIDEO_LOGO
CONFIG_CONSOLE_EXTRA_INFO
additional board info beside
the logo
CONFIG_HIDE_LOGO_VERSION
do not display bootloader
version string
When CONFIG_CFB_CONSOLE is defined, the video console is the
default console. The serial console can be forced by setting the
environment 'console=serial'.
config CFB_CONSOLE_ANSI
bool "Support ANSI escape sequences"
depends on CFB_CONSOLE
help
This allows the colour buffer frame buffer driver to support
a limited number of ANSI escape sequences (cursor control,
erase functions and limited graphics rendition control). Normal
output from U-Boot will pass through this filter.
config VGA_AS_SINGLE_DEVICE
bool "Set the video as an output-only device"
depends on CFB_CONSOLE
default y
help
If enable the framebuffer device will be initialized as an
output-only device. The Keyboard driver will not be set up. This
may be used if you have no keyboard device, or more than one
(USB Keyboard, AT Keyboard).
config VIDEO_SW_CURSOR
bool "Enable a software cursor"
depends on CFB_CONSOLE
default y if CFB_CONSOLE
help
This draws a cursor after the last character. No blinking is
provided. This makes it possible to see the current cursor
position when entering text on the console. It is recommended to
enable this.
config CONSOLE_EXTRA_INFO
bool "Display additional board information"
depends on CFB_CONSOLE
help
Display additional board information strings that normally go to
the serial port. When this option is enabled, a board-specific
function video_get_info_str() is called to get the string for
each line of the display. The function should return the string,
which can be empty if there is nothing to display for that line.
config CONSOLE_SCROLL_LINES
int "Number of lines to scroll the console by"
depends on CFB_CONSOLE || DM_VIDEO || LCD
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 SYS_CONSOLE_BG_COL
hex "Background colour"
depends on CFB_CONSOLE
default 0x00
help
Defines the background colour for the console. The value is from
0x00 to 0xff and the meaning depends on the graphics card.
Typically, 0x00 means black and 0xff means white. Do not set
the background and foreground to the same colour or you will see
nothing.
config SYS_CONSOLE_FG_COL
hex "Foreground colour"
depends on CFB_CONSOLE
default 0xa0
help
Defines the foreground colour for the console. The value is from
0x00 to 0xff and the meaning depends on the graphics card.
Typically, 0x00 means black and 0xff means white. Do not set
the background and foreground to the same colour or you will see
nothing.
config LCD
bool "Enable legacy LCD support"
help
Define this to enable LCD support (for output to LCD display).
You will also need to select an LCD driver using an additional
CONFIG option. See the README for details. Drives which have been
converted to driver model will instead used CONFIG_DM_VIDEO.
config VIDEO_DW_HDMI
bool
help
Enables the common driver code for the Designware HDMI TX
block found in SoCs from various vendors.
As this does not provide any functionality by itself (but
rather requires a SoC-specific glue driver to call it), it
can not be enabled from the configuration menu.
config VIDEO_DSI_HOST_SANDBOX
bool "Enable sandbox for dsi host"
depends on SANDBOX
select VIDEO_MIPI_DSI
help
Enable support for sandbox dsi host device used for testing
purposes.
Display Serial Interface (DSI) defines a serial bus and
a communication protocol between the host and the device
(panel, bridge).
config VIDEO_DW_MIPI_DSI
bool
select VIDEO_MIPI_DSI
help
Enables the common driver code for the Synopsis Designware
MIPI DSI block found in SoCs from various vendors.
As this does not provide any functionality by itself (but
rather requires a SoC-specific glue driver to call it), it
can not be enabled from the configuration menu.
config VIDEO_SIMPLE
bool "Simple display driver for preconfigured display"
help
Enables a simple generic display driver which utilizes the
simple-framebuffer devicetree bindings.
This driver assumes that the display hardware has been initialized
before u-boot starts, and u-boot will simply render to the pre-
allocated frame buffer surface.
config VIDEO_DT_SIMPLEFB
bool "Enable SimpleFB support for passing framebuffer to OS"
help
Enables the code to pass the framebuffer to the kernel as a
simple framebuffer in the device tree.
The video output is initialized by U-Boot, and kept by the
kernel.
config OSD
bool "Enable OSD support"
depends on DM
default n
help
This supports drivers that provide a OSD (on-screen display), which
is a (usually text-oriented) graphics buffer to show information on
a display.
config SANDBOX_OSD
bool "Enable sandbox OSD"
depends on OSD
help
Enable support for sandbox OSD device used for testing purposes.
config IHS_VIDEO_OUT
bool "Enable IHS video out driver"
depends on OSD
help
Enable support for the gdsys Integrated Hardware Systems (IHS) video
out On-screen Display (OSD) used on gdsys FPGAs to control dynamic
textual overlays of the display outputs.
endmenu
@@ -0,0 +1,73 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
ifdef CONFIG_DM
obj-$(CONFIG_BACKLIGHT_GPIO) += backlight_gpio.o
obj-$(CONFIG_BACKLIGHT_PWM) += pwm_backlight.o
obj-$(CONFIG_CONSOLE_NORMAL) += console_normal.o
obj-$(CONFIG_CONSOLE_ROTATION) += console_rotate.o
obj-$(CONFIG_CONSOLE_TRUETYPE) += console_truetype.o fonts/
obj-$(CONFIG_DISPLAY) += display-uclass.o
obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o
obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi-host-uclass.o
obj-$(CONFIG_DM_VIDEO) += panel-uclass.o simple_panel.o
obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o
obj-$(CONFIG_DM_VIDEO) += video_bmp.o
endif
obj-${CONFIG_EXYNOS_FB} += exynos/
obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
obj-${CONFIG_VIDEO_STM32} += stm32/
obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o
obj-$(CONFIG_FORMIKE) += formike.o
obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
obj-$(CONFIG_IHS_VIDEO_OUT) += ihs_video_out.o
obj-$(CONFIG_LD9040) += ld9040.o
obj-$(CONFIG_LG4573) += lg4573.o
obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o
obj-$(CONFIG_NXP_TDA19988) += tda19988.o
obj-$(CONFIG_OSD) += video_osd-uclass.o
obj-$(CONFIG_PXA_LCD) += pxa_lcd.o
obj-$(CONFIG_SANDBOX_OSD) += sandbox_osd.o
obj-$(CONFIG_S6E8AX0) += s6e8ax0.o
obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o
obj-$(CONFIG_VIDEO_ARM_MALIDP) += mali_dp.o
obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o
obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o
obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o
obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o
obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o
obj-$(CONFIG_VIDEO_EFI) += efi.o
obj-$(CONFIG_VIDEO_FSL_DCU_FB) += fsl_dcu_fb.o videomodes.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_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_SSD2828) += ssd2828.o
obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o
obj-${CONFIG_VIDEO_MESON} += meson/
obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o
obj-$(CONFIG_VIDEO_MVEBU) += mvebu_lcd.o
obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o
obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
obj-$(CONFIG_VIDEO_DSI_HOST_SANDBOX) += sandbox_dsi_host.o
obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
obj-$(CONFIG_VIDEO_VESA) += vesa.o
obj-y += bridge/
obj-y += sunxi/
@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2013-2018 Hannes Schmelzer <oe5hpm@oevsv.at>
* B&R Industrial Automation GmbH - http://www.br-automation.com
*
* minimal framebuffer driver for TI's AM335x SoC to be compatible with
* Wolfgang Denk's LCD-Framework (CONFIG_LCD, common/lcd.c)
*
* - supporting 16/24/32bit RGB/TFT raster Mode (not using palette)
* - sets up LCD controller as in 'am335x_lcdpanel' struct given
* - starts output DMA from gd->fb_base buffer
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/omap.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <lcd.h>
#include "am335x-fb.h"
#if !defined(LCD_CNTL_BASE)
#error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!"
#endif
#define LCDC_FMAX 200000000
/* LCD Control Register */
#define LCD_CLK_DIVISOR(x) ((x) << 8)
#define LCD_RASTER_MODE 0x01
/* LCD Clock Enable Register */
#define LCD_CORECLKEN (0x01 << 0)
#define LCD_LIDDCLKEN (0x01 << 1)
#define LCD_DMACLKEN (0x01 << 2)
/* LCD DMA Control Register */
#define LCD_DMA_BURST_SIZE(x) ((x) << 4)
#define LCD_DMA_BURST_1 0x0
#define LCD_DMA_BURST_2 0x1
#define LCD_DMA_BURST_4 0x2
#define LCD_DMA_BURST_8 0x3
#define LCD_DMA_BURST_16 0x4
/* LCD Timing_0 Register */
#define LCD_HBPLSB(x) ((((x)-1) & 0xFF) << 24)
#define LCD_HFPLSB(x) ((((x)-1) & 0xFF) << 16)
#define LCD_HSWLSB(x) ((((x)-1) & 0x3F) << 10)
#define LCD_HORLSB(x) (((((x) >> 4)-1) & 0x3F) << 4)
#define LCD_HORMSB(x) (((((x) >> 4)-1) & 0x40) >> 4)
/* LCD Timing_1 Register */
#define LCD_VBP(x) ((x) << 24)
#define LCD_VFP(x) ((x) << 16)
#define LCD_VSW(x) (((x)-1) << 10)
#define LCD_VERLSB(x) (((x)-1) & 0x3FF)
/* LCD Timing_2 Register */
#define LCD_HSWMSB(x) ((((x)-1) & 0x3C0) << 21)
#define LCD_VERMSB(x) ((((x)-1) & 0x400) << 16)
#define LCD_HBPMSB(x) ((((x)-1) & 0x300) >> 4)
#define LCD_HFPMSB(x) ((((x)-1) & 0x300) >> 8)
#define LCD_INVMASK(x) ((x) & 0x3F00000)
/* LCD Raster Ctrl Register */
#define LCD_TFT_24BPP_MODE (1 << 25)
#define LCD_TFT_24BPP_UNPACK (1 << 26)
#define LCD_PALMODE_RAWDATA (0x02 << 20)
#define LCD_TFT_MODE (0x01 << 7)
#define LCD_RASTER_ENABLE (0x01 << 0)
/* Macro definitions */
#define FBSIZE(x) ((x->hactive * x->vactive * x->bpp) >> 3)
struct am335x_lcdhw {
unsigned int pid; /* 0x00 */
unsigned int ctrl; /* 0x04 */
unsigned int gap0; /* 0x08 */
unsigned int lidd_ctrl; /* 0x0C */
unsigned int lidd_cs0_conf; /* 0x10 */
unsigned int lidd_cs0_addr; /* 0x14 */
unsigned int lidd_cs0_data; /* 0x18 */
unsigned int lidd_cs1_conf; /* 0x1C */
unsigned int lidd_cs1_addr; /* 0x20 */
unsigned int lidd_cs1_data; /* 0x24 */
unsigned int raster_ctrl; /* 0x28 */
unsigned int raster_timing0; /* 0x2C */
unsigned int raster_timing1; /* 0x30 */
unsigned int raster_timing2; /* 0x34 */
unsigned int raster_subpanel; /* 0x38 */
unsigned int raster_subpanel2; /* 0x3C */
unsigned int lcddma_ctrl; /* 0x40 */
unsigned int lcddma_fb0_base; /* 0x44 */
unsigned int lcddma_fb0_ceiling; /* 0x48 */
unsigned int lcddma_fb1_base; /* 0x4C */
unsigned int lcddma_fb1_ceiling; /* 0x50 */
unsigned int sysconfig; /* 0x54 */
unsigned int irqstatus_raw; /* 0x58 */
unsigned int irqstatus; /* 0x5C */
unsigned int irqenable_set; /* 0x60 */
unsigned int irqenable_clear; /* 0x64 */
unsigned int gap1; /* 0x68 */
unsigned int clkc_enable; /* 0x6C */
unsigned int clkc_reset; /* 0x70 */
};
static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE;
DECLARE_GLOBAL_DATA_PTR;
int lcd_get_size(int *line_length)
{
*line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
return *line_length * panel_info.vl_row + 0x20;
}
int am335xfb_init(struct am335x_lcdpanel *panel)
{
u32 raster_ctrl = 0;
struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL;
struct dpll_params dpll_disp = { 1, 0, 1, -1, -1, -1, -1 };
unsigned int m, n, d, best_d = 2;
int err = 0, err_r = 0;
if (gd->fb_base == 0) {
printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n");
return -1;
}
if (panel == NULL) {
printf("ERROR: missing ptr to am335x_lcdpanel!\n");
return -1;
}
/* We can already set the bits for the raster_ctrl in this check */
switch (panel->bpp) {
case 16:
break;
case 32:
raster_ctrl |= LCD_TFT_24BPP_UNPACK;
/* fallthrough */
case 24:
raster_ctrl |= LCD_TFT_24BPP_MODE;
break;
default:
pr_err("am335x-fb: invalid bpp value: %d\n", panel->bpp);
return -1;
}
/* check given clock-frequency */
if (panel->pxl_clk > (LCDC_FMAX / 2)) {
pr_err("am335x-fb: requested pxl-clk: %d not supported!\n",
panel->pxl_clk);
return -1;
}
debug("setting up LCD-Controller for %dx%dx%d (hfp=%d,hbp=%d,hsw=%d / ",
panel->hactive, panel->vactive, panel->bpp,
panel->hfp, panel->hbp, panel->hsw);
debug("vfp=%d,vbp=%d,vsw=%d / clk=%d)\n",
panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk);
debug("using frambuffer at 0x%08x with size %d.\n",
(unsigned int)gd->fb_base, FBSIZE(panel));
/* setup display pll for requested clock frequency */
err = panel->pxl_clk;
err_r = err;
for (d = 2; d < 255; d++) {
for (m = 2; m < 2047; m++) {
if ((V_OSCK * m) < (panel->pxl_clk * d))
continue;
n = (V_OSCK * m) / (panel->pxl_clk * d);
if (n > 127)
break;
if (((V_OSCK * m) / n) > LCDC_FMAX)
break;
err = abs((V_OSCK * m) / n / d - panel->pxl_clk);
if (err < err_r) {
err_r = err;
dpll_disp.m = m;
dpll_disp.n = n;
best_d = d;
}
}
}
debug("%s: PLL: best error %d Hz (M %d, N %d, DISP %d)\n",
__func__, err_r, dpll_disp.m, dpll_disp.n, best_d);
do_setup_dpll(&dpll_disp_regs, &dpll_disp);
/* clock source for LCDC from dispPLL M2 */
writel(0x0, &cmdpll->clklcdcpixelclk);
/* palette default entry */
memset((void *)gd->fb_base, 0, 0x20);
*(unsigned int *)gd->fb_base = 0x4000;
/* point fb behind palette */
gd->fb_base += 0x20;
/* turn ON display through powercontrol function if accessible */
if (panel->panel_power_ctrl != NULL)
panel->panel_power_ctrl(1);
debug("am335x-fb: wait for stable power ...\n");
mdelay(panel->pup_delay);
lcdhw->clkc_enable = LCD_CORECLKEN | LCD_LIDDCLKEN | LCD_DMACLKEN;
lcdhw->raster_ctrl = 0;
lcdhw->ctrl = LCD_CLK_DIVISOR(best_d) | LCD_RASTER_MODE;
lcdhw->lcddma_fb0_base = gd->fb_base;
lcdhw->lcddma_fb0_ceiling = gd->fb_base + FBSIZE(panel);
lcdhw->lcddma_fb1_base = gd->fb_base;
lcdhw->lcddma_fb1_ceiling = gd->fb_base + FBSIZE(panel);
lcdhw->lcddma_ctrl = LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16);
lcdhw->raster_timing0 = LCD_HORLSB(panel->hactive) |
LCD_HORMSB(panel->hactive) |
LCD_HFPLSB(panel->hfp) |
LCD_HBPLSB(panel->hbp) |
LCD_HSWLSB(panel->hsw);
lcdhw->raster_timing1 = LCD_VBP(panel->vbp) |
LCD_VFP(panel->vfp) |
LCD_VSW(panel->vsw) |
LCD_VERLSB(panel->vactive);
lcdhw->raster_timing2 = LCD_HSWMSB(panel->hsw) |
LCD_VERMSB(panel->vactive) |
LCD_INVMASK(panel->pol) |
LCD_HBPMSB(panel->hbp) |
LCD_HFPMSB(panel->hfp) |
0x0000FF00; /* clk cycles for ac-bias */
lcdhw->raster_ctrl = raster_ctrl |
LCD_PALMODE_RAWDATA |
LCD_TFT_MODE |
LCD_RASTER_ENABLE;
debug("am335x-fb: waiting picture to be stable.\n.");
mdelay(panel->pon_delay);
return 0;
}
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2013-2018 Hannes Schmelzer <oe5hpm@oevsv.at> -
* B&R Industrial Automation GmbH - http://www.br-automation.com
*/
#ifndef AM335X_FB_H
#define AM335X_FB_H
#define HSVS_CONTROL (0x01 << 25) /*
* 0 = lcd_lp and lcd_fp are driven on
* opposite edges of pixel clock than
* the lcd_pixel_o
* 1 = lcd_lp and lcd_fp are driven
* according to bit 24 Note that this
* bit MUST be set to '0' for Passive
* Matrix displays the edge timing is
* fixed
*/
#define HSVS_RISEFALL (0x01 << 24) /*
* 0 = lcd_lp and lcd_fp are driven on
* the rising edge of pixel clock (bit
* 25 must be set to 1)
* 1 = lcd_lp and lcd_fp are driven on
* the falling edge of pixel clock (bit
* 25 must be set to 1)
*/
#define DE_INVERT (0x01 << 23) /*
* 0 = DE is low-active
* 1 = DE is high-active
*/
#define PXCLK_INVERT (0x01 << 22) /*
* 0 = pix-clk is high-active
* 1 = pic-clk is low-active
*/
#define HSYNC_INVERT (0x01 << 21) /*
* 0 = HSYNC is active high
* 1 = HSYNC is avtive low
*/
#define VSYNC_INVERT (0x01 << 20) /*
* 0 = VSYNC is active high
* 1 = VSYNC is active low
*/
struct am335x_lcdpanel {
unsigned int hactive; /* Horizontal active area */
unsigned int vactive; /* Vertical active area */
unsigned int bpp; /* bits per pixel */
unsigned int hfp; /* Horizontal front porch */
unsigned int hbp; /* Horizontal back porch */
unsigned int hsw; /* Horizontal Sync Pulse Width */
unsigned int vfp; /* Vertical front porch */
unsigned int vbp; /* Vertical back porch */
unsigned int vsw; /* Vertical Sync Pulse Width */
unsigned int pxl_clk; /* Pixel clock */
unsigned int pol; /* polarity of sync, clock signals */
unsigned int pup_delay; /*
* time in ms after power on to
* initialization of lcd-controller
* (VCC ramp up time)
*/
unsigned int pon_delay; /*
* time in ms after initialization of
* lcd-controller (pic stabilization)
*/
void (*panel_power_ctrl)(int); /* fp for power on/off display */
};
int am335xfb_init(struct am335x_lcdpanel *panel);
#endif /* AM335X_FB_H */
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) 2015 Hans de Goede <hdegoede@redhat.com>
*/
/*
* Support for the ANX9804 bridge chip, which can take pixel data coming
* from a parallel LCD interface and translate it on the flight into a DP
* interface for driving eDP TFT displays.
*/
#include <common.h>
#include <i2c.h>
#include "anx98xx-edp.h"
#include "anx9804.h"
/**
* anx9804_init() - Init anx9804 parallel lcd to edp bridge chip
*
* This function will init an anx9804 parallel lcd to dp bridge chip
* using the passed in parameters.
*
* @i2c_bus: Number of the i2c bus to which the anx9804 is connected.
* @lanes: Number of displayport lanes to use
* @data_rate: Register value for the bandwidth reg 0x06: 1.62G, 0x0a: 2.7G
* @bpp: Bits per pixel, must be 18 or 24
*/
void anx9804_init(unsigned int i2c_bus, u8 lanes, u8 data_rate, int bpp)
{
unsigned int orig_i2c_bus = i2c_get_bus_num();
u8 c, colordepth;
int i;
i2c_set_bus_num(i2c_bus);
if (bpp == 18)
colordepth = 0x00; /* 6 bit */
else
colordepth = 0x10; /* 8 bit */
/* Reset */
i2c_reg_write(0x39, ANX9804_RST_CTRL_REG, 1);
mdelay(100);
i2c_reg_write(0x39, ANX9804_RST_CTRL_REG, 0);
/* Write 0 to the powerdown reg (powerup everything) */
i2c_reg_write(0x39, ANX9804_POWERD_CTRL_REG, 0);
c = i2c_reg_read(0x39, ANX9804_DEV_IDH_REG);
if (c != 0x98) {
printf("Error anx9804 chipid mismatch\n");
i2c_set_bus_num(orig_i2c_bus);
return;
}
for (i = 0; i < 100; i++) {
c = i2c_reg_read(0x38, ANX9804_SYS_CTRL2_REG);
i2c_reg_write(0x38, ANX9804_SYS_CTRL2_REG, c);
c = i2c_reg_read(0x38, ANX9804_SYS_CTRL2_REG);
if ((c & ANX9804_SYS_CTRL2_CHA_STA) == 0)
break;
mdelay(5);
}
if (i == 100)
printf("Error anx9804 clock is not stable\n");
i2c_reg_write(0x39, ANX9804_VID_CTRL2_REG, colordepth);
/* Set a bunch of analog related register values */
i2c_reg_write(0x38, ANX9804_PLL_CTRL_REG, 0x07);
i2c_reg_write(0x39, ANX9804_PLL_FILTER_CTRL3, 0x19);
i2c_reg_write(0x39, ANX9804_PLL_CTRL3, 0xd9);
i2c_reg_write(0x39, ANX9804_RST_CTRL2_REG, ANX9804_RST_CTRL2_AC_MODE);
i2c_reg_write(0x39, ANX9804_ANALOG_DEBUG_REG1, 0xf0);
i2c_reg_write(0x39, ANX9804_ANALOG_DEBUG_REG3, 0x99);
i2c_reg_write(0x39, ANX9804_PLL_FILTER_CTRL1, 0x7b);
i2c_reg_write(0x38, ANX9804_LINK_DEBUG_REG, 0x30);
i2c_reg_write(0x39, ANX9804_PLL_FILTER_CTRL, 0x06);
/* Force HPD */
i2c_reg_write(0x38, ANX9804_SYS_CTRL3_REG,
ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL);
/* Power up and configure lanes */
i2c_reg_write(0x38, ANX9804_ANALOG_POWER_DOWN_REG, 0x00);
i2c_reg_write(0x38, ANX9804_TRAINING_LANE0_SET_REG, 0x00);
i2c_reg_write(0x38, ANX9804_TRAINING_LANE1_SET_REG, 0x00);
i2c_reg_write(0x38, ANX9804_TRAINING_LANE2_SET_REG, 0x00);
i2c_reg_write(0x38, ANX9804_TRAINING_LANE3_SET_REG, 0x00);
/* Reset AUX CH */
i2c_reg_write(0x39, ANX9804_RST_CTRL2_REG,
ANX9804_RST_CTRL2_AC_MODE | ANX9804_RST_CTRL2_AUX);
i2c_reg_write(0x39, ANX9804_RST_CTRL2_REG,
ANX9804_RST_CTRL2_AC_MODE);
/* Powerdown audio and some other unused bits */
i2c_reg_write(0x39, ANX9804_POWERD_CTRL_REG, ANX9804_POWERD_AUDIO);
i2c_reg_write(0x38, ANX9804_HDCP_CONTROL_0_REG, 0x00);
i2c_reg_write(0x38, 0xa7, 0x00);
/* Set data-rate / lanes */
i2c_reg_write(0x38, ANX9804_LINK_BW_SET_REG, data_rate);
i2c_reg_write(0x38, ANX9804_LANE_COUNT_SET_REG, lanes);
/* Link training */
i2c_reg_write(0x38, ANX9804_LINK_TRAINING_CTRL_REG,
ANX9804_LINK_TRAINING_CTRL_EN);
mdelay(5);
for (i = 0; i < 100; i++) {
c = i2c_reg_read(0x38, ANX9804_LINK_TRAINING_CTRL_REG);
if ((c & 0x01) == 0)
break;
mdelay(5);
}
if(i == 100) {
printf("Error anx9804 link training timeout\n");
i2c_set_bus_num(orig_i2c_bus);
return;
}
/* Enable */
i2c_reg_write(0x39, ANX9804_VID_CTRL1_REG,
ANX9804_VID_CTRL1_VID_EN | ANX9804_VID_CTRL1_EDGE);
/* Force stream valid */
i2c_reg_write(0x38, ANX9804_SYS_CTRL3_REG,
ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL |
ANX9804_SYS_CTRL3_F_VALID | ANX9804_SYS_CTRL3_VALID_CTRL);
i2c_set_bus_num(orig_i2c_bus);
}
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) 2015 Hans de Goede <hdegoede@redhat.com>
*/
/*
* Support for the ANX9804 bridge chip, which can take pixel data coming
* from a parallel LCD interface and translate it on the flight into a DP
* interface for driving eDP TFT displays.
*/
#ifndef _ANX9804_H
#define _ANX9804_H
#define ANX9804_DATA_RATE_1620M 0x06
#define ANX9804_DATA_RATE_2700M 0x0a
#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
void anx9804_init(unsigned int i2c_bus, u8 lanes, u8 data_rate, int bpp);
#else
static inline void anx9804_init(unsigned int i2c_bus, u8 lanes, u8 data_rate,
int bpp) {}
#endif
#endif
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2017 Vasily Khoruzhick <anarsoul@gmail.com>
*/
/* Registers at i2c address 0x38 */
#define ANX9804_HDCP_CONTROL_0_REG 0x01
#define ANX9804_SYS_CTRL1_REG 0x80
#define ANX9804_SYS_CTRL1_PD_IO 0x80
#define ANX9804_SYS_CTRL1_PD_VID 0x40
#define ANX9804_SYS_CTRL1_PD_LINK 0x20
#define ANX9804_SYS_CTRL1_PD_TOTAL 0x10
#define ANX9804_SYS_CTRL1_MODE_SEL 0x08
#define ANX9804_SYS_CTRL1_DET_STA 0x04
#define ANX9804_SYS_CTRL1_FORCE_DET 0x02
#define ANX9804_SYS_CTRL1_DET_CTRL 0x01
#define ANX9804_SYS_CTRL2_REG 0x81
#define ANX9804_SYS_CTRL2_CHA_STA 0x04
#define ANX9804_SYS_CTRL3_REG 0x82
#define ANX9804_SYS_CTRL3_VALID_CTRL BIT(0)
#define ANX9804_SYS_CTRL3_F_VALID BIT(1)
#define ANX9804_SYS_CTRL3_HPD_CTRL BIT(4)
#define ANX9804_SYS_CTRL3_F_HPD BIT(5)
#define ANX9804_LINK_BW_SET_REG 0xa0
#define ANX9804_LANE_COUNT_SET_REG 0xa1
#define ANX9804_TRAINING_PTN_SET_REG 0xa2
#define ANX9804_TRAINING_LANE0_SET_REG 0xa3
#define ANX9804_TRAINING_LANE1_SET_REG 0xa4
#define ANX9804_TRAINING_LANE2_SET_REG 0xa5
#define ANX9804_TRAINING_LANE3_SET_REG 0xa6
#define ANX9804_LINK_TRAINING_CTRL_REG 0xa8
#define ANX9804_LINK_TRAINING_CTRL_EN BIT(0)
#define ANX9804_LINK_DEBUG_REG 0xb8
#define ANX9804_PLL_CTRL_REG 0xc7
#define ANX9804_ANALOG_POWER_DOWN_REG 0xc8
#define ANX9804_AUX_CH_STA 0xe0
#define ANX9804_AUX_BUSY BIT(4)
#define ANX9804_AUX_STATUS_MASK 0x0f
#define ANX9804_DP_AUX_RX_COMM 0xe3
#define ANX9804_AUX_RX_COMM_I2C_DEFER BIT(3)
#define ANX9804_AUX_RX_COMM_AUX_DEFER BIT(1)
#define ANX9804_DP_AUX_CH_CTL_1 0xe5
#define ANX9804_AUX_LENGTH(x) (((x - 1) & 0x0f) << 4)
#define ANX9804_AUX_TX_COMM_MASK 0x0f
#define ANX9804_AUX_TX_COMM_DP_TRANSACTION BIT(3)
#define ANX9804_AUX_TX_COMM_MOT BIT(2)
#define ANX9804_AUX_TX_COMM_READ BIT(0)
#define ANX9804_DP_AUX_ADDR_7_0 0xe6
#define ANX9804_DP_AUX_ADDR_15_8 0xe7
#define ANX9804_DP_AUX_ADDR_19_16 0xe8
#define ANX9804_DP_AUX_CH_CTL_2 0xe9
#define ANX9804_ADDR_ONLY BIT(1)
#define ANX9804_AUX_EN BIT(0)
#define ANX9804_BUF_DATA_0 0xf0
/* Registers at i2c address 0x39 */
#define ANX9804_DEV_IDH_REG 0x03
#define ANX9804_POWERD_CTRL_REG 0x05
#define ANX9804_POWERD_AUDIO BIT(4)
#define ANX9804_RST_CTRL_REG 0x06
#define ANX9804_RST_CTRL2_REG 0x07
#define ANX9804_RST_CTRL2_AUX BIT(2)
#define ANX9804_RST_CTRL2_AC_MODE BIT(6)
#define ANX9804_VID_CTRL1_REG 0x08
#define ANX9804_VID_CTRL1_VID_EN BIT(7)
#define ANX9804_VID_CTRL1_EDGE BIT(0)
#define ANX9804_VID_CTRL2_REG 0x09
#define ANX9804_ANALOG_DEBUG_REG1 0xdc
#define ANX9804_ANALOG_DEBUG_REG3 0xde
#define ANX9804_PLL_FILTER_CTRL1 0xdf
#define ANX9804_PLL_FILTER_CTRL3 0xe1
#define ANX9804_PLL_FILTER_CTRL 0xe2
#define ANX9804_PLL_CTRL3 0xe6
#define ANX9804_DP_INT_STA 0xf7
#define ANX9804_RPLY_RECEIV BIT(1)
#define ANX9804_AUX_ERR BIT(0)
@@ -0,0 +1,211 @@
/*
* ATI PCI IDs from XFree86, kept here to make sync'ing with
* XFree much simpler. Currently, this list is only used by
* radeonfb
*/
#define PCI_CHIP_RV380_3150 0x3150
#define PCI_CHIP_RV380_3151 0x3151
#define PCI_CHIP_RV380_3152 0x3152
#define PCI_CHIP_RV380_3153 0x3153
#define PCI_CHIP_RV380_3154 0x3154
#define PCI_CHIP_RV380_3156 0x3156
#define PCI_CHIP_RV380_3E50 0x3E50
#define PCI_CHIP_RV380_3E51 0x3E51
#define PCI_CHIP_RV380_3E52 0x3E52
#define PCI_CHIP_RV380_3E53 0x3E53
#define PCI_CHIP_RV380_3E54 0x3E54
#define PCI_CHIP_RV380_3E56 0x3E56
#define PCI_CHIP_RS100_4136 0x4136
#define PCI_CHIP_RS200_4137 0x4137
#define PCI_CHIP_R300_AD 0x4144
#define PCI_CHIP_R300_AE 0x4145
#define PCI_CHIP_R300_AF 0x4146
#define PCI_CHIP_R300_AG 0x4147
#define PCI_CHIP_R350_AH 0x4148
#define PCI_CHIP_R350_AI 0x4149
#define PCI_CHIP_R350_AJ 0x414A
#define PCI_CHIP_R350_AK 0x414B
#define PCI_CHIP_RV350_AP 0x4150
#define PCI_CHIP_RV350_AQ 0x4151
#define PCI_CHIP_RV360_AR 0x4152
#define PCI_CHIP_RV350_AS 0x4153
#define PCI_CHIP_RV350_AT 0x4154
#define PCI_CHIP_RV350_AV 0x4156
#define PCI_CHIP_MACH32 0x4158
#define PCI_CHIP_RS250_4237 0x4237
#define PCI_CHIP_R200_BB 0x4242
#define PCI_CHIP_R200_BC 0x4243
#define PCI_CHIP_RS100_4336 0x4336
#define PCI_CHIP_RS200_4337 0x4337
#define PCI_CHIP_MACH64CT 0x4354
#define PCI_CHIP_MACH64CX 0x4358
#define PCI_CHIP_RS250_4437 0x4437
#define PCI_CHIP_MACH64ET 0x4554
#define PCI_CHIP_MACH64GB 0x4742
#define PCI_CHIP_MACH64GD 0x4744
#define PCI_CHIP_MACH64GI 0x4749
#define PCI_CHIP_MACH64GL 0x474C
#define PCI_CHIP_MACH64GM 0x474D
#define PCI_CHIP_MACH64GN 0x474E
#define PCI_CHIP_MACH64GO 0x474F
#define PCI_CHIP_MACH64GP 0x4750
#define PCI_CHIP_MACH64GQ 0x4751
#define PCI_CHIP_MACH64GR 0x4752
#define PCI_CHIP_MACH64GS 0x4753
#define PCI_CHIP_MACH64GT 0x4754
#define PCI_CHIP_MACH64GU 0x4755
#define PCI_CHIP_MACH64GV 0x4756
#define PCI_CHIP_MACH64GW 0x4757
#define PCI_CHIP_MACH64GX 0x4758
#define PCI_CHIP_MACH64GY 0x4759
#define PCI_CHIP_MACH64GZ 0x475A
#define PCI_CHIP_RV250_Id 0x4964
#define PCI_CHIP_RV250_Ie 0x4965
#define PCI_CHIP_RV250_If 0x4966
#define PCI_CHIP_RV250_Ig 0x4967
#define PCI_CHIP_R420_JH 0x4A48
#define PCI_CHIP_R420_JI 0x4A49
#define PCI_CHIP_R420_JJ 0x4A4A
#define PCI_CHIP_R420_JK 0x4A4B
#define PCI_CHIP_R420_JL 0x4A4C
#define PCI_CHIP_R420_JM 0x4A4D
#define PCI_CHIP_R420_JN 0x4A4E
#define PCI_CHIP_R420_JP 0x4A50
#define PCI_CHIP_MACH64LB 0x4C42
#define PCI_CHIP_MACH64LD 0x4C44
#define PCI_CHIP_RAGE128LE 0x4C45
#define PCI_CHIP_RAGE128LF 0x4C46
#define PCI_CHIP_MACH64LG 0x4C47
#define PCI_CHIP_MACH64LI 0x4C49
#define PCI_CHIP_MACH64LM 0x4C4D
#define PCI_CHIP_MACH64LN 0x4C4E
#define PCI_CHIP_MACH64LP 0x4C50
#define PCI_CHIP_MACH64LQ 0x4C51
#define PCI_CHIP_MACH64LR 0x4C52
#define PCI_CHIP_MACH64LS 0x4C53
#define PCI_CHIP_MACH64LT 0x4C54
#define PCI_CHIP_RADEON_LW 0x4C57
#define PCI_CHIP_RADEON_LX 0x4C58
#define PCI_CHIP_RADEON_LY 0x4C59
#define PCI_CHIP_RADEON_LZ 0x4C5A
#define PCI_CHIP_RV250_Ld 0x4C64
#define PCI_CHIP_RV250_Le 0x4C65
#define PCI_CHIP_RV250_Lf 0x4C66
#define PCI_CHIP_RV250_Lg 0x4C67
#define PCI_CHIP_RV250_Ln 0x4C6E
#define PCI_CHIP_RAGE128MF 0x4D46
#define PCI_CHIP_RAGE128ML 0x4D4C
#define PCI_CHIP_R300_ND 0x4E44
#define PCI_CHIP_R300_NE 0x4E45
#define PCI_CHIP_R300_NF 0x4E46
#define PCI_CHIP_R300_NG 0x4E47
#define PCI_CHIP_R350_NH 0x4E48
#define PCI_CHIP_R350_NI 0x4E49
#define PCI_CHIP_R360_NJ 0x4E4A
#define PCI_CHIP_R350_NK 0x4E4B
#define PCI_CHIP_RV350_NP 0x4E50
#define PCI_CHIP_RV350_NQ 0x4E51
#define PCI_CHIP_RV350_NR 0x4E52
#define PCI_CHIP_RV350_NS 0x4E53
#define PCI_CHIP_RV350_NT 0x4E54
#define PCI_CHIP_RV350_NV 0x4E56
#define PCI_CHIP_RAGE128PA 0x5041
#define PCI_CHIP_RAGE128PB 0x5042
#define PCI_CHIP_RAGE128PC 0x5043
#define PCI_CHIP_RAGE128PD 0x5044
#define PCI_CHIP_RAGE128PE 0x5045
#define PCI_CHIP_RAGE128PF 0x5046
#define PCI_CHIP_RAGE128PG 0x5047
#define PCI_CHIP_RAGE128PH 0x5048
#define PCI_CHIP_RAGE128PI 0x5049
#define PCI_CHIP_RAGE128PJ 0x504A
#define PCI_CHIP_RAGE128PK 0x504B
#define PCI_CHIP_RAGE128PL 0x504C
#define PCI_CHIP_RAGE128PM 0x504D
#define PCI_CHIP_RAGE128PN 0x504E
#define PCI_CHIP_RAGE128PO 0x504F
#define PCI_CHIP_RAGE128PP 0x5050
#define PCI_CHIP_RAGE128PQ 0x5051
#define PCI_CHIP_RAGE128PR 0x5052
#define PCI_CHIP_RAGE128PS 0x5053
#define PCI_CHIP_RAGE128PT 0x5054
#define PCI_CHIP_RAGE128PU 0x5055
#define PCI_CHIP_RAGE128PV 0x5056
#define PCI_CHIP_RAGE128PW 0x5057
#define PCI_CHIP_RAGE128PX 0x5058
#define PCI_CHIP_RADEON_QD 0x5144
#define PCI_CHIP_RADEON_QE 0x5145
#define PCI_CHIP_RADEON_QF 0x5146
#define PCI_CHIP_RADEON_QG 0x5147
#define PCI_CHIP_R200_QH 0x5148
#define PCI_CHIP_R200_QI 0x5149
#define PCI_CHIP_R200_QJ 0x514A
#define PCI_CHIP_R200_QK 0x514B
#define PCI_CHIP_R200_QL 0x514C
#define PCI_CHIP_R200_QM 0x514D
#define PCI_CHIP_R200_QN 0x514E
#define PCI_CHIP_R200_QO 0x514F
#define PCI_CHIP_RV200_QW 0x5157
#define PCI_CHIP_RV200_QX 0x5158
#define PCI_CHIP_RV100_QY 0x5159
#define PCI_CHIP_RV100_QZ 0x515A
#define PCI_CHIP_RN50 0x515E
#define PCI_CHIP_RAGE128RE 0x5245
#define PCI_CHIP_RAGE128RF 0x5246
#define PCI_CHIP_RAGE128RG 0x5247
#define PCI_CHIP_RAGE128RK 0x524B
#define PCI_CHIP_RAGE128RL 0x524C
#define PCI_CHIP_RAGE128SE 0x5345
#define PCI_CHIP_RAGE128SF 0x5346
#define PCI_CHIP_RAGE128SG 0x5347
#define PCI_CHIP_RAGE128SH 0x5348
#define PCI_CHIP_RAGE128SK 0x534B
#define PCI_CHIP_RAGE128SL 0x534C
#define PCI_CHIP_RAGE128SM 0x534D
#define PCI_CHIP_RAGE128SN 0x534E
#define PCI_CHIP_RAGE128TF 0x5446
#define PCI_CHIP_RAGE128TL 0x544C
#define PCI_CHIP_RAGE128TR 0x5452
#define PCI_CHIP_RAGE128TS 0x5453
#define PCI_CHIP_RAGE128TT 0x5454
#define PCI_CHIP_RAGE128TU 0x5455
#define PCI_CHIP_RV370_5460 0x5460
#define PCI_CHIP_RV370_5461 0x5461
#define PCI_CHIP_RV370_5462 0x5462
#define PCI_CHIP_RV370_5463 0x5463
#define PCI_CHIP_RV370_5464 0x5464
#define PCI_CHIP_RV370_5465 0x5465
#define PCI_CHIP_RV370_5466 0x5466
#define PCI_CHIP_RV370_5467 0x5467
#define PCI_CHIP_R423_UH 0x5548
#define PCI_CHIP_R423_UI 0x5549
#define PCI_CHIP_R423_UJ 0x554A
#define PCI_CHIP_R423_UK 0x554B
#define PCI_CHIP_R423_UQ 0x5551
#define PCI_CHIP_R423_UR 0x5552
#define PCI_CHIP_R423_UT 0x5554
#define PCI_CHIP_MACH64VT 0x5654
#define PCI_CHIP_MACH64VU 0x5655
#define PCI_CHIP_MACH64VV 0x5656
#define PCI_CHIP_RS300_5834 0x5834
#define PCI_CHIP_RS300_5835 0x5835
#define PCI_CHIP_RS300_5836 0x5836
#define PCI_CHIP_RS300_5837 0x5837
#define PCI_CHIP_RV370_5B60 0x5B60
#define PCI_CHIP_RV370_5B61 0x5B61
#define PCI_CHIP_RV370_5B62 0x5B62
#define PCI_CHIP_RV370_5B63 0x5B63
#define PCI_CHIP_RV370_5B64 0x5B64
#define PCI_CHIP_RV370_5B65 0x5B65
#define PCI_CHIP_RV370_5B66 0x5B66
#define PCI_CHIP_RV370_5B67 0x5B67
#define PCI_CHIP_RV280_5960 0x5960
#define PCI_CHIP_RV280_5961 0x5961
#define PCI_CHIP_RV280_5962 0x5962
#define PCI_CHIP_RV280_5964 0x5964
#define PCI_CHIP_RV280_5C61 0x5C61
#define PCI_CHIP_RV280_5C63 0x5C63
#define PCI_CHIP_R423_5D57 0x5D57
#define PCI_CHIP_RS350_7834 0x7834
#define PCI_CHIP_RS350_7835 0x7835
@@ -0,0 +1,760 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* ATI Radeon Video card Framebuffer driver.
*
* Copyright 2007 Freescale Semiconductor, Inc.
* Zhang Wei <wei.zhang@freescale.com>
* Jason Jin <jason.jin@freescale.com>
*
* Some codes of this file is partly ported from Linux kernel
* ATI video framebuffer driver.
*
* Now the driver is tested on below ATI chips:
* 9200
* X300
* X700
*/
#include <common.h>
#include <command.h>
#include <bios_emul.h>
#include <env.h>
#include <pci.h>
#include <asm/processor.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <malloc.h>
#include <video_fb.h>
#include "videomodes.h"
#include <radeon.h>
#include "ati_ids.h"
#include "ati_radeon_fb.h"
#undef DEBUG
#ifdef DEBUG
#define DPRINT(x...) printf(x)
#else
#define DPRINT(x...) do{}while(0)
#endif
#define MAX_MAPPED_VRAM (2048*2048*4)
#define MIN_MAPPED_VRAM (1024*768*1)
#define RADEON_BUFFER_ALIGN 0x00000fff
#define SURF_UPPER_BOUND(x,y,bpp) (((((x) * (((y) + 15) & ~15) * (bpp)/8) + RADEON_BUFFER_ALIGN) \
& ~RADEON_BUFFER_ALIGN) - 1)
#define RADEON_CRT_PITCH(width, bpp) ((((width) * (bpp) + ((bpp) * 8 - 1)) / ((bpp) * 8)) | \
((((width) * (bpp) + ((bpp) * 8 - 1)) / ((bpp) * 8)) << 16))
#define CRTC_H_TOTAL_DISP_VAL(htotal, hdisp) \
(((((htotal) / 8) - 1) & 0x3ff) | (((((hdisp) / 8) - 1) & 0x1ff) << 16))
#define CRTC_HSYNC_STRT_WID_VAL(hsync_srtr, hsync_wid) \
(((hsync_srtr) & 0x1fff) | (((hsync_wid) & 0x3f) << 16))
#define CRTC_V_TOTAL_DISP_VAL(vtotal, vdisp) \
((((vtotal) - 1) & 0xffff) | (((vdisp) - 1) << 16))
#define CRTC_VSYNC_STRT_WID_VAL(vsync_srtr, vsync_wid) \
((((vsync_srtr) - 1) & 0xfff) | (((vsync_wid) & 0x1f) << 16))
/*#define PCI_VENDOR_ID_ATI*/
#define PCI_CHIP_RV280_5960 0x5960
#define PCI_CHIP_RV280_5961 0x5961
#define PCI_CHIP_RV280_5962 0x5962
#define PCI_CHIP_RV280_5964 0x5964
#define PCI_CHIP_RV280_5C63 0x5C63
#define PCI_CHIP_RV370_5B60 0x5B60
#define PCI_CHIP_RV380_5657 0x5657
#define PCI_CHIP_R420_554d 0x554d
static struct pci_device_id ati_radeon_pci_ids[] = {
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5960},
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5961},
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5962},
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5964},
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5C63},
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV370_5B60},
{PCI_VENDOR_ID_ATI, PCI_CHIP_RV380_5657},
{PCI_VENDOR_ID_ATI, PCI_CHIP_R420_554d},
{0, 0}
};
static u16 ati_radeon_id_family_table[][2] = {
{PCI_CHIP_RV280_5960, CHIP_FAMILY_RV280},
{PCI_CHIP_RV280_5961, CHIP_FAMILY_RV280},
{PCI_CHIP_RV280_5962, CHIP_FAMILY_RV280},
{PCI_CHIP_RV280_5964, CHIP_FAMILY_RV280},
{PCI_CHIP_RV280_5C63, CHIP_FAMILY_RV280},
{PCI_CHIP_RV370_5B60, CHIP_FAMILY_RV380},
{PCI_CHIP_RV380_5657, CHIP_FAMILY_RV380},
{PCI_CHIP_R420_554d, CHIP_FAMILY_R420},
{0, 0}
};
u16 get_radeon_id_family(u16 device)
{
int i;
for (i=0; ati_radeon_id_family_table[0][i]; i+=2)
if (ati_radeon_id_family_table[0][i] == device)
return ati_radeon_id_family_table[0][i + 1];
return 0;
}
struct radeonfb_info *rinfo;
static void radeon_identify_vram(struct radeonfb_info *rinfo)
{
u32 tmp;
/* framebuffer size */
if ((rinfo->family == CHIP_FAMILY_RS100) ||
(rinfo->family == CHIP_FAMILY_RS200) ||
(rinfo->family == CHIP_FAMILY_RS300)) {
u32 tom = INREG(NB_TOM);
tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024);
radeon_fifo_wait(6);
OUTREG(MC_FB_LOCATION, tom);
OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16);
OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16);
OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16);
/* This is supposed to fix the crtc2 noise problem. */
OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000);
if ((rinfo->family == CHIP_FAMILY_RS100) ||
(rinfo->family == CHIP_FAMILY_RS200)) {
/* This is to workaround the asic bug for RMX, some versions
of BIOS dosen't have this register initialized correctly.
*/
OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN,
~CRTC_H_CUTOFF_ACTIVE_EN);
}
} else {
tmp = INREG(CONFIG_MEMSIZE);
}
/* mem size is bits [28:0], mask off the rest */
rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK;
/*
* Hack to get around some busted production M6's
* reporting no ram
*/
if (rinfo->video_ram == 0) {
switch (rinfo->pdev.device) {
case PCI_CHIP_RADEON_LY:
case PCI_CHIP_RADEON_LZ:
rinfo->video_ram = 8192 * 1024;
break;
default:
break;
}
}
/*
* Now try to identify VRAM type
*/
if ((rinfo->family >= CHIP_FAMILY_R300) ||
(INREG(MEM_SDRAM_MODE_REG) & (1<<30)))
rinfo->vram_ddr = 1;
else
rinfo->vram_ddr = 0;
tmp = INREG(MEM_CNTL);
if (IS_R300_VARIANT(rinfo)) {
tmp &= R300_MEM_NUM_CHANNELS_MASK;
switch (tmp) {
case 0: rinfo->vram_width = 64; break;
case 1: rinfo->vram_width = 128; break;
case 2: rinfo->vram_width = 256; break;
default: rinfo->vram_width = 128; break;
}
} else if ((rinfo->family == CHIP_FAMILY_RV100) ||
(rinfo->family == CHIP_FAMILY_RS100) ||
(rinfo->family == CHIP_FAMILY_RS200)){
if (tmp & RV100_MEM_HALF_MODE)
rinfo->vram_width = 32;
else
rinfo->vram_width = 64;
} else {
if (tmp & MEM_NUM_CHANNELS_MASK)
rinfo->vram_width = 128;
else
rinfo->vram_width = 64;
}
/* This may not be correct, as some cards can have half of channel disabled
* ToDo: identify these cases
*/
DPRINT("radeonfb: Found %dk of %s %d bits wide videoram\n",
rinfo->video_ram / 1024,
rinfo->vram_ddr ? "DDR" : "SDRAM",
rinfo->vram_width);
}
static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *mode)
{
int i;
radeon_fifo_wait(20);
#if 0
/* Workaround from XFree */
if (rinfo->is_mobility) {
/* A temporal workaround for the occational blanking on certain laptop
* panels. This appears to related to the PLL divider registers
* (fail to lock?). It occurs even when all dividers are the same
* with their old settings. In this case we really don't need to
* fiddle with PLL registers. By doing this we can avoid the blanking
* problem with some panels.
*/
if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) &&
(mode->ppll_div_3 == (INPLL(PPLL_DIV_3) &
(PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) {
/* We still have to force a switch to selected PPLL div thanks to
* an XFree86 driver bug which will switch it away in some cases
* even when using UseFDev */
OUTREGP(CLOCK_CNTL_INDEX,
mode->clk_cntl_index & PPLL_DIV_SEL_MASK,
~PPLL_DIV_SEL_MASK);
radeon_pll_errata_after_index(rinfo);
radeon_pll_errata_after_data(rinfo);
return;
}
}
#endif
if(rinfo->pdev.device == PCI_CHIP_RV370_5B60) return;
/* Swich VCKL clock input to CPUCLK so it stays fed while PPLL updates*/
OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK);
/* Reset PPLL & enable atomic update */
OUTPLLP(PPLL_CNTL,
PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN,
~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
/* Switch to selected PPLL divider */
OUTREGP(CLOCK_CNTL_INDEX,
mode->clk_cntl_index & PPLL_DIV_SEL_MASK,
~PPLL_DIV_SEL_MASK);
/* Set PPLL ref. div */
if (rinfo->family == CHIP_FAMILY_R300 ||
rinfo->family == CHIP_FAMILY_RS300 ||
rinfo->family == CHIP_FAMILY_R350 ||
rinfo->family == CHIP_FAMILY_RV350) {
if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
/* When restoring console mode, use saved PPLL_REF_DIV
* setting.
*/
OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, 0);
} else {
/* R300 uses ref_div_acc field as real ref divider */
OUTPLLP(PPLL_REF_DIV,
(mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT),
~R300_PPLL_REF_DIV_ACC_MASK);
}
} else
OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK);
/* Set PPLL divider 3 & post divider*/
OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK);
OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK);
/* Write update */
while (INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R)
;
OUTPLLP(PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W);
/* Wait read update complete */
/* FIXME: Certain revisions of R300 can't recover here. Not sure of
the cause yet, but this workaround will mask the problem for now.
Other chips usually will pass at the very first test, so the
workaround shouldn't have any effect on them. */
for (i = 0; (i < 10000 && INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); i++)
;
OUTPLL(HTOTAL_CNTL, 0);
/* Clear reset & atomic update */
OUTPLLP(PPLL_CNTL, 0,
~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
/* We may want some locking ... oh well */
udelay(5000);
/* Switch back VCLK source to PPLL */
OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK);
}
typedef struct {
u16 reg;
u32 val;
} reg_val;
#if 0 /* unused ? -> scheduled for removal */
/* these common regs are cleared before mode setting so they do not
* interfere with anything
*/
static reg_val common_regs[] = {
{ OVR_CLR, 0 },
{ OVR_WID_LEFT_RIGHT, 0 },
{ OVR_WID_TOP_BOTTOM, 0 },
{ OV0_SCALE_CNTL, 0 },
{ SUBPIC_CNTL, 0 },
{ VIPH_CONTROL, 0 },
{ I2C_CNTL_1, 0 },
{ GEN_INT_CNTL, 0 },
{ CAP0_TRIG_CNTL, 0 },
{ CAP1_TRIG_CNTL, 0 },
};
#endif /* 0 */
void radeon_setmode(void)
{
struct radeon_regs *mode = malloc(sizeof(struct radeon_regs));
mode->crtc_gen_cntl = 0x03000200;
mode->crtc_ext_cntl = 0x00008048;
mode->dac_cntl = 0xff002100;
mode->crtc_h_total_disp = 0x4f0063;
mode->crtc_h_sync_strt_wid = 0x8c02a2;
mode->crtc_v_total_disp = 0x01df020c;
mode->crtc_v_sync_strt_wid = 0x8201ea;
mode->crtc_pitch = 0x00500050;
OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl);
OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl,
~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS));
OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING);
OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp);
OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid);
OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp);
OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid);
OUTREG(CRTC_OFFSET, 0);
OUTREG(CRTC_OFFSET_CNTL, 0);
OUTREG(CRTC_PITCH, mode->crtc_pitch);
mode->clk_cntl_index = 0x300;
mode->ppll_ref_div = 0xc;
mode->ppll_div_3 = 0x00030059;
radeon_write_pll_regs(rinfo, mode);
}
static void set_pal(void)
{
int idx, val = 0;
for (idx = 0; idx < 256; idx++) {
OUTREG8(PALETTE_INDEX, idx);
OUTREG(PALETTE_DATA, val);
val += 0x00010101;
}
}
void radeon_setmode_9200(int vesa_idx, int bpp)
{
struct radeon_regs *mode = malloc(sizeof(struct radeon_regs));
mode->crtc_gen_cntl = CRTC_EN | CRTC_EXT_DISP_EN;
mode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN | CRTC_CRT_ON;
mode->dac_cntl = DAC_MASK_ALL | DAC_VGA_ADR_EN | DAC_8BIT_EN;
mode->crtc_offset_cntl = CRTC_OFFSET_CNTL__CRTC_TILE_EN;
switch (bpp) {
case 24:
mode->crtc_gen_cntl |= 0x6 << 8; /* x888 */
#if defined(__BIG_ENDIAN)
mode->surface_cntl = NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP;
mode->surf_info[0] = NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP;
#endif
break;
case 16:
mode->crtc_gen_cntl |= 0x4 << 8; /* 565 */
#if defined(__BIG_ENDIAN)
mode->surface_cntl = NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP;
mode->surf_info[0] = NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP;
#endif
break;
default:
mode->crtc_gen_cntl |= 0x2 << 8; /* palette */
mode->surface_cntl = 0x00000000;
break;
}
switch (vesa_idx) {
case RES_MODE_1280x1024:
mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1688,1280);
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(1066,1024);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(1025,3);
#if defined(CONFIG_RADEON_VREFRESH_75HZ)
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1288,18);
mode->ppll_div_3 = 0x00010078;
#else /* default @ 60 Hz */
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1320,14);
mode->ppll_div_3 = 0x00010060;
#endif
/*
* for this mode pitch expands to the same value for 32, 16 and 8 bpp,
* so we set it here once only.
*/
mode->crtc_pitch = RADEON_CRT_PITCH(1280,32);
switch (bpp) {
case 24:
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1280 * 4 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,32);
break;
case 16:
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1280 * 2 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,16);
break;
default: /* 8 bpp */
mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1280 * 1 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,8);
break;
}
break;
case RES_MODE_1024x768:
#if defined(CONFIG_RADEON_VREFRESH_75HZ)
mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1312,1024);
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1032,12);
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(800,768);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(769,3);
mode->ppll_div_3 = 0x0002008c;
#else /* @ 60 Hz */
mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1344,1024);
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1040,17) | CRTC_H_SYNC_POL;
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(806,768);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(771,6) | CRTC_V_SYNC_POL;
mode->ppll_div_3 = 0x00020074;
#endif
/* also same pitch value for 32, 16 and 8 bpp */
mode->crtc_pitch = RADEON_CRT_PITCH(1024,32);
switch (bpp) {
case 24:
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1024 * 4 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,32);
break;
case 16:
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1024 * 2 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,16);
break;
default: /* 8 bpp */
mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1024 * 1 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,8);
break;
}
break;
case RES_MODE_800x600:
mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1056,800);
#if defined(CONFIG_RADEON_VREFRESH_75HZ)
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(808,10);
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(625,600);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(601,3);
mode->ppll_div_3 = 0x000300b0;
#else /* @ 60 Hz */
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(832,16);
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(628,600);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(601,4);
mode->ppll_div_3 = 0x0003008e;
#endif
switch (bpp) {
case 24:
mode->crtc_pitch = RADEON_CRT_PITCH(832,32);
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (832 * 4 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(832,600,32);
break;
case 16:
mode->crtc_pitch = RADEON_CRT_PITCH(896,16);
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (896 * 2 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(896,600,16);
break;
default: /* 8 bpp */
mode->crtc_pitch = RADEON_CRT_PITCH(1024,8);
mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1024 * 1 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,600,8);
break;
}
break;
default: /* RES_MODE_640x480 */
#if defined(CONFIG_RADEON_VREFRESH_75HZ)
mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(840,640);
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(648,8) | CRTC_H_SYNC_POL;
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(500,480);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(481,3) | CRTC_V_SYNC_POL;
mode->ppll_div_3 = 0x00030070;
#else /* @ 60 Hz */
mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(800,640);
mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(674,12) | CRTC_H_SYNC_POL;
mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(525,480);
mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(491,2) | CRTC_V_SYNC_POL;
mode->ppll_div_3 = 0x00030059;
#endif
/* also same pitch value for 32, 16 and 8 bpp */
mode->crtc_pitch = RADEON_CRT_PITCH(640,32);
switch (bpp) {
case 24:
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (640 * 4 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(640,480,32);
break;
case 16:
mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (640 * 2 / 16);
mode->surf_upper_bound[0] = SURF_UPPER_BOUND(640,480,16);
break;
default: /* 8 bpp */
mode->crtc_offset_cntl = 0x00000000;
break;
}
break;
}
OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl | CRTC_DISP_REQ_EN_B);
OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl,
(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS));
OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING);
OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp);
OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid);
OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp);
OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid);
OUTREG(CRTC_OFFSET, 0);
OUTREG(CRTC_OFFSET_CNTL, mode->crtc_offset_cntl);
OUTREG(CRTC_PITCH, mode->crtc_pitch);
OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl);
mode->clk_cntl_index = 0x300;
mode->ppll_ref_div = 0xc;
radeon_write_pll_regs(rinfo, mode);
OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl,
~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS));
OUTREG(SURFACE0_INFO, mode->surf_info[0]);
OUTREG(SURFACE0_LOWER_BOUND, 0);
OUTREG(SURFACE0_UPPER_BOUND, mode->surf_upper_bound[0]);
OUTREG(SURFACE_CNTL, mode->surface_cntl);
if (bpp > 8)
set_pal();
free(mode);
}
#include "../bios_emulator/include/biosemu.h"
int radeon_probe(struct radeonfb_info *rinfo)
{
pci_dev_t pdev;
u16 did;
pdev = pci_find_devices(ati_radeon_pci_ids, 0);
if (pdev != -1) {
pci_read_config_word(pdev, PCI_DEVICE_ID, &did);
printf("ATI Radeon video card (%04x, %04x) found @(%d:%d:%d)\n",
PCI_VENDOR_ID_ATI, did, (pdev >> 16) & 0xff,
(pdev >> 11) & 0x1f, (pdev >> 8) & 0x7);
strcpy(rinfo->name, "ATI Radeon");
rinfo->pdev.vendor = PCI_VENDOR_ID_ATI;
rinfo->pdev.device = did;
rinfo->family = get_radeon_id_family(rinfo->pdev.device);
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0,
&rinfo->fb_base_bus);
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2,
&rinfo->mmio_base_bus);
rinfo->fb_base_bus &= 0xfffff000;
rinfo->mmio_base_bus &= ~0x04;
rinfo->mmio_base = pci_bus_to_virt(pdev, rinfo->mmio_base_bus,
PCI_REGION_MEM, 0, MAP_NOCACHE);
DPRINT("rinfo->mmio_base = 0x%p bus=0x%x\n",
rinfo->mmio_base, rinfo->mmio_base_bus);
rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16;
DPRINT("rinfo->fb_local_base = 0x%x\n",rinfo->fb_local_base);
/* PostBIOS with x86 emulater */
if (!BootVideoCardBIOS(pdev, NULL, 0))
return -1;
/*
* Check for errata
* (These will be added in the future for the chipfamily
* R300, RV200, RS200, RV100, RS100.)
*/
/* Get VRAM size and type */
radeon_identify_vram(rinfo);
rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM,
rinfo->video_ram);
rinfo->fb_base = pci_bus_to_virt(pdev, rinfo->fb_base_bus,
PCI_REGION_MEM, 0, MAP_NOCACHE);
DPRINT("Radeon: framebuffer base address 0x%08x, "
"bus address 0x%08x\n"
"MMIO base address 0x%08x, bus address 0x%08x, "
"framebuffer local base 0x%08x.\n ",
(u32)rinfo->fb_base, rinfo->fb_base_bus,
(u32)rinfo->mmio_base, rinfo->mmio_base_bus,
rinfo->fb_local_base);
return 0;
}
return -1;
}
/*
* The Graphic Device
*/
GraphicDevice ctfb;
#define CURSOR_SIZE 0x1000 /* in KByte for HW Cursor */
#define PATTERN_ADR (pGD->dprBase + CURSOR_SIZE) /* pattern Memory after Cursor Memory */
#define PATTERN_SIZE 8*8*4 /* 4 Bytes per Pixel 8 x 8 Pixel */
#define ACCELMEMORY (CURSOR_SIZE + PATTERN_SIZE) /* reserved Memory for BITBlt and hw cursor */
void *video_hw_init(void)
{
GraphicDevice *pGD = (GraphicDevice *) & ctfb;
u32 *vm;
char *penv;
unsigned long t1, hsynch, vsynch;
int bits_per_pixel, i, tmp, vesa_idx = 0, videomode;
struct ctfb_res_modes *res_mode;
struct ctfb_res_modes var_mode;
rinfo = malloc(sizeof(struct radeonfb_info));
printf("Video: ");
if(radeon_probe(rinfo)) {
printf("No radeon video card found!\n");
return NULL;
}
tmp = 0;
videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
/* get video mode via environment */
penv = env_get("videomode");
if (penv) {
/* deceide if it is a string */
if (penv[0] <= '9') {
videomode = (int) simple_strtoul (penv, NULL, 16);
tmp = 1;
}
} else {
tmp = 1;
}
if (tmp) {
/* parameter are vesa modes */
/* search params */
for (i = 0; i < VESA_MODES_COUNT; i++) {
if (vesa_modes[i].vesanr == videomode)
break;
}
if (i == VESA_MODES_COUNT) {
printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE);
i = 0;
}
res_mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].resindex];
bits_per_pixel = vesa_modes[i].bits_per_pixel;
vesa_idx = vesa_modes[i].resindex;
} else {
res_mode = (struct ctfb_res_modes *) &var_mode;
bits_per_pixel = video_get_params (res_mode, penv);
}
/* calculate hsynch and vsynch freq (info only) */
t1 = (res_mode->left_margin + res_mode->xres +
res_mode->right_margin + res_mode->hsync_len) / 8;
t1 *= 8;
t1 *= res_mode->pixclock;
t1 /= 1000;
hsynch = 1000000000L / t1;
t1 *= (res_mode->upper_margin + res_mode->yres +
res_mode->lower_margin + res_mode->vsync_len);
t1 /= 1000;
vsynch = 1000000000L / t1;
/* fill in Graphic device struct */
sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres,
res_mode->yres, bits_per_pixel, (hsynch / 1000),
(vsynch / 1000));
printf ("%s\n", pGD->modeIdent);
pGD->winSizeX = res_mode->xres;
pGD->winSizeY = res_mode->yres;
pGD->plnSizeX = res_mode->xres;
pGD->plnSizeY = res_mode->yres;
switch (bits_per_pixel) {
case 24:
pGD->gdfBytesPP = 4;
pGD->gdfIndex = GDF_32BIT_X888RGB;
if (res_mode->xres == 800) {
pGD->winSizeX = 832;
pGD->plnSizeX = 832;
}
break;
case 16:
pGD->gdfBytesPP = 2;
pGD->gdfIndex = GDF_16BIT_565RGB;
if (res_mode->xres == 800) {
pGD->winSizeX = 896;
pGD->plnSizeX = 896;
}
break;
default:
if (res_mode->xres == 800) {
pGD->winSizeX = 1024;
pGD->plnSizeX = 1024;
}
pGD->gdfBytesPP = 1;
pGD->gdfIndex = GDF__8BIT_INDEX;
break;
}
pGD->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS;
pGD->pciBase = (unsigned int)rinfo->fb_base;
pGD->frameAdrs = (unsigned int)rinfo->fb_base;
pGD->memSize = 64 * 1024 * 1024;
/* Cursor Start Address */
pGD->dprBase = (pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP) +
(unsigned int)rinfo->fb_base;
if ((pGD->dprBase & 0x0fff) != 0) {
/* allign it */
pGD->dprBase &= 0xfffff000;
pGD->dprBase += 0x00001000;
}
DPRINT ("Cursor Start %x Pattern Start %x\n", pGD->dprBase,
PATTERN_ADR);
pGD->vprBase = (unsigned int)rinfo->fb_base; /* Dummy */
pGD->cprBase = (unsigned int)rinfo->fb_base; /* Dummy */
/* set up Hardware */
/* Clear video memory (only visible screen area) */
i = pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP / 4;
vm = (unsigned int *) pGD->pciBase;
while (i--)
*vm++ = 0;
/*SetDrawingEngine (bits_per_pixel);*/
if (rinfo->family == CHIP_FAMILY_RV280)
radeon_setmode_9200(vesa_idx, bits_per_pixel);
else
radeon_setmode();
return ((void *) pGD);
}
void video_set_lut (unsigned int index, /* color number */
unsigned char r, /* red */
unsigned char g, /* green */
unsigned char b /* blue */
)
{
OUTREG(PALETTE_INDEX, index);
OUTREG(PALETTE_DATA, (r << 16) | (g << 8) | b);
}
@@ -0,0 +1,282 @@
#ifndef __ATI_RADEON_FB_H
#define __ATI_RADEON_FB_H
/***************************************************************
* Most of the definitions here are adapted right from XFree86 *
***************************************************************/
/*
* Chip families. Must fit in the low 16 bits of a long word
*/
enum radeon_family {
CHIP_FAMILY_UNKNOW,
CHIP_FAMILY_LEGACY,
CHIP_FAMILY_RADEON,
CHIP_FAMILY_RV100,
CHIP_FAMILY_RS100, /* U1 (IGP320M) or A3 (IGP320)*/
CHIP_FAMILY_RV200,
CHIP_FAMILY_RS200, /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350),
RS250 (IGP 7000) */
CHIP_FAMILY_R200,
CHIP_FAMILY_RV250,
CHIP_FAMILY_RS300, /* Radeon 9000 IGP */
CHIP_FAMILY_RV280,
CHIP_FAMILY_R300,
CHIP_FAMILY_R350,
CHIP_FAMILY_RV350,
CHIP_FAMILY_RV380, /* RV370/RV380/M22/M24 */
CHIP_FAMILY_R420, /* R420/R423/M18 */
CHIP_FAMILY_LAST,
};
#define IS_RV100_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_RV100) || \
((rinfo)->family == CHIP_FAMILY_RV200) || \
((rinfo)->family == CHIP_FAMILY_RS100) || \
((rinfo)->family == CHIP_FAMILY_RS200) || \
((rinfo)->family == CHIP_FAMILY_RV250) || \
((rinfo)->family == CHIP_FAMILY_RV280) || \
((rinfo)->family == CHIP_FAMILY_RS300))
#define IS_R300_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_R300) || \
((rinfo)->family == CHIP_FAMILY_RV350) || \
((rinfo)->family == CHIP_FAMILY_R350) || \
((rinfo)->family == CHIP_FAMILY_RV380) || \
((rinfo)->family == CHIP_FAMILY_R420))
struct radeonfb_info {
char name[20];
struct pci_device_id pdev;
u16 family;
u32 fb_base_bus;
u32 mmio_base_bus;
void *mmio_base;
void *fb_base;
u32 video_ram;
u32 mapped_vram;
int vram_width;
int vram_ddr;
u32 fb_local_base;
};
#define INREG8(addr) readb((rinfo->mmio_base)+addr)
#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr)
#define INREG16(addr) readw((rinfo->mmio_base)+addr)
#define OUTREG16(addr,val) writew(val, (rinfo->mmio_base)+addr)
#define INREG(addr) readl((rinfo->mmio_base)+addr)
#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr)
static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr,
u32 val, u32 mask)
{
unsigned int tmp;
tmp = INREG(addr);
tmp &= (mask);
tmp |= (val);
OUTREG(addr, tmp);
}
#define OUTREGP(addr,val,mask) _OUTREGP(rinfo, addr, val,mask)
/*
* 2D Engine helper routines
*/
static inline void radeon_engine_flush (struct radeonfb_info *rinfo)
{
int i;
/* initiate flush */
OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
~RB2D_DC_FLUSH_ALL);
for (i=0; i < 2000000; i++) {
if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
return;
udelay(1);
}
printf("radeonfb: Flush Timeout !\n");
}
static inline void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries)
{
int i;
for (i=0; i<2000000; i++) {
if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
return;
udelay(1);
}
printf("radeonfb: FIFO Timeout !\n");
}
static inline void _radeon_engine_idle(struct radeonfb_info *rinfo)
{
int i;
/* ensure FIFO is empty before waiting for idle */
_radeon_fifo_wait (rinfo, 64);
for (i=0; i<2000000; i++) {
if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
radeon_engine_flush (rinfo);
return;
}
udelay(1);
}
printf("radeonfb: Idle Timeout !\n");
}
#define radeon_engine_idle() _radeon_engine_idle(rinfo)
#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries)
#define radeon_msleep(ms) _radeon_msleep(rinfo,ms)
/*
* This structure contains the various registers manipulated by this
* driver for setting or restoring a mode. It's mostly copied from
* XFree's RADEONSaveRec structure. A few chip settings might still be
* tweaked without beeing reflected or saved in these registers though
*/
struct radeon_regs {
/* Common registers */
u32 ovr_clr;
u32 ovr_wid_left_right;
u32 ovr_wid_top_bottom;
u32 ov0_scale_cntl;
u32 mpp_tb_config;
u32 mpp_gp_config;
u32 subpic_cntl;
u32 viph_control;
u32 i2c_cntl_1;
u32 gen_int_cntl;
u32 cap0_trig_cntl;
u32 cap1_trig_cntl;
u32 bus_cntl;
u32 surface_cntl;
u32 bios_5_scratch;
/* Other registers to save for VT switches or driver load/unload */
u32 dp_datatype;
u32 rbbm_soft_reset;
u32 clock_cntl_index;
u32 amcgpio_en_reg;
u32 amcgpio_mask;
/* Surface/tiling registers */
u32 surf_lower_bound[8];
u32 surf_upper_bound[8];
u32 surf_info[8];
/* CRTC registers */
u32 crtc_gen_cntl;
u32 crtc_ext_cntl;
u32 dac_cntl;
u32 crtc_h_total_disp;
u32 crtc_h_sync_strt_wid;
u32 crtc_v_total_disp;
u32 crtc_v_sync_strt_wid;
u32 crtc_offset;
u32 crtc_offset_cntl;
u32 crtc_pitch;
u32 disp_merge_cntl;
u32 grph_buffer_cntl;
u32 crtc_more_cntl;
/* CRTC2 registers */
u32 crtc2_gen_cntl;
u32 dac2_cntl;
u32 disp_output_cntl;
u32 disp_hw_debug;
u32 disp2_merge_cntl;
u32 grph2_buffer_cntl;
u32 crtc2_h_total_disp;
u32 crtc2_h_sync_strt_wid;
u32 crtc2_v_total_disp;
u32 crtc2_v_sync_strt_wid;
u32 crtc2_offset;
u32 crtc2_offset_cntl;
u32 crtc2_pitch;
/* Flat panel regs */
u32 fp_crtc_h_total_disp;
u32 fp_crtc_v_total_disp;
u32 fp_gen_cntl;
u32 fp2_gen_cntl;
u32 fp_h_sync_strt_wid;
u32 fp2_h_sync_strt_wid;
u32 fp_horz_stretch;
u32 fp_panel_cntl;
u32 fp_v_sync_strt_wid;
u32 fp2_v_sync_strt_wid;
u32 fp_vert_stretch;
u32 lvds_gen_cntl;
u32 lvds_pll_cntl;
u32 tmds_crc;
u32 tmds_transmitter_cntl;
/* Computed values for PLL */
u32 dot_clock_freq;
int feedback_div;
int post_div;
/* PLL registers */
u32 ppll_div_3;
u32 ppll_ref_div;
u32 vclk_ecp_cntl;
u32 clk_cntl_index;
/* Computed values for PLL2 */
u32 dot_clock_freq_2;
int feedback_div_2;
int post_div_2;
/* PLL2 registers */
u32 p2pll_ref_div;
u32 p2pll_div_0;
u32 htotal_cntl2;
/* Palette */
int palette_valid;
};
static inline u32 __INPLL(struct radeonfb_info *rinfo, u32 addr)
{
u32 data;
OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
/* radeon_pll_errata_after_index(rinfo); */
data = INREG(CLOCK_CNTL_DATA);
/* radeon_pll_errata_after_data(rinfo); */
return data;
}
static inline void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index,
u32 val)
{
OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080);
/* radeon_pll_errata_after_index(rinfo); */
OUTREG(CLOCK_CNTL_DATA, val);
/* radeon_pll_errata_after_data(rinfo); */
}
static inline void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
u32 val, u32 mask)
{
unsigned int tmp;
tmp = __INPLL(rinfo, index);
tmp &= (mask);
tmp |= (val);
__OUTPLL(rinfo, index, tmp);
}
#define INPLL(addr) __INPLL(rinfo, addr)
#define OUTPLL(index, val) __OUTPLL(rinfo, index, val)
#define OUTPLLP(index, val, mask) __OUTPLLP(rinfo, index, val, mask)
#endif
@@ -0,0 +1,564 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for AT91/AT32 MULTI LAYER LCD Controller
*
* Copyright (C) 2012 Atmel Corporation
*/
#include <common.h>
#include <cpu_func.h>
#include <asm/io.h>
#include <asm/arch/gpio.h>
#include <asm/arch/clk.h>
#include <clk.h>
#include <dm.h>
#include <fdtdec.h>
#include <lcd.h>
#include <video.h>
#include <wait_bit.h>
#include <atmel_hlcdc.h>
#if defined(CONFIG_LCD_LOGO)
#include <bmp_logo.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_DM_VIDEO
/* configurable parameters */
#define ATMEL_LCDC_CVAL_DEFAULT 0xc8
#define ATMEL_LCDC_DMA_BURST_LEN 8
#ifndef ATMEL_LCDC_GUARD_TIME
#define ATMEL_LCDC_GUARD_TIME 1
#endif
#define ATMEL_LCDC_FIFO_SIZE 512
/*
* the CLUT register map as following
* RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0)
*/
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
{
writel(panel_info.mmio + ATMEL_LCDC_LUT(regno),
((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk)
| ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk)
| ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk));
}
ushort *configuration_get_cmap(void)
{
#if defined(CONFIG_LCD_LOGO)
return bmp_logo_palette;
#else
return NULL;
#endif
}
void lcd_ctrl_init(void *lcdbase)
{
unsigned long value;
struct lcd_dma_desc *desc;
struct atmel_hlcd_regs *regs;
int ret;
if (!has_lcdc())
return; /* No lcdc */
regs = (struct atmel_hlcd_regs *)panel_info.mmio;
/* Disable DISP signal */
writel(LCDC_LCDDIS_DISPDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Disable synchronization */
writel(LCDC_LCDDIS_SYNCDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Disable pixel clock */
writel(LCDC_LCDDIS_CLKDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Disable PWM */
writel(LCDC_LCDDIS_PWMDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Set pixel clock */
value = get_lcdc_clk_rate(0) / panel_info.vl_clk;
if (get_lcdc_clk_rate(0) % panel_info.vl_clk)
value++;
if (value < 1) {
/* Using system clock as pixel clock */
writel(LCDC_LCDCFG0_CLKDIV(0)
| LCDC_LCDCFG0_CGDISHCR
| LCDC_LCDCFG0_CGDISHEO
| LCDC_LCDCFG0_CGDISOVR1
| LCDC_LCDCFG0_CGDISBASE
| panel_info.vl_clk_pol
| LCDC_LCDCFG0_CLKSEL,
&regs->lcdc_lcdcfg0);
} else {
writel(LCDC_LCDCFG0_CLKDIV(value - 2)
| LCDC_LCDCFG0_CGDISHCR
| LCDC_LCDCFG0_CGDISHEO
| LCDC_LCDCFG0_CGDISOVR1
| LCDC_LCDCFG0_CGDISBASE
| panel_info.vl_clk_pol,
&regs->lcdc_lcdcfg0);
}
/* Initialize control register 5 */
value = 0;
value |= panel_info.vl_sync;
#ifndef LCD_OUTPUT_BPP
/* Output is 24bpp */
value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
#else
switch (LCD_OUTPUT_BPP) {
case 12:
value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
break;
case 16:
value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
break;
case 18:
value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
break;
case 24:
value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
break;
default:
BUG();
break;
}
#endif
value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME);
value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
writel(value, &regs->lcdc_lcdcfg5);
/* Vertical & Horizontal Timing */
value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1);
value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1);
writel(value, &regs->lcdc_lcdcfg1);
value = LCDC_LCDCFG2_VBPW(panel_info.vl_upper_margin);
value |= LCDC_LCDCFG2_VFPW(panel_info.vl_lower_margin - 1);
writel(value, &regs->lcdc_lcdcfg2);
value = LCDC_LCDCFG3_HBPW(panel_info.vl_left_margin - 1);
value |= LCDC_LCDCFG3_HFPW(panel_info.vl_right_margin - 1);
writel(value, &regs->lcdc_lcdcfg3);
/* Display size */
value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1);
value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1);
writel(value, &regs->lcdc_lcdcfg4);
writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
&regs->lcdc_basecfg0);
switch (NBITS(panel_info.vl_bpix)) {
case 16:
writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
&regs->lcdc_basecfg1);
break;
case 32:
writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
&regs->lcdc_basecfg1);
break;
default:
BUG();
break;
}
writel(LCDC_BASECFG2_XSTRIDE(0), &regs->lcdc_basecfg2);
writel(0, &regs->lcdc_basecfg3);
writel(LCDC_BASECFG4_DMA, &regs->lcdc_basecfg4);
/* Disable all interrupts */
writel(~0UL, &regs->lcdc_lcdidr);
writel(~0UL, &regs->lcdc_baseidr);
/* Setup the DMA descriptor, this descriptor will loop to itself */
desc = (struct lcd_dma_desc *)(lcdbase - 16);
desc->address = (u32)lcdbase;
/* Disable DMA transfer interrupt & descriptor loaded interrupt. */
desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
| LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
desc->next = (u32)desc;
/* Flush the DMA descriptor if we enabled dcache */
flush_dcache_range((u32)desc, (u32)desc + sizeof(*desc));
writel(desc->address, &regs->lcdc_baseaddr);
writel(desc->control, &regs->lcdc_basectrl);
writel(desc->next, &regs->lcdc_basenext);
writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
&regs->lcdc_basecher);
/* Enable LCD */
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_CLKEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_SYNCEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_DISPEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_PWMEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Enable flushing if we enabled dcache */
lcd_set_flush_dcache(1);
}
#else
enum {
LCD_MAX_WIDTH = 1024,
LCD_MAX_HEIGHT = 768,
LCD_MAX_LOG2_BPP = VIDEO_BPP16,
};
struct atmel_hlcdc_priv {
struct atmel_hlcd_regs *regs;
struct display_timing timing;
unsigned int vl_bpix;
unsigned int output_mode;
unsigned int guard_time;
ulong clk_rate;
};
static int at91_hlcdc_enable_clk(struct udevice *dev)
{
struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
struct clk clk;
ulong clk_rate;
int ret;
ret = clk_get_by_index(dev, 0, &clk);
if (ret)
return -EINVAL;
ret = clk_enable(&clk);
if (ret)
return ret;
clk_rate = clk_get_rate(&clk);
if (!clk_rate) {
clk_disable(&clk);
return -ENODEV;
}
priv->clk_rate = clk_rate;
clk_free(&clk);
return 0;
}
static void atmel_hlcdc_init(struct udevice *dev)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
struct atmel_hlcd_regs *regs = priv->regs;
struct display_timing *timing = &priv->timing;
struct lcd_dma_desc *desc;
unsigned long value, vl_clk_pol;
int ret;
/* Disable DISP signal */
writel(LCDC_LCDDIS_DISPDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Disable synchronization */
writel(LCDC_LCDDIS_SYNCDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Disable pixel clock */
writel(LCDC_LCDDIS_CLKDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Disable PWM */
writel(LCDC_LCDDIS_PWMDIS, &regs->lcdc_lcddis);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
false, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
/* Set pixel clock */
value = priv->clk_rate / timing->pixelclock.typ;
if (priv->clk_rate % timing->pixelclock.typ)
value++;
vl_clk_pol = 0;
if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
vl_clk_pol = LCDC_LCDCFG0_CLKPOL;
if (value < 1) {
/* Using system clock as pixel clock */
writel(LCDC_LCDCFG0_CLKDIV(0)
| LCDC_LCDCFG0_CGDISHCR
| LCDC_LCDCFG0_CGDISHEO
| LCDC_LCDCFG0_CGDISOVR1
| LCDC_LCDCFG0_CGDISBASE
| vl_clk_pol
| LCDC_LCDCFG0_CLKSEL,
&regs->lcdc_lcdcfg0);
} else {
writel(LCDC_LCDCFG0_CLKDIV(value - 2)
| LCDC_LCDCFG0_CGDISHCR
| LCDC_LCDCFG0_CGDISHEO
| LCDC_LCDCFG0_CGDISOVR1
| LCDC_LCDCFG0_CGDISBASE
| vl_clk_pol,
&regs->lcdc_lcdcfg0);
}
/* Initialize control register 5 */
value = 0;
if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
value |= LCDC_LCDCFG5_HSPOL;
if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
value |= LCDC_LCDCFG5_VSPOL;
switch (priv->output_mode) {
case 12:
value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
break;
case 16:
value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
break;
case 18:
value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
break;
case 24:
value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
break;
default:
BUG();
break;
}
value |= LCDC_LCDCFG5_GUARDTIME(priv->guard_time);
value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
writel(value, &regs->lcdc_lcdcfg5);
/* Vertical & Horizontal Timing */
value = LCDC_LCDCFG1_VSPW(timing->vsync_len.typ - 1);
value |= LCDC_LCDCFG1_HSPW(timing->hsync_len.typ - 1);
writel(value, &regs->lcdc_lcdcfg1);
value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ);
value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1);
writel(value, &regs->lcdc_lcdcfg2);
value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1);
value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1);
writel(value, &regs->lcdc_lcdcfg3);
/* Display size */
value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1);
value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1);
writel(value, &regs->lcdc_lcdcfg4);
writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
&regs->lcdc_basecfg0);
switch (VNBITS(priv->vl_bpix)) {
case 16:
writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
&regs->lcdc_basecfg1);
break;
case 32:
writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
&regs->lcdc_basecfg1);
break;
default:
BUG();
break;
}
writel(LCDC_BASECFG2_XSTRIDE(0), &regs->lcdc_basecfg2);
writel(0, &regs->lcdc_basecfg3);
writel(LCDC_BASECFG4_DMA, &regs->lcdc_basecfg4);
/* Disable all interrupts */
writel(~0UL, &regs->lcdc_lcdidr);
writel(~0UL, &regs->lcdc_baseidr);
/* Setup the DMA descriptor, this descriptor will loop to itself */
desc = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*desc));
if (!desc)
return;
desc->address = (u32)uc_plat->base;
/* Disable DMA transfer interrupt & descriptor loaded interrupt. */
desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
| LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
desc->next = (u32)desc;
/* Flush the DMA descriptor if we enabled dcache */
flush_dcache_range((u32)desc,
ALIGN(((u32)desc + sizeof(*desc)),
CONFIG_SYS_CACHELINE_SIZE));
writel(desc->address, &regs->lcdc_baseaddr);
writel(desc->control, &regs->lcdc_basectrl);
writel(desc->next, &regs->lcdc_basenext);
writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
&regs->lcdc_basecher);
/* Enable LCD */
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_CLKEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_SYNCEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_DISPEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
value = readl(&regs->lcdc_lcden);
writel(value | LCDC_LCDEN_PWMEN, &regs->lcdc_lcden);
ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
true, 1000, false);
if (ret)
printf("%s: %d: Timeout!\n", __func__, __LINE__);
}
static int atmel_hlcdc_probe(struct udevice *dev)
{
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
int ret;
ret = at91_hlcdc_enable_clk(dev);
if (ret)
return ret;
atmel_hlcdc_init(dev);
uc_priv->xsize = priv->timing.hactive.typ;
uc_priv->ysize = priv->timing.vactive.typ;
uc_priv->bpix = priv->vl_bpix;
/* Enable flushing if we enabled dcache */
video_set_flush_dcache(dev, true);
return 0;
}
static int atmel_hlcdc_ofdata_to_platdata(struct udevice *dev)
{
struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
priv->regs = (struct atmel_hlcd_regs *)devfdt_get_addr(dev);
if (!priv->regs) {
debug("%s: No display controller address\n", __func__);
return -EINVAL;
}
if (fdtdec_decode_display_timing(blob, dev_of_offset(dev),
0, &priv->timing)) {
debug("%s: Failed to decode display timing\n", __func__);
return -EINVAL;
}
if (priv->timing.hactive.typ > LCD_MAX_WIDTH)
priv->timing.hactive.typ = LCD_MAX_WIDTH;
if (priv->timing.vactive.typ > LCD_MAX_HEIGHT)
priv->timing.vactive.typ = LCD_MAX_HEIGHT;
priv->vl_bpix = fdtdec_get_int(blob, node, "atmel,vl-bpix", 0);
if (!priv->vl_bpix) {
debug("%s: Failed to get bits per pixel\n", __func__);
return -EINVAL;
}
priv->output_mode = fdtdec_get_int(blob, node, "atmel,output-mode", 24);
priv->guard_time = fdtdec_get_int(blob, node, "atmel,guard-time", 1);
return 0;
}
static int atmel_hlcdc_bind(struct udevice *dev)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
(1 << LCD_MAX_LOG2_BPP) / 8;
debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
return 0;
}
static const struct udevice_id atmel_hlcdc_ids[] = {
{ .compatible = "atmel,sama5d2-hlcdc" },
{ .compatible = "atmel,at91sam9x5-hlcdc" },
{ }
};
U_BOOT_DRIVER(atmel_hlcdfb) = {
.name = "atmel_hlcdfb",
.id = UCLASS_VIDEO,
.of_match = atmel_hlcdc_ids,
.bind = atmel_hlcdc_bind,
.probe = atmel_hlcdc_probe,
.ofdata_to_platdata = atmel_hlcdc_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct atmel_hlcdc_priv),
};
#endif
@@ -0,0 +1,309 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for AT91/AT32 LCD Controller
*
* Copyright (C) 2007 Atmel Corporation
*/
#include <common.h>
#include <atmel_lcd.h>
#include <dm.h>
#include <fdtdec.h>
#include <video.h>
#include <asm/io.h>
#include <asm/arch/gpio.h>
#include <asm/arch/clk.h>
#include <lcd.h>
#include <bmp_layout.h>
#include <atmel_lcdc.h>
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_DM_VIDEO
enum {
/* Maximum LCD size we support */
LCD_MAX_WIDTH = 1366,
LCD_MAX_HEIGHT = 768,
LCD_MAX_LOG2_BPP = VIDEO_BPP16,
};
#endif
struct atmel_fb_priv {
struct display_timing timing;
};
/* configurable parameters */
#define ATMEL_LCDC_CVAL_DEFAULT 0xc8
#define ATMEL_LCDC_DMA_BURST_LEN 8
#ifndef ATMEL_LCDC_GUARD_TIME
#define ATMEL_LCDC_GUARD_TIME 1
#endif
#if defined(CONFIG_AT91SAM9263)
#define ATMEL_LCDC_FIFO_SIZE 2048
#else
#define ATMEL_LCDC_FIFO_SIZE 512
#endif
#define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg))
#define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg))
#ifndef CONFIG_DM_VIDEO
ushort *configuration_get_cmap(void)
{
return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0));
}
#if defined(CONFIG_BMP_16BPP) && defined(CONFIG_ATMEL_LCD_BGR555)
void fb_put_word(uchar **fb, uchar **from)
{
*(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03);
*(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2);
*from += 2;
}
#endif
#ifdef CONFIG_LCD_LOGO
#include <bmp_logo.h>
void lcd_logo_set_cmap(void)
{
int i;
uint lut_entry;
ushort colreg;
uint *cmap = (uint *)configuration_get_cmap();
for (i = 0; i < BMP_LOGO_COLORS; ++i) {
colreg = bmp_logo_palette[i];
#ifdef CONFIG_ATMEL_LCD_BGR555
lut_entry = ((colreg & 0x000F) << 11) |
((colreg & 0x00F0) << 2) |
((colreg & 0x0F00) >> 7);
#else
lut_entry = ((colreg & 0x000F) << 1) |
((colreg & 0x00F0) << 3) |
((colreg & 0x0F00) << 4);
#endif
*(cmap + BMP_LOGO_OFFSET) = lut_entry;
cmap++;
}
}
#endif
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
{
#if defined(CONFIG_ATMEL_LCD_BGR555)
lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
(red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7));
#else
lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
(blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8));
#endif
}
void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
{
int i;
for (i = 0; i < colors; ++i) {
struct bmp_color_table_entry cte = bmp->color_table[i];
lcd_setcolreg(i, cte.red, cte.green, cte.blue);
}
}
#endif
static void atmel_fb_init(ulong addr, struct display_timing *timing, int bpix,
bool tft, bool cont_pol_low, ulong lcdbase)
{
unsigned long value;
void *reg = (void *)addr;
/* Turn off the LCD controller and the DMA controller */
lcdc_writel(reg, ATMEL_LCDC_PWRCON,
ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET);
/* Wait for the LCDC core to become idle */
while (lcdc_readl(reg, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
udelay(10);
lcdc_writel(reg, ATMEL_LCDC_DMACON, 0);
/* Reset LCDC DMA */
lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
/* ...set frame size and burst length = 8 words (?) */
value = (timing->hactive.typ * timing->vactive.typ *
(1 << bpix)) / 32;
value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
lcdc_writel(reg, ATMEL_LCDC_DMAFRMCFG, value);
/* Set pixel clock */
value = get_lcdc_clk_rate(0) / timing->pixelclock.typ;
if (get_lcdc_clk_rate(0) % timing->pixelclock.typ)
value++;
value = (value / 2) - 1;
if (!value) {
lcdc_writel(reg, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
} else
lcdc_writel(reg, ATMEL_LCDC_LCDCON1,
value << ATMEL_LCDC_CLKVAL_OFFSET);
/* Initialize control register 2 */
value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
if (tft)
value |= ATMEL_LCDC_DISTYPE_TFT;
if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
value |= ATMEL_LCDC_INVLINE_INVERTED;
if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
value |= ATMEL_LCDC_INVFRAME_INVERTED;
value |= bpix << 5;
lcdc_writel(reg, ATMEL_LCDC_LCDCON2, value);
/* Vertical timing */
value = (timing->vsync_len.typ - 1) << ATMEL_LCDC_VPW_OFFSET;
value |= timing->vback_porch.typ << ATMEL_LCDC_VBP_OFFSET;
value |= timing->vfront_porch.typ;
/* Magic! (Datasheet says "Bit 31 must be written to 1") */
value |= 1U << 31;
lcdc_writel(reg, ATMEL_LCDC_TIM1, value);
/* Horizontal timing */
value = (timing->hfront_porch.typ - 1) << ATMEL_LCDC_HFP_OFFSET;
value |= (timing->hsync_len.typ - 1) << ATMEL_LCDC_HPW_OFFSET;
value |= (timing->hback_porch.typ - 1);
lcdc_writel(reg, ATMEL_LCDC_TIM2, value);
/* Display size */
value = (timing->hactive.typ - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
value |= timing->vactive.typ - 1;
lcdc_writel(reg, ATMEL_LCDC_LCDFRMCFG, value);
/* FIFO Threshold: Use formula from data sheet */
value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
lcdc_writel(reg, ATMEL_LCDC_FIFO, value);
/* Toggle LCD_MODE every frame */
lcdc_writel(reg, ATMEL_LCDC_MVAL, 0);
/* Disable all interrupts */
lcdc_writel(reg, ATMEL_LCDC_IDR, ~0UL);
/* Set contrast */
value = ATMEL_LCDC_PS_DIV8 |
ATMEL_LCDC_ENA_PWMENABLE;
if (!cont_pol_low)
value |= ATMEL_LCDC_POL_POSITIVE;
lcdc_writel(reg, ATMEL_LCDC_CONTRAST_CTR, value);
lcdc_writel(reg, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
/* Set framebuffer DMA base address and pixel offset */
lcdc_writel(reg, ATMEL_LCDC_DMABADDR1, lcdbase);
lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
lcdc_writel(reg, ATMEL_LCDC_PWRCON,
(ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
}
#ifndef CONFIG_DM_VIDEO
void lcd_ctrl_init(void *lcdbase)
{
struct display_timing timing;
timing.flags = 0;
if (!(panel_info.vl_sync & ATMEL_LCDC_INVLINE_INVERTED))
timing.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
if (!(panel_info.vl_sync & ATMEL_LCDC_INVFRAME_INVERTED))
timing.flags |= DISPLAY_FLAGS_VSYNC_LOW;
timing.pixelclock.typ = panel_info.vl_clk;
timing.hactive.typ = panel_info.vl_col;
timing.hfront_porch.typ = panel_info.vl_right_margin;
timing.hback_porch.typ = panel_info.vl_left_margin;
timing.hsync_len.typ = panel_info.vl_hsync_len;
timing.vactive.typ = panel_info.vl_row;
timing.vfront_porch.typ = panel_info.vl_clk;
timing.vback_porch.typ = panel_info.vl_clk;
timing.vsync_len.typ = panel_info.vl_clk;
atmel_fb_init(panel_info.mmio, &timing, panel_info.vl_bpix,
panel_info.vl_tft, panel_info.vl_cont_pol_low,
(ulong)lcdbase);
}
ulong calc_fbsize(void)
{
return ((panel_info.vl_col * panel_info.vl_row *
NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE;
}
#endif
#ifdef CONFIG_DM_VIDEO
static int atmel_fb_lcd_probe(struct udevice *dev)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct atmel_fb_priv *priv = dev_get_priv(dev);
struct display_timing *timing = &priv->timing;
/*
* For now some values are hard-coded. We could use the device tree
* bindings in simple-framebuffer.txt to specify the format/bpp and
* some Atmel-specific binding for tft and cont_pol_low.
*/
atmel_fb_init(ATMEL_BASE_LCDC, timing, VIDEO_BPP16, true, false,
uc_plat->base);
uc_priv->xsize = timing->hactive.typ;
uc_priv->ysize = timing->vactive.typ;
uc_priv->bpix = VIDEO_BPP16;
video_set_flush_dcache(dev, true);
debug("LCD frame buffer at %lx, size %x, %dx%d pixels\n", uc_plat->base,
uc_plat->size, uc_priv->xsize, uc_priv->ysize);
return 0;
}
static int atmel_fb_ofdata_to_platdata(struct udevice *dev)
{
struct atmel_lcd_platdata *plat = dev_get_platdata(dev);
struct atmel_fb_priv *priv = dev_get_priv(dev);
struct display_timing *timing = &priv->timing;
const void *blob = gd->fdt_blob;
if (fdtdec_decode_display_timing(blob, dev_of_offset(dev),
plat->timing_index, timing)) {
debug("%s: Failed to decode display timing\n", __func__);
return -EINVAL;
}
return 0;
}
static int atmel_fb_lcd_bind(struct udevice *dev)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
(1 << VIDEO_BPP16) / 8;
debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
return 0;
}
static const struct udevice_id atmel_fb_lcd_ids[] = {
{ .compatible = "atmel,at91sam9g45-lcdc" },
{ }
};
U_BOOT_DRIVER(atmel_fb) = {
.name = "atmel_fb",
.id = UCLASS_VIDEO,
.of_match = atmel_fb_lcd_ids,
.bind = atmel_fb_lcd_bind,
.ofdata_to_platdata = atmel_fb_ofdata_to_platdata,
.probe = atmel_fb_lcd_probe,
.platdata_auto_alloc_size = sizeof(struct atmel_lcd_platdata),
.priv_auto_alloc_size = sizeof(struct atmel_fb_priv),
};
#endif
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <backlight.h>
int backlight_enable(struct udevice *dev)
{
const struct backlight_ops *ops = backlight_get_ops(dev);
if (!ops->enable)
return -ENOSYS;
return ops->enable(dev);
}
int backlight_set_brightness(struct udevice *dev, int percent)
{
const struct backlight_ops *ops = backlight_get_ops(dev);
if (!ops->set_brightness)
return -ENOSYS;
return ops->set_brightness(dev, percent);
}
UCLASS_DRIVER(backlight) = {
.id = UCLASS_PANEL_BACKLIGHT,
.name = "backlight",
};
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author: Patrick Delaunay <patrick.delaunay@st.com>
*/
#include <common.h>
#include <dm.h>
#include <backlight.h>
#include <asm/gpio.h>
struct gpio_backlight_priv {
struct gpio_desc gpio;
bool def_value;
};
static int gpio_backlight_enable(struct udevice *dev)
{
struct gpio_backlight_priv *priv = dev_get_priv(dev);
dm_gpio_set_value(&priv->gpio, 1);
return 0;
}
static int gpio_backlight_ofdata_to_platdata(struct udevice *dev)
{
struct gpio_backlight_priv *priv = dev_get_priv(dev);
int ret;
ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio,
GPIOD_IS_OUT);
if (ret) {
debug("%s: Warning: cannot get GPIO: ret=%d\n",
__func__, ret);
return ret;
}
priv->def_value = dev_read_bool(dev, "default-on");
return 0;
}
static int gpio_backlight_probe(struct udevice *dev)
{
struct gpio_backlight_priv *priv = dev_get_priv(dev);
if (priv->def_value)
gpio_backlight_enable(dev);
return 0;
}
static const struct backlight_ops gpio_backlight_ops = {
.enable = gpio_backlight_enable,
};
static const struct udevice_id gpio_backlight_ids[] = {
{ .compatible = "gpio-backlight" },
{ }
};
U_BOOT_DRIVER(gpio_backlight) = {
.name = "gpio_backlight",
.id = UCLASS_PANEL_BACKLIGHT,
.of_match = gpio_backlight_ids,
.ops = &gpio_backlight_ops,
.ofdata_to_platdata = gpio_backlight_ofdata_to_platdata,
.probe = gpio_backlight_probe,
.priv_auto_alloc_size = sizeof(struct gpio_backlight_priv),
};
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2012 Stephen Warren
*/
#include <common.h>
#include <dm.h>
#include <video.h>
#include <asm/arch/mbox.h>
#include <asm/arch/msg.h>
static int bcm2835_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
int ret;
int w, h, pitch;
ulong fb_base, fb_size, fb_start, fb_end;
debug("bcm2835: Query resolution...\n");
ret = bcm2835_get_video_size(&w, &h);
if (ret || w == 0 || h == 0)
return -EIO;
debug("bcm2835: Setting up display for %d x %d\n", w, h);
ret = bcm2835_set_video_params(&w, &h, 32, BCM2835_MBOX_PIXEL_ORDER_RGB,
BCM2835_MBOX_ALPHA_MODE_IGNORED,
&fb_base, &fb_size, &pitch);
if (ret)
return -EIO;
debug("bcm2835: Final resolution is %d x %d\n", w, h);
/* Enable dcache for the frame buffer */
fb_start = fb_base & ~(MMU_SECTION_SIZE - 1);
fb_end = fb_base + fb_size;
fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
DCACHE_WRITEBACK);
video_set_flush_dcache(dev, true);
uc_priv->xsize = w;
uc_priv->ysize = h;
uc_priv->bpix = VIDEO_BPP32;
plat->base = fb_base;
plat->size = fb_size;
return 0;
}
static const struct udevice_id bcm2835_video_ids[] = {
{ .compatible = "brcm,bcm2835-hdmi" },
{ .compatible = "brcm,bcm2708-fb" },
{ }
};
U_BOOT_DRIVER(bcm2835_video) = {
.name = "bcm2835_video",
.id = UCLASS_VIDEO,
.of_match = bcm2835_video_ids,
.probe = bcm2835_video_probe,
};
@@ -0,0 +1,35 @@
config VIDEO_BRIDGE
bool "Support video bridges"
depends on DM
help
Some platforms use video bridges to convert from one output to
another. For example, where the SoC only supports eDP and the LCD
requires LVDS, an eDP->LVDS bridge chip can be used to provide the
necessary conversion. This option enables support for these devices.
config VIDEO_BRIDGE_PARADE_PS862X
bool "Support Parade PS862X DP->LVDS bridge"
depends on VIDEO_BRIDGE
help
The Parade PS8622 and PS8625 are DisplayPort-to-LVDS (Low voltage
differential signalling) converters. They enable an LVDS LCD panel
to be connected to an eDP output device such as an SoC that lacks
LVDS capability, or where LVDS requires too many signals to route
on the PCB. Setup parameters are provided in the device tree.
config VIDEO_BRIDGE_NXP_PTN3460
bool "Support NXP PTN3460 DP->LVDS bridge"
depends on VIDEO_BRIDGE
help
The NXP PTN3460 is a DisplayPort-to-LVDS (Low voltage differential
signalling) converter. It enables an LVDS LCD panel to be connected
to an eDP output device such as an SoC that lacks LVDS capability,
or where LVDS requires too many signals to route on the PCB.
config VIDEO_BRIDGE_ANALOGIX_ANX6345
bool "Support Analogix ANX6345 RGB->DP bridge"
depends on VIDEO_BRIDGE
select DM_I2C
help
The Analogix ANX6345 is RGB-to-DP converter. It enables an eDP LCD
panel to be connected to an parallel LCD interface.
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
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
@@ -0,0 +1,423 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Vasily Khoruzhick <anarsoul@gmail.com>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <edid.h>
#include <video_bridge.h>
#include "../anx98xx-edp.h"
#define DP_MAX_LINK_RATE 0x001
#define DP_MAX_LANE_COUNT 0x002
#define DP_MAX_LANE_COUNT_MASK 0x1f
struct anx6345_priv {
u8 edid[EDID_SIZE];
};
static int anx6345_write(struct udevice *dev, unsigned int addr_off,
unsigned char reg_addr, unsigned char value)
{
uint8_t buf[2];
struct i2c_msg msg;
int ret;
msg.addr = addr_off;
msg.flags = 0;
buf[0] = reg_addr;
buf[1] = value;
msg.buf = buf;
msg.len = 2;
ret = dm_i2c_xfer(dev, &msg, 1);
if (ret) {
debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n",
__func__, reg_addr, value, ret);
return ret;
}
return 0;
}
static int anx6345_read(struct udevice *dev, unsigned int addr_off,
unsigned char reg_addr, unsigned char *value)
{
uint8_t addr, val;
struct i2c_msg msg[2];
int ret;
msg[0].addr = addr_off;
msg[0].flags = 0;
addr = reg_addr;
msg[0].buf = &addr;
msg[0].len = 1;
msg[1].addr = addr_off;
msg[1].flags = I2C_M_RD;
msg[1].buf = &val;
msg[1].len = 1;
ret = dm_i2c_xfer(dev, msg, 2);
if (ret) {
debug("%s: read failed, reg=%.2x, value=%p, ret=%d\n",
__func__, (int)reg_addr, value, ret);
return ret;
}
*value = val;
return 0;
}
static int anx6345_write_r0(struct udevice *dev, unsigned char reg_addr,
unsigned char value)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
return anx6345_write(dev, chip->chip_addr, reg_addr, value);
}
static int anx6345_read_r0(struct udevice *dev, unsigned char reg_addr,
unsigned char *value)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
return anx6345_read(dev, chip->chip_addr, reg_addr, value);
}
static int anx6345_write_r1(struct udevice *dev, unsigned char reg_addr,
unsigned char value)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
return anx6345_write(dev, chip->chip_addr + 1, reg_addr, value);
}
static int anx6345_read_r1(struct udevice *dev, unsigned char reg_addr,
unsigned char *value)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
return anx6345_read(dev, chip->chip_addr + 1, reg_addr, value);
}
static int anx6345_set_backlight(struct udevice *dev, int percent)
{
return -ENOSYS;
}
static int anx6345_aux_wait(struct udevice *dev)
{
int ret = -ETIMEDOUT;
u8 v;
int retries = 1000;
do {
anx6345_read_r0(dev, ANX9804_DP_AUX_CH_CTL_2, &v);
if (!(v & ANX9804_AUX_EN)) {
ret = 0;
break;
}
udelay(100);
} while (retries--);
if (ret) {
debug("%s: timed out waiting for AUX_EN to clear\n", __func__);
return ret;
}
ret = -ETIMEDOUT;
retries = 1000;
do {
anx6345_read_r1(dev, ANX9804_DP_INT_STA, &v);
if (v & ANX9804_RPLY_RECEIV) {
ret = 0;
break;
}
udelay(100);
} while (retries--);
if (ret) {
debug("%s: timed out waiting to receive reply\n", __func__);
return ret;
}
/* Clear RPLY_RECEIV bit */
anx6345_write_r1(dev, ANX9804_DP_INT_STA, v);
anx6345_read_r0(dev, ANX9804_AUX_CH_STA, &v);
if ((v & ANX9804_AUX_STATUS_MASK) != 0) {
debug("AUX status: %d\n", v & ANX9804_AUX_STATUS_MASK);
ret = -EIO;
}
return ret;
}
static void anx6345_aux_addr(struct udevice *dev, u32 addr)
{
u8 val;
val = addr & 0xff;
anx6345_write_r0(dev, ANX9804_DP_AUX_ADDR_7_0, val);
val = (addr >> 8) & 0xff;
anx6345_write_r0(dev, ANX9804_DP_AUX_ADDR_15_8, val);
val = (addr >> 16) & 0x0f;
anx6345_write_r0(dev, ANX9804_DP_AUX_ADDR_19_16, val);
}
static int anx6345_aux_transfer(struct udevice *dev, u8 req,
u32 addr, u8 *buf, size_t len)
{
int i, ret;
u8 ctrl1 = req;
u8 ctrl2 = ANX9804_AUX_EN;
if (len > 16)
return -E2BIG;
if (len)
ctrl1 |= ANX9804_AUX_LENGTH(len);
else
ctrl2 |= ANX9804_ADDR_ONLY;
if (len && !(req & ANX9804_AUX_TX_COMM_READ)) {
for (i = 0; i < len; i++)
anx6345_write_r0(dev, ANX9804_BUF_DATA_0 + i, buf[i]);
}
anx6345_aux_addr(dev, addr);
anx6345_write_r0(dev, ANX9804_DP_AUX_CH_CTL_1, ctrl1);
anx6345_write_r0(dev, ANX9804_DP_AUX_CH_CTL_2, ctrl2);
ret = anx6345_aux_wait(dev);
if (ret) {
debug("AUX transaction timed out\n");
return ret;
}
if (len && (req & ANX9804_AUX_TX_COMM_READ)) {
for (i = 0; i < len; i++)
anx6345_read_r0(dev, ANX9804_BUF_DATA_0 + i, &buf[i]);
}
return 0;
}
static int anx6345_read_aux_i2c(struct udevice *dev, u8 chip_addr,
u8 offset, size_t count, u8 *buf)
{
int i, ret;
size_t cur_cnt;
u8 cur_offset;
for (i = 0; i < count; i += 16) {
cur_cnt = (count - i) > 16 ? 16 : count - i;
cur_offset = offset + i;
ret = anx6345_aux_transfer(dev, ANX9804_AUX_TX_COMM_MOT,
chip_addr, &cur_offset, 1);
if (ret) {
debug("%s: failed to set i2c offset: %d\n",
__func__, ret);
return ret;
}
ret = anx6345_aux_transfer(dev, ANX9804_AUX_TX_COMM_READ,
chip_addr, buf + i, cur_cnt);
if (ret) {
debug("%s: failed to read from i2c device: %d\n",
__func__, ret);
return ret;
}
}
return 0;
}
static int anx6345_read_dpcd(struct udevice *dev, u32 reg, u8 *val)
{
int ret;
ret = anx6345_aux_transfer(dev,
ANX9804_AUX_TX_COMM_READ |
ANX9804_AUX_TX_COMM_DP_TRANSACTION,
reg, val, 1);
if (ret) {
debug("Failed to read DPCD\n");
return ret;
}
return 0;
}
static int anx6345_read_edid(struct udevice *dev, u8 *buf, int size)
{
struct anx6345_priv *priv = dev_get_priv(dev);
if (size > EDID_SIZE)
size = EDID_SIZE;
memcpy(buf, priv->edid, size);
return size;
}
static int anx6345_attach(struct udevice *dev)
{
/* No-op */
return 0;
}
static int anx6345_enable(struct udevice *dev)
{
u8 chipid, colordepth, lanes, data_rate, c;
int ret, i, bpp;
struct display_timing timing;
struct anx6345_priv *priv = dev_get_priv(dev);
/* Deassert reset and enable power */
ret = video_bridge_set_active(dev, true);
if (ret)
return ret;
/* Reset */
anx6345_write_r1(dev, ANX9804_RST_CTRL_REG, 1);
mdelay(100);
anx6345_write_r1(dev, ANX9804_RST_CTRL_REG, 0);
/* Write 0 to the powerdown reg (powerup everything) */
anx6345_write_r1(dev, ANX9804_POWERD_CTRL_REG, 0);
ret = anx6345_read_r1(dev, ANX9804_DEV_IDH_REG, &chipid);
if (ret)
debug("%s: read id failed: %d\n", __func__, ret);
switch (chipid) {
case 0x63:
debug("ANX63xx detected.\n");
break;
default:
debug("Error anx6345 chipid mismatch: %.2x\n", (int)chipid);
return -ENODEV;
}
for (i = 0; i < 100; i++) {
anx6345_read_r0(dev, ANX9804_SYS_CTRL2_REG, &c);
anx6345_write_r0(dev, ANX9804_SYS_CTRL2_REG, c);
anx6345_read_r0(dev, ANX9804_SYS_CTRL2_REG, &c);
if ((c & ANX9804_SYS_CTRL2_CHA_STA) == 0)
break;
mdelay(5);
}
if (i == 100)
debug("Error anx6345 clock is not stable\n");
/* Set a bunch of analog related register values */
anx6345_write_r0(dev, ANX9804_PLL_CTRL_REG, 0x00);
anx6345_write_r1(dev, ANX9804_ANALOG_DEBUG_REG1, 0x70);
anx6345_write_r0(dev, ANX9804_LINK_DEBUG_REG, 0x30);
/* Force HPD */
anx6345_write_r0(dev, ANX9804_SYS_CTRL3_REG,
ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL);
/* Power up and configure lanes */
anx6345_write_r0(dev, ANX9804_ANALOG_POWER_DOWN_REG, 0x00);
anx6345_write_r0(dev, ANX9804_TRAINING_LANE0_SET_REG, 0x00);
anx6345_write_r0(dev, ANX9804_TRAINING_LANE1_SET_REG, 0x00);
anx6345_write_r0(dev, ANX9804_TRAINING_LANE2_SET_REG, 0x00);
anx6345_write_r0(dev, ANX9804_TRAINING_LANE3_SET_REG, 0x00);
/* Reset AUX CH */
anx6345_write_r1(dev, ANX9804_RST_CTRL2_REG,
ANX9804_RST_CTRL2_AUX);
anx6345_write_r1(dev, ANX9804_RST_CTRL2_REG, 0);
/* Powerdown audio and some other unused bits */
anx6345_write_r1(dev, ANX9804_POWERD_CTRL_REG, ANX9804_POWERD_AUDIO);
anx6345_write_r0(dev, ANX9804_HDCP_CONTROL_0_REG, 0x00);
anx6345_write_r0(dev, 0xa7, 0x00);
anx6345_read_aux_i2c(dev, 0x50, 0x0, EDID_SIZE, priv->edid);
if (edid_get_timing(priv->edid, EDID_SIZE, &timing, &bpp) != 0) {
debug("Failed to parse EDID\n");
return -EIO;
}
debug("%s: panel found: %dx%d, bpp %d\n", __func__,
timing.hactive.typ, timing.vactive.typ, bpp);
if (bpp == 6)
colordepth = 0x00; /* 6 bit */
else
colordepth = 0x10; /* 8 bit */
anx6345_write_r1(dev, ANX9804_VID_CTRL2_REG, colordepth);
if (anx6345_read_dpcd(dev, DP_MAX_LINK_RATE, &data_rate)) {
debug("%s: Failed to DP_MAX_LINK_RATE\n", __func__);
return -EIO;
}
debug("%s: data_rate: %d\n", __func__, (int)data_rate);
if (anx6345_read_dpcd(dev, DP_MAX_LANE_COUNT, &lanes)) {
debug("%s: Failed to read DP_MAX_LANE_COUNT\n", __func__);
return -EIO;
}
lanes &= DP_MAX_LANE_COUNT_MASK;
debug("%s: lanes: %d\n", __func__, (int)lanes);
/* Set data-rate / lanes */
anx6345_write_r0(dev, ANX9804_LINK_BW_SET_REG, data_rate);
anx6345_write_r0(dev, ANX9804_LANE_COUNT_SET_REG, lanes);
/* Link training */
anx6345_write_r0(dev, ANX9804_LINK_TRAINING_CTRL_REG,
ANX9804_LINK_TRAINING_CTRL_EN);
mdelay(5);
for (i = 0; i < 100; i++) {
anx6345_read_r0(dev, ANX9804_LINK_TRAINING_CTRL_REG, &c);
if ((chipid == 0x63) && (c & 0x80) == 0)
break;
mdelay(5);
}
if (i == 100) {
debug("Error anx6345 link training timeout\n");
return -ENODEV;
}
/* Enable */
anx6345_write_r1(dev, ANX9804_VID_CTRL1_REG,
ANX9804_VID_CTRL1_VID_EN | ANX9804_VID_CTRL1_EDGE);
/* Force stream valid */
anx6345_write_r0(dev, ANX9804_SYS_CTRL3_REG,
ANX9804_SYS_CTRL3_F_HPD |
ANX9804_SYS_CTRL3_HPD_CTRL |
ANX9804_SYS_CTRL3_F_VALID |
ANX9804_SYS_CTRL3_VALID_CTRL);
return 0;
}
static int anx6345_probe(struct udevice *dev)
{
if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
return -EPROTONOSUPPORT;
return anx6345_enable(dev);
}
struct video_bridge_ops anx6345_ops = {
.attach = anx6345_attach,
.set_backlight = anx6345_set_backlight,
.read_edid = anx6345_read_edid,
};
static const struct udevice_id anx6345_ids[] = {
{ .compatible = "analogix,anx6345", },
{ }
};
U_BOOT_DRIVER(analogix_anx6345) = {
.name = "analogix_anx6345",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = anx6345_ids,
.probe = anx6345_probe,
.ops = &anx6345_ops,
.priv_auto_alloc_size = sizeof(struct anx6345_priv),
};
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <video_bridge.h>
#include <power/regulator.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* Initialisation of the chip is a process of writing certain values into
* certain registers over i2c bus. The chip in fact responds to a range of
* addresses on the i2c bus, so for each written value three parameters are
* required: i2c address, register address and the actual value.
*
* The base address is derived from the device tree, but oddly the chip
* responds on several addresses with different register sets for each.
*/
/**
* ps8622_write() Write a PS8622 eDP bridge i2c register
*
* @param dev I2C device
* @param addr_off offset from the i2c base address for ps8622
* @param reg_addr register address to write
* @param value value to be written
* @return 0 on success, non-0 on failure
*/
static int ps8622_write(struct udevice *dev, unsigned addr_off,
unsigned char reg_addr, unsigned char value)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
uint8_t buf[2];
struct i2c_msg msg;
int ret;
msg.addr = chip->chip_addr + addr_off;
msg.flags = 0;
buf[0] = reg_addr;
buf[1] = value;
msg.buf = buf;
msg.len = 2;
ret = dm_i2c_xfer(dev, &msg, 1);
if (ret) {
debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n",
__func__, reg_addr, value, ret);
return ret;
}
return 0;
}
static int ps8622_set_backlight(struct udevice *dev, int percent)
{
int level = percent * 255 / 100;
debug("%s: level=%d\n", __func__, level);
return ps8622_write(dev, 0x01, 0xa7, level);
}
static int ps8622_attach(struct udevice *dev)
{
const uint8_t *params;
struct udevice *reg;
int ret, i, len;
debug("%s: %s\n", __func__, dev->name);
/* set the LDO providing the 1.2V rail to the Parade bridge */
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply", &reg);
if (!ret) {
ret = regulator_autoset(reg);
} else if (ret != -ENOENT) {
debug("%s: Failed to enable power: ret=%d\n", __func__, ret);
return ret;
}
ret = video_bridge_set_active(dev, true);
if (ret)
return ret;
params = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "parade,regs",
&len);
if (!params || len % 3) {
debug("%s: missing/invalid params=%p, len=%x\n", __func__,
params, len);
return -EINVAL;
}
/* need to wait 20ms after power on before doing I2C writes */
mdelay(20);
for (i = 0; i < len; i += 3) {
ret = ps8622_write(dev, params[i + 0], params[i + 1],
params[i + 2]);
if (ret)
return ret;
}
return 0;
}
static int ps8622_probe(struct udevice *dev)
{
debug("%s\n", __func__);
if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
return -EPROTONOSUPPORT;
return 0;
}
struct video_bridge_ops ps8622_ops = {
.attach = ps8622_attach,
.set_backlight = ps8622_set_backlight,
};
static const struct udevice_id ps8622_ids[] = {
{ .compatible = "parade,ps8622", },
{ .compatible = "parade,ps8625", },
{ }
};
U_BOOT_DRIVER(parade_ps8622) = {
.name = "parade_ps8622",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = ps8622_ids,
.probe = ps8622_probe,
.ops = &ps8622_ops,
};
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <video_bridge.h>
static int ptn3460_attach(struct udevice *dev)
{
debug("%s: %s\n", __func__, dev->name);
return video_bridge_set_active(dev, true);
}
struct video_bridge_ops ptn3460_ops = {
.attach = ptn3460_attach,
};
static const struct udevice_id ptn3460_ids[] = {
{ .compatible = "nxp,ptn3460", },
{ }
};
U_BOOT_DRIVER(parade_ptn3460) = {
.name = "nmp_ptn3460",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = ptn3460_ids,
.ops = &ptn3460_ops,
};
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <edid.h>
#include <video_bridge.h>
int video_bridge_set_backlight(struct udevice *dev, int percent)
{
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
if (!ops->set_backlight)
return -ENOSYS;
return ops->set_backlight(dev, percent);
}
int video_bridge_attach(struct udevice *dev)
{
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
if (!ops->attach)
return -ENOSYS;
return ops->attach(dev);
}
int video_bridge_check_attached(struct udevice *dev)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
int ret;
if (!ops->check_attached) {
ret = dm_gpio_get_value(&uc_priv->hotplug);
return ret > 0 ? 0 : ret == 0 ? -ENOTCONN : ret;
}
return ops->check_attached(dev);
}
int video_bridge_read_edid(struct udevice *dev, u8 *buf, int buf_size)
{
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
if (!ops || !ops->read_edid)
return -ENOSYS;
return ops->read_edid(dev, buf, buf_size);
}
static int video_bridge_pre_probe(struct udevice *dev)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
int ret;
debug("%s\n", __func__);
ret = gpio_request_by_name(dev, "sleep-gpios", 0,
&uc_priv->sleep, GPIOD_IS_OUT);
if (ret) {
debug("%s: Could not decode sleep-gpios (%d)\n", __func__, ret);
if (ret != -ENOENT)
return ret;
}
/*
* Drop this for now as we do not have driver model pinctrl support
*
* ret = dm_gpio_set_pull(&uc_priv->sleep, GPIO_PULL_NONE);
* if (ret) {
* debug("%s: Could not set sleep pull value\n", __func__);
* return ret;
* }
*/
ret = gpio_request_by_name(dev, "reset-gpios", 0, &uc_priv->reset,
GPIOD_IS_OUT);
if (ret) {
debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret);
if (ret != -ENOENT)
return ret;
}
/*
* Drop this for now as we do not have driver model pinctrl support
*
* ret = dm_gpio_set_pull(&uc_priv->reset, GPIO_PULL_NONE);
* if (ret) {
* debug("%s: Could not set reset pull value\n", __func__);
* return ret;
* }
*/
ret = gpio_request_by_name(dev, "hotplug-gpios", 0, &uc_priv->hotplug,
GPIOD_IS_IN);
if (ret) {
debug("%s: Could not decode hotplug (%d)\n", __func__, ret);
if (ret != -ENOENT)
return ret;
}
return 0;
}
int video_bridge_set_active(struct udevice *dev, bool active)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
int ret = 0;
debug("%s: %d\n", __func__, active);
if (uc_priv->sleep.dev) {
ret = dm_gpio_set_value(&uc_priv->sleep, !active);
if (ret)
return ret;
}
if (!active)
return 0;
if (uc_priv->reset.dev) {
ret = dm_gpio_set_value(&uc_priv->reset, true);
if (ret)
return ret;
udelay(10);
ret = dm_gpio_set_value(&uc_priv->reset, false);
}
return ret;
}
UCLASS_DRIVER(video_bridge) = {
.id = UCLASS_VIDEO_BRIDGE,
.name = "video_bridge",
.per_device_auto_alloc_size = sizeof(struct video_bridge_priv),
.pre_probe = video_bridge_pre_probe,
};
@@ -0,0 +1,768 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From coreboot src/soc/intel/broadwell/igd.c
*
* Copyright (C) 2016 Google, Inc
*/
#include <common.h>
#include <bios_emul.h>
#include <dm.h>
#include <vbe.h>
#include <video.h>
#include <asm/cpu.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/mtrr.h>
#include <asm/arch/cpu.h>
#include <asm/arch/iomap.h>
#include <asm/arch/pch.h>
#include "i915_reg.h"
struct broadwell_igd_priv {
u8 *regs;
};
struct broadwell_igd_plat {
u32 dp_hotplug[3];
int port_select;
int power_up_delay;
int power_backlight_on_delay;
int power_down_delay;
int power_backlight_off_delay;
int power_cycle_delay;
int cpu_backlight;
int pch_backlight;
int cdclk;
int pre_graphics_delay;
};
#define GT_RETRY 1000
#define GT_CDCLK_337 0
#define GT_CDCLK_450 1
#define GT_CDCLK_540 2
#define GT_CDCLK_675 3
u32 board_map_oprom_vendev(u32 vendev)
{
return SA_IGD_OPROM_VENDEV;
}
static int poll32(u8 *addr, uint mask, uint value)
{
ulong start;
start = get_timer(0);
debug("%s: addr %p = %x\n", __func__, addr, readl(addr));
while ((readl(addr) & mask) != value) {
if (get_timer(start) > GT_RETRY) {
debug("poll32: timeout: %x\n", readl(addr));
return -ETIMEDOUT;
}
}
return 0;
}
static int haswell_early_init(struct udevice *dev)
{
struct broadwell_igd_priv *priv = dev_get_priv(dev);
u8 *regs = priv->regs;
int ret;
/* Enable Force Wake */
writel(0x00000020, regs + 0xa180);
writel(0x00010001, regs + 0xa188);
ret = poll32(regs + 0x130044, 1, 1);
if (ret)
goto err;
/* Enable Counters */
setbits_le32(regs + 0xa248, 0x00000016);
/* GFXPAUSE settings */
writel(0x00070020, regs + 0xa000);
/* ECO Settings */
clrsetbits_le32(regs + 0xa180, ~0xff3fffff, 0x15000000);
/* Enable DOP Clock Gating */
writel(0x000003fd, regs + 0x9424);
/* Enable Unit Level Clock Gating */
writel(0x00000080, regs + 0x9400);
writel(0x40401000, regs + 0x9404);
writel(0x00000000, regs + 0x9408);
writel(0x02000001, regs + 0x940c);
/*
* RC6 Settings
*/
/* Wake Rate Limits */
setbits_le32(regs + 0xa090, 0x00000000);
setbits_le32(regs + 0xa098, 0x03e80000);
setbits_le32(regs + 0xa09c, 0x00280000);
setbits_le32(regs + 0xa0a8, 0x0001e848);
setbits_le32(regs + 0xa0ac, 0x00000019);
/* Render/Video/Blitter Idle Max Count */
writel(0x0000000a, regs + 0x02054);
writel(0x0000000a, regs + 0x12054);
writel(0x0000000a, regs + 0x22054);
writel(0x0000000a, regs + 0x1a054);
/* RC Sleep / RCx Thresholds */
setbits_le32(regs + 0xa0b0, 0x00000000);
setbits_le32(regs + 0xa0b4, 0x000003e8);
setbits_le32(regs + 0xa0b8, 0x0000c350);
/* RP Settings */
setbits_le32(regs + 0xa010, 0x000f4240);
setbits_le32(regs + 0xa014, 0x12060000);
setbits_le32(regs + 0xa02c, 0x0000e808);
setbits_le32(regs + 0xa030, 0x0003bd08);
setbits_le32(regs + 0xa068, 0x000101d0);
setbits_le32(regs + 0xa06c, 0x00055730);
setbits_le32(regs + 0xa070, 0x0000000a);
/* RP Control */
writel(0x00000b92, regs + 0xa024);
/* HW RC6 Control */
writel(0x88040000, regs + 0xa090);
/* Video Frequency Request */
writel(0x08000000, regs + 0xa00c);
/* Set RC6 VIDs */
ret = poll32(regs + 0x138124, (1 << 31), 0);
if (ret)
goto err;
writel(0, regs + 0x138128);
writel(0x80000004, regs + 0x138124);
ret = poll32(regs + 0x138124, (1 << 31), 0);
if (ret)
goto err;
/* Enable PM Interrupts */
writel(0x03000076, regs + 0x4402c);
/* Enable RC6 in idle */
writel(0x00040000, regs + 0xa094);
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
};
static int haswell_late_init(struct udevice *dev)
{
struct broadwell_igd_priv *priv = dev_get_priv(dev);
u8 *regs = priv->regs;
int ret;
/* Lock settings */
setbits_le32(regs + 0x0a248, (1 << 31));
setbits_le32(regs + 0x0a004, (1 << 4));
setbits_le32(regs + 0x0a080, (1 << 2));
setbits_le32(regs + 0x0a180, (1 << 31));
/* Disable Force Wake */
writel(0x00010000, regs + 0xa188);
ret = poll32(regs + 0x130044, 1, 0);
if (ret)
goto err;
writel(0x00000001, regs + 0xa188);
/* Enable power well for DP and Audio */
setbits_le32(regs + 0x45400, (1 << 31));
ret = poll32(regs + 0x45400, 1 << 30, 1 << 30);
if (ret)
goto err;
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
};
static int broadwell_early_init(struct udevice *dev)
{
struct broadwell_igd_priv *priv = dev_get_priv(dev);
u8 *regs = priv->regs;
int ret;
/* Enable Force Wake */
writel(0x00010001, regs + 0xa188);
ret = poll32(regs + 0x130044, 1, 1);
if (ret)
goto err;
/* Enable push bus metric control and shift */
writel(0x00000004, regs + 0xa248);
writel(0x000000ff, regs + 0xa250);
writel(0x00000010, regs + 0xa25c);
/* GFXPAUSE settings (set based on stepping) */
/* ECO Settings */
writel(0x45200000, regs + 0xa180);
/* Enable DOP Clock Gating */
writel(0x000000fd, regs + 0x9424);
/* Enable Unit Level Clock Gating */
writel(0x00000000, regs + 0x9400);
writel(0x40401000, regs + 0x9404);
writel(0x00000000, regs + 0x9408);
writel(0x02000001, regs + 0x940c);
writel(0x0000000a, regs + 0x1a054);
/* Video Frequency Request */
writel(0x08000000, regs + 0xa00c);
writel(0x00000009, regs + 0x138158);
writel(0x0000000d, regs + 0x13815c);
/*
* RC6 Settings
*/
/* Wake Rate Limits */
clrsetbits_le32(regs + 0x0a090, ~0, 0);
setbits_le32(regs + 0x0a098, 0x03e80000);
setbits_le32(regs + 0x0a09c, 0x00280000);
setbits_le32(regs + 0x0a0a8, 0x0001e848);
setbits_le32(regs + 0x0a0ac, 0x00000019);
/* Render/Video/Blitter Idle Max Count */
writel(0x0000000a, regs + 0x02054);
writel(0x0000000a, regs + 0x12054);
writel(0x0000000a, regs + 0x22054);
/* RC Sleep / RCx Thresholds */
setbits_le32(regs + 0x0a0b0, 0x00000000);
setbits_le32(regs + 0x0a0b8, 0x00000271);
/* RP Settings */
setbits_le32(regs + 0x0a010, 0x000f4240);
setbits_le32(regs + 0x0a014, 0x12060000);
setbits_le32(regs + 0x0a02c, 0x0000e808);
setbits_le32(regs + 0x0a030, 0x0003bd08);
setbits_le32(regs + 0x0a068, 0x000101d0);
setbits_le32(regs + 0x0a06c, 0x00055730);
setbits_le32(regs + 0x0a070, 0x0000000a);
setbits_le32(regs + 0x0a168, 0x00000006);
/* RP Control */
writel(0x00000b92, regs + 0xa024);
/* HW RC6 Control */
writel(0x90040000, regs + 0xa090);
/* Set RC6 VIDs */
ret = poll32(regs + 0x138124, (1 << 31), 0);
if (ret)
goto err;
writel(0, regs + 0x138128);
writel(0x80000004, regs + 0x138124);
ret = poll32(regs + 0x138124, (1 << 31), 0);
if (ret)
goto err;
/* Enable PM Interrupts */
writel(0x03000076, regs + 0x4402c);
/* Enable RC6 in idle */
writel(0x00040000, regs + 0xa094);
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
}
static int broadwell_late_init(struct udevice *dev)
{
struct broadwell_igd_priv *priv = dev_get_priv(dev);
u8 *regs = priv->regs;
int ret;
/* Lock settings */
setbits_le32(regs + 0x0a248, 1 << 31);
setbits_le32(regs + 0x0a000, 1 << 18);
setbits_le32(regs + 0x0a180, 1 << 31);
/* Disable Force Wake */
writel(0x00010000, regs + 0xa188);
ret = poll32(regs + 0x130044, 1, 0);
if (ret)
goto err;
/* Enable power well for DP and Audio */
setbits_le32(regs + 0x45400, 1 << 31);
ret = poll32(regs + 0x45400, 1 << 30, 1 << 30);
if (ret)
goto err;
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
};
static unsigned long gtt_read(struct broadwell_igd_priv *priv,
unsigned long reg)
{
return readl(priv->regs + reg);
}
static void gtt_write(struct broadwell_igd_priv *priv, unsigned long reg,
unsigned long data)
{
writel(data, priv->regs + reg);
}
static inline void gtt_clrsetbits(struct broadwell_igd_priv *priv, u32 reg,
u32 bic, u32 or)
{
clrsetbits_le32(priv->regs + reg, bic, or);
}
static int gtt_poll(struct broadwell_igd_priv *priv, u32 reg, u32 mask,
u32 value)
{
unsigned try = GT_RETRY;
u32 data;
while (try--) {
data = gtt_read(priv, reg);
if ((data & mask) == value)
return 0;
udelay(10);
}
debug("GT init timeout\n");
return -ETIMEDOUT;
}
static void igd_setup_panel(struct udevice *dev)
{
struct broadwell_igd_plat *plat = dev_get_platdata(dev);
struct broadwell_igd_priv *priv = dev_get_priv(dev);
u32 reg32;
/* Setup Digital Port Hotplug */
reg32 = (plat->dp_hotplug[0] & 0x7) << 2;
reg32 |= (plat->dp_hotplug[1] & 0x7) << 10;
reg32 |= (plat->dp_hotplug[2] & 0x7) << 18;
gtt_write(priv, PCH_PORT_HOTPLUG, reg32);
/* Setup Panel Power On Delays */
reg32 = (plat->port_select & 0x3) << 30;
reg32 |= (plat->power_up_delay & 0x1fff) << 16;
reg32 |= (plat->power_backlight_on_delay & 0x1fff);
gtt_write(priv, PCH_PP_ON_DELAYS, reg32);
/* Setup Panel Power Off Delays */
reg32 = (plat->power_down_delay & 0x1fff) << 16;
reg32 |= (plat->power_backlight_off_delay & 0x1fff);
gtt_write(priv, PCH_PP_OFF_DELAYS, reg32);
/* Setup Panel Power Cycle Delay */
if (plat->power_cycle_delay) {
reg32 = gtt_read(priv, PCH_PP_DIVISOR);
reg32 &= ~0xff;
reg32 |= plat->power_cycle_delay & 0xff;
gtt_write(priv, PCH_PP_DIVISOR, reg32);
}
/* Enable Backlight if needed */
if (plat->cpu_backlight) {
gtt_write(priv, BLC_PWM_CPU_CTL2, BLC_PWM2_ENABLE);
gtt_write(priv, BLC_PWM_CPU_CTL, plat->cpu_backlight);
}
if (plat->pch_backlight) {
gtt_write(priv, BLC_PWM_PCH_CTL1, BLM_PCH_PWM_ENABLE);
gtt_write(priv, BLC_PWM_PCH_CTL2, plat->pch_backlight);
}
}
static int igd_cdclk_init_haswell(struct udevice *dev)
{
struct broadwell_igd_plat *plat = dev_get_platdata(dev);
struct broadwell_igd_priv *priv = dev_get_priv(dev);
int cdclk = plat->cdclk;
u16 devid;
int gpu_is_ulx = 0;
u32 dpdiv, lpcll;
int ret;
dm_pci_read_config16(dev, PCI_DEVICE_ID, &devid);
/* Check for ULX GT1 or GT2 */
if (devid == 0x0a0e || devid == 0x0a1e)
gpu_is_ulx = 1;
/* 675MHz is not supported on haswell */
if (cdclk == GT_CDCLK_675)
cdclk = GT_CDCLK_337;
/* If CD clock is fixed or ULT then set to 450MHz */
if ((gtt_read(priv, 0x42014) & 0x1000000) || cpu_is_ult())
cdclk = GT_CDCLK_450;
/* 540MHz is not supported on ULX */
if (gpu_is_ulx && cdclk == GT_CDCLK_540)
cdclk = GT_CDCLK_337;
/* 337.5MHz is not supported on non-ULT/ULX */
if (!gpu_is_ulx && !cpu_is_ult() && cdclk == GT_CDCLK_337)
cdclk = GT_CDCLK_450;
/* Set variables based on CD Clock setting */
switch (cdclk) {
case GT_CDCLK_337:
dpdiv = 169;
lpcll = (1 << 26);
break;
case GT_CDCLK_450:
dpdiv = 225;
lpcll = 0;
break;
case GT_CDCLK_540:
dpdiv = 270;
lpcll = (1 << 26);
break;
default:
ret = -EDOM;
goto err;
}
/* Set LPCLL_CTL CD Clock Frequency Select */
gtt_clrsetbits(priv, 0x130040, ~0xf3ffffff, lpcll);
/* ULX: Inform power controller of selected frequency */
if (gpu_is_ulx) {
if (cdclk == GT_CDCLK_450)
gtt_write(priv, 0x138128, 0x00000000); /* 450MHz */
else
gtt_write(priv, 0x138128, 0x00000001); /* 337.5MHz */
gtt_write(priv, 0x13812c, 0x00000000);
gtt_write(priv, 0x138124, 0x80000017);
}
/* Set CPU DP AUX 2X bit clock dividers */
gtt_clrsetbits(priv, 0x64010, ~0xfffff800, dpdiv);
gtt_clrsetbits(priv, 0x64810, ~0xfffff800, dpdiv);
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
}
static int igd_cdclk_init_broadwell(struct udevice *dev)
{
struct broadwell_igd_plat *plat = dev_get_platdata(dev);
struct broadwell_igd_priv *priv = dev_get_priv(dev);
int cdclk = plat->cdclk;
u32 dpdiv, lpcll, pwctl, cdset;
int ret;
/* Inform power controller of upcoming frequency change */
gtt_write(priv, 0x138128, 0);
gtt_write(priv, 0x13812c, 0);
gtt_write(priv, 0x138124, 0x80000018);
/* Poll GT driver mailbox for run/busy clear */
if (gtt_poll(priv, 0x138124, 1 << 31, 0 << 31))
cdclk = GT_CDCLK_450;
if (gtt_read(priv, 0x42014) & 0x1000000) {
/* If CD clock is fixed then set to 450MHz */
cdclk = GT_CDCLK_450;
} else {
/* Program CD clock to highest supported freq */
if (cpu_is_ult())
cdclk = GT_CDCLK_540;
else
cdclk = GT_CDCLK_675;
}
/* CD clock frequency 675MHz not supported on ULT */
if (cpu_is_ult() && cdclk == GT_CDCLK_675)
cdclk = GT_CDCLK_540;
/* Set variables based on CD Clock setting */
switch (cdclk) {
case GT_CDCLK_337:
cdset = 337;
lpcll = (1 << 27);
pwctl = 2;
dpdiv = 169;
break;
case GT_CDCLK_450:
cdset = 449;
lpcll = 0;
pwctl = 0;
dpdiv = 225;
break;
case GT_CDCLK_540:
cdset = 539;
lpcll = (1 << 26);
pwctl = 1;
dpdiv = 270;
break;
case GT_CDCLK_675:
cdset = 674;
lpcll = (1 << 26) | (1 << 27);
pwctl = 3;
dpdiv = 338;
break;
default:
ret = -EDOM;
goto err;
}
debug("%s: frequency = %d\n", __func__, cdclk);
/* Set LPCLL_CTL CD Clock Frequency Select */
gtt_clrsetbits(priv, 0x130040, ~0xf3ffffff, lpcll);
/* Inform power controller of selected frequency */
gtt_write(priv, 0x138128, pwctl);
gtt_write(priv, 0x13812c, 0);
gtt_write(priv, 0x138124, 0x80000017);
/* Program CD Clock Frequency */
gtt_clrsetbits(priv, 0x46200, ~0xfffffc00, cdset);
/* Set CPU DP AUX 2X bit clock dividers */
gtt_clrsetbits(priv, 0x64010, ~0xfffff800, dpdiv);
gtt_clrsetbits(priv, 0x64810, ~0xfffff800, dpdiv);
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
}
u8 systemagent_revision(struct udevice *bus)
{
ulong val;
pci_bus_read_config(bus, PCI_BDF(0, 0, 0), PCI_REVISION_ID, &val,
PCI_SIZE_32);
return val;
}
static int igd_pre_init(struct udevice *dev, bool is_broadwell)
{
struct broadwell_igd_plat *plat = dev_get_platdata(dev);
struct broadwell_igd_priv *priv = dev_get_priv(dev);
u32 rp1_gfx_freq;
int ret;
mdelay(plat->pre_graphics_delay);
/* Early init steps */
if (is_broadwell) {
ret = broadwell_early_init(dev);
if (ret)
goto err;
/* Set GFXPAUSE based on stepping */
if (cpu_get_stepping() <= (CPUID_BROADWELL_E0 & 0xf) &&
systemagent_revision(pci_get_controller(dev)) <= 9) {
gtt_write(priv, 0xa000, 0x300ff);
} else {
gtt_write(priv, 0xa000, 0x30020);
}
} else {
ret = haswell_early_init(dev);
if (ret)
goto err;
}
/* Set RP1 graphics frequency */
rp1_gfx_freq = (readl(MCHBAR_REG(0x5998)) >> 8) & 0xff;
gtt_write(priv, 0xa008, rp1_gfx_freq << 24);
/* Post VBIOS panel setup */
igd_setup_panel(dev);
return 0;
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
}
static int igd_post_init(struct udevice *dev, bool is_broadwell)
{
int ret;
/* Late init steps */
if (is_broadwell) {
ret = igd_cdclk_init_broadwell(dev);
if (ret)
return ret;
ret = broadwell_late_init(dev);
if (ret)
return ret;
} else {
igd_cdclk_init_haswell(dev);
ret = haswell_late_init(dev);
if (ret)
return ret;
}
return 0;
}
static int broadwell_igd_int15_handler(void)
{
int res = 0;
debug("%s: INT15 function %04x!\n", __func__, M.x86.R_AX);
switch (M.x86.R_AX) {
case 0x5f35:
/*
* Boot Display Device Hook:
* bit 0 = CRT
* bit 1 = TV (eDP)
* bit 2 = EFP
* bit 3 = LFP
* bit 4 = CRT2
* bit 5 = TV2 (eDP)
* bit 6 = EFP2
* bit 7 = LFP2
*/
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000; /* Use video bios default */
res = 1;
break;
default:
debug("Unknown INT15 function %04x!\n", M.x86.R_AX);
break;
}
return res;
}
static int broadwell_igd_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
bool is_broadwell;
int ret;
if (!ll_boot_init()) {
/*
* If we are running from EFI or coreboot, this driver can't
* work.
*/
printf("Not available (previous bootloader prevents it)\n");
return -EPERM;
}
is_broadwell = cpu_get_family_model() == BROADWELL_FAMILY_ULT;
bootstage_start(BOOTSTAGE_ID_ACCUM_LCD, "vesa display");
debug("%s: is_broadwell=%d\n", __func__, is_broadwell);
ret = igd_pre_init(dev, is_broadwell);
if (!ret) {
ret = vbe_setup_video(dev, broadwell_igd_int15_handler);
if (ret)
debug("failed to run video BIOS: %d\n", ret);
}
if (!ret)
ret = igd_post_init(dev, is_broadwell);
bootstage_accum(BOOTSTAGE_ID_ACCUM_LCD);
if (ret)
return ret;
/* Use write-combining for the graphics memory, 256MB */
ret = mtrr_add_request(MTRR_TYPE_WRCOMB, plat->base, 256 << 20);
if (!ret)
ret = mtrr_commit(true);
if (ret && ret != -ENOSYS) {
printf("Failed to add MTRR: Display will be slow (err %d)\n",
ret);
}
debug("fb=%lx, size %x, display size=%d %d %d\n", plat->base,
plat->size, uc_priv->xsize, uc_priv->ysize, uc_priv->bpix);
return 0;
}
static int broadwell_igd_ofdata_to_platdata(struct udevice *dev)
{
struct broadwell_igd_plat *plat = dev_get_platdata(dev);
struct broadwell_igd_priv *priv = dev_get_priv(dev);
int node = dev_of_offset(dev);
const void *blob = gd->fdt_blob;
if (fdtdec_get_int_array(blob, node, "intel,dp-hotplug",
plat->dp_hotplug,
ARRAY_SIZE(plat->dp_hotplug)))
return -EINVAL;
plat->port_select = fdtdec_get_int(blob, node, "intel,port-select", 0);
plat->power_cycle_delay = fdtdec_get_int(blob, node,
"intel,power-cycle-delay", 0);
plat->power_up_delay = fdtdec_get_int(blob, node,
"intel,power-up-delay", 0);
plat->power_down_delay = fdtdec_get_int(blob, node,
"intel,power-down-delay", 0);
plat->power_backlight_on_delay = fdtdec_get_int(blob, node,
"intel,power-backlight-on-delay", 0);
plat->power_backlight_off_delay = fdtdec_get_int(blob, node,
"intel,power-backlight-off-delay", 0);
plat->cpu_backlight = fdtdec_get_int(blob, node,
"intel,cpu-backlight", 0);
plat->pch_backlight = fdtdec_get_int(blob, node,
"intel,pch-backlight", 0);
plat->pre_graphics_delay = fdtdec_get_int(blob, node,
"intel,pre-graphics-delay", 0);
priv->regs = (u8 *)dm_pci_read_bar32(dev, 0);
debug("%s: regs at %p\n", __func__, priv->regs);
debug("dp_hotplug %d %d %d\n", plat->dp_hotplug[0], plat->dp_hotplug[1],
plat->dp_hotplug[2]);
debug("port_select = %d\n", plat->port_select);
debug("power_up_delay = %d\n", plat->power_up_delay);
debug("power_backlight_on_delay = %d\n",
plat->power_backlight_on_delay);
debug("power_down_delay = %d\n", plat->power_down_delay);
debug("power_backlight_off_delay = %d\n",
plat->power_backlight_off_delay);
debug("power_cycle_delay = %d\n", plat->power_cycle_delay);
debug("cpu_backlight = %x\n", plat->cpu_backlight);
debug("pch_backlight = %x\n", plat->pch_backlight);
debug("cdclk = %d\n", plat->cdclk);
debug("pre_graphics_delay = %d\n", plat->pre_graphics_delay);
return 0;
}
static const struct video_ops broadwell_igd_ops = {
};
static const struct udevice_id broadwell_igd_ids[] = {
{ .compatible = "intel,broadwell-igd" },
{ }
};
U_BOOT_DRIVER(broadwell_igd) = {
.name = "broadwell_igd",
.id = UCLASS_VIDEO,
.of_match = broadwell_igd_ids,
.ops = &broadwell_igd_ops,
.ofdata_to_platdata = broadwell_igd_ofdata_to_platdata,
.probe = broadwell_igd_probe,
.priv_auto_alloc_size = sizeof(struct broadwell_igd_priv),
.platdata_auto_alloc_size = sizeof(struct broadwell_igd_plat),
};
@@ -0,0 +1,425 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2005-2009
* Jens Scharsig @ BuS Elektronik GmbH & Co. KG, <esw@bus-elektronik.de>
*/
#include <common.h>
#include <bmp_layout.h>
#include <asm/io.h>
vu_char *vcxk_bws = ((vu_char *) (CONFIG_SYS_VCXK_BASE));
vu_short *vcxk_bws_word = ((vu_short *)(CONFIG_SYS_VCXK_BASE));
vu_long *vcxk_bws_long = ((vu_long *) (CONFIG_SYS_VCXK_BASE));
#ifdef CONFIG_AT91RM9200
#include <asm/arch/hardware.h>
#include <asm/arch/at91_pio.h>
#ifndef VCBITMASK
#define VCBITMASK(bitno) (0x0001 << (bitno % 16))
#endif
at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
#define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \
do { \
writel(PIN, &pio->PORT.per); \
writel(PIN, &pio->PORT.DDR); \
writel(PIN, &pio->PORT.mddr); \
if (!I0O1) \
writel(PIN, &pio->PORT.puer); \
} while (0);
#define VCXK_SET_PIN(PORT, PIN) writel(PIN, &pio->PORT.sodr);
#define VCXK_CLR_PIN(PORT, PIN) writel(PIN, &pio->PORT.codr);
#define VCXK_ACKNOWLEDGE \
(!(readl(&pio->CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT.pdsr) & \
CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN))
#elif defined(CONFIG_MCF52x2)
#include <asm/m5282.h>
#ifndef VCBITMASK
#define VCBITMASK(bitno) (0x8000 >> (bitno % 16))
#endif
#define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \
if (I0O1) DDR |= PIN; else DDR &= ~PIN;
#define VCXK_SET_PIN(PORT, PIN) PORT |= PIN;
#define VCXK_CLR_PIN(PORT, PIN) PORT &= ~PIN;
#define VCXK_ACKNOWLEDGE \
(!(CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT & \
CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN))
#else
#error no vcxk support for selected ARCH
#endif
#define VCXK_DISABLE\
VCXK_SET_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, CONFIG_SYS_VCXK_ENABLE_PIN)
#define VCXK_ENABLE\
VCXK_CLR_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, CONFIG_SYS_VCXK_ENABLE_PIN)
#ifndef CONFIG_SYS_VCXK_DOUBLEBUFFERED
#define VCXK_BWS(x, data) vcxk_bws[x] = data;
#define VCXK_BWS_WORD_SET(x, mask) vcxk_bws_word[x] |= mask;
#define VCXK_BWS_WORD_CLEAR(x, mask) vcxk_bws_word[x] &= ~mask;
#define VCXK_BWS_LONG(x, data) vcxk_bws_long[x] = data;
#else
u_char double_bws[16384];
u_short *double_bws_word;
u_long *double_bws_long;
#define VCXK_BWS(x,data) \
double_bws[x] = data; vcxk_bws[x] = data;
#define VCXK_BWS_WORD_SET(x,mask) \
double_bws_word[x] |= mask; \
vcxk_bws_word[x] = double_bws_word[x];
#define VCXK_BWS_WORD_CLEAR(x,mask) \
double_bws_word[x] &= ~mask; \
vcxk_bws_word[x] = double_bws_word[x];
#define VCXK_BWS_LONG(x,data) \
double_bws_long[x] = data; vcxk_bws_long[x] = data;
#endif
#define VC4K16_Bright1 vcxk_bws_word[0x20004 / 2]
#define VC4K16_Bright2 vcxk_bws_word[0x20006 / 2]
#define VC2K_Bright vcxk_bws[0x8000]
#define VC8K_BrightH vcxk_bws[0xC000]
#define VC8K_BrightL vcxk_bws[0xC001]
vu_char VC4K16;
u_long display_width;
u_long display_height;
u_long display_bwidth;
ulong search_vcxk_driver(void);
void vcxk_cls(void);
void vcxk_setbrightness(unsigned int side, short brightness);
int vcxk_request(void);
int vcxk_acknowledge_wait(void);
void vcxk_clear(void);
/*
****f* bus_vcxk/vcxk_init
* FUNCTION
* initialalize Video Controller
* PARAMETERS
* width visible display width in pixel
* height visible display height in pixel
***
*/
int vcxk_init(unsigned long width, unsigned long height)
{
#ifdef CONFIG_SYS_VCXK_RESET_PORT
VCXK_INIT_PIN(CONFIG_SYS_VCXK_RESET_PORT,
CONFIG_SYS_VCXK_RESET_PIN, CONFIG_SYS_VCXK_RESET_DDR, 1)
VCXK_SET_PIN(CONFIG_SYS_VCXK_RESET_PORT, CONFIG_SYS_VCXK_RESET_PIN);
#endif
#ifdef CONFIG_SYS_VCXK_DOUBLEBUFFERED
double_bws_word = (u_short *)double_bws;
double_bws_long = (u_long *)double_bws;
debug("%px %px %px\n", double_bws, double_bws_word, double_bws_long);
#endif
display_width = width;
display_height = height;
#if (CONFIG_SYS_VCXK_DEFAULT_LINEALIGN == 4)
display_bwidth = ((width + 31) / 8) & ~0x3;
#elif (CONFIG_SYS_VCXK_DEFAULT_LINEALIGN == 2)
display_bwidth = ((width + 15) / 8) & ~0x1;
#else
#error CONFIG_SYS_VCXK_DEFAULT_LINEALIGN is invalid
#endif
debug("linesize ((%ld + 15) / 8 & ~0x1) = %ld\n",
display_width, display_bwidth);
#ifdef CONFIG_SYS_VCXK_AUTODETECT
VC4K16 = 0;
vcxk_bws_long[1] = 0x0;
vcxk_bws_long[1] = 0x55AAAA55;
vcxk_bws_long[5] = 0x0;
if (vcxk_bws_long[1] == 0x55AAAA55)
VC4K16 = 1;
#else
VC4K16 = 1;
debug("No autodetect: use vc4k\n");
#endif
VCXK_INIT_PIN(CONFIG_SYS_VCXK_INVERT_PORT,
CONFIG_SYS_VCXK_INVERT_PIN, CONFIG_SYS_VCXK_INVERT_DDR, 1)
VCXK_SET_PIN(CONFIG_SYS_VCXK_INVERT_PORT, CONFIG_SYS_VCXK_INVERT_PIN)
VCXK_SET_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, CONFIG_SYS_VCXK_REQUEST_PIN);
VCXK_INIT_PIN(CONFIG_SYS_VCXK_REQUEST_PORT,
CONFIG_SYS_VCXK_REQUEST_PIN, CONFIG_SYS_VCXK_REQUEST_DDR, 1)
VCXK_INIT_PIN(CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT,
CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN,
CONFIG_SYS_VCXK_ACKNOWLEDGE_DDR, 0)
VCXK_DISABLE;
VCXK_INIT_PIN(CONFIG_SYS_VCXK_ENABLE_PORT,
CONFIG_SYS_VCXK_ENABLE_PIN, CONFIG_SYS_VCXK_ENABLE_DDR, 1)
vcxk_cls();
vcxk_cls(); /* clear second/hidden page */
vcxk_setbrightness(3, 1000);
VCXK_ENABLE;
return 1;
}
/*
****f* bus_vcxk/vcxk_setpixel
* FUNCTION
* set the pixel[x,y] with the given color
* PARAMETER
* x pixel colum
* y pixel row
* color <0x40 off/black
* >0x40 on
***
*/
void vcxk_setpixel(int x, int y, unsigned long color)
{
vu_short dataptr;
if ((x < display_width) && (y < display_height)) {
dataptr = ((x / 16)) + (y * (display_bwidth >> 1));
color = ((color >> 16) & 0xFF) |
((color >> 8) & 0xFF) | (color & 0xFF);
if (color > 0x40) {
VCXK_BWS_WORD_SET(dataptr, VCBITMASK(x));
} else {
VCXK_BWS_WORD_CLEAR(dataptr, VCBITMASK(x));
}
}
}
/*
****f* bus_vcxk/vcxk_loadimage
* FUNCTION
* copies a binary image to display memory
***
*/
void vcxk_loadimage(ulong source)
{
int cnt;
vcxk_acknowledge_wait();
if (VC4K16) {
for (cnt = 0; cnt < (16384 / 4); cnt++) {
VCXK_BWS_LONG(cnt, (*(ulong *) source));
source = source + 4;
}
} else {
for (cnt = 0; cnt < 16384; cnt++) {
VCXK_BWS_LONG(cnt*2, (*(vu_char *) source));
source++;
}
}
vcxk_request();
}
/*
****f* bus_vcxk/vcxk_cls
* FUNCTION
* clear the display
***
*/
void vcxk_cls(void)
{
vcxk_acknowledge_wait();
vcxk_clear();
vcxk_request();
}
/*
****f* bus_vcxk/vcxk_clear(void)
* FUNCTION
* clear the display memory
***
*/
void vcxk_clear(void)
{
int cnt;
for (cnt = 0; cnt < (16384 / 4); cnt++) {
VCXK_BWS_LONG(cnt, 0)
}
}
/*
****f* bus_vcxk/vcxk_setbrightness
* FUNCTION
* set the display brightness
* PARAMETER
* side 1 set front side brightness
* 2 set back side brightness
* 3 set brightness for both sides
* brightness 0..1000
***
*/
void vcxk_setbrightness(unsigned int side, short brightness)
{
if (VC4K16) {
if ((side == 0) || (side & 0x1))
VC4K16_Bright1 = brightness + 23;
if ((side == 0) || (side & 0x2))
VC4K16_Bright2 = brightness + 23;
} else {
VC2K_Bright = (brightness >> 4) + 2;
VC8K_BrightH = (brightness + 23) >> 8;
VC8K_BrightL = (brightness + 23) & 0xFF;
}
}
/*
****f* bus_vcxk/vcxk_request
* FUNCTION
* requests viewing of display memory
***
*/
int vcxk_request(void)
{
VCXK_CLR_PIN(CONFIG_SYS_VCXK_REQUEST_PORT,
CONFIG_SYS_VCXK_REQUEST_PIN)
VCXK_SET_PIN(CONFIG_SYS_VCXK_REQUEST_PORT,
CONFIG_SYS_VCXK_REQUEST_PIN);
return 1;
}
/*
****f* bus_vcxk/vcxk_acknowledge_wait
* FUNCTION
* wait for acknowledge viewing requests
***
*/
int vcxk_acknowledge_wait(void)
{
while (VCXK_ACKNOWLEDGE)
;
return 1;
}
/*
****f* bus_vcxk/vcxk_draw_mono
* FUNCTION
* copies a monochrom bitmap (BMP-Format) from given memory
* PARAMETER
* dataptr pointer to bitmap
* x output bitmap @ columne
* y output bitmap @ row
***
*/
void vcxk_draw_mono(unsigned char *dataptr, unsigned long linewidth,
unsigned long cp_width, unsigned long cp_height)
{
unsigned char *lineptr;
unsigned long xcnt, ycnt;
for (ycnt = cp_height; ycnt > 0; ycnt--) {
lineptr = dataptr;
for (xcnt = 0; xcnt < cp_width; xcnt++) {
if ((*lineptr << (xcnt % 8)) & 0x80)
vcxk_setpixel(xcnt, ycnt - 1, 0xFFFFFF);
else
vcxk_setpixel(xcnt, ycnt-1, 0);
if ((xcnt % 8) == 7)
lineptr++;
} /* endfor xcnt */
dataptr = dataptr + linewidth;
} /* endfor ycnt */
}
/*
****f* bus_vcxk/vcxk_display_bitmap
* FUNCTION
* copies a bitmap (BMP-Format) to the given position
* PARAMETER
* addr pointer to bitmap
* x output bitmap @ columne
* y output bitmap @ row
***
*/
int vcxk_display_bitmap(ulong addr, int x, int y)
{
struct bmp_image *bmp;
unsigned long width;
unsigned long height;
unsigned long bpp;
unsigned long lw;
unsigned long c_width;
unsigned long c_height;
unsigned char *dataptr;
bmp = (struct bmp_image *)addr;
if ((bmp->header.signature[0] == 'B') &&
(bmp->header.signature[1] == 'M')) {
width = le32_to_cpu(bmp->header.width);
height = le32_to_cpu(bmp->header.height);
bpp = le16_to_cpu(bmp->header.bit_count);
dataptr = (unsigned char *) bmp +
le32_to_cpu(bmp->header.data_offset);
if (display_width < (width + x))
c_width = display_width - x;
else
c_width = width;
if (display_height < (height + y))
c_height = display_height - y;
else
c_height = height;
lw = (((width + 7) / 8) + 3) & ~0x3;
if (c_height < height)
dataptr = dataptr + lw * (height - c_height);
switch (bpp) {
case 1:
vcxk_draw_mono(dataptr, lw, c_width, c_height);
break;
default:
printf("Error: %ld bit per pixel "
"not supported by VCxK\n", bpp);
return 0;
}
} else {
printf("Error: no valid bmp at %lx\n", (ulong) bmp);
return 0;
}
return 1;
}
/*
****f* bus_vcxk/video_display_bitmap
***
*/
int video_display_bitmap(ulong addr, int x, int y)
{
vcxk_acknowledge_wait();
if (vcxk_display_bitmap(addr, x, y)) {
vcxk_request();
return 0;
}
return 1;
}
/* EOF */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* (C) Copyright 2001-2015
* DENX Software Engineering -- wd@denx.de
* Compulab Ltd - http://compulab.co.il/
* Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
*/
#include <common.h>
#include <dm.h>
#include <video.h>
#include <video_console.h>
#include <video_font.h> /* Get font data, width and height */
static int console_normal_set_row(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *line;
int pixels = VIDEO_FONT_HEIGHT * vid_priv->xsize;
int i;
line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * vid_priv->line_length;
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
default:
return -ENOSYS;
}
return 0;
}
static int console_normal_move_rows(struct udevice *dev, uint rowdst,
uint rowsrc, uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *dst;
void *src;
dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * vid_priv->line_length;
src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * vid_priv->line_length;
memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count);
return 0;
}
static int console_normal_putc_xy(struct udevice *dev, uint x_frac, uint y,
char ch)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
int i, row;
void *line = vid_priv->fb + y * vid_priv->line_length +
VID_TO_PIXEL(x_frac) * VNBYTES(vid_priv->bpix);
if (x_frac + VID_TO_POS(vc_priv->x_charsize) > vc_priv->xsize_frac)
return -EAGAIN;
for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
unsigned int idx = (u8)ch * VIDEO_FONT_HEIGHT + row;
uchar bits = video_fontdata[idx];
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
*dst++ = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
bits <<= 1;
}
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
*dst++ = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
bits <<= 1;
}
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
*dst++ = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
bits <<= 1;
}
break;
}
default:
return -ENOSYS;
}
line += vid_priv->line_length;
}
return VID_TO_POS(VIDEO_FONT_WIDTH);
}
static int console_normal_probe(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid_dev = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
vc_priv->x_charsize = VIDEO_FONT_WIDTH;
vc_priv->y_charsize = VIDEO_FONT_HEIGHT;
vc_priv->cols = vid_priv->xsize / VIDEO_FONT_WIDTH;
vc_priv->rows = vid_priv->ysize / VIDEO_FONT_HEIGHT;
return 0;
}
struct vidconsole_ops console_normal_ops = {
.putc_xy = console_normal_putc_xy,
.move_rows = console_normal_move_rows,
.set_row = console_normal_set_row,
};
U_BOOT_DRIVER(vidconsole_normal) = {
.name = "vidconsole0",
.id = UCLASS_VIDEO_CONSOLE,
.ops = &console_normal_ops,
.probe = console_normal_probe,
};
@@ -0,0 +1,460 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* (C) Copyright 2015
* Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
*/
#include <common.h>
#include <dm.h>
#include <video.h>
#include <video_console.h>
#include <video_font.h> /* Get font data, width and height */
static int console_set_row_1(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
int pbytes = VNBYTES(vid_priv->bpix);
void *line;
int i, j;
line = vid_priv->fb + vid_priv->line_length -
(row + 1) * VIDEO_FONT_HEIGHT * pbytes;
for (j = 0; j < vid_priv->ysize; j++) {
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
*dst++ = clr;
break;
}
default:
return -ENOSYS;
}
line += vid_priv->line_length;
}
return 0;
}
static int console_move_rows_1(struct udevice *dev, uint rowdst, uint rowsrc,
uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *dst;
void *src;
int pbytes = VNBYTES(vid_priv->bpix);
int j;
dst = vid_priv->fb + vid_priv->line_length -
(rowdst + count) * VIDEO_FONT_HEIGHT * pbytes;
src = vid_priv->fb + vid_priv->line_length -
(rowsrc + count) * VIDEO_FONT_HEIGHT * pbytes;
for (j = 0; j < vid_priv->ysize; j++) {
memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count);
src += vid_priv->line_length;
dst += vid_priv->line_length;
}
return 0;
}
static int console_putc_xy_1(struct udevice *dev, uint x_frac, uint y, char ch)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
int pbytes = VNBYTES(vid_priv->bpix);
int i, col;
int mask = 0x80;
void *line;
uchar *pfont = video_fontdata + (u8)ch * VIDEO_FONT_HEIGHT;
line = vid_priv->fb + (VID_TO_PIXEL(x_frac) + 1) *
vid_priv->line_length - (y + 1) * pbytes;
if (x_frac + VID_TO_POS(vc_priv->x_charsize) > vc_priv->xsize_frac)
return -EAGAIN;
for (col = 0; col < VIDEO_FONT_HEIGHT; col++) {
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
*dst-- = (pfont[i] & mask) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
}
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
*dst-- = (pfont[i] & mask) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
}
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
*dst-- = (pfont[i] & mask) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
}
break;
}
default:
return -ENOSYS;
}
line += vid_priv->line_length;
mask >>= 1;
}
return VID_TO_POS(VIDEO_FONT_WIDTH);
}
static int console_set_row_2(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *line;
int pixels = VIDEO_FONT_HEIGHT * vid_priv->xsize;
int i;
line = vid_priv->fb + vid_priv->ysize * vid_priv->line_length -
(row + 1) * VIDEO_FONT_HEIGHT * vid_priv->line_length;
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
default:
return -ENOSYS;
}
return 0;
}
static int console_move_rows_2(struct udevice *dev, uint rowdst, uint rowsrc,
uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *dst;
void *src;
void *end;
end = vid_priv->fb + vid_priv->ysize * vid_priv->line_length;
dst = end - (rowdst + count) * VIDEO_FONT_HEIGHT *
vid_priv->line_length;
src = end - (rowsrc + count) * VIDEO_FONT_HEIGHT *
vid_priv->line_length;
memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count);
return 0;
}
static int console_putc_xy_2(struct udevice *dev, uint x_frac, uint y, char ch)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
int i, row;
void *line;
if (x_frac + VID_TO_POS(vc_priv->x_charsize) > vc_priv->xsize_frac)
return -EAGAIN;
line = vid_priv->fb + (vid_priv->ysize - y - 1) *
vid_priv->line_length +
(vid_priv->xsize - VID_TO_PIXEL(x_frac) -
VIDEO_FONT_WIDTH - 1) * VNBYTES(vid_priv->bpix);
for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
unsigned int idx = (u8)ch * VIDEO_FONT_HEIGHT + row;
uchar bits = video_fontdata[idx];
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
*dst-- = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
bits <<= 1;
}
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
*dst-- = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
bits <<= 1;
}
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
*dst-- = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
bits <<= 1;
}
break;
}
default:
return -ENOSYS;
}
line -= vid_priv->line_length;
}
return VID_TO_POS(VIDEO_FONT_WIDTH);
}
static int console_set_row_3(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
int pbytes = VNBYTES(vid_priv->bpix);
void *line;
int i, j;
line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * pbytes;
for (j = 0; j < vid_priv->ysize; j++) {
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
*dst++ = clr;
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
*dst++ = clr;
break;
}
default:
return -ENOSYS;
}
line += vid_priv->line_length;
}
return 0;
}
static int console_move_rows_3(struct udevice *dev, uint rowdst, uint rowsrc,
uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *dst;
void *src;
int pbytes = VNBYTES(vid_priv->bpix);
int j;
dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * pbytes;
src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * pbytes;
for (j = 0; j < vid_priv->ysize; j++) {
memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count);
src += vid_priv->line_length;
dst += vid_priv->line_length;
}
return 0;
}
static int console_putc_xy_3(struct udevice *dev, uint x_frac, uint y, char ch)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
int pbytes = VNBYTES(vid_priv->bpix);
int i, col;
int mask = 0x80;
void *line = vid_priv->fb +
(vid_priv->ysize - VID_TO_PIXEL(x_frac) - 1) *
vid_priv->line_length + y * pbytes;
uchar *pfont = video_fontdata + (u8)ch * VIDEO_FONT_HEIGHT;
if (x_frac + VID_TO_POS(vc_priv->x_charsize) > vc_priv->xsize_frac)
return -EAGAIN;
for (col = 0; col < VIDEO_FONT_HEIGHT; col++) {
switch (vid_priv->bpix) {
case VIDEO_BPP8:
if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
uint8_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
*dst++ = (pfont[i] & mask) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
}
break;
}
case VIDEO_BPP16:
if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
uint16_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
*dst++ = (pfont[i] & mask) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
}
break;
}
case VIDEO_BPP32:
if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
uint32_t *dst = line;
for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
*dst++ = (pfont[i] & mask) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
}
break;
}
default:
return -ENOSYS;
}
line -= vid_priv->line_length;
mask >>= 1;
}
return VID_TO_POS(VIDEO_FONT_WIDTH);
}
static int console_probe_2(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid_dev = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
vc_priv->x_charsize = VIDEO_FONT_WIDTH;
vc_priv->y_charsize = VIDEO_FONT_HEIGHT;
vc_priv->cols = vid_priv->xsize / VIDEO_FONT_WIDTH;
vc_priv->rows = vid_priv->ysize / VIDEO_FONT_HEIGHT;
return 0;
}
static int console_probe_1_3(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid_dev = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
vc_priv->x_charsize = VIDEO_FONT_WIDTH;
vc_priv->y_charsize = VIDEO_FONT_HEIGHT;
vc_priv->cols = vid_priv->ysize / VIDEO_FONT_WIDTH;
vc_priv->rows = vid_priv->xsize / VIDEO_FONT_HEIGHT;
vc_priv->xsize_frac = VID_TO_POS(vid_priv->ysize);
return 0;
}
struct vidconsole_ops console_ops_1 = {
.putc_xy = console_putc_xy_1,
.move_rows = console_move_rows_1,
.set_row = console_set_row_1,
};
struct vidconsole_ops console_ops_2 = {
.putc_xy = console_putc_xy_2,
.move_rows = console_move_rows_2,
.set_row = console_set_row_2,
};
struct vidconsole_ops console_ops_3 = {
.putc_xy = console_putc_xy_3,
.move_rows = console_move_rows_3,
.set_row = console_set_row_3,
};
U_BOOT_DRIVER(vidconsole_1) = {
.name = "vidconsole1",
.id = UCLASS_VIDEO_CONSOLE,
.ops = &console_ops_1,
.probe = console_probe_1_3,
};
U_BOOT_DRIVER(vidconsole_2) = {
.name = "vidconsole2",
.id = UCLASS_VIDEO_CONSOLE,
.ops = &console_ops_2,
.probe = console_probe_2,
};
U_BOOT_DRIVER(vidconsole_3) = {
.name = "vidconsole3",
.id = UCLASS_VIDEO_CONSOLE,
.ops = &console_ops_3,
.probe = console_probe_1_3,
};
@@ -0,0 +1,550 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2016 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <video.h>
#include <video_console.h>
/* Functions needed by stb_truetype.h */
static int tt_floor(double val)
{
if (val < 0)
return (int)(val - 0.999);
return (int)val;
}
static int tt_ceil(double val)
{
if (val < 0)
return (int)val;
return (int)(val + 0.999);
}
static double frac(double val)
{
return val - tt_floor(val);
}
static double tt_fabs(double x)
{
return x < 0 ? -x : x;
}
/*
* Simple square root algorithm. This is from:
* http://stackoverflow.com/questions/1623375/writing-your-own-square-root-function
* Written by Chihung Yu
* Creative Commons license
* http://creativecommons.org/licenses/by-sa/3.0/legalcode
* It has been modified to compile correctly, and for U-Boot style.
*/
static double tt_sqrt(double value)
{
double lo = 1.0;
double hi = value;
while (hi - lo > 0.00001) {
double mid = lo + (hi - lo) / 2;
if (mid * mid - value > 0.00001)
hi = mid;
else
lo = mid;
}
return lo;
}
#define STBTT_ifloor tt_floor
#define STBTT_iceil tt_ceil
#define STBTT_fabs tt_fabs
#define STBTT_sqrt tt_sqrt
#define STBTT_malloc(size, u) ((void)(u), malloc(size))
#define STBTT_free(size, u) ((void)(u), free(size))
#define STBTT_assert(x)
#define STBTT_strlen(x) strlen(x)
#define STBTT_memcpy memcpy
#define STBTT_memset memset
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
/**
* struct pos_info - Records a cursor position
*
* @xpos_frac: Fractional X position in pixels (multiplied by VID_FRAC_DIV)
* @ypos: Y position (pixels from the top)
*/
struct pos_info {
int xpos_frac;
int ypos;
};
/*
* Allow one for each character on the command line plus one for each newline.
* This is just an estimate, but it should not be exceeded.
*/
#define POS_HISTORY_SIZE (CONFIG_SYS_CBSIZE * 11 / 10)
/**
* struct console_tt_priv - Private data for this driver
*
* @font_size: Vertical font size in pixels
* @font_data: Pointer to TrueType font file contents
* @font: TrueType font information for the current font
* @pos: List of cursor positions for each character written. This is
* used to handle backspace. We clear the frame buffer between
* the last position and the current position, thus erasing the
* last character. We record enough characters to go back to the
* start of the current command line.
* @pos_ptr: Current position in the position history
* @baseline: Pixel offset of the font's baseline from the cursor position.
* This is the 'ascent' of the font, scaled to pixel coordinates.
* It measures the distance from the baseline to the top of the
* font.
* @scale: Scale of the font. This is calculated from the pixel height
* of the font. It is used by the STB library to generate images
* of the correct size.
*/
struct console_tt_priv {
int font_size;
u8 *font_data;
stbtt_fontinfo font;
struct pos_info pos[POS_HISTORY_SIZE];
int pos_ptr;
int baseline;
double scale;
};
static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
struct console_tt_priv *priv = dev_get_priv(dev);
void *line;
int pixels = priv->font_size * vid_priv->line_length;
int i;
line = vid_priv->fb + row * priv->font_size * vid_priv->line_length;
switch (vid_priv->bpix) {
#ifdef CONFIG_VIDEO_BPP8
case VIDEO_BPP8: {
uint8_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
#endif
#ifdef CONFIG_VIDEO_BPP16
case VIDEO_BPP16: {
uint16_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
#endif
#ifdef CONFIG_VIDEO_BPP32
case VIDEO_BPP32: {
uint32_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
#endif
default:
return -ENOSYS;
}
return 0;
}
static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
uint rowsrc, uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
struct console_tt_priv *priv = dev_get_priv(dev);
void *dst;
void *src;
int i, diff;
dst = vid_priv->fb + rowdst * priv->font_size * vid_priv->line_length;
src = vid_priv->fb + rowsrc * priv->font_size * vid_priv->line_length;
memmove(dst, src, priv->font_size * vid_priv->line_length * count);
/* Scroll up our position history */
diff = (rowsrc - rowdst) * priv->font_size;
for (i = 0; i < priv->pos_ptr; i++)
priv->pos[i].ypos -= diff;
return 0;
}
static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
char ch)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
struct console_tt_priv *priv = dev_get_priv(dev);
stbtt_fontinfo *font = &priv->font;
int width, height, xoff, yoff;
double xpos, x_shift;
int lsb;
int width_frac, linenum;
struct pos_info *pos;
u8 *bits, *data;
int advance;
void *line;
int row;
/* First get some basic metrics about this character */
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
/*
* First out our current X position in fractional pixels. If we wrote
* a character previously, using kerning to fine-tune the position of
* this character */
xpos = frac(VID_TO_PIXEL((double)x));
if (vc_priv->last_ch) {
xpos += priv->scale * stbtt_GetCodepointKernAdvance(font,
vc_priv->last_ch, ch);
}
/*
* Figure out where the cursor will move to after this character, and
* abort if we are out of space on this line. Also calculate the
* effective width of this character, which will be our return value:
* it dictates how much the cursor will move forward on the line.
*/
x_shift = xpos - (double)tt_floor(xpos);
xpos += advance * priv->scale;
width_frac = (int)VID_TO_POS(xpos);
if (x + width_frac >= vc_priv->xsize_frac)
return -EAGAIN;
/* Write the current cursor position into history */
if (priv->pos_ptr < POS_HISTORY_SIZE) {
pos = &priv->pos[priv->pos_ptr];
pos->xpos_frac = vc_priv->xcur_frac;
pos->ypos = vc_priv->ycur;
priv->pos_ptr++;
}
/*
* Figure out how much past the start of a pixel we are, and pass this
* information into the render, which will return a 8-bit-per-pixel
* image of the character. For empty characters, like ' ', data will
* return NULL;
*/
data = stbtt_GetCodepointBitmapSubpixel(font, priv->scale, priv->scale,
x_shift, 0, ch, &width, &height,
&xoff, &yoff);
if (!data)
return width_frac;
/* Figure out where to write the character in the frame buffer */
bits = data;
line = vid_priv->fb + y * vid_priv->line_length +
VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix);
linenum = priv->baseline + yoff;
if (linenum > 0)
line += linenum * vid_priv->line_length;
/*
* Write a row at a time, converting the 8bpp image into the colour
* depth of the display. We only expect white-on-black or the reverse
* so the code only handles this simple case.
*/
for (row = 0; row < height; row++) {
switch (vid_priv->bpix) {
#ifdef CONFIG_VIDEO_BPP16
case VIDEO_BPP16: {
uint16_t *dst = (uint16_t *)line + xoff;
int i;
for (i = 0; i < width; i++) {
int val = *bits;
int out;
if (vid_priv->colour_bg)
val = 255 - val;
out = val >> 3 |
(val >> 2) << 5 |
(val >> 3) << 11;
if (vid_priv->colour_fg)
*dst++ |= out;
else
*dst++ &= out;
bits++;
}
break;
}
#endif
default:
free(data);
return -ENOSYS;
}
line += vid_priv->line_length;
}
free(data);
return width_frac;
}
/**
* console_truetype_erase() - Erase a character
*
* This is used for backspace. We erase a square of the display within the
* given bounds.
*
* @dev: Device to update
* @xstart: X start position in pixels from the left
* @ystart: Y start position in pixels from the top
* @xend: X end position in pixels from the left
* @yend: Y end position in pixels from the top
* @clr: Value to write
* @return 0 if OK, -ENOSYS if the display depth is not supported
*/
static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
int xend, int yend, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
void *line;
int pixels = xend - xstart;
int row, i;
line = vid_priv->fb + ystart * vid_priv->line_length;
line += xstart * VNBYTES(vid_priv->bpix);
for (row = ystart; row < yend; row++) {
switch (vid_priv->bpix) {
#ifdef CONFIG_VIDEO_BPP8
case VIDEO_BPP8: {
uint8_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
#endif
#ifdef CONFIG_VIDEO_BPP16
case VIDEO_BPP16: {
uint16_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
#endif
#ifdef CONFIG_VIDEO_BPP32
case VIDEO_BPP32: {
uint32_t *dst = line;
for (i = 0; i < pixels; i++)
*dst++ = clr;
break;
}
#endif
default:
return -ENOSYS;
}
line += vid_priv->line_length;
}
return 0;
}
/**
* console_truetype_backspace() - Handle a backspace operation
*
* This clears the previous character so that the console looks as if it had
* not been entered.
*
* @dev: Device to update
* @return 0 if OK, -ENOSYS if not supported
*/
static int console_truetype_backspace(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_tt_priv *priv = dev_get_priv(dev);
struct udevice *vid_dev = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
struct pos_info *pos;
int xend;
/*
* This indicates a very strange error higher in the stack. The caller
* has sent out n character and n + 1 backspaces.
*/
if (!priv->pos_ptr)
return -ENOSYS;
/* Pop the last cursor position off the stack */
pos = &priv->pos[--priv->pos_ptr];
/*
* Figure out the end position for clearing. Normlly it is the current
* cursor position, but if we are clearing a character on the previous
* line, we clear from the end of the line.
*/
if (pos->ypos == vc_priv->ycur)
xend = VID_TO_PIXEL(vc_priv->xcur_frac);
else
xend = vid_priv->xsize;
console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
xend, pos->ypos + vc_priv->y_charsize,
vid_priv->colour_bg);
/* Move the cursor back to where it was when we pushed this record */
vc_priv->xcur_frac = pos->xpos_frac;
vc_priv->ycur = pos->ypos;
return 0;
}
static int console_truetype_entry_start(struct udevice *dev)
{
struct console_tt_priv *priv = dev_get_priv(dev);
/* A new input line has start, so clear our history */
priv->pos_ptr = 0;
return 0;
}
/*
* Provides a list of fonts which can be obtained at run-time in U-Boot. These
* are compiled in by the Makefile.
*
* At present there is no mechanism to select a particular font - the first
* one found is the one that is used. But the build system and the code here
* supports multiple fonts, which may be useful for certain firmware screens.
*/
struct font_info {
char *name;
u8 *begin;
u8 *end;
};
#define FONT_DECL(_name) \
extern u8 __ttf_ ## _name ## _begin[]; \
extern u8 __ttf_ ## _name ## _end[];
#define FONT_ENTRY(_name) { \
.name = #_name, \
.begin = __ttf_ ## _name ## _begin, \
.end = __ttf_ ## _name ## _end, \
}
FONT_DECL(nimbus_sans_l_regular);
FONT_DECL(ankacoder_c75_r);
FONT_DECL(rufscript010);
FONT_DECL(cantoraone_regular);
static struct font_info font_table[] = {
#ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
FONT_ENTRY(nimbus_sans_l_regular),
#endif
#ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
FONT_ENTRY(ankacoder_c75_r),
#endif
#ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
FONT_ENTRY(rufscript010),
#endif
#ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
FONT_ENTRY(cantoraone_regular),
#endif
{} /* sentinel */
};
#define FONT_BEGIN(name) __ttf_ ## name ## _begin
#define FONT_END(name) __ttf_ ## name ## _end
#define FONT_IS_VALID(name) (abs(FONT_END(name) - FONT_BEGIN) > 4)
/**
* console_truetype_find_font() - Find a suitable font
*
* This searched for the first available font.
*
* @return pointer to the font, or NULL if none is found
*/
static u8 *console_truetype_find_font(void)
{
struct font_info *tab;
for (tab = font_table; tab->begin; tab++) {
if (abs(tab->begin - tab->end) > 4) {
debug("%s: Font '%s', at %p, size %lx\n", __func__,
tab->name, tab->begin,
(ulong)(tab->end - tab->begin));
return tab->begin;
}
}
return NULL;
}
static int console_truetype_probe(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_tt_priv *priv = dev_get_priv(dev);
struct udevice *vid_dev = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
stbtt_fontinfo *font = &priv->font;
int ascent;
debug("%s: start\n", __func__);
if (vid_priv->font_size)
priv->font_size = vid_priv->font_size;
else
priv->font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
priv->font_data = console_truetype_find_font();
if (!priv->font_data) {
debug("%s: Could not find any fonts\n", __func__);
return -EBFONT;
}
vc_priv->x_charsize = priv->font_size;
vc_priv->y_charsize = priv->font_size;
vc_priv->xstart_frac = VID_TO_POS(2);
vc_priv->cols = vid_priv->xsize / priv->font_size;
vc_priv->rows = vid_priv->ysize / priv->font_size;
vc_priv->tab_width_frac = VID_TO_POS(priv->font_size) * 8 / 2;
if (!stbtt_InitFont(font, priv->font_data, 0)) {
debug("%s: Font init failed\n", __func__);
return -EPERM;
}
/* Pre-calculate some things we will need regularly */
priv->scale = stbtt_ScaleForPixelHeight(font, priv->font_size);
stbtt_GetFontVMetrics(font, &ascent, 0, 0);
priv->baseline = (int)(ascent * priv->scale);
debug("%s: ready\n", __func__);
return 0;
}
struct vidconsole_ops console_truetype_ops = {
.putc_xy = console_truetype_putc_xy,
.move_rows = console_truetype_move_rows,
.set_row = console_truetype_set_row,
.backspace = console_truetype_backspace,
.entry_start = console_truetype_entry_start,
};
U_BOOT_DRIVER(vidconsole_truetype) = {
.name = "vidconsole_tt",
.id = UCLASS_VIDEO_CONSOLE,
.ops = &console_truetype_ops,
.probe = console_truetype_probe,
.priv_auto_alloc_size = sizeof(struct console_tt_priv),
};
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <dm.h>
#include <vbe.h>
#include <video.h>
#include <asm/arch/sysinfo.h>
static int save_vesa_mode(struct cb_framebuffer *fb,
struct vesa_mode_info *vesa)
{
/*
* If there is no framebuffer structure, bail out and keep
* running on the serial console.
*/
if (!fb)
return -ENXIO;
vesa->x_resolution = fb->x_resolution;
vesa->y_resolution = fb->y_resolution;
vesa->bits_per_pixel = fb->bits_per_pixel;
vesa->bytes_per_scanline = fb->bytes_per_line;
vesa->phys_base_ptr = fb->physical_address;
vesa->red_mask_size = fb->red_mask_size;
vesa->red_mask_pos = fb->red_mask_pos;
vesa->green_mask_size = fb->green_mask_size;
vesa->green_mask_pos = fb->green_mask_pos;
vesa->blue_mask_size = fb->blue_mask_size;
vesa->blue_mask_pos = fb->blue_mask_pos;
vesa->reserved_mask_size = fb->reserved_mask_size;
vesa->reserved_mask_pos = fb->reserved_mask_pos;
return 0;
}
static int coreboot_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct cb_framebuffer *fb = lib_sysinfo.framebuffer;
struct vesa_mode_info *vesa = &mode_info.vesa;
int ret;
printf("Video: ");
/* Initialize vesa_mode_info structure */
ret = save_vesa_mode(fb, vesa);
if (ret)
goto err;
ret = vbe_setup_video_priv(vesa, uc_priv, plat);
if (ret)
goto err;
printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
vesa->bits_per_pixel);
return 0;
err:
printf("No video mode configured in coreboot!\n");
return ret;
}
static const struct udevice_id coreboot_video_ids[] = {
{ .compatible = "coreboot-fb" },
{ }
};
U_BOOT_DRIVER(coreboot_video) = {
.name = "coreboot_video",
.id = UCLASS_VIDEO,
.of_match = coreboot_video_ids,
.probe = coreboot_video_probe,
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,115 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Porting to u-boot:
*
* (C) Copyright 2011
* Stefano Babic, DENX Software Engineering, sbabic@denx.de.
*
* Copyright (C) 2008-2009 MontaVista Software Inc.
* Copyright (C) 2008-2009 Texas Instruments Inc
*
* Based on the LCD driver for TI Avalanche processors written by
* Ajay Singh and Shalom Hai.
*/
#ifndef DA8XX_FB_H
#define DA8XX_FB_H
enum panel_type {
QVGA = 0,
WVGA
};
enum panel_shade {
MONOCHROME = 0,
COLOR_ACTIVE,
COLOR_PASSIVE,
};
enum raster_load_mode {
LOAD_DATA = 1,
LOAD_PALETTE,
};
struct display_panel {
enum panel_type panel_type; /* QVGA */
int max_bpp;
int min_bpp;
enum panel_shade panel_shade;
};
struct da8xx_panel {
const char name[25]; /* Full name <vendor>_<model> */
unsigned short width;
unsigned short height;
int hfp; /* Horizontal front porch */
int hbp; /* Horizontal back porch */
int hsw; /* Horizontal Sync Pulse Width */
int vfp; /* Vertical front porch */
int vbp; /* Vertical back porch */
int vsw; /* Vertical Sync Pulse Width */
unsigned int pxl_clk; /* Pixel clock */
unsigned char invert_pxl_clk; /* Invert Pixel clock */
};
struct da8xx_lcdc_platform_data {
const char manu_name[10];
void *controller_data;
const char type[25];
void (*panel_power_ctrl)(int);
};
struct lcd_ctrl_config {
const struct display_panel *p_disp_panel;
/* AC Bias Pin Frequency */
int ac_bias;
/* AC Bias Pin Transitions per Interrupt */
int ac_bias_intrpt;
/* DMA burst size */
int dma_burst_sz;
/* Bits per pixel */
int bpp;
/* FIFO DMA Request Delay */
int fdd;
/* TFT Alternative Signal Mapping (Only for active) */
unsigned char tft_alt_mode;
/* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */
unsigned char stn_565_mode;
/* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */
unsigned char mono_8bit_mode;
/* Invert line clock */
unsigned char invert_line_clock;
/* Invert frame clock */
unsigned char invert_frm_clock;
/* Horizontal and Vertical Sync Edge: 0=rising 1=falling */
unsigned char sync_edge;
/* Horizontal and Vertical Sync: Control: 0=ignore */
unsigned char sync_ctrl;
/* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */
unsigned char raster_order;
};
struct lcd_sync_arg {
int back_porch;
int front_porch;
int pulse_width;
};
void da8xx_video_init(const struct da8xx_panel *panel,
const struct lcd_ctrl_config *lcd_cfg,
int bits_pixel);
#endif /* ifndef DA8XX_FB_H */
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2014 Google Inc.
*/
#include <common.h>
#include <dm.h>
#include <display.h>
#include <edid.h>
#include <errno.h>
int display_read_edid(struct udevice *dev, u8 *buf, int buf_size)
{
struct dm_display_ops *ops = display_get_ops(dev);
if (!ops || !ops->read_edid)
return -ENOSYS;
return ops->read_edid(dev, buf, buf_size);
}
int display_enable(struct udevice *dev, int panel_bpp,
const struct display_timing *timing)
{
struct dm_display_ops *ops = display_get_ops(dev);
struct display_plat *disp_uc_plat;
int ret;
if (!ops || !ops->enable)
return -ENOSYS;
ret = ops->enable(dev, panel_bpp, timing);
if (ret)
return ret;
disp_uc_plat = dev_get_uclass_platdata(dev);
disp_uc_plat->in_use = true;
return 0;
}
static bool display_mode_valid(void *priv, const struct display_timing *timing)
{
struct udevice *dev = priv;
struct dm_display_ops *ops = display_get_ops(dev);
if (ops && ops->mode_valid)
return ops->mode_valid(dev, timing);
return true;
}
int display_read_timing(struct udevice *dev, struct display_timing *timing)
{
struct dm_display_ops *ops = display_get_ops(dev);
int panel_bits_per_colour;
u8 buf[EDID_EXT_SIZE];
int ret;
if (ops && ops->read_timing)
return ops->read_timing(dev, timing);
if (!ops || !ops->read_edid)
return -ENOSYS;
ret = ops->read_edid(dev, buf, sizeof(buf));
if (ret < 0)
return ret;
return edid_get_timing_validate(buf, ret, timing,
&panel_bits_per_colour,
display_mode_valid, dev);
}
bool display_in_use(struct udevice *dev)
{
struct display_plat *disp_uc_plat = dev_get_uclass_platdata(dev);
return disp_uc_plat->in_use;
}
UCLASS_DRIVER(display) = {
.id = UCLASS_DISPLAY,
.name = "display",
.per_device_platdata_auto_alloc_size = sizeof(struct display_plat),
};
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 STMicroelectronics - All Rights Reserved
* Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
*
*/
#include <common.h>
#include <dm.h>
#include <dsi_host.h>
int dsi_host_init(struct udevice *dev,
struct mipi_dsi_device *device,
struct display_timing *timings,
unsigned int max_data_lanes,
const struct mipi_dsi_phy_ops *phy_ops)
{
struct dsi_host_ops *ops = dsi_host_get_ops(dev);
if (!ops->init)
return -ENOSYS;
return ops->init(dev, device, timings, max_data_lanes, phy_ops);
}
int dsi_host_enable(struct udevice *dev)
{
struct dsi_host_ops *ops = dsi_host_get_ops(dev);
if (!ops->enable)
return -ENOSYS;
return ops->enable(dev);
}
UCLASS_DRIVER(dsi_host) = {
.id = UCLASS_DSI_HOST,
.name = "dsi_host",
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,832 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd
* Copyright (C) 2019, STMicroelectronics - All Rights Reserved
* Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
* Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
*
* This generic Synopsys DesignWare MIPI DSI host driver is inspired from
* the Linux Kernel driver drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c.
*/
#include <common.h>
#include <clk.h>
#include <dsi_host.h>
#include <dm.h>
#include <errno.h>
#include <panel.h>
#include <video.h>
#include <asm/io.h>
#include <asm/arch/gpio.h>
#include <dm/device-internal.h>
#include <linux/iopoll.h>
#include <video_bridge.h>
#define HWVER_131 0x31333100 /* IP version 1.31 */
#define DSI_VERSION 0x00
#define VERSION GENMASK(31, 8)
#define DSI_PWR_UP 0x04
#define RESET 0
#define POWERUP BIT(0)
#define DSI_CLKMGR_CFG 0x08
#define TO_CLK_DIVISION(div) (((div) & 0xff) << 8)
#define TX_ESC_CLK_DIVISION(div) ((div) & 0xff)
#define DSI_DPI_VCID 0x0c
#define DPI_VCID(vcid) ((vcid) & 0x3)
#define DSI_DPI_COLOR_CODING 0x10
#define LOOSELY18_EN BIT(8)
#define DPI_COLOR_CODING_16BIT_1 0x0
#define DPI_COLOR_CODING_16BIT_2 0x1
#define DPI_COLOR_CODING_16BIT_3 0x2
#define DPI_COLOR_CODING_18BIT_1 0x3
#define DPI_COLOR_CODING_18BIT_2 0x4
#define DPI_COLOR_CODING_24BIT 0x5
#define DSI_DPI_CFG_POL 0x14
#define COLORM_ACTIVE_LOW BIT(4)
#define SHUTD_ACTIVE_LOW BIT(3)
#define HSYNC_ACTIVE_LOW BIT(2)
#define VSYNC_ACTIVE_LOW BIT(1)
#define DATAEN_ACTIVE_LOW BIT(0)
#define DSI_DPI_LP_CMD_TIM 0x18
#define OUTVACT_LPCMD_TIME(p) (((p) & 0xff) << 16)
#define INVACT_LPCMD_TIME(p) ((p) & 0xff)
#define DSI_DBI_VCID 0x1c
#define DSI_DBI_CFG 0x20
#define DSI_DBI_PARTITIONING_EN 0x24
#define DSI_DBI_CMDSIZE 0x28
#define DSI_PCKHDL_CFG 0x2c
#define CRC_RX_EN BIT(4)
#define ECC_RX_EN BIT(3)
#define BTA_EN BIT(2)
#define EOTP_RX_EN BIT(1)
#define EOTP_TX_EN BIT(0)
#define DSI_GEN_VCID 0x30
#define DSI_MODE_CFG 0x34
#define ENABLE_VIDEO_MODE 0
#define ENABLE_CMD_MODE BIT(0)
#define DSI_VID_MODE_CFG 0x38
#define ENABLE_LOW_POWER (0x3f << 8)
#define ENABLE_LOW_POWER_MASK (0x3f << 8)
#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES 0x0
#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS 0x1
#define VID_MODE_TYPE_BURST 0x2
#define VID_MODE_TYPE_MASK 0x3
#define DSI_VID_PKT_SIZE 0x3c
#define VID_PKT_SIZE(p) ((p) & 0x3fff)
#define DSI_VID_NUM_CHUNKS 0x40
#define VID_NUM_CHUNKS(c) ((c) & 0x1fff)
#define DSI_VID_NULL_SIZE 0x44
#define VID_NULL_SIZE(b) ((b) & 0x1fff)
#define DSI_VID_HSA_TIME 0x48
#define DSI_VID_HBP_TIME 0x4c
#define DSI_VID_HLINE_TIME 0x50
#define DSI_VID_VSA_LINES 0x54
#define DSI_VID_VBP_LINES 0x58
#define DSI_VID_VFP_LINES 0x5c
#define DSI_VID_VACTIVE_LINES 0x60
#define DSI_EDPI_CMD_SIZE 0x64
#define DSI_CMD_MODE_CFG 0x68
#define MAX_RD_PKT_SIZE_LP BIT(24)
#define DCS_LW_TX_LP BIT(19)
#define DCS_SR_0P_TX_LP BIT(18)
#define DCS_SW_1P_TX_LP BIT(17)
#define DCS_SW_0P_TX_LP BIT(16)
#define GEN_LW_TX_LP BIT(14)
#define GEN_SR_2P_TX_LP BIT(13)
#define GEN_SR_1P_TX_LP BIT(12)
#define GEN_SR_0P_TX_LP BIT(11)
#define GEN_SW_2P_TX_LP BIT(10)
#define GEN_SW_1P_TX_LP BIT(9)
#define GEN_SW_0P_TX_LP BIT(8)
#define ACK_RQST_EN BIT(1)
#define TEAR_FX_EN BIT(0)
#define CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \
DCS_LW_TX_LP | \
DCS_SR_0P_TX_LP | \
DCS_SW_1P_TX_LP | \
DCS_SW_0P_TX_LP | \
GEN_LW_TX_LP | \
GEN_SR_2P_TX_LP | \
GEN_SR_1P_TX_LP | \
GEN_SR_0P_TX_LP | \
GEN_SW_2P_TX_LP | \
GEN_SW_1P_TX_LP | \
GEN_SW_0P_TX_LP)
#define DSI_GEN_HDR 0x6c
#define DSI_GEN_PLD_DATA 0x70
#define DSI_CMD_PKT_STATUS 0x74
#define GEN_RD_CMD_BUSY BIT(6)
#define GEN_PLD_R_FULL BIT(5)
#define GEN_PLD_R_EMPTY BIT(4)
#define GEN_PLD_W_FULL BIT(3)
#define GEN_PLD_W_EMPTY BIT(2)
#define GEN_CMD_FULL BIT(1)
#define GEN_CMD_EMPTY BIT(0)
#define DSI_TO_CNT_CFG 0x78
#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16)
#define LPRX_TO_CNT(p) ((p) & 0xffff)
#define DSI_HS_RD_TO_CNT 0x7c
#define DSI_LP_RD_TO_CNT 0x80
#define DSI_HS_WR_TO_CNT 0x84
#define DSI_LP_WR_TO_CNT 0x88
#define DSI_BTA_TO_CNT 0x8c
#define DSI_LPCLK_CTRL 0x94
#define AUTO_CLKLANE_CTRL BIT(1)
#define PHY_TXREQUESTCLKHS BIT(0)
#define DSI_PHY_TMR_LPCLK_CFG 0x98
#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
#define DSI_PHY_TMR_CFG 0x9c
#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24)
#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16)
#define MAX_RD_TIME(lbcc) ((lbcc) & 0x7fff)
#define PHY_HS2LP_TIME_V131(lbcc) (((lbcc) & 0x3ff) << 16)
#define PHY_LP2HS_TIME_V131(lbcc) ((lbcc) & 0x3ff)
#define DSI_PHY_RSTZ 0xa0
#define PHY_DISFORCEPLL 0
#define PHY_ENFORCEPLL BIT(3)
#define PHY_DISABLECLK 0
#define PHY_ENABLECLK BIT(2)
#define PHY_RSTZ 0
#define PHY_UNRSTZ BIT(1)
#define PHY_SHUTDOWNZ 0
#define PHY_UNSHUTDOWNZ BIT(0)
#define DSI_PHY_IF_CFG 0xa4
#define PHY_STOP_WAIT_TIME(cycle) (((cycle) & 0xff) << 8)
#define N_LANES(n) (((n) - 1) & 0x3)
#define DSI_PHY_ULPS_CTRL 0xa8
#define DSI_PHY_TX_TRIGGERS 0xac
#define DSI_PHY_STATUS 0xb0
#define PHY_STOP_STATE_CLK_LANE BIT(2)
#define PHY_LOCK BIT(0)
#define DSI_PHY_TST_CTRL0 0xb4
#define PHY_TESTCLK BIT(1)
#define PHY_UNTESTCLK 0
#define PHY_TESTCLR BIT(0)
#define PHY_UNTESTCLR 0
#define DSI_PHY_TST_CTRL1 0xb8
#define PHY_TESTEN BIT(16)
#define PHY_UNTESTEN 0
#define PHY_TESTDOUT(n) (((n) & 0xff) << 8)
#define PHY_TESTDIN(n) ((n) & 0xff)
#define DSI_INT_ST0 0xbc
#define DSI_INT_ST1 0xc0
#define DSI_INT_MSK0 0xc4
#define DSI_INT_MSK1 0xc8
#define DSI_PHY_TMR_RD_CFG 0xf4
#define MAX_RD_TIME_V131(lbcc) ((lbcc) & 0x7fff)
#define PHY_STATUS_TIMEOUT_US 10000
#define CMD_PKT_STATUS_TIMEOUT_US 20000
#define MSEC_PER_SEC 1000
struct dw_mipi_dsi {
struct mipi_dsi_host dsi_host;
struct mipi_dsi_device *device;
void __iomem *base;
unsigned int lane_mbps; /* per lane */
u32 channel;
unsigned int max_data_lanes;
const struct mipi_dsi_phy_ops *phy_ops;
};
static int dsi_mode_vrefresh(struct display_timing *timings)
{
int refresh = 0;
unsigned int calc_val;
u32 htotal = timings->hactive.typ + timings->hfront_porch.typ +
timings->hback_porch.typ + timings->hsync_len.typ;
u32 vtotal = timings->vactive.typ + timings->vfront_porch.typ +
timings->vback_porch.typ + timings->vsync_len.typ;
if (htotal > 0 && vtotal > 0) {
calc_val = timings->pixelclock.typ;
calc_val /= htotal;
refresh = (calc_val + vtotal / 2) / vtotal;
}
return refresh;
}
/*
* The controller should generate 2 frames before
* preparing the peripheral.
*/
static void dw_mipi_dsi_wait_for_two_frames(struct display_timing *timings)
{
int refresh, two_frames;
refresh = dsi_mode_vrefresh(timings);
two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
mdelay(two_frames);
}
static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
{
return container_of(host, struct dw_mipi_dsi, dsi_host);
}
static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
{
writel(val, dsi->base + reg);
}
static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
{
return readl(dsi->base + reg);
}
static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
if (device->lanes > dsi->max_data_lanes) {
dev_err(device->dev,
"the number of data lanes(%u) is too many\n",
device->lanes);
return -EINVAL;
}
dsi->channel = device->channel;
return 0;
}
static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
const struct mipi_dsi_msg *msg)
{
bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
u32 val = 0;
if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
val |= ACK_RQST_EN;
if (lpm)
val |= CMD_MODE_ALL_LP;
dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS);
dsi_write(dsi, DSI_CMD_MODE_CFG, val);
}
static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
{
int ret;
u32 val, mask;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_CMD_FULL),
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(dsi->dev, "failed to get available command FIFO\n");
return ret;
}
dsi_write(dsi, DSI_GEN_HDR, hdr_val);
mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, (val & mask) == mask,
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(dsi->dev, "failed to write command FIFO\n");
return ret;
}
return 0;
}
static int dw_mipi_dsi_write(struct dw_mipi_dsi *dsi,
const struct mipi_dsi_packet *packet)
{
const u8 *tx_buf = packet->payload;
int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
__le32 word;
u32 val;
while (len) {
if (len < pld_data_bytes) {
word = 0;
memcpy(&word, tx_buf, len);
dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
len = 0;
} else {
memcpy(&word, tx_buf, pld_data_bytes);
dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
tx_buf += pld_data_bytes;
len -= pld_data_bytes;
}
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_W_FULL),
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(dsi->dev,
"failed to get available write payload FIFO\n");
return ret;
}
}
word = 0;
memcpy(&word, packet->header, sizeof(packet->header));
return dw_mipi_dsi_gen_pkt_hdr_write(dsi, le32_to_cpu(word));
}
static int dw_mipi_dsi_read(struct dw_mipi_dsi *dsi,
const struct mipi_dsi_msg *msg)
{
int i, j, ret, len = msg->rx_len;
u8 *buf = msg->rx_buf;
u32 val;
/* Wait end of the read operation */
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_RD_CMD_BUSY),
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(dsi->dev, "Timeout during read operation\n");
return ret;
}
for (i = 0; i < len; i += 4) {
/* Read fifo must not be empty before all bytes are read */
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_R_EMPTY),
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(dsi->dev, "Read payload FIFO is empty\n");
return ret;
}
val = dsi_read(dsi, DSI_GEN_PLD_DATA);
for (j = 0; j < 4 && j + i < len; j++)
buf[i + j] = val >> (8 * j);
}
return ret;
}
static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
struct mipi_dsi_packet packet;
int ret, nb_bytes;
ret = mipi_dsi_create_packet(&packet, msg);
if (ret) {
dev_err(dsi->dev, "failed to create packet: %d\n", ret);
return ret;
}
dw_mipi_message_config(dsi, msg);
ret = dw_mipi_dsi_write(dsi, &packet);
if (ret)
return ret;
if (msg->rx_buf && msg->rx_len) {
ret = dw_mipi_dsi_read(dsi, msg);
if (ret)
return ret;
nb_bytes = msg->rx_len;
} else {
nb_bytes = packet.size;
}
return nb_bytes;
}
static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
.attach = dw_mipi_dsi_host_attach,
.transfer = dw_mipi_dsi_host_transfer,
};
static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
{
struct mipi_dsi_device *device = dsi->device;
u32 val;
/*
* TODO dw drv improvements
* enabling low power is panel-dependent, we should use the
* panel configuration here...
*/
val = ENABLE_LOW_POWER;
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
val |= VID_MODE_TYPE_BURST;
else if (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
else
val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
dsi_write(dsi, DSI_VID_MODE_CFG, val);
}
static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
unsigned long mode_flags)
{
const struct mipi_dsi_phy_ops *phy_ops = dsi->phy_ops;
dsi_write(dsi, DSI_PWR_UP, RESET);
if (mode_flags & MIPI_DSI_MODE_VIDEO) {
dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
dw_mipi_dsi_video_mode_config(dsi);
dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
} else {
dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
}
if (phy_ops->post_set_mode)
phy_ops->post_set_mode(dsi->device, mode_flags);
dsi_write(dsi, DSI_PWR_UP, POWERUP);
}
static void dw_mipi_dsi_init_pll(struct dw_mipi_dsi *dsi)
{
/*
* The maximum permitted escape clock is 20MHz and it is derived from
* lanebyteclk, which is running at "lane_mbps / 8". Thus we want:
*
* (lane_mbps >> 3) / esc_clk_division < 20
* which is:
* (lane_mbps >> 3) / 20 > esc_clk_division
*/
u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1;
dsi_write(dsi, DSI_PWR_UP, RESET);
/*
* TODO dw drv improvements
* timeout clock division should be computed with the
* high speed transmission counter timeout and byte lane...
*/
dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
TX_ESC_CLK_DIVISION(esc_clk_division));
}
static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
struct display_timing *timings)
{
struct mipi_dsi_device *device = dsi->device;
u32 val = 0, color = 0;
switch (device->format) {
case MIPI_DSI_FMT_RGB888:
color = DPI_COLOR_CODING_24BIT;
break;
case MIPI_DSI_FMT_RGB666:
color = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
color = DPI_COLOR_CODING_18BIT_1;
break;
case MIPI_DSI_FMT_RGB565:
color = DPI_COLOR_CODING_16BIT_1;
break;
}
if (device->mode_flags & DISPLAY_FLAGS_VSYNC_HIGH)
val |= VSYNC_ACTIVE_LOW;
if (device->mode_flags & DISPLAY_FLAGS_HSYNC_HIGH)
val |= HSYNC_ACTIVE_LOW;
dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
dsi_write(dsi, DSI_DPI_CFG_POL, val);
/*
* TODO dw drv improvements
* largest packet sizes during hfp or during vsa/vpb/vfp
* should be computed according to byte lane, lane number and only
* if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS)
*/
dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
| INVACT_LPCMD_TIME(4));
}
static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
{
dsi_write(dsi, DSI_PCKHDL_CFG, CRC_RX_EN | ECC_RX_EN | BTA_EN);
}
static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
struct display_timing *timings)
{
/*
* TODO dw drv improvements
* only burst mode is supported here. For non-burst video modes,
* we should compute DSI_VID_PKT_SIZE, DSI_VCCR.NUMC &
* DSI_VNPCR.NPSIZE... especially because this driver supports
* non-burst video modes, see dw_mipi_dsi_video_mode_config()...
*/
dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(timings->hactive.typ));
}
static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
{
const struct mipi_dsi_phy_ops *phy_ops = dsi->phy_ops;
/*
* TODO dw drv improvements
* compute high speed transmission counter timeout according
* to the timeout clock division (TO_CLK_DIVISION) and byte lane...
*/
dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
/*
* TODO dw drv improvements
* the Bus-Turn-Around Timeout Counter should be computed
* according to byte lane...
*/
dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
if (phy_ops->post_set_mode)
phy_ops->post_set_mode(dsi->device, 0);
}
/* Get lane byte clock cycles. */
static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
struct display_timing *timings,
u32 hcomponent)
{
u32 frac, lbcc;
lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
frac = lbcc % (timings->pixelclock.typ / 1000);
lbcc = lbcc / (timings->pixelclock.typ / 1000);
if (frac)
lbcc++;
return lbcc;
}
static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi,
struct display_timing *timings)
{
u32 htotal, hsa, hbp, lbcc;
htotal = timings->hactive.typ + timings->hfront_porch.typ +
timings->hback_porch.typ + timings->hsync_len.typ;
hsa = timings->hback_porch.typ;
hbp = timings->hsync_len.typ;
/*
* TODO dw drv improvements
* computations below may be improved...
*/
lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, timings, htotal);
dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, timings, hsa);
dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, timings, hbp);
dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
}
static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi,
struct display_timing *timings)
{
u32 vactive, vsa, vfp, vbp;
vactive = timings->vactive.typ;
vsa = timings->vback_porch.typ;
vfp = timings->vfront_porch.typ;
vbp = timings->vsync_len.typ;
dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
}
static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
{
u32 hw_version;
/*
* TODO dw drv improvements
* data & clock lane timers should be computed according to panel
* blankings and to the automatic clock lane control mode...
* note: DSI_PHY_TMR_CFG.MAX_RD_TIME should be in line with
* DSI_CMD_MODE_CFG.MAX_RD_PKT_SIZE_LP (see CMD_MODE_ALL_LP)
*/
hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
if (hw_version >= HWVER_131) {
dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME_V131(0x40) |
PHY_LP2HS_TIME_V131(0x40));
dsi_write(dsi, DSI_PHY_TMR_RD_CFG, MAX_RD_TIME_V131(10000));
} else {
dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) |
PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
}
dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
| PHY_CLKLP2HS_TIME(0x40));
}
static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
{
struct mipi_dsi_device *device = dsi->device;
/*
* TODO dw drv improvements
* stop wait time should be the maximum between host dsi
* and panel stop wait times
*/
dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
N_LANES(device->lanes));
}
static void dw_mipi_dsi_dphy_init(struct dw_mipi_dsi *dsi)
{
/* Clear PHY state */
dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
| PHY_RSTZ | PHY_SHUTDOWNZ);
dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLR);
dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
}
static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
{
u32 val;
int ret;
dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
val & PHY_LOCK, PHY_STATUS_TIMEOUT_US);
if (ret)
dev_warn(dsi->dev, "failed to wait phy lock state\n");
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
val, val & PHY_STOP_STATE_CLK_LANE,
PHY_STATUS_TIMEOUT_US);
if (ret)
dev_warn(dsi->dev, "failed to wait phy clk lane stop state\n");
}
static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
{
dsi_read(dsi, DSI_INT_ST0);
dsi_read(dsi, DSI_INT_ST1);
dsi_write(dsi, DSI_INT_MSK0, 0);
dsi_write(dsi, DSI_INT_MSK1, 0);
}
static void dw_mipi_dsi_bridge_set(struct dw_mipi_dsi *dsi,
struct display_timing *timings)
{
const struct mipi_dsi_phy_ops *phy_ops = dsi->phy_ops;
struct mipi_dsi_device *device = dsi->device;
int ret;
ret = phy_ops->get_lane_mbps(dsi->device, timings, device->lanes,
device->format, &dsi->lane_mbps);
if (ret)
dev_warn(dsi->dev, "Phy get_lane_mbps() failed\n");
dw_mipi_dsi_init_pll(dsi);
dw_mipi_dsi_dpi_config(dsi, timings);
dw_mipi_dsi_packet_handler_config(dsi);
dw_mipi_dsi_video_mode_config(dsi);
dw_mipi_dsi_video_packet_config(dsi, timings);
dw_mipi_dsi_command_mode_config(dsi);
dw_mipi_dsi_line_timer_config(dsi, timings);
dw_mipi_dsi_vertical_timing_config(dsi, timings);
dw_mipi_dsi_dphy_init(dsi);
dw_mipi_dsi_dphy_timing_config(dsi);
dw_mipi_dsi_dphy_interface_config(dsi);
dw_mipi_dsi_clear_err(dsi);
ret = phy_ops->init(dsi->device);
if (ret)
dev_warn(dsi->dev, "Phy init() failed\n");
dw_mipi_dsi_dphy_enable(dsi);
dw_mipi_dsi_wait_for_two_frames(timings);
/* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
dw_mipi_dsi_set_mode(dsi, 0);
}
static int dw_mipi_dsi_init(struct udevice *dev,
struct mipi_dsi_device *device,
struct display_timing *timings,
unsigned int max_data_lanes,
const struct mipi_dsi_phy_ops *phy_ops)
{
struct dw_mipi_dsi *dsi = dev_get_priv(dev);
struct clk clk;
int ret;
if (!phy_ops->init || !phy_ops->get_lane_mbps) {
dev_err(device->dev, "Phy not properly configured\n");
return -ENODEV;
}
dsi->phy_ops = phy_ops;
dsi->max_data_lanes = max_data_lanes;
dsi->device = device;
dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
device->host = &dsi->dsi_host;
dsi->base = (void *)dev_read_addr(device->dev);
if ((fdt_addr_t)dsi->base == FDT_ADDR_T_NONE) {
dev_err(device->dev, "dsi dt register address error\n");
return -EINVAL;
}
ret = clk_get_by_name(device->dev, "px_clk", &clk);
if (ret) {
dev_err(device->dev, "peripheral clock get error %d\n", ret);
return ret;
}
/* get the pixel clock set by the clock framework */
timings->pixelclock.typ = clk_get_rate(&clk);
dw_mipi_dsi_bridge_set(dsi, timings);
return 0;
}
static int dw_mipi_dsi_enable(struct udevice *dev)
{
struct dw_mipi_dsi *dsi = dev_get_priv(dev);
/* Switch to video mode for panel-bridge enable & panel enable */
dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
return 0;
}
struct dsi_host_ops dw_mipi_dsi_ops = {
.init = dw_mipi_dsi_init,
.enable = dw_mipi_dsi_enable,
};
static int dw_mipi_dsi_probe(struct udevice *dev)
{
return 0;
}
U_BOOT_DRIVER(dw_mipi_dsi) = {
.name = "dw_mipi_dsi",
.id = UCLASS_DSI_HOST,
.probe = dw_mipi_dsi_probe,
.ops = &dw_mipi_dsi_ops,
.priv_auto_alloc_size = sizeof(struct dw_mipi_dsi),
};
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
MODULE_AUTHOR("Yannick Fertré <yannick.fertre@st.com>");
MODULE_DESCRIPTION("DW MIPI DSI host controller driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dw-mipi-dsi");
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
*
* EFI framebuffer driver based on GOP
*/
#include <common.h>
#include <dm.h>
#include <efi_api.h>
#include <vbe.h>
#include <video.h>
struct pixel {
u8 pos;
u8 size;
};
static const struct efi_framebuffer {
struct pixel red;
struct pixel green;
struct pixel blue;
struct pixel rsvd;
} efi_framebuffer_format_map[] = {
[EFI_GOT_RGBA8] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
[EFI_GOT_BGRA8] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
};
static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size)
{
u8 first, len;
first = 0;
len = 0;
if (mask) {
while (!(mask & 0x1)) {
mask = mask >> 1;
first++;
}
while (mask & 0x1) {
mask = mask >> 1;
len++;
}
}
*pos = first;
*size = len;
}
static int save_vesa_mode(struct vesa_mode_info *vesa)
{
struct efi_entry_gopmode *mode;
const struct efi_framebuffer *fbinfo;
int size;
int ret;
ret = efi_info_get(EFIET_GOP_MODE, (void **)&mode, &size);
if (ret == -ENOENT) {
debug("efi graphics output protocol mode not found\n");
return -ENXIO;
}
vesa->phys_base_ptr = mode->fb_base;
vesa->x_resolution = mode->info->width;
vesa->y_resolution = mode->info->height;
if (mode->info->pixel_format < EFI_GOT_BITMASK) {
fbinfo = &efi_framebuffer_format_map[mode->info->pixel_format];
vesa->red_mask_size = fbinfo->red.size;
vesa->red_mask_pos = fbinfo->red.pos;
vesa->green_mask_size = fbinfo->green.size;
vesa->green_mask_pos = fbinfo->green.pos;
vesa->blue_mask_size = fbinfo->blue.size;
vesa->blue_mask_pos = fbinfo->blue.pos;
vesa->reserved_mask_size = fbinfo->rsvd.size;
vesa->reserved_mask_pos = fbinfo->rsvd.pos;
vesa->bits_per_pixel = 32;
vesa->bytes_per_scanline = mode->info->pixels_per_scanline * 4;
} else if (mode->info->pixel_format == EFI_GOT_BITMASK) {
efi_find_pixel_bits(mode->info->pixel_bitmask[0],
&vesa->red_mask_pos,
&vesa->red_mask_size);
efi_find_pixel_bits(mode->info->pixel_bitmask[1],
&vesa->green_mask_pos,
&vesa->green_mask_size);
efi_find_pixel_bits(mode->info->pixel_bitmask[2],
&vesa->blue_mask_pos,
&vesa->blue_mask_size);
efi_find_pixel_bits(mode->info->pixel_bitmask[3],
&vesa->reserved_mask_pos,
&vesa->reserved_mask_size);
vesa->bits_per_pixel = vesa->red_mask_size +
vesa->green_mask_size +
vesa->blue_mask_size +
vesa->reserved_mask_size;
vesa->bytes_per_scanline = (mode->info->pixels_per_scanline *
vesa->bits_per_pixel) / 8;
} else {
debug("efi set unknown framebuffer format: %d\n",
mode->info->pixel_format);
return -EINVAL;
}
return 0;
}
static int efi_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct vesa_mode_info *vesa = &mode_info.vesa;
int ret;
/* Initialize vesa_mode_info structure */
ret = save_vesa_mode(vesa);
if (ret)
goto err;
ret = vbe_setup_video_priv(vesa, uc_priv, plat);
if (ret)
goto err;
printf("Video: %dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
vesa->bits_per_pixel);
return 0;
err:
printf("No video mode configured in EFI!\n");
return ret;
}
static const struct udevice_id efi_video_ids[] = {
{ .compatible = "efi-fb" },
{ }
};
U_BOOT_DRIVER(efi_video) = {
.name = "efi_video",
.id = UCLASS_VIDEO,
.of_match = efi_video_ids,
.probe = efi_video_probe,
};
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o
obj-$(CONFIG_EXYNOS_FB) += exynos_fb.o
obj-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
exynos_mipi_dsi_lowlevel.o
obj-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#ifndef _EXYNOS_EDP_LOWLEVEL_H
#define _EXYNOS_EDP_LOWLEVEL_H
void exynos_dp_enable_video_bist(struct exynos_dp *dp_regs,
unsigned int enable);
void exynos_dp_enable_video_mute(struct exynos_dp *dp_regs,
unsigned int enable);
void exynos_dp_reset(struct exynos_dp *dp_regs);
void exynos_dp_enable_sw_func(struct exynos_dp *dp_regs, unsigned int enable);
unsigned int exynos_dp_set_analog_power_down(struct exynos_dp *dp_regs,
unsigned int block, u32 enable);
unsigned int exynos_dp_get_pll_lock_status(struct exynos_dp *dp_regs);
int exynos_dp_init_analog_func(struct exynos_dp *dp_regs);
void exynos_dp_init_hpd(struct exynos_dp *dp_regs);
void exynos_dp_init_aux(struct exynos_dp *dp_regs);
void exynos_dp_config_interrupt(struct exynos_dp *dp_regs);
unsigned int exynos_dp_get_plug_in_status(struct exynos_dp *dp_regs);
unsigned int exynos_dp_detect_hpd(struct exynos_dp *dp_regs);
unsigned int exynos_dp_start_aux_transaction(struct exynos_dp *dp_regs);
unsigned int exynos_dp_write_byte_to_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned char data);
unsigned int exynos_dp_read_byte_from_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned char *data);
unsigned int exynos_dp_write_bytes_to_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned int count,
unsigned char data[]);
unsigned int exynos_dp_read_bytes_from_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned int count,
unsigned char data[]);
int exynos_dp_select_i2c_device(struct exynos_dp *dp_regs,
unsigned int device_addr,
unsigned int reg_addr);
int exynos_dp_read_byte_from_i2c(struct exynos_dp *dp_regs,
unsigned int device_addr,
unsigned int reg_addr, unsigned int *data);
int exynos_dp_read_bytes_from_i2c(struct exynos_dp *dp_regs,
unsigned int device_addr,
unsigned int reg_addr, unsigned int count,
unsigned char edid[]);
void exynos_dp_reset_macro(struct exynos_dp *dp_regs);
void exynos_dp_set_link_bandwidth(struct exynos_dp *dp_regs,
unsigned char bwtype);
unsigned char exynos_dp_get_link_bandwidth(struct exynos_dp *dp_regs);
void exynos_dp_set_lane_count(struct exynos_dp *dp_regs, unsigned char count);
unsigned int exynos_dp_get_lane_count(struct exynos_dp *dp_regs);
unsigned char exynos_dp_get_lanex_pre_emphasis(struct exynos_dp *dp_regs,
unsigned char lanecnt);
void exynos_dp_set_lane_pre_emphasis(struct exynos_dp *dp_regs,
unsigned int level, unsigned char lanecnt);
void exynos_dp_set_lanex_pre_emphasis(struct exynos_dp *dp_regs,
unsigned char request_val,
unsigned char lanecnt);
void exynos_dp_set_training_pattern(struct exynos_dp *dp_regs,
unsigned int pattern);
void exynos_dp_enable_enhanced_mode(struct exynos_dp *dp_regs,
unsigned char enable);
void exynos_dp_enable_scrambling(struct exynos_dp *dp_regs,
unsigned int enable);
int exynos_dp_init_video(struct exynos_dp *dp_regs);
void exynos_dp_config_video_slave_mode(struct exynos_dp *dp_regs,
struct edp_video_info *video_info);
void exynos_dp_set_video_color_format(struct exynos_dp *dp_regs,
struct edp_video_info *video_info);
int exynos_dp_config_video_bist(struct exynos_dp *dp_regs,
struct exynos_dp_priv *priv);
unsigned int exynos_dp_is_slave_video_stream_clock_on(
struct exynos_dp *dp_regs);
void exynos_dp_set_video_cr_mn(struct exynos_dp *dp_regs, unsigned int type,
unsigned int m_value, unsigned int n_value);
void exynos_dp_set_video_timing_mode(struct exynos_dp *dp_regs,
unsigned int type);
void exynos_dp_enable_video_master(struct exynos_dp *dp_regs,
unsigned int enable);
void exynos_dp_start_video(struct exynos_dp *dp_regs);
unsigned int exynos_dp_is_video_stream_on(struct exynos_dp *dp_regs);
#endif /* _EXYNOS_DP_LOWLEVEL_H */
@@ -0,0 +1,719 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <config.h>
#include <common.h>
#include <display.h>
#include <div64.h>
#include <dm.h>
#include <fdtdec.h>
#include <linux/libfdt.h>
#include <panel.h>
#include <video.h>
#include <video_bridge.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <asm/arch/clk.h>
#include <asm/arch/mipi_dsim.h>
#include <asm/arch/dp_info.h>
#include <asm/arch/fb.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/system.h>
#include <asm/gpio.h>
#include <linux/errno.h>
DECLARE_GLOBAL_DATA_PTR;
enum {
FIMD_RGB_INTERFACE = 1,
FIMD_CPU_INTERFACE = 2,
};
enum exynos_fb_rgb_mode_t {
MODE_RGB_P = 0,
MODE_BGR_P = 1,
MODE_RGB_S = 2,
MODE_BGR_S = 3,
};
struct exynos_fb_priv {
ushort vl_col; /* Number of columns (i.e. 640) */
ushort vl_row; /* Number of rows (i.e. 480) */
ushort vl_rot; /* Rotation of Display (0, 1, 2, 3) */
ushort vl_width; /* Width of display area in millimeters */
ushort vl_height; /* Height of display area in millimeters */
/* LCD configuration register */
u_char vl_freq; /* Frequency */
u_char vl_clkp; /* Clock polarity */
u_char vl_oep; /* Output Enable polarity */
u_char vl_hsp; /* Horizontal Sync polarity */
u_char vl_vsp; /* Vertical Sync polarity */
u_char vl_dp; /* Data polarity */
u_char vl_bpix; /* Bits per pixel */
/* Horizontal control register. Timing from data sheet */
u_char vl_hspw; /* Horz sync pulse width */
u_char vl_hfpd; /* Wait before of line */
u_char vl_hbpd; /* Wait end of line */
/* Vertical control register. */
u_char vl_vspw; /* Vertical sync pulse width */
u_char vl_vfpd; /* Wait before of frame */
u_char vl_vbpd; /* Wait end of frame */
u_char vl_cmd_allow_len; /* Wait end of frame */
unsigned int win_id;
unsigned int init_delay;
unsigned int power_on_delay;
unsigned int reset_delay;
unsigned int interface_mode;
unsigned int mipi_enabled;
unsigned int dp_enabled;
unsigned int cs_setup;
unsigned int wr_setup;
unsigned int wr_act;
unsigned int wr_hold;
unsigned int logo_on;
unsigned int logo_width;
unsigned int logo_height;
int logo_x_offset;
int logo_y_offset;
unsigned long logo_addr;
unsigned int rgb_mode;
unsigned int resolution;
/* parent clock name(MPLL, EPLL or VPLL) */
unsigned int pclk_name;
/* ratio value for source clock from parent clock. */
unsigned int sclk_div;
unsigned int dual_lcd_enabled;
struct exynos_fb *reg;
struct exynos_platform_mipi_dsim *dsim_platform_data_dt;
};
static void exynos_fimd_set_dualrgb(struct exynos_fb_priv *priv, bool enabled)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
if (enabled) {
cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
EXYNOS_DUALRGB_VDEN_EN_ENABLE;
/* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
cfg |= EXYNOS_DUALRGB_SUB_CNT(priv->vl_col / 2) |
EXYNOS_DUALRGB_MAIN_CNT(0);
}
writel(cfg, &reg->dualrgb);
}
static void exynos_fimd_set_dp_clkcon(struct exynos_fb_priv *priv,
unsigned int enabled)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
if (enabled)
cfg = EXYNOS_DP_CLK_ENABLE;
writel(cfg, &reg->dp_mie_clkcon);
}
static void exynos_fimd_set_par(struct exynos_fb_priv *priv,
unsigned int win_id)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
/* set window control */
cfg = readl((unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
/* DATAPATH is DMA */
cfg |= EXYNOS_WINCON_DATAPATH_DMA;
cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
/* dma burst is 16 */
cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
switch (priv->vl_bpix) {
case 4:
cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
break;
default:
cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888;
break;
}
writel(cfg, (unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
/* set window position to x=0, y=0*/
cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
writel(cfg, (unsigned int)&reg->vidosd0a +
EXYNOS_VIDOSD(win_id));
cfg = EXYNOS_VIDOSD_RIGHT_X(priv->vl_col - 1) |
EXYNOS_VIDOSD_BOTTOM_Y(priv->vl_row - 1) |
EXYNOS_VIDOSD_RIGHT_X_E(1) |
EXYNOS_VIDOSD_BOTTOM_Y_E(0);
writel(cfg, (unsigned int)&reg->vidosd0b +
EXYNOS_VIDOSD(win_id));
/* set window size for window0*/
cfg = EXYNOS_VIDOSD_SIZE(priv->vl_col * priv->vl_row);
writel(cfg, (unsigned int)&reg->vidosd0c +
EXYNOS_VIDOSD(win_id));
}
static void exynos_fimd_set_buffer_address(struct exynos_fb_priv *priv,
unsigned int win_id,
ulong lcd_base_addr)
{
struct exynos_fb *reg = priv->reg;
unsigned long start_addr, end_addr;
start_addr = lcd_base_addr;
end_addr = start_addr + ((priv->vl_col * (VNBITS(priv->vl_bpix) / 8)) *
priv->vl_row);
writel(start_addr, (unsigned int)&reg->vidw00add0b0 +
EXYNOS_BUFFER_OFFSET(win_id));
writel(end_addr, (unsigned int)&reg->vidw00add1b0 +
EXYNOS_BUFFER_OFFSET(win_id));
}
static void exynos_fimd_set_clock(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0, div = 0, remainder, remainder_div;
unsigned long pixel_clock;
unsigned long long src_clock;
if (priv->dual_lcd_enabled) {
pixel_clock = priv->vl_freq *
(priv->vl_hspw + priv->vl_hfpd +
priv->vl_hbpd + priv->vl_col / 2) *
(priv->vl_vspw + priv->vl_vfpd +
priv->vl_vbpd + priv->vl_row);
} else if (priv->interface_mode == FIMD_CPU_INTERFACE) {
pixel_clock = priv->vl_freq *
priv->vl_width * priv->vl_height *
(priv->cs_setup + priv->wr_setup +
priv->wr_act + priv->wr_hold + 1);
} else {
pixel_clock = priv->vl_freq *
(priv->vl_hspw + priv->vl_hfpd +
priv->vl_hbpd + priv->vl_col) *
(priv->vl_vspw + priv->vl_vfpd +
priv->vl_vbpd + priv->vl_row);
}
cfg = readl(&reg->vidcon0);
cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
EXYNOS_VIDCON0_CLKDIR_MASK);
cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
src_clock = (unsigned long long) get_lcd_clk();
/* get quotient and remainder. */
remainder = do_div(src_clock, pixel_clock);
div = src_clock;
remainder *= 10;
remainder_div = remainder / pixel_clock;
/* round about one places of decimals. */
if (remainder_div >= 5)
div++;
/* in case of dual lcd mode. */
if (priv->dual_lcd_enabled)
div--;
cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
writel(cfg, &reg->vidcon0);
}
void exynos_set_trigger(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
cfg = readl(&reg->trigcon);
cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
writel(cfg, &reg->trigcon);
}
int exynos_is_i80_frame_done(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
int status;
cfg = readl(&reg->trigcon);
/* frame done func is valid only when TRIMODE[0] is set to 1. */
status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
EXYNOS_I80STATUS_TRIG_DONE;
return status;
}
static void exynos_fimd_lcd_on(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
/* display on */
cfg = readl(&reg->vidcon0);
cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
writel(cfg, &reg->vidcon0);
}
static void exynos_fimd_window_on(struct exynos_fb_priv *priv,
unsigned int win_id)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
/* enable window */
cfg = readl((unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
writel(cfg, (unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg = readl(&reg->winshmap);
cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
writel(cfg, &reg->winshmap);
}
void exynos_fimd_lcd_off(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
cfg = readl(&reg->vidcon0);
cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
writel(cfg, &reg->vidcon0);
}
void exynos_fimd_window_off(struct exynos_fb_priv *priv, unsigned int win_id)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
cfg = readl((unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
writel(cfg, (unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg = readl(&reg->winshmap);
cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
writel(cfg, &reg->winshmap);
}
/*
* The reset value for FIMD SYSMMU register MMU_CTRL is 3
* on Exynos5420 and newer versions.
* This means FIMD SYSMMU is on by default on Exynos5420
* and newer versions.
* Since in u-boot we don't use SYSMMU, we should disable
* those FIMD SYSMMU.
* Note that there are 2 SYSMMU for FIMD: m0 and m1.
* m0 handles windows 0 and 4, and m1 handles windows 1, 2 and 3.
* We disable both of them here.
*/
void exynos_fimd_disable_sysmmu(void)
{
u32 *sysmmufimd;
unsigned int node;
int node_list[2];
int count;
int i;
count = fdtdec_find_aliases_for_id(gd->fdt_blob, "fimd",
COMPAT_SAMSUNG_EXYNOS_SYSMMU, node_list, 2);
for (i = 0; i < count; i++) {
node = node_list[i];
if (node <= 0) {
debug("Can't get device node for fimd sysmmu\n");
return;
}
sysmmufimd = (u32 *)fdtdec_get_addr(gd->fdt_blob, node, "reg");
if (!sysmmufimd) {
debug("Can't get base address for sysmmu fimdm0");
return;
}
writel(0x0, sysmmufimd);
}
}
void exynos_fimd_lcd_init(struct udevice *dev)
{
struct exynos_fb_priv *priv = dev_get_priv(dev);
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0, rgb_mode;
unsigned int offset;
unsigned int node;
node = dev_of_offset(dev);
if (fdtdec_get_bool(gd->fdt_blob, node, "samsung,disable-sysmmu"))
exynos_fimd_disable_sysmmu();
offset = exynos_fimd_get_base_offset();
rgb_mode = priv->rgb_mode;
if (priv->interface_mode == FIMD_RGB_INTERFACE) {
cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
writel(cfg, &reg->vidcon0);
cfg = readl(&reg->vidcon2);
cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
EXYNOS_VIDCON2_TVFORMATSEL_MASK |
EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
cfg |= EXYNOS_VIDCON2_WB_DISABLE;
writel(cfg, &reg->vidcon2);
/* set polarity */
cfg = 0;
if (!priv->vl_clkp)
cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
if (!priv->vl_hsp)
cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
if (!priv->vl_vsp)
cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
if (!priv->vl_dp)
cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
writel(cfg, (unsigned int)&reg->vidcon1 + offset);
/* set timing */
cfg = EXYNOS_VIDTCON0_VFPD(priv->vl_vfpd - 1);
cfg |= EXYNOS_VIDTCON0_VBPD(priv->vl_vbpd - 1);
cfg |= EXYNOS_VIDTCON0_VSPW(priv->vl_vspw - 1);
writel(cfg, (unsigned int)&reg->vidtcon0 + offset);
cfg = EXYNOS_VIDTCON1_HFPD(priv->vl_hfpd - 1);
cfg |= EXYNOS_VIDTCON1_HBPD(priv->vl_hbpd - 1);
cfg |= EXYNOS_VIDTCON1_HSPW(priv->vl_hspw - 1);
writel(cfg, (unsigned int)&reg->vidtcon1 + offset);
/* set lcd size */
cfg = EXYNOS_VIDTCON2_HOZVAL(priv->vl_col - 1) |
EXYNOS_VIDTCON2_LINEVAL(priv->vl_row - 1) |
EXYNOS_VIDTCON2_HOZVAL_E(priv->vl_col - 1) |
EXYNOS_VIDTCON2_LINEVAL_E(priv->vl_row - 1);
writel(cfg, (unsigned int)&reg->vidtcon2 + offset);
}
/* set display mode */
cfg = readl(&reg->vidcon0);
cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
writel(cfg, &reg->vidcon0);
/* set par */
exynos_fimd_set_par(priv, priv->win_id);
/* set memory address */
exynos_fimd_set_buffer_address(priv, priv->win_id, plat->base);
/* set buffer size */
cfg = EXYNOS_VIDADDR_PAGEWIDTH(priv->vl_col *
VNBITS(priv->vl_bpix) / 8) |
EXYNOS_VIDADDR_PAGEWIDTH_E(priv->vl_col *
VNBITS(priv->vl_bpix) / 8) |
EXYNOS_VIDADDR_OFFSIZE(0) |
EXYNOS_VIDADDR_OFFSIZE_E(0);
writel(cfg, (unsigned int)&reg->vidw00add2 +
EXYNOS_BUFFER_SIZE(priv->win_id));
/* set clock */
exynos_fimd_set_clock(priv);
/* set rgb mode to dual lcd. */
exynos_fimd_set_dualrgb(priv, priv->dual_lcd_enabled);
/* display on */
exynos_fimd_lcd_on(priv);
/* window on */
exynos_fimd_window_on(priv, priv->win_id);
exynos_fimd_set_dp_clkcon(priv, priv->dp_enabled);
}
unsigned long exynos_fimd_calc_fbsize(struct exynos_fb_priv *priv)
{
return priv->vl_col * priv->vl_row * (VNBITS(priv->vl_bpix) / 8);
}
int exynos_fb_ofdata_to_platdata(struct udevice *dev)
{
struct exynos_fb_priv *priv = dev_get_priv(dev);
unsigned int node = dev_of_offset(dev);
const void *blob = gd->fdt_blob;
fdt_addr_t addr;
addr = devfdt_get_addr(dev);
if (addr == FDT_ADDR_T_NONE) {
debug("Can't get the FIMD base address\n");
return -EINVAL;
}
priv->reg = (struct exynos_fb *)addr;
priv->vl_col = fdtdec_get_int(blob, node, "samsung,vl-col", 0);
if (priv->vl_col == 0) {
debug("Can't get XRES\n");
return -ENXIO;
}
priv->vl_row = fdtdec_get_int(blob, node, "samsung,vl-row", 0);
if (priv->vl_row == 0) {
debug("Can't get YRES\n");
return -ENXIO;
}
priv->vl_width = fdtdec_get_int(blob, node,
"samsung,vl-width", 0);
priv->vl_height = fdtdec_get_int(blob, node,
"samsung,vl-height", 0);
priv->vl_freq = fdtdec_get_int(blob, node, "samsung,vl-freq", 0);
if (priv->vl_freq == 0) {
debug("Can't get refresh rate\n");
return -ENXIO;
}
if (fdtdec_get_bool(blob, node, "samsung,vl-clkp"))
priv->vl_clkp = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-oep"))
priv->vl_oep = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-hsp"))
priv->vl_hsp = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-vsp"))
priv->vl_vsp = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-dp"))
priv->vl_dp = VIDEO_ACTIVE_LOW;
priv->vl_bpix = fdtdec_get_int(blob, node, "samsung,vl-bpix", 0);
if (priv->vl_bpix == 0) {
debug("Can't get bits per pixel\n");
return -ENXIO;
}
priv->vl_hspw = fdtdec_get_int(blob, node, "samsung,vl-hspw", 0);
if (priv->vl_hspw == 0) {
debug("Can't get hsync width\n");
return -ENXIO;
}
priv->vl_hfpd = fdtdec_get_int(blob, node, "samsung,vl-hfpd", 0);
if (priv->vl_hfpd == 0) {
debug("Can't get right margin\n");
return -ENXIO;
}
priv->vl_hbpd = (u_char)fdtdec_get_int(blob, node,
"samsung,vl-hbpd", 0);
if (priv->vl_hbpd == 0) {
debug("Can't get left margin\n");
return -ENXIO;
}
priv->vl_vspw = (u_char)fdtdec_get_int(blob, node,
"samsung,vl-vspw", 0);
if (priv->vl_vspw == 0) {
debug("Can't get vsync width\n");
return -ENXIO;
}
priv->vl_vfpd = fdtdec_get_int(blob, node,
"samsung,vl-vfpd", 0);
if (priv->vl_vfpd == 0) {
debug("Can't get lower margin\n");
return -ENXIO;
}
priv->vl_vbpd = fdtdec_get_int(blob, node, "samsung,vl-vbpd", 0);
if (priv->vl_vbpd == 0) {
debug("Can't get upper margin\n");
return -ENXIO;
}
priv->vl_cmd_allow_len = fdtdec_get_int(blob, node,
"samsung,vl-cmd-allow-len", 0);
priv->win_id = fdtdec_get_int(blob, node, "samsung,winid", 0);
priv->init_delay = fdtdec_get_int(blob, node,
"samsung,init-delay", 0);
priv->power_on_delay = fdtdec_get_int(blob, node,
"samsung,power-on-delay", 0);
priv->reset_delay = fdtdec_get_int(blob, node,
"samsung,reset-delay", 0);
priv->interface_mode = fdtdec_get_int(blob, node,
"samsung,interface-mode", 0);
priv->mipi_enabled = fdtdec_get_int(blob, node,
"samsung,mipi-enabled", 0);
priv->dp_enabled = fdtdec_get_int(blob, node,
"samsung,dp-enabled", 0);
priv->cs_setup = fdtdec_get_int(blob, node,
"samsung,cs-setup", 0);
priv->wr_setup = fdtdec_get_int(blob, node,
"samsung,wr-setup", 0);
priv->wr_act = fdtdec_get_int(blob, node, "samsung,wr-act", 0);
priv->wr_hold = fdtdec_get_int(blob, node, "samsung,wr-hold", 0);
priv->logo_on = fdtdec_get_int(blob, node, "samsung,logo-on", 0);
if (priv->logo_on) {
priv->logo_width = fdtdec_get_int(blob, node,
"samsung,logo-width", 0);
priv->logo_height = fdtdec_get_int(blob, node,
"samsung,logo-height", 0);
priv->logo_addr = fdtdec_get_int(blob, node,
"samsung,logo-addr", 0);
}
priv->rgb_mode = fdtdec_get_int(blob, node,
"samsung,rgb-mode", 0);
priv->pclk_name = fdtdec_get_int(blob, node,
"samsung,pclk-name", 0);
priv->sclk_div = fdtdec_get_int(blob, node,
"samsung,sclk-div", 0);
priv->dual_lcd_enabled = fdtdec_get_int(blob, node,
"samsung,dual-lcd-enabled", 0);
return 0;
}
static int exynos_fb_probe(struct udevice *dev)
{
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct exynos_fb_priv *priv = dev_get_priv(dev);
struct udevice *panel, *bridge;
struct udevice *dp;
int ret;
debug("%s: start\n", __func__);
set_system_display_ctrl();
set_lcd_clk();
#ifdef CONFIG_EXYNOS_MIPI_DSIM
exynos_init_dsim_platform_data(&panel_info);
#endif
exynos_fimd_lcd_init(dev);
ret = uclass_first_device(UCLASS_PANEL, &panel);
if (ret) {
printf("LCD panel failed to probe\n");
return ret;
}
if (!panel) {
printf("LCD panel not found\n");
return -ENODEV;
}
ret = uclass_first_device(UCLASS_DISPLAY, &dp);
if (ret) {
debug("%s: Display device error %d\n", __func__, ret);
return ret;
}
if (!dev) {
debug("%s: Display device missing\n", __func__);
return -ENODEV;
}
ret = display_enable(dp, 18, NULL);
if (ret) {
debug("%s: Display enable error %d\n", __func__, ret);
return ret;
}
/* backlight / pwm */
ret = panel_enable_backlight(panel);
if (ret) {
debug("%s: backlight error: %d\n", __func__, ret);
return ret;
}
ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
if (!ret)
ret = video_bridge_set_backlight(bridge, 80);
if (ret) {
debug("%s: No video bridge, or no backlight on bridge\n",
__func__);
exynos_pinmux_config(PERIPH_ID_PWM0, 0);
}
uc_priv->xsize = priv->vl_col;
uc_priv->ysize = priv->vl_row;
uc_priv->bpix = priv->vl_bpix;
/* Enable flushing after LCD writes if requested */
video_set_flush_dcache(dev, true);
return 0;
}
static int exynos_fb_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
/* This is the maximum panel size we expect to see */
plat->size = 1920 * 1080 * 2;
return 0;
}
static const struct video_ops exynos_fb_ops = {
};
static const struct udevice_id exynos_fb_ids[] = {
{ .compatible = "samsung,exynos-fimd" },
{ }
};
U_BOOT_DRIVER(exynos_fb) = {
.name = "exynos_fb",
.id = UCLASS_VIDEO,
.of_match = exynos_fb_ids,
.ops = &exynos_fb_ops,
.bind = exynos_fb_bind,
.probe = exynos_fb_probe,
.ofdata_to_platdata = exynos_fb_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct exynos_fb_priv),
};
@@ -0,0 +1,323 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <malloc.h>
#include <fdtdec.h>
#include <linux/libfdt.h>
#include <linux/compat.h>
#include <linux/err.h>
#include <asm/arch/dsim.h>
#include <asm/arch/mipi_dsim.h>
#include <asm/arch/power.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clk.h>
#include "exynos_mipi_dsi_lowlevel.h"
#include "exynos_mipi_dsi_common.h"
#define master_to_driver(a) (a->dsim_lcd_drv)
#define master_to_device(a) (a->dsim_lcd_dev)
DECLARE_GLOBAL_DATA_PTR;
struct mipi_dsim_ddi {
int bus_id;
struct list_head list;
struct mipi_dsim_lcd_device *dsim_lcd_dev;
struct mipi_dsim_lcd_driver *dsim_lcd_drv;
};
static LIST_HEAD(dsim_ddi_list);
static LIST_HEAD(dsim_lcd_dev_list);
int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
{
struct mipi_dsim_ddi *dsim_ddi;
if (!lcd_dev) {
debug("mipi_dsim_lcd_device is NULL.\n");
return -EFAULT;
}
if (!lcd_dev->name) {
debug("dsim_lcd_device name is NULL.\n");
return -EFAULT;
}
dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
if (!dsim_ddi) {
debug("failed to allocate dsim_ddi object.\n");
return -EFAULT;
}
dsim_ddi->dsim_lcd_dev = lcd_dev;
list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
return 0;
}
struct mipi_dsim_ddi
*exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv)
{
struct mipi_dsim_ddi *dsim_ddi;
struct mipi_dsim_lcd_device *lcd_dev;
list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) {
lcd_dev = dsim_ddi->dsim_lcd_dev;
if (!lcd_dev)
continue;
if (lcd_drv->id >= 0) {
if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0 &&
lcd_drv->id == lcd_dev->id) {
/**
* bus_id would be used to identify
* connected bus.
*/
dsim_ddi->bus_id = lcd_dev->bus_id;
return dsim_ddi;
}
} else {
if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) {
/**
* bus_id would be used to identify
* connected bus.
*/
dsim_ddi->bus_id = lcd_dev->bus_id;
return dsim_ddi;
}
}
kfree(dsim_ddi);
list_del(&dsim_ddi_list);
}
return NULL;
}
int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv)
{
struct mipi_dsim_ddi *dsim_ddi;
if (!lcd_drv) {
debug("mipi_dsim_lcd_driver is NULL.\n");
return -EFAULT;
}
if (!lcd_drv->name) {
debug("dsim_lcd_driver name is NULL.\n");
return -EFAULT;
}
dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
if (!dsim_ddi) {
debug("mipi_dsim_ddi object not found.\n");
return -EFAULT;
}
dsim_ddi->dsim_lcd_drv = lcd_drv;
debug("registered panel driver(%s) to mipi-dsi driver.\n",
lcd_drv->name);
return 0;
}
struct mipi_dsim_ddi
*exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim,
const char *name)
{
struct mipi_dsim_ddi *dsim_ddi;
struct mipi_dsim_lcd_driver *lcd_drv;
struct mipi_dsim_lcd_device *lcd_dev;
list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) {
lcd_drv = dsim_ddi->dsim_lcd_drv;
lcd_dev = dsim_ddi->dsim_lcd_dev;
if (!lcd_drv || !lcd_dev)
continue;
debug("lcd_drv->id = %d, lcd_dev->id = %d\n",
lcd_drv->id, lcd_dev->id);
if ((strcmp(lcd_drv->name, name) == 0)) {
lcd_dev->master = dsim;
dsim->dsim_lcd_dev = lcd_dev;
dsim->dsim_lcd_drv = lcd_drv;
return dsim_ddi;
}
}
return NULL;
}
/* define MIPI-DSI Master operations. */
static struct mipi_dsim_master_ops master_ops = {
.cmd_write = exynos_mipi_dsi_wr_data,
.get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status,
.clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done,
};
int exynos_mipi_dsi_init(struct exynos_platform_mipi_dsim *dsim_pd)
{
struct mipi_dsim_device *dsim;
struct mipi_dsim_config *dsim_config;
struct mipi_dsim_ddi *dsim_ddi;
dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL);
if (!dsim) {
debug("failed to allocate dsim object.\n");
return -EFAULT;
}
/* get mipi_dsim_config. */
dsim_config = dsim_pd->dsim_config;
if (dsim_config == NULL) {
debug("failed to get dsim config data.\n");
return -EFAULT;
}
dsim->pd = dsim_pd;
dsim->dsim_config = dsim_config;
dsim->master_ops = &master_ops;
/* bind lcd ddi matched with panel name. */
dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
if (!dsim_ddi) {
debug("mipi_dsim_ddi object not found.\n");
return -ENOSYS;
}
if (dsim_pd->lcd_power)
dsim_pd->lcd_power();
if (dsim_pd->mipi_power)
dsim_pd->mipi_power();
/* phy_enable(unsigned int dev_index, unsigned int enable) */
if (dsim_pd->phy_enable)
dsim_pd->phy_enable(0, 1);
set_mipi_clk();
exynos_mipi_dsi_init_dsim(dsim);
exynos_mipi_dsi_init_link(dsim);
exynos_mipi_dsi_set_hs_enable(dsim);
/* set display timing. */
exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config);
/* initialize mipi-dsi client(lcd panel). */
if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->mipi_panel_init) {
dsim_ddi->dsim_lcd_drv->mipi_panel_init(dsim);
dsim_ddi->dsim_lcd_drv->mipi_display_on(dsim);
}
debug("mipi-dsi driver(%s mode) has been probed.\n",
(dsim_config->e_interface == DSIM_COMMAND) ?
"CPU" : "RGB");
return 0;
}
int exynos_dsim_config_parse_dt(const void *blob, struct mipi_dsim_config *dt,
struct mipi_dsim_lcd_device *lcd_dt)
{
int node;
node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_MIPI_DSI);
if (node <= 0) {
printf("exynos_mipi_dsi: Can't get device node for mipi dsi\n");
return -ENODEV;
}
dt->e_interface = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-interface", 0);
dt->e_virtual_ch = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-virtual-ch", 0);
dt->e_pixel_format = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-pixel-format", 0);
dt->e_burst_mode = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-burst-mode", 0);
dt->e_no_data_lane = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-no-data-lane", 0);
dt->e_byte_clk = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-byte-clk", 0);
dt->hfp = fdtdec_get_int(blob, node,
"samsung,dsim-config-hfp", 0);
dt->p = fdtdec_get_int(blob, node,
"samsung,dsim-config-p", 0);
dt->m = fdtdec_get_int(blob, node,
"samsung,dsim-config-m", 0);
dt->s = fdtdec_get_int(blob, node,
"samsung,dsim-config-s", 0);
dt->pll_stable_time = fdtdec_get_int(blob, node,
"samsung,dsim-config-pll-stable-time", 0);
dt->esc_clk = fdtdec_get_int(blob, node,
"samsung,dsim-config-esc-clk", 0);
dt->stop_holding_cnt = fdtdec_get_int(blob, node,
"samsung,dsim-config-stop-holding-cnt", 0);
dt->bta_timeout = fdtdec_get_int(blob, node,
"samsung,dsim-config-bta-timeout", 0);
dt->rx_timeout = fdtdec_get_int(blob, node,
"samsung,dsim-config-rx-timeout", 0);
lcd_dt->name = fdtdec_get_config_string(blob,
"samsung,dsim-device-name");
lcd_dt->id = fdtdec_get_int(blob, node,
"samsung,dsim-device-id", 0);
lcd_dt->bus_id = fdtdec_get_int(blob, node,
"samsung,dsim-device-bus_id", 0);
lcd_dt->reverse_panel = fdtdec_get_int(blob, node,
"samsung,dsim-device-reverse-panel", 0);
return 0;
}
void exynos_init_dsim_platform_data(vidinfo_t *vid)
{
static struct mipi_dsim_config dsim_config_dt;
static struct exynos_platform_mipi_dsim dsim_platform_data_dt;
static struct mipi_dsim_lcd_device mipi_lcd_device_dt;
if (exynos_dsim_config_parse_dt(gd->fdt_blob, &dsim_config_dt,
&mipi_lcd_device_dt))
debug("Can't get proper dsim config.\n");
strcpy(dsim_platform_data_dt.lcd_panel_name, mipi_lcd_device_dt.name);
dsim_platform_data_dt.dsim_config = &dsim_config_dt;
dsim_platform_data_dt.mipi_power = mipi_power;
dsim_platform_data_dt.phy_enable = set_mipi_phy_ctrl;
dsim_platform_data_dt.lcd_panel_info = (void *)vid;
mipi_lcd_device_dt.platform_data = (void *)&dsim_platform_data_dt;
exynos_mipi_dsi_register_lcd_device(&mipi_lcd_device_dt);
vid->dsim_platform_data_dt = &dsim_platform_data_dt;
}
@@ -0,0 +1,619 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <lcd.h>
#include <linux/err.h>
#include <asm/arch/dsim.h>
#include <asm/arch/mipi_dsim.h>
#include "exynos_mipi_dsi_lowlevel.h"
#define MHZ (1000 * 1000)
#define FIN_HZ (24 * MHZ)
#define DFIN_PLL_MIN_HZ (6 * MHZ)
#define DFIN_PLL_MAX_HZ (12 * MHZ)
#define DFVCO_MIN_HZ (500 * MHZ)
#define DFVCO_MAX_HZ (1000 * MHZ)
#define TRY_GET_FIFO_TIMEOUT (5000 * 2)
/* MIPI-DSIM status types. */
enum {
DSIM_STATE_INIT, /* should be initialized. */
DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */
DSIM_STATE_HSCLKEN, /* HS clock was enabled. */
DSIM_STATE_ULPS
};
/* define DSI lane types. */
enum {
DSIM_LANE_CLOCK = (1 << 0),
DSIM_LANE_DATA0 = (1 << 1),
DSIM_LANE_DATA1 = (1 << 2),
DSIM_LANE_DATA2 = (1 << 3),
DSIM_LANE_DATA3 = (1 << 4)
};
static unsigned int dpll_table[15] = {
100, 120, 170, 220, 270,
320, 390, 450, 510, 560,
640, 690, 770, 870, 950
};
static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
const unsigned char *data0, unsigned int data1)
{
unsigned int data_cnt = 0, payload = 0;
/* in case that data count is more then 4 */
for (data_cnt = 0; data_cnt < data1; data_cnt += 4) {
/*
* after sending 4bytes per one time,
* send remainder data less then 4.
*/
if ((data1 - data_cnt) < 4) {
if ((data1 - data_cnt) == 3) {
payload = data0[data_cnt] |
data0[data_cnt + 1] << 8 |
data0[data_cnt + 2] << 16;
debug("count = 3 payload = %x, %x %x %x\n",
payload, data0[data_cnt],
data0[data_cnt + 1],
data0[data_cnt + 2]);
} else if ((data1 - data_cnt) == 2) {
payload = data0[data_cnt] |
data0[data_cnt + 1] << 8;
debug("count = 2 payload = %x, %x %x\n", payload,
data0[data_cnt], data0[data_cnt + 1]);
} else if ((data1 - data_cnt) == 1) {
payload = data0[data_cnt];
}
} else {
/* send 4bytes per one time. */
payload = data0[data_cnt] |
data0[data_cnt + 1] << 8 |
data0[data_cnt + 2] << 16 |
data0[data_cnt + 3] << 24;
debug("count = 4 payload = %x, %x %x %x %x\n",
payload, *(u8 *)(data0 + data_cnt),
data0[data_cnt + 1],
data0[data_cnt + 2],
data0[data_cnt + 3]);
}
exynos_mipi_dsi_wr_tx_data(dsim, payload);
}
}
int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
const unsigned char *data0, unsigned int data1)
{
unsigned int timeout = TRY_GET_FIFO_TIMEOUT;
unsigned long delay_val, delay;
unsigned int check_rx_ack = 0;
if (dsim->state == DSIM_STATE_ULPS) {
debug("state is ULPS.\n");
return -EINVAL;
}
delay_val = MHZ / dsim->dsim_config->esc_clk;
delay = 10 * delay_val;
mdelay(delay);
/* only if transfer mode is LPDT, wait SFR becomes empty. */
if (dsim->state == DSIM_STATE_STOP) {
while (!(exynos_mipi_dsi_get_fifo_state(dsim) &
SFR_HEADER_EMPTY)) {
if ((timeout--) > 0)
mdelay(1);
else {
debug("SRF header fifo is not empty.\n");
return -EINVAL;
}
}
}
switch (data_id) {
/* short packet types of packet types for command. */
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_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
debug("data0 = %x data1 = %x\n",
data0[0], data0[1]);
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
if (check_rx_ack) {
/* process response func should be implemented */
return 0;
} else {
return -EINVAL;
}
/* general command */
case MIPI_DSI_COLOR_MODE_OFF:
case MIPI_DSI_COLOR_MODE_ON:
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
case MIPI_DSI_TURN_ON_PERIPHERAL:
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
if (check_rx_ack) {
/* process response func should be implemented. */
return 0;
} else {
return -EINVAL;
}
/* packet types for video data */
case MIPI_DSI_V_SYNC_START:
case MIPI_DSI_V_SYNC_END:
case MIPI_DSI_H_SYNC_START:
case MIPI_DSI_H_SYNC_END:
case MIPI_DSI_END_OF_TRANSMISSION:
return 0;
/* short and response packet types for command */
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_READ:
exynos_mipi_dsi_clear_all_interrupt(dsim);
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
/* process response func should be implemented. */
return 0;
/* long packet type and null packet */
case MIPI_DSI_NULL_PACKET:
case MIPI_DSI_BLANKING_PACKET:
return 0;
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
{
unsigned int payload = 0;
/* if data count is less then 4, then send 3bytes data. */
if (data1 < 4) {
payload = data0[0] |
data0[1] << 8 |
data0[2] << 16;
exynos_mipi_dsi_wr_tx_data(dsim, payload);
debug("count = %d payload = %x,%x %x %x\n",
data1, payload, data0[0],
data0[1], data0[2]);
} else {
/* in case that data count is more then 4 */
exynos_mipi_dsi_long_data_wr(dsim, data0, data1);
}
/* put data into header fifo */
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data1 & 0xff,
(data1 & 0xff00) >> 8);
}
if (check_rx_ack)
/* process response func should be implemented. */
return 0;
else
return -EINVAL;
/* packet typo for video data */
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
if (check_rx_ack) {
/* process response func should be implemented. */
return 0;
} else {
return -EINVAL;
}
default:
debug("data id %x is not supported current DSI spec.\n",
data_id);
return -EINVAL;
}
return 0;
}
int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable)
{
int sw_timeout;
if (enable) {
sw_timeout = 1000;
exynos_mipi_dsi_clear_interrupt(dsim);
exynos_mipi_dsi_enable_pll(dsim, 1);
while (1) {
sw_timeout--;
if (exynos_mipi_dsi_is_pll_stable(dsim))
return 0;
if (sw_timeout == 0)
return -EINVAL;
}
} else
exynos_mipi_dsi_enable_pll(dsim, 0);
return 0;
}
unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler)
{
unsigned long dfin_pll, dfvco, dpll_out;
unsigned int i, freq_band = 0xf;
dfin_pll = (FIN_HZ / pre_divider);
/******************************************************
* Serial Clock(=ByteClk X 8) FreqBand[3:0] *
******************************************************
* ~ 99.99 MHz 0000
* 100 ~ 119.99 MHz 0001
* 120 ~ 159.99 MHz 0010
* 160 ~ 199.99 MHz 0011
* 200 ~ 239.99 MHz 0100
* 140 ~ 319.99 MHz 0101
* 320 ~ 389.99 MHz 0110
* 390 ~ 449.99 MHz 0111
* 450 ~ 509.99 MHz 1000
* 510 ~ 559.99 MHz 1001
* 560 ~ 639.99 MHz 1010
* 640 ~ 689.99 MHz 1011
* 690 ~ 769.99 MHz 1100
* 770 ~ 869.99 MHz 1101
* 870 ~ 949.99 MHz 1110
* 950 ~ 1000 MHz 1111
******************************************************/
if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) {
debug("fin_pll range should be 6MHz ~ 12MHz\n");
exynos_mipi_dsi_enable_afc(dsim, 0, 0);
} else {
if (dfin_pll < 7 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x1);
else if (dfin_pll < 8 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x0);
else if (dfin_pll < 9 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x3);
else if (dfin_pll < 10 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x2);
else if (dfin_pll < 11 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x5);
else
exynos_mipi_dsi_enable_afc(dsim, 1, 0x4);
}
dfvco = dfin_pll * main_divider;
debug("dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
dfvco, dfin_pll, main_divider);
if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
debug("fvco range should be 500MHz ~ 1000MHz\n");
dpll_out = dfvco / (1 << scaler);
debug("dpll_out = %lu, dfvco = %lu, scaler = %d\n",
dpll_out, dfvco, scaler);
for (i = 0; i < ARRAY_SIZE(dpll_table); i++) {
if (dpll_out < dpll_table[i] * MHZ) {
freq_band = i;
break;
}
}
debug("freq_band = %d\n", freq_band);
exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler);
exynos_mipi_dsi_hs_zero_ctrl(dsim, 0);
exynos_mipi_dsi_prep_ctrl(dsim, 0);
/* Freq Band */
exynos_mipi_dsi_pll_freq_band(dsim, freq_band);
/* Stable time */
exynos_mipi_dsi_pll_stable_time(dsim,
dsim->dsim_config->pll_stable_time);
/* Enable PLL */
debug("FOUT of mipi dphy pll is %luMHz\n",
(dpll_out / MHZ));
return dpll_out;
}
int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
unsigned int byte_clk_sel, unsigned int enable)
{
unsigned int esc_div;
unsigned long esc_clk_error_rate;
unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0;
if (enable) {
dsim->e_clk_src = byte_clk_sel;
/* Escape mode clock and byte clock source */
exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel);
/* DPHY, DSIM Link : D-PHY clock out */
if (byte_clk_sel == DSIM_PLL_OUT_DIV8) {
hs_clk = exynos_mipi_dsi_change_pll(dsim,
dsim->dsim_config->p, dsim->dsim_config->m,
dsim->dsim_config->s);
if (hs_clk == 0) {
debug("failed to get hs clock.\n");
return -EINVAL;
}
byte_clk = hs_clk / 8;
exynos_mipi_dsi_enable_pll_bypass(dsim, 0);
exynos_mipi_dsi_pll_on(dsim, 1);
/* DPHY : D-PHY clock out, DSIM link : external clock out */
} else if (byte_clk_sel == DSIM_EXT_CLK_DIV8)
debug("not support EXT CLK source for MIPI DSIM\n");
else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS)
debug("not support EXT CLK source for MIPI DSIM\n");
/* escape clock divider */
esc_div = byte_clk / (dsim->dsim_config->esc_clk);
debug("esc_div = %d, byte_clk = %lu, esc_clk = %lu\n",
esc_div, byte_clk, dsim->dsim_config->esc_clk);
if ((byte_clk / esc_div) >= (20 * MHZ) ||
(byte_clk / esc_div) > dsim->dsim_config->esc_clk)
esc_div += 1;
escape_clk = byte_clk / esc_div;
debug("escape_clk = %lu, byte_clk = %lu, esc_div = %d\n",
escape_clk, byte_clk, esc_div);
/* enable escape clock. */
exynos_mipi_dsi_enable_byte_clock(dsim, 1);
/* enable byte clk and escape clock */
exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div);
/* escape clock on lane */
exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
(DSIM_LANE_CLOCK | dsim->data_lane), 1);
debug("byte clock is %luMHz\n",
(byte_clk / MHZ));
debug("escape clock that user's need is %lu\n",
(dsim->dsim_config->esc_clk / MHZ));
debug("escape clock divider is %x\n", esc_div);
debug("escape clock is %luMHz\n",
((byte_clk / esc_div) / MHZ));
if ((byte_clk / esc_div) > escape_clk) {
esc_clk_error_rate = escape_clk /
(byte_clk / esc_div);
debug("error rate is %lu over.\n",
(esc_clk_error_rate / 100));
} else if ((byte_clk / esc_div) < (escape_clk)) {
esc_clk_error_rate = (byte_clk / esc_div) /
escape_clk;
debug("error rate is %lu under.\n",
(esc_clk_error_rate / 100));
}
} else {
exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
(DSIM_LANE_CLOCK | dsim->data_lane), 0);
exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0);
/* disable escape clock. */
exynos_mipi_dsi_enable_byte_clock(dsim, 0);
if (byte_clk_sel == DSIM_PLL_OUT_DIV8)
exynos_mipi_dsi_pll_on(dsim, 0);
}
return 0;
}
int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim)
{
dsim->state = DSIM_STATE_INIT;
switch (dsim->dsim_config->e_no_data_lane) {
case DSIM_DATA_LANE_1:
dsim->data_lane = DSIM_LANE_DATA0;
break;
case DSIM_DATA_LANE_2:
dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1;
break;
case DSIM_DATA_LANE_3:
dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
DSIM_LANE_DATA2;
break;
case DSIM_DATA_LANE_4:
dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
DSIM_LANE_DATA2 | DSIM_LANE_DATA3;
break;
default:
debug("data lane is invalid.\n");
return -EINVAL;
};
exynos_mipi_dsi_sw_reset(dsim);
exynos_mipi_dsi_dp_dn_swap(dsim, 0);
return 0;
}
int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
unsigned int enable)
{
/* enable only frame done interrupt */
exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable);
return 0;
}
static void convert_to_fb_videomode(struct fb_videomode *mode1,
struct vidinfo *mode2)
{
mode1->xres = mode2->vl_width;
mode1->yres = mode2->vl_height;
mode1->upper_margin = mode2->vl_vfpd;
mode1->lower_margin = mode2->vl_vbpd;
mode1->left_margin = mode2->vl_hfpd;
mode1->right_margin = mode2->vl_hbpd;
mode1->vsync_len = mode2->vl_vspw;
mode1->hsync_len = mode2->vl_hspw;
}
int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_config)
{
struct exynos_platform_mipi_dsim *dsim_pd;
struct fb_videomode lcd_video;
struct vidinfo *vid;
dsim_pd = (struct exynos_platform_mipi_dsim *)dsim->pd;
vid = (struct vidinfo *)dsim_pd->lcd_panel_info;
convert_to_fb_videomode(&lcd_video, vid);
/* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
if (dsim->dsim_config->e_interface == (u32) DSIM_VIDEO) {
if (dsim->dsim_config->auto_vertical_cnt == 0) {
exynos_mipi_dsi_set_main_disp_vporch(dsim,
vid->vl_cmd_allow_len,
lcd_video.upper_margin,
lcd_video.lower_margin);
exynos_mipi_dsi_set_main_disp_hporch(dsim,
lcd_video.left_margin,
lcd_video.right_margin);
exynos_mipi_dsi_set_main_disp_sync_area(dsim,
lcd_video.vsync_len,
lcd_video.hsync_len);
}
}
exynos_mipi_dsi_set_main_disp_resol(dsim, lcd_video.xres,
lcd_video.yres);
exynos_mipi_dsi_display_config(dsim, dsim->dsim_config);
debug("lcd panel ==> width = %d, height = %d\n",
lcd_video.xres, lcd_video.yres);
return 0;
}
int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim)
{
unsigned int time_out = 100;
switch (dsim->state) {
case DSIM_STATE_INIT:
exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f);
/* dsi configuration */
exynos_mipi_dsi_init_config(dsim);
exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1);
exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1);
/* set clock configuration */
exynos_mipi_dsi_set_clock(dsim,
dsim->dsim_config->e_byte_clk, 1);
/* check clock and data lane state are stop state */
while (!(exynos_mipi_dsi_is_lane_state(dsim))) {
time_out--;
if (time_out == 0) {
debug("DSI Master is not stop state.\n");
debug("Check initialization process\n");
return -EINVAL;
}
}
dsim->state = DSIM_STATE_STOP;
/* BTA sequence counters */
exynos_mipi_dsi_set_stop_state_counter(dsim,
dsim->dsim_config->stop_holding_cnt);
exynos_mipi_dsi_set_bta_timeout(dsim,
dsim->dsim_config->bta_timeout);
exynos_mipi_dsi_set_lpdr_timeout(dsim,
dsim->dsim_config->rx_timeout);
return 0;
default:
debug("DSI Master is already init.\n");
return 0;
}
return 0;
}
int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim)
{
if (dsim->state == DSIM_STATE_STOP) {
if (dsim->e_clk_src != DSIM_EXT_CLK_BYPASS) {
dsim->state = DSIM_STATE_HSCLKEN;
/* set LCDC and CPU transfer mode to HS. */
exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
exynos_mipi_dsi_enable_hs_clock(dsim, 1);
return 0;
} else
debug("clock source is external bypass.\n");
} else
debug("DSIM is not stop state.\n");
return 0;
}
int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int mode)
{
if (mode) {
if (dsim->state != DSIM_STATE_HSCLKEN) {
debug("HS Clock lane is not enabled.\n");
return -EINVAL;
}
exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
} else {
if (dsim->state == DSIM_STATE_INIT || dsim->state ==
DSIM_STATE_ULPS) {
debug("DSI Master is not STOP or HSDT state.\n");
return -EINVAL;
}
exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
}
return 0;
}
int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
{
return _exynos_mipi_dsi_get_frame_done_status(dsim);
}
int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
{
_exynos_mipi_dsi_clear_frame_done(dsim);
return 0;
}
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <linux/fb.h>
#ifndef _EXYNOS_MIPI_DSI_COMMON_H
#define _EXYNOS_MIPI_DSI_COMMON_H
int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
const unsigned char *data0, unsigned int data1);
int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable);
unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler);
int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
unsigned int byte_clk_sel, unsigned int enable);
int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_info);
int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int mode);
int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
unsigned int enable);
int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
#endif /* _EXYNOS_MIPI_DSI_COMMON_H */
@@ -0,0 +1,638 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <asm/arch/dsim.h>
#include <asm/arch/mipi_dsim.h>
#include <asm/arch/power.h>
#include <asm/arch/cpu.h>
#include "exynos_mipi_dsi_lowlevel.h"
#include "exynos_mipi_dsi_common.h"
void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->swrst);
reg |= DSIM_FUNCRST;
writel(reg, &mipi_dsim->swrst);
}
void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim)
{
unsigned int reg = 0;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->swrst);
reg |= DSIM_SWRST;
reg |= DSIM_FUNCRST;
writel(reg, &mipi_dsim->swrst);
}
void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
reg |= INTSRC_SWRST_RELEASE;
writel(reg, &mipi_dsim->intsrc);
}
void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
unsigned int mode, unsigned int mask)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intmsk);
if (mask)
reg |= mode;
else
reg &= ~mode;
writel(reg, &mipi_dsim->intmsk);
}
void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
unsigned int cfg)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->fifoctrl);
writel(reg & ~(cfg), &mipi_dsim->fifoctrl);
udelay(10 * 1000);
reg |= cfg;
writel(reg, &mipi_dsim->fifoctrl);
}
/*
* this function set PLL P, M and S value in D-PHY
*/
void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
unsigned int value)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(DSIM_AFC_CTL(value), &mipi_dsim->phyacchr);
}
void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
unsigned int width_resol, unsigned int height_resol)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
/* standby should be set after configuration so set to not ready*/
reg = (readl(&mipi_dsim->mdresol)) & ~(DSIM_MAIN_STAND_BY);
writel(reg, &mipi_dsim->mdresol);
/* reset resolution */
reg &= ~(DSIM_MAIN_VRESOL(0x7ff) | DSIM_MAIN_HRESOL(0x7ff));
reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol);
reg |= DSIM_MAIN_STAND_BY;
writel(reg, &mipi_dsim->mdresol);
}
void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
unsigned int cmd_allow, unsigned int vfront, unsigned int vback)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->mvporch)) &
~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) |
(DSIM_MAIN_VBP_MASK));
reg |= ((cmd_allow & 0xf) << DSIM_CMD_ALLOW_SHIFT) |
((vfront & 0x7ff) << DSIM_STABLE_VFP_SHIFT) |
((vback & 0x7ff) << DSIM_MAIN_VBP_SHIFT);
writel(reg, &mipi_dsim->mvporch);
}
void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
unsigned int front, unsigned int back)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->mhporch)) &
~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK));
reg |= (front << DSIM_MAIN_HFP_SHIFT) | (back << DSIM_MAIN_HBP_SHIFT);
writel(reg, &mipi_dsim->mhporch);
}
void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->msync)) &
~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK));
reg |= ((vert & 0x3ff) << DSIM_MAIN_VSA_SHIFT) |
(hori << DSIM_MAIN_HSA_SHIFT);
writel(reg, &mipi_dsim->msync);
}
void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->sdresol)) &
~(DSIM_SUB_STANDY_MASK);
writel(reg, &mipi_dsim->sdresol);
reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK);
reg |= ((vert & 0x7ff) << DSIM_SUB_VRESOL_SHIFT) |
((hori & 0x7ff) << DSIM_SUB_HRESOL_SHIFT);
writel(reg, &mipi_dsim->sdresol);
/* DSIM STANDBY */
reg |= (1 << DSIM_SUB_STANDY_SHIFT);
writel(reg, &mipi_dsim->sdresol);
}
void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim)
{
struct mipi_dsim_config *dsim_config = dsim->dsim_config;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int cfg = (readl(&mipi_dsim->config)) &
~((1 << DSIM_EOT_PACKET_SHIFT) |
(0x1f << DSIM_HSA_MODE_SHIFT) |
(0x3 << DSIM_NUM_OF_DATALANE_SHIFT));
cfg |= (dsim_config->auto_flush << DSIM_AUTO_FLUSH_SHIFT) |
(dsim_config->eot_disable << DSIM_EOT_PACKET_SHIFT) |
(dsim_config->auto_vertical_cnt << DSIM_AUTO_MODE_SHIFT) |
(dsim_config->hse << DSIM_HSE_MODE_SHIFT) |
(dsim_config->hfp << DSIM_HFP_MODE_SHIFT) |
(dsim_config->hbp << DSIM_HBP_MODE_SHIFT) |
(dsim_config->hsa << DSIM_HSA_MODE_SHIFT) |
(dsim_config->e_no_data_lane << DSIM_NUM_OF_DATALANE_SHIFT);
writel(cfg, &mipi_dsim->config);
}
void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_config)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
u32 reg = (readl(&mipi_dsim->config)) &
~((0x3 << DSIM_BURST_MODE_SHIFT) | (1 << DSIM_VIDEO_MODE_SHIFT)
| (0x3 << DSIM_MAINVC_SHIFT) | (0x7 << DSIM_MAINPIX_SHIFT)
| (0x3 << DSIM_SUBVC_SHIFT) | (0x7 << DSIM_SUBPIX_SHIFT));
if (dsim_config->e_interface == DSIM_VIDEO)
reg |= (1 << DSIM_VIDEO_MODE_SHIFT);
else if (dsim_config->e_interface == DSIM_COMMAND)
reg &= ~(1 << DSIM_VIDEO_MODE_SHIFT);
else {
printf("unknown lcd type.\n");
return;
}
/* main lcd */
reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << DSIM_BURST_MODE_SHIFT
| ((u8) (dsim_config->e_virtual_ch) & 0x3) << DSIM_MAINVC_SHIFT
| ((u8) (dsim_config->e_pixel_format) & 0x7) << DSIM_MAINPIX_SHIFT;
writel(reg, &mipi_dsim->config);
}
void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim,
unsigned int lane, unsigned int enable)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->config);
if (enable)
reg |= DSIM_LANE_ENx(lane);
else
reg &= ~DSIM_LANE_ENx(lane);
writel(reg, &mipi_dsim->config);
}
void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
unsigned int count)
{
unsigned int cfg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
/* get the data lane number. */
cfg = DSIM_NUM_OF_DATA_LANE(count);
writel(cfg, &mipi_dsim->config);
}
void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int afc_code)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->phyacchr);
reg = 0;
if (enable) {
reg |= DSIM_AFC_EN;
reg &= ~(0x7 << DSIM_AFC_CTL_SHIFT);
reg |= DSIM_AFC_CTL(afc_code);
} else
reg &= ~DSIM_AFC_EN;
writel(reg, &mipi_dsim->phyacchr);
}
void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(DSIM_PLL_BYPASS_EXTERNAL);
reg |= enable << DSIM_PLL_BYPASS_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
unsigned int freq_band)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x1f << DSIM_FREQ_BAND_SHIFT);
reg |= ((freq_band & 0x1f) << DSIM_FREQ_BAND_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x7ffff << 1);
reg |= ((pre_divider & 0x3f) << DSIM_PREDIV_SHIFT) |
((main_divider & 0x1ff) << DSIM_MAIN_SHIFT) |
((scaler & 0x7) << DSIM_SCALER_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
unsigned int lock_time)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(lock_time, &mipi_dsim->plltmr);
}
void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x1 << DSIM_PLL_EN_SHIFT);
reg |= ((enable & 0x1) << DSIM_PLL_EN_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
unsigned int src)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(0x3 << DSIM_BYTE_CLK_SRC_SHIFT);
reg |= ((unsigned int) src) << DSIM_BYTE_CLK_SRC_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(1 << DSIM_BYTE_CLKEN_SHIFT);
reg |= enable << DSIM_BYTE_CLKEN_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int prs_val)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~((1 << DSIM_ESC_CLKEN_SHIFT) | (0xffff));
reg |= enable << DSIM_ESC_CLKEN_SHIFT;
if (enable)
reg |= prs_val;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
unsigned int lane_sel, unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->clkctrl);
if (enable)
reg |= DSIM_LANE_ESC_CLKEN(lane_sel);
else
reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel);
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->escmode)) &
~(0x1 << DSIM_FORCE_STOP_STATE_SHIFT);
reg |= ((enable & 0x1) << DSIM_FORCE_STOP_STATE_SHIFT);
writel(reg, &mipi_dsim->escmode);
}
unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->status);
/**
* check clock and data lane states.
* if MIPI-DSI controller was enabled at bootloader then
* TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK.
* so it should be checked for two case.
*/
if ((reg & DSIM_STOP_STATE_DAT(0xf)) &&
((reg & DSIM_STOP_STATE_CLK) ||
(reg & DSIM_TX_READY_HS_CLK)))
return 1;
else
return 0;
}
void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
unsigned int cnt_val)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->escmode)) &
~(0x7ff << DSIM_STOP_STATE_CNT_SHIFT);
reg |= ((cnt_val & 0x7ff) << DSIM_STOP_STATE_CNT_SHIFT);
writel(reg, &mipi_dsim->escmode);
}
void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->timeout)) &
~(0xff << DSIM_BTA_TOUT_SHIFT);
reg |= (timeout << DSIM_BTA_TOUT_SHIFT);
writel(reg, &mipi_dsim->timeout);
}
void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->timeout)) &
~(0xffff << DSIM_LPDR_TOUT_SHIFT);
reg |= (timeout << DSIM_LPDR_TOUT_SHIFT);
writel(reg, &mipi_dsim->timeout);
}
void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->escmode);
reg &= ~DSIM_CMD_LPDT_LP;
if (lp)
reg |= DSIM_CMD_LPDT_LP;
writel(reg, &mipi_dsim->escmode);
}
void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->escmode);
reg &= ~DSIM_TX_LPDT_LP;
if (lp)
reg |= DSIM_TX_LPDT_LP;
writel(reg, &mipi_dsim->escmode);
}
void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(1 << DSIM_TX_REQUEST_HSCLK_SHIFT);
reg |= enable << DSIM_TX_REQUEST_HSCLK_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
unsigned int swap_en)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->phyacchr1);
reg &= ~(0x3 << DSIM_DPDN_SWAP_DATA_SHIFT);
reg |= (swap_en & 0x3) << DSIM_DPDN_SWAP_DATA_SHIFT;
writel(reg, &mipi_dsim->phyacchr1);
}
void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
unsigned int hs_zero)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0xf << DSIM_ZEROCTRL_SHIFT);
reg |= ((hs_zero & 0xf) << DSIM_ZEROCTRL_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x7 << DSIM_PRECTRL_SHIFT);
reg |= ((prep & 0x7) << DSIM_PRECTRL_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
reg |= INTSRC_PLL_STABLE;
writel(reg, &mipi_dsim->intsrc);
}
void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(0xffffffff, &mipi_dsim->intsrc);
}
unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->status);
return reg & DSIM_PLL_STABLE ? 1 : 0;
}
unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
return readl(&mipi_dsim->fifoctrl) & ~(0x1f);
}
void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
unsigned int di, const unsigned char data0, const unsigned char data1)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (DSIM_PKTHDR_DAT1(data1) | DSIM_PKTHDR_DAT0(data0) |
DSIM_PKTHDR_DI(di));
writel(reg, &mipi_dsim->pkthdr);
}
unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device
*dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
return (reg & INTSRC_FRAME_DONE) ? 1 : 0;
}
void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
writel(reg | INTSRC_FRAME_DONE, &mipi_dsim->intsrc);
}
void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
unsigned int tx_data)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(tx_data, &mipi_dsim->payload);
}
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H
#define _EXYNOS_MIPI_DSI_LOWLEVEL_H
void exynos_mipi_dsi_register(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
unsigned int mode, unsigned int mask);
void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
unsigned int count);
void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
unsigned int cfg);
void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
unsigned int value);
void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
unsigned int value);
void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
unsigned int width_resol, unsigned int height_resol);
void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
unsigned int cmd_allow, unsigned int vfront, unsigned int vback);
void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
unsigned int front, unsigned int back);
void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori);
void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori);
void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_config);
void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
unsigned int count);
void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim,
unsigned int lane, unsigned int enable);
void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int afc_code);
void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
unsigned int freq_band);
void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler);
void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
unsigned int lock_time);
void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
unsigned int src);
void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int prs_val);
void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
unsigned int lane_sel, unsigned int enable);
void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
unsigned int enable);
unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
unsigned int cnt_val);
void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout);
void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout);
void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp);
void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp);
void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
unsigned int swap_en);
void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
unsigned int hs_zero);
void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim,
unsigned int prep);
void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim);
unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim);
unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim);
unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device
*dsim);
void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
unsigned int di, const unsigned char data0, const unsigned char data1);
void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
unsigned int tx_data);
#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* PWM BACKLIGHT driver for Board based on EXYNOS.
*
* Author: Donghwa Lee <dh09.lee@samsung.com>
*
* Derived from linux/drivers/video/backlight/pwm_backlight.c
*/
#include <common.h>
#include <pwm.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/pwm.h>
#include <asm/arch/pwm_backlight.h>
static struct pwm_backlight_data *pwm;
static int exynos_pwm_backlight_update_status(void)
{
int brightness = pwm->brightness;
int max = pwm->max_brightness;
if (brightness == 0) {
pwm_config(pwm->pwm_id, 0, pwm->period);
pwm_disable(pwm->pwm_id);
} else {
pwm_config(pwm->pwm_id,
brightness * pwm->period / max, pwm->period);
pwm_enable(pwm->pwm_id);
}
return 0;
}
int exynos_pwm_backlight_init(struct pwm_backlight_data *pd)
{
pwm = pd;
exynos_pwm_backlight_update_status();
return 0;
}
@@ -0,0 +1,52 @@
#
# Video fonts
#
menu "TrueType Fonts"
config CONSOLE_TRUETYPE_NIMBUS
bool "Nimbus Sans Regular"
depends on CONSOLE_TRUETYPE
default y
help
Nimbus Sans L is a version of Nimbus Sans using Adobe font sources.
It was designed in 1987. A subset of Nimbus Sans L were released
under the GPL. Although the characters are not exactly the same,
Nimbus Sans L has metrics almost identical to Helvetica and Arial.
(From Wikipedia, the free encyclopedia)
From: https://fontlibrary.org/en/font/nimbus-sans-l
License: GNU GPL v3
http://www.gnu.org/copyleft/gpl.html
config CONSOLE_TRUETYPE_ANKACODER
bool "Anka Coder Narrow"
depends on CONSOLE_TRUETYPE
help
The Anka/Coder family is a monospaced, courier-width font for source
code and terminals, in two styles and weights. Anka/Coder Narrow was
developed for printing source code.
https://code.google.com/p/anka-coder-fonts/
From: https://fontlibrary.org/en/font/anka-coder-narrow
License: SIL Open Font Licence
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
config CONSOLE_TRUETYPE_RUFSCRIPT
bool "Ruf Script"
depends on CONSOLE_TRUETYPE
help
A laid-back handwritten font.
Font: https://fontlibrary.org/en/font/rufscript
License: GPL with font exception
http://www.gnu.org/copyleft/gpl.html
config CONSOLE_TRUETYPE_CANTORAONE
bool "Cantoraone"
depends on CONSOLE_TRUETYPE
help
Cantora is a friendly semi formal, semi condensed, semi sans-serif
with a hint of handwriting. Perfect for headlines.
From https://fontlibrary.org/en/font/cantora
License: SIL Open Font Licence
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
endmenu
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_CONSOLE_TRUETYPE_NIMBUS) += nimbus_sans_l_regular.o
obj-$(CONFIG_CONSOLE_TRUETYPE_ANKACODER) += ankacoder_c75_r.o
obj-$(CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT) += rufscript010.o
obj-$(CONFIG_CONSOLE_TRUETYPE_CANTORAONE) += cantoraone_regular.o
@@ -0,0 +1,511 @@
// SPDX-License-Identifier: GPL-2.0
/*
* LCD: Formike, TFT 4.3", 480x800, RGB24, KWH043ST20-F01, DriverIC NT35510-16
* LCD initialization via SPI
* Based on:
*
*/
#include <common.h>
#include <errno.h>
#include <spi.h>
#define TAG_READ 0x80
#define TAG_WRITE 0x00
#define TAG_DATA 0x40
#define TAG_COMMAND 0x00
#define TAG_ADDR_H 0x20
#define TAG_ADDR_L 0x00
static int spi_write_tag_val(struct spi_slave *spi, unsigned char tag,
unsigned char val)
{
unsigned long flags = SPI_XFER_BEGIN;
u8 buf[2];
int ret;
buf[0] = tag;
ret = spi_xfer(spi, 8, buf, NULL, flags);
buf[0] = val;
flags = SPI_XFER_END;
ret = spi_xfer(spi, 8, buf, NULL, flags);
#ifdef KWH043ST20_F01_SPI_DEBUG
printf("spi_write_tag_val: tag=%02X, val=%02X ret: %d\n",
tag, val, ret);
#endif /* KWH043ST20_F01_SPI_DEBUG */
if (ret)
debug("%s: Failed to send: %d\n", __func__, ret);
return ret;
}
static void spi_write_dat(struct spi_slave *spi, unsigned int val)
{
spi_write_tag_val(spi, TAG_WRITE|TAG_DATA, val);
}
static void spi_write_com(struct spi_slave *spi, unsigned int addr)
{
spi_write_tag_val(spi, TAG_WRITE|TAG_COMMAND|TAG_ADDR_H,
(addr & 0xff00) >> 8);
spi_write_tag_val(spi, TAG_WRITE|TAG_COMMAND|TAG_ADDR_L,
(addr & 0x00ff) >> 0);
}
int kwh043st20_f01_spi_startup(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
struct spi_slave *spi;
int ret;
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
if (!spi) {
debug("%s: Failed to set up slave\n", __func__);
return -1;
}
ret = spi_claim_bus(spi);
if (ret) {
debug("%s: Failed to claim SPI bus: %d\n", __func__, ret);
goto err_claim_bus;
}
/* LV2 Page 1 enable */
spi_write_com(spi, 0xF000); spi_write_dat(spi, 0x55);
spi_write_com(spi, 0xF001); spi_write_dat(spi, 0xAA);
spi_write_com(spi, 0xF002); spi_write_dat(spi, 0x52);
spi_write_com(spi, 0xF003); spi_write_dat(spi, 0x08);
spi_write_com(spi, 0xF004); spi_write_dat(spi, 0x01);
/* AVDD Set AVDD 5.2V */
spi_write_com(spi, 0xB000); spi_write_dat(spi, 0x0D);
spi_write_com(spi, 0xB001); spi_write_dat(spi, 0x0D);
spi_write_com(spi, 0xB002); spi_write_dat(spi, 0x0D);
/* AVDD ratio */
spi_write_com(spi, 0xB600); spi_write_dat(spi, 0x34);
spi_write_com(spi, 0xB601); spi_write_dat(spi, 0x34);
spi_write_com(spi, 0xB602); spi_write_dat(spi, 0x34);
/* AVEE -5.2V */
spi_write_com(spi, 0xB100); spi_write_dat(spi, 0x0D);
spi_write_com(spi, 0xB101); spi_write_dat(spi, 0x0D);
spi_write_com(spi, 0xB102); spi_write_dat(spi, 0x0D);
/* AVEE ratio */
spi_write_com(spi, 0xB700); spi_write_dat(spi, 0x35);
spi_write_com(spi, 0xB701); spi_write_dat(spi, 0x35);
spi_write_com(spi, 0xB702); spi_write_dat(spi, 0x35);
/* VCL -2.5V */
spi_write_com(spi, 0xB200); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xB201); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xB202); spi_write_dat(spi, 0x00);
/* VCL ratio */
spi_write_com(spi, 0xB800); spi_write_dat(spi, 0x24);
spi_write_com(spi, 0xB801); spi_write_dat(spi, 0x24);
spi_write_com(spi, 0xB802); spi_write_dat(spi, 0x24);
/* VGH 15V */
spi_write_com(spi, 0xBF00); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xB300); spi_write_dat(spi, 0x08);
spi_write_com(spi, 0xB301); spi_write_dat(spi, 0x08);
spi_write_com(spi, 0xB302); spi_write_dat(spi, 0x08);
/* VGH ratio */
spi_write_com(spi, 0xB900); spi_write_dat(spi, 0x34);
spi_write_com(spi, 0xB901); spi_write_dat(spi, 0x34);
spi_write_com(spi, 0xB902); spi_write_dat(spi, 0x34);
/* VGLX ratio */
spi_write_com(spi, 0xBA00); spi_write_dat(spi, 0x24);
spi_write_com(spi, 0xBA01); spi_write_dat(spi, 0x24);
spi_write_com(spi, 0xBA02); spi_write_dat(spi, 0x24);
/* VGMP/VGSP 4.7V/0V */
spi_write_com(spi, 0xBC00); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xBC01); spi_write_dat(spi, 0x88);
spi_write_com(spi, 0xBC02); spi_write_dat(spi, 0x00);
/* VGMN/VGSN -4.7V/0V */
spi_write_com(spi, 0xBD00); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xBD01); spi_write_dat(spi, 0x88);
spi_write_com(spi, 0xBD02); spi_write_dat(spi, 0x00);
/* VCOM 1.525V */
spi_write_com(spi, 0xBE00); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xBE01); spi_write_dat(spi, 0x7A);
/* Gamma Setting */
spi_write_com(spi, 0xD100); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD101); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xD102); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD103); spi_write_dat(spi, 0x15);
spi_write_com(spi, 0xD104); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD105); spi_write_dat(spi, 0x30);
spi_write_com(spi, 0xD106); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD107); spi_write_dat(spi, 0x47);
spi_write_com(spi, 0xD108); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD109); spi_write_dat(spi, 0x5B);
spi_write_com(spi, 0xD10A); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD10B); spi_write_dat(spi, 0x7D);
spi_write_com(spi, 0xD10C); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD10D); spi_write_dat(spi, 0x9D);
spi_write_com(spi, 0xD10E); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD10F); spi_write_dat(spi, 0xCC);
spi_write_com(spi, 0xD110); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD111); spi_write_dat(spi, 0xF3);
spi_write_com(spi, 0xD112); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD113); spi_write_dat(spi, 0x32);
spi_write_com(spi, 0xD114); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD115); spi_write_dat(spi, 0x63);
spi_write_com(spi, 0xD116); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD117); spi_write_dat(spi, 0xB1);
spi_write_com(spi, 0xD118); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD119); spi_write_dat(spi, 0xF0);
spi_write_com(spi, 0xD11A); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD11B); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD11C); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD11D); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD11E); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD11F); spi_write_dat(spi, 0x67);
spi_write_com(spi, 0xD120); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD121); spi_write_dat(spi, 0x90);
spi_write_com(spi, 0xD122); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD123); spi_write_dat(spi, 0xCB);
spi_write_com(spi, 0xD124); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD125); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD126); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD127); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD128); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD129); spi_write_dat(spi, 0x51);
spi_write_com(spi, 0xD12A); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD12B); spi_write_dat(spi, 0x80);
spi_write_com(spi, 0xD12C); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD12D); spi_write_dat(spi, 0x9F);
spi_write_com(spi, 0xD12E); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD12F); spi_write_dat(spi, 0xBE);
spi_write_com(spi, 0xD130); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD131); spi_write_dat(spi, 0xF9);
spi_write_com(spi, 0xD132); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD133); spi_write_dat(spi, 0xFF);
spi_write_com(spi, 0xD200); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD201); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xD202); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD203); spi_write_dat(spi, 0x15);
spi_write_com(spi, 0xD204); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD205); spi_write_dat(spi, 0x30);
spi_write_com(spi, 0xD206); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD207); spi_write_dat(spi, 0x47);
spi_write_com(spi, 0xD208); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD209); spi_write_dat(spi, 0x5B);
spi_write_com(spi, 0xD20A); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD20B); spi_write_dat(spi, 0x7D);
spi_write_com(spi, 0xD20C); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD20D); spi_write_dat(spi, 0x9D);
spi_write_com(spi, 0xD20E); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD20F); spi_write_dat(spi, 0xCC);
spi_write_com(spi, 0xD210); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD211); spi_write_dat(spi, 0xF3);
spi_write_com(spi, 0xD212); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD213); spi_write_dat(spi, 0x32);
spi_write_com(spi, 0xD214); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD215); spi_write_dat(spi, 0x63);
spi_write_com(spi, 0xD216); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD217); spi_write_dat(spi, 0xB1);
spi_write_com(spi, 0xD218); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD219); spi_write_dat(spi, 0xF0);
spi_write_com(spi, 0xD21A); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD21B); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD21C); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD21D); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD21E); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD21F); spi_write_dat(spi, 0x67);
spi_write_com(spi, 0xD220); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD221); spi_write_dat(spi, 0x90);
spi_write_com(spi, 0xD222); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD223); spi_write_dat(spi, 0xCB);
spi_write_com(spi, 0xD224); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD225); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD226); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD227); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD228); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD229); spi_write_dat(spi, 0x51);
spi_write_com(spi, 0xD22A); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD22B); spi_write_dat(spi, 0x80);
spi_write_com(spi, 0xD22C); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD22D); spi_write_dat(spi, 0x9F);
spi_write_com(spi, 0xD22E); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD22F); spi_write_dat(spi, 0xBE);
spi_write_com(spi, 0xD230); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD231); spi_write_dat(spi, 0xF9);
spi_write_com(spi, 0xD232); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD233); spi_write_dat(spi, 0xFF);
spi_write_com(spi, 0xD300); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD301); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xD302); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD303); spi_write_dat(spi, 0x15);
spi_write_com(spi, 0xD304); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD305); spi_write_dat(spi, 0x30);
spi_write_com(spi, 0xD306); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD307); spi_write_dat(spi, 0x47);
spi_write_com(spi, 0xD308); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD309); spi_write_dat(spi, 0x5B);
spi_write_com(spi, 0xD30A); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD30B); spi_write_dat(spi, 0x7D);
spi_write_com(spi, 0xD30C); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD30D); spi_write_dat(spi, 0x9D);
spi_write_com(spi, 0xD30E); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD30F); spi_write_dat(spi, 0xCC);
spi_write_com(spi, 0xD310); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD311); spi_write_dat(spi, 0xF3);
spi_write_com(spi, 0xD312); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD313); spi_write_dat(spi, 0x32);
spi_write_com(spi, 0xD314); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD315); spi_write_dat(spi, 0x63);
spi_write_com(spi, 0xD316); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD317); spi_write_dat(spi, 0xB1);
spi_write_com(spi, 0xD318); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD319); spi_write_dat(spi, 0xF0);
spi_write_com(spi, 0xD31A); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD31B); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD31C); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD31D); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD31E); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD31F); spi_write_dat(spi, 0x67);
spi_write_com(spi, 0xD320); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD321); spi_write_dat(spi, 0x90);
spi_write_com(spi, 0xD322); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD323); spi_write_dat(spi, 0xCB);
spi_write_com(spi, 0xD324); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD325); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD326); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD327); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD328); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD329); spi_write_dat(spi, 0x51);
spi_write_com(spi, 0xD32A); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD32B); spi_write_dat(spi, 0x80);
spi_write_com(spi, 0xD32C); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD32D); spi_write_dat(spi, 0x9F);
spi_write_com(spi, 0xD32E); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD32F); spi_write_dat(spi, 0xBE);
spi_write_com(spi, 0xD330); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD331); spi_write_dat(spi, 0xF9);
spi_write_com(spi, 0xD332); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD333); spi_write_dat(spi, 0xFF);
spi_write_com(spi, 0xD400); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD401); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xD402); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD403); spi_write_dat(spi, 0x15);
spi_write_com(spi, 0xD404); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD405); spi_write_dat(spi, 0x30);
spi_write_com(spi, 0xD406); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD407); spi_write_dat(spi, 0x47);
spi_write_com(spi, 0xD408); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD409); spi_write_dat(spi, 0x5B);
spi_write_com(spi, 0xD40A); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD40B); spi_write_dat(spi, 0x7D);
spi_write_com(spi, 0xD40C); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD40D); spi_write_dat(spi, 0x9D);
spi_write_com(spi, 0xD40E); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD40F); spi_write_dat(spi, 0xCC);
spi_write_com(spi, 0xD410); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD411); spi_write_dat(spi, 0xF3);
spi_write_com(spi, 0xD412); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD413); spi_write_dat(spi, 0x32);
spi_write_com(spi, 0xD414); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD415); spi_write_dat(spi, 0x63);
spi_write_com(spi, 0xD416); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD417); spi_write_dat(spi, 0xB1);
spi_write_com(spi, 0xD418); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD419); spi_write_dat(spi, 0xF0);
spi_write_com(spi, 0xD41A); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD41B); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD41C); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD41D); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD41E); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD41F); spi_write_dat(spi, 0x67);
spi_write_com(spi, 0xD420); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD421); spi_write_dat(spi, 0x90);
spi_write_com(spi, 0xD422); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD423); spi_write_dat(spi, 0xCB);
spi_write_com(spi, 0xD424); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD425); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD426); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD427); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD428); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD429); spi_write_dat(spi, 0x51);
spi_write_com(spi, 0xD42A); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD42B); spi_write_dat(spi, 0x80);
spi_write_com(spi, 0xD42C); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD42D); spi_write_dat(spi, 0x9F);
spi_write_com(spi, 0xD42E); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD42F); spi_write_dat(spi, 0xBE);
spi_write_com(spi, 0xD430); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD431); spi_write_dat(spi, 0xF9);
spi_write_com(spi, 0xD432); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD433); spi_write_dat(spi, 0xFF);
spi_write_com(spi, 0xD500); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD501); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xD502); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD503); spi_write_dat(spi, 0x15);
spi_write_com(spi, 0xD504); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD505); spi_write_dat(spi, 0x30);
spi_write_com(spi, 0xD506); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD507); spi_write_dat(spi, 0x47);
spi_write_com(spi, 0xD508); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD509); spi_write_dat(spi, 0x5B);
spi_write_com(spi, 0xD50A); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD50B); spi_write_dat(spi, 0x7D);
spi_write_com(spi, 0xD50C); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD50D); spi_write_dat(spi, 0x9D);
spi_write_com(spi, 0xD50E); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD50F); spi_write_dat(spi, 0xCC);
spi_write_com(spi, 0xD510); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD511); spi_write_dat(spi, 0xF3);
spi_write_com(spi, 0xD512); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD513); spi_write_dat(spi, 0x32);
spi_write_com(spi, 0xD514); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD515); spi_write_dat(spi, 0x63);
spi_write_com(spi, 0xD516); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD517); spi_write_dat(spi, 0xB1);
spi_write_com(spi, 0xD518); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD519); spi_write_dat(spi, 0xF0);
spi_write_com(spi, 0xD51A); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD51B); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD51C); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD51D); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD51E); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD51F); spi_write_dat(spi, 0x67);
spi_write_com(spi, 0xD520); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD521); spi_write_dat(spi, 0x90);
spi_write_com(spi, 0xD522); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD523); spi_write_dat(spi, 0xCB);
spi_write_com(spi, 0xD524); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD525); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD526); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD527); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD528); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD529); spi_write_dat(spi, 0x51);
spi_write_com(spi, 0xD52A); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD52B); spi_write_dat(spi, 0x80);
spi_write_com(spi, 0xD52C); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD52D); spi_write_dat(spi, 0x9F);
spi_write_com(spi, 0xD52E); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD52F); spi_write_dat(spi, 0xBE);
spi_write_com(spi, 0xD530); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD531); spi_write_dat(spi, 0xF9);
spi_write_com(spi, 0xD532); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD533); spi_write_dat(spi, 0xFF);
spi_write_com(spi, 0xD600); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD601); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xD602); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD603); spi_write_dat(spi, 0x15);
spi_write_com(spi, 0xD604); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD605); spi_write_dat(spi, 0x30);
spi_write_com(spi, 0xD606); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD607); spi_write_dat(spi, 0x47);
spi_write_com(spi, 0xD608); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD609); spi_write_dat(spi, 0x5B);
spi_write_com(spi, 0xD60A); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD60B); spi_write_dat(spi, 0x7D);
spi_write_com(spi, 0xD60C); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD60D); spi_write_dat(spi, 0x9D);
spi_write_com(spi, 0xD60E); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD60F); spi_write_dat(spi, 0xCC);
spi_write_com(spi, 0xD610); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xD611); spi_write_dat(spi, 0xF3);
spi_write_com(spi, 0xD612); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD613); spi_write_dat(spi, 0x32);
spi_write_com(spi, 0xD614); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD615); spi_write_dat(spi, 0x63);
spi_write_com(spi, 0xD616); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD617); spi_write_dat(spi, 0xB1);
spi_write_com(spi, 0xD618); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD619); spi_write_dat(spi, 0xF0);
spi_write_com(spi, 0xD61A); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xD61B); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD61C); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD61D); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD61E); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD61F); spi_write_dat(spi, 0x67);
spi_write_com(spi, 0xD620); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD621); spi_write_dat(spi, 0x90);
spi_write_com(spi, 0xD622); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD623); spi_write_dat(spi, 0xCB);
spi_write_com(spi, 0xD624); spi_write_dat(spi, 0x02);
spi_write_com(spi, 0xD625); spi_write_dat(spi, 0xF2);
spi_write_com(spi, 0xD626); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD627); spi_write_dat(spi, 0x2A);
spi_write_com(spi, 0xD628); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD629); spi_write_dat(spi, 0x51);
spi_write_com(spi, 0xD62A); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD62B); spi_write_dat(spi, 0x80);
spi_write_com(spi, 0xD62C); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD62D); spi_write_dat(spi, 0x9F);
spi_write_com(spi, 0xD62E); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD62F); spi_write_dat(spi, 0xBE);
spi_write_com(spi, 0xD630); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD631); spi_write_dat(spi, 0xF9);
spi_write_com(spi, 0xD632); spi_write_dat(spi, 0x03);
spi_write_com(spi, 0xD633); spi_write_dat(spi, 0xFF);
/* LV2 Page 0 enable */
spi_write_com(spi, 0xF000); spi_write_dat(spi, 0x55);
spi_write_com(spi, 0xF001); spi_write_dat(spi, 0xAA);
spi_write_com(spi, 0xF002); spi_write_dat(spi, 0x52);
spi_write_com(spi, 0xF003); spi_write_dat(spi, 0x08);
spi_write_com(spi, 0xF004); spi_write_dat(spi, 0x00);
/* Display control */
spi_write_com(spi, 0xB100); spi_write_dat(spi, 0xFC);
spi_write_com(spi, 0xB101); spi_write_dat(spi, 0x00);
/* Source hold time */
spi_write_com(spi, 0xB600); spi_write_dat(spi, 0x05);
/* Gate EQ control */
spi_write_com(spi, 0xB700); spi_write_dat(spi, 0x70);
spi_write_com(spi, 0xB701); spi_write_dat(spi, 0x70);
/* Source EQ control (Mode 2) */
spi_write_com(spi, 0xB800); spi_write_dat(spi, 0x01);
spi_write_com(spi, 0xB801); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xB802); spi_write_dat(spi, 0x05);
spi_write_com(spi, 0xB803); spi_write_dat(spi, 0x05);
/* Inversion mode (Column) */
spi_write_com(spi, 0xBC00); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xBC01); spi_write_dat(spi, 0x00);
spi_write_com(spi, 0xBC02); spi_write_dat(spi, 0x00);
/* Timing control 8phase dual side/4H/4delay/RST_EN */
spi_write_com(spi, 0xC900); spi_write_dat(spi, 0xD0);
spi_write_com(spi, 0xC901); spi_write_dat(spi, 0x82);
spi_write_com(spi, 0xC902); spi_write_dat(spi, 0x50);
spi_write_com(spi, 0xC903); spi_write_dat(spi, 0x50);
spi_write_com(spi, 0xC904); spi_write_dat(spi, 0x50);
spi_write_com(spi, 0x3A00); spi_write_dat(spi, 0x55);
mdelay(120);
spi_write_com(spi, 0x1100);
mdelay(120);
spi_write_com(spi, 0x2900);
mdelay(120);
/* spi_write_com(spi, 0x2100); spi_write_dat(spi, 0x00); */
spi_write_com(spi, 0x2C00);
return 0;
err_claim_bus:
spi_free_slave(spi);
return -1;
}
@@ -0,0 +1,547 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2014 Freescale Semiconductor, Inc.
* Copyright 2019 Toradex AG
*
* FSL DCU Framebuffer driver
*/
#include <init.h>
#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <fdt_support.h>
#include <fsl_dcu_fb.h>
#include <linux/fb.h>
#include <malloc.h>
#include <video.h>
#include <video_fb.h>
#include "videomodes.h"
/* Convert the X,Y resolution pair into a single number */
#define RESOLUTION(x, y) (((u32)(x) << 16) | (y))
#ifdef CONFIG_SYS_FSL_DCU_LE
#define dcu_read32 in_le32
#define dcu_write32 out_le32
#elif defined(CONFIG_SYS_FSL_DCU_BE)
#define dcu_read32 in_be32
#define dcu_write32 out_be32
#endif
#define DCU_MODE_BLEND_ITER(x) ((x) << 20)
#define DCU_MODE_RASTER_EN (1 << 14)
#define DCU_MODE_NORMAL 1
#define DCU_MODE_COLORBAR 3
#define DCU_BGND_R(x) ((x) << 16)
#define DCU_BGND_G(x) ((x) << 8)
#define DCU_BGND_B(x) (x)
#define DCU_DISP_SIZE_DELTA_Y(x) ((x) << 16)
#define DCU_DISP_SIZE_DELTA_X(x) (x)
#define DCU_HSYN_PARA_BP(x) ((x) << 22)
#define DCU_HSYN_PARA_PW(x) ((x) << 11)
#define DCU_HSYN_PARA_FP(x) (x)
#define DCU_VSYN_PARA_BP(x) ((x) << 22)
#define DCU_VSYN_PARA_PW(x) ((x) << 11)
#define DCU_VSYN_PARA_FP(x) (x)
#define DCU_SYN_POL_INV_PXCK_FALL (1 << 6)
#define DCU_SYN_POL_NEG_REMAIN (0 << 5)
#define DCU_SYN_POL_INV_VS_LOW (1 << 1)
#define DCU_SYN_POL_INV_HS_LOW (1)
#define DCU_THRESHOLD_LS_BF_VS(x) ((x) << 16)
#define DCU_THRESHOLD_OUT_BUF_HIGH(x) ((x) << 8)
#define DCU_THRESHOLD_OUT_BUF_LOW(x) (x)
#define DCU_UPDATE_MODE_MODE (1 << 31)
#define DCU_UPDATE_MODE_READREG (1 << 30)
#define DCU_CTRLDESCLN_1_HEIGHT(x) ((x) << 16)
#define DCU_CTRLDESCLN_1_WIDTH(x) (x)
#define DCU_CTRLDESCLN_2_POSY(x) ((x) << 16)
#define DCU_CTRLDESCLN_2_POSX(x) (x)
#define DCU_CTRLDESCLN_4_EN (1 << 31)
#define DCU_CTRLDESCLN_4_TILE_EN (1 << 30)
#define DCU_CTRLDESCLN_4_DATA_SEL_CLUT (1 << 29)
#define DCU_CTRLDESCLN_4_SAFETY_EN (1 << 28)
#define DCU_CTRLDESCLN_4_TRANS(x) ((x) << 20)
#define DCU_CTRLDESCLN_4_BPP(x) ((x) << 16)
#define DCU_CTRLDESCLN_4_RLE_EN (1 << 15)
#define DCU_CTRLDESCLN_4_LUOFFS(x) ((x) << 4)
#define DCU_CTRLDESCLN_4_BB_ON (1 << 2)
#define DCU_CTRLDESCLN_4_AB(x) (x)
#define DCU_CTRLDESCLN_5_CKMAX_R(x) ((x) << 16)
#define DCU_CTRLDESCLN_5_CKMAX_G(x) ((x) << 8)
#define DCU_CTRLDESCLN_5_CKMAX_B(x) (x)
#define DCU_CTRLDESCLN_6_CKMIN_R(x) ((x) << 16)
#define DCU_CTRLDESCLN_6_CKMIN_G(x) ((x) << 8)
#define DCU_CTRLDESCLN_6_CKMIN_B(x) (x)
#define DCU_CTRLDESCLN_7_TILE_VER(x) ((x) << 16)
#define DCU_CTRLDESCLN_7_TILE_HOR(x) (x)
#define DCU_CTRLDESCLN_8_FG_FCOLOR(x) (x)
#define DCU_CTRLDESCLN_9_BG_BCOLOR(x) (x)
#define BPP_16_RGB565 4
#define BPP_24_RGB888 5
#define BPP_32_ARGB8888 6
DECLARE_GLOBAL_DATA_PTR;
/*
* This setting is used for the TWR_LCD_RGB card
*/
static struct fb_videomode fsl_dcu_mode_480_272 = {
.name = "480x272-60",
.refresh = 60,
.xres = 480,
.yres = 272,
.pixclock = 91996,
.left_margin = 2,
.right_margin = 2,
.upper_margin = 1,
.lower_margin = 1,
.hsync_len = 41,
.vsync_len = 2,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
};
/*
* This setting is used for Siliconimage SiI9022A HDMI
*/
static struct fb_videomode fsl_dcu_cea_mode_640_480 = {
.name = "640x480-60",
.refresh = 60,
.xres = 640,
.yres = 480,
.pixclock = 39722,
.left_margin = 48,
.right_margin = 16,
.upper_margin = 33,
.lower_margin = 10,
.hsync_len = 96,
.vsync_len = 2,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_videomode fsl_dcu_mode_640_480 = {
.name = "640x480-60",
.refresh = 60,
.xres = 640,
.yres = 480,
.pixclock = 25175,
.left_margin = 40,
.right_margin = 24,
.upper_margin = 32,
.lower_margin = 11,
.hsync_len = 96,
.vsync_len = 2,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_videomode fsl_dcu_mode_800_480 = {
.name = "800x480-60",
.refresh = 60,
.xres = 800,
.yres = 480,
.pixclock = 33260,
.left_margin = 216,
.right_margin = 40,
.upper_margin = 35,
.lower_margin = 10,
.hsync_len = 128,
.vsync_len = 2,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_videomode fsl_dcu_mode_1024_600 = {
.name = "1024x600-60",
.refresh = 60,
.xres = 1024,
.yres = 600,
.pixclock = 48000,
.left_margin = 104,
.right_margin = 43,
.upper_margin = 24,
.lower_margin = 20,
.hsync_len = 5,
.vsync_len = 5,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
};
/*
* DCU register map
*/
struct dcu_reg {
u32 desc_cursor[4];
u32 mode;
u32 bgnd;
u32 disp_size;
u32 hsyn_para;
u32 vsyn_para;
u32 synpol;
u32 threshold;
u32 int_status;
u32 int_mask;
u32 colbar[8];
u32 div_ratio;
u32 sign_calc[2];
u32 crc_val;
u8 res_064[0x6c-0x64];
u32 parr_err_status1;
u8 res_070[0x7c-0x70];
u32 parr_err_status3;
u32 mparr_err_status1;
u8 res_084[0x90-0x84];
u32 mparr_err_status3;
u32 threshold_inp_buf[2];
u8 res_09c[0xa0-0x9c];
u32 luma_comp;
u32 chroma_red;
u32 chroma_green;
u32 chroma_blue;
u32 crc_pos;
u32 lyr_intpol_en;
u32 lyr_luma_comp;
u32 lyr_chrm_red;
u32 lyr_chrm_grn;
u32 lyr_chrm_blue;
u8 res_0c4[0xcc-0xc8];
u32 update_mode;
u32 underrun;
u8 res_0d4[0x100-0xd4];
u32 gpr;
u32 slr_l[2];
u32 slr_disp_size;
u32 slr_hvsync_para;
u32 slr_pol;
u32 slr_l_transp[2];
u8 res_120[0x200-0x120];
u32 ctrldescl[DCU_LAYER_MAX_NUM][16];
};
static void reset_total_layers(void)
{
struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR;
int i;
for (i = 0; i < DCU_LAYER_MAX_NUM; i++) {
dcu_write32(&regs->ctrldescl[i][0], 0);
dcu_write32(&regs->ctrldescl[i][1], 0);
dcu_write32(&regs->ctrldescl[i][2], 0);
dcu_write32(&regs->ctrldescl[i][3], 0);
dcu_write32(&regs->ctrldescl[i][4], 0);
dcu_write32(&regs->ctrldescl[i][5], 0);
dcu_write32(&regs->ctrldescl[i][6], 0);
dcu_write32(&regs->ctrldescl[i][7], 0);
dcu_write32(&regs->ctrldescl[i][8], 0);
dcu_write32(&regs->ctrldescl[i][9], 0);
dcu_write32(&regs->ctrldescl[i][10], 0);
}
}
static int layer_ctrldesc_init(struct fb_info fbinfo,
int index, u32 pixel_format)
{
struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR;
unsigned int bpp = BPP_24_RGB888;
dcu_write32(&regs->ctrldescl[index][0],
DCU_CTRLDESCLN_1_HEIGHT(fbinfo.var.yres) |
DCU_CTRLDESCLN_1_WIDTH(fbinfo.var.xres));
dcu_write32(&regs->ctrldescl[index][1],
DCU_CTRLDESCLN_2_POSY(0) |
DCU_CTRLDESCLN_2_POSX(0));
dcu_write32(&regs->ctrldescl[index][2],
(unsigned int)fbinfo.screen_base);
switch (pixel_format) {
case 16:
bpp = BPP_16_RGB565;
break;
case 24:
bpp = BPP_24_RGB888;
break;
case 32:
bpp = BPP_32_ARGB8888;
break;
default:
printf("unsupported color depth: %u\n", pixel_format);
}
dcu_write32(&regs->ctrldescl[index][3],
DCU_CTRLDESCLN_4_EN |
DCU_CTRLDESCLN_4_TRANS(0xff) |
DCU_CTRLDESCLN_4_BPP(bpp) |
DCU_CTRLDESCLN_4_AB(0));
dcu_write32(&regs->ctrldescl[index][4],
DCU_CTRLDESCLN_5_CKMAX_R(0xff) |
DCU_CTRLDESCLN_5_CKMAX_G(0xff) |
DCU_CTRLDESCLN_5_CKMAX_B(0xff));
dcu_write32(&regs->ctrldescl[index][5],
DCU_CTRLDESCLN_6_CKMIN_R(0) |
DCU_CTRLDESCLN_6_CKMIN_G(0) |
DCU_CTRLDESCLN_6_CKMIN_B(0));
dcu_write32(&regs->ctrldescl[index][6],
DCU_CTRLDESCLN_7_TILE_VER(0) |
DCU_CTRLDESCLN_7_TILE_HOR(0));
dcu_write32(&regs->ctrldescl[index][7], DCU_CTRLDESCLN_8_FG_FCOLOR(0));
dcu_write32(&regs->ctrldescl[index][8], DCU_CTRLDESCLN_9_BG_BCOLOR(0));
return 0;
}
int fsl_dcu_init(struct fb_info *fbinfo, unsigned int xres,
unsigned int yres, unsigned int pixel_format)
{
struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR;
unsigned int div, mode;
/*
* When DM_VIDEO is enabled reservation of framebuffer is done
* in advance during bind() call.
*/
#if !CONFIG_IS_ENABLED(DM_VIDEO)
fbinfo->screen_size = fbinfo->var.xres * fbinfo->var.yres *
(fbinfo->var.bits_per_pixel / 8);
if (fbinfo->screen_size > CONFIG_VIDEO_FSL_DCU_MAX_FB_SIZE_MB) {
fbinfo->screen_size = 0;
return -ENOMEM;
}
/* Reserve framebuffer at the end of memory */
gd->fb_base = gd->bd->bi_dram[0].start +
gd->bd->bi_dram[0].size - fbinfo->screen_size;
fbinfo->screen_base = (char *)gd->fb_base;
memset(fbinfo->screen_base, 0, fbinfo->screen_size);
#endif
reset_total_layers();
dcu_write32(&regs->disp_size,
DCU_DISP_SIZE_DELTA_Y(fbinfo->var.yres) |
DCU_DISP_SIZE_DELTA_X(fbinfo->var.xres / 16));
dcu_write32(&regs->hsyn_para,
DCU_HSYN_PARA_BP(fbinfo->var.left_margin) |
DCU_HSYN_PARA_PW(fbinfo->var.hsync_len) |
DCU_HSYN_PARA_FP(fbinfo->var.right_margin));
dcu_write32(&regs->vsyn_para,
DCU_VSYN_PARA_BP(fbinfo->var.upper_margin) |
DCU_VSYN_PARA_PW(fbinfo->var.vsync_len) |
DCU_VSYN_PARA_FP(fbinfo->var.lower_margin));
dcu_write32(&regs->synpol,
DCU_SYN_POL_INV_PXCK_FALL |
DCU_SYN_POL_NEG_REMAIN |
DCU_SYN_POL_INV_VS_LOW |
DCU_SYN_POL_INV_HS_LOW);
dcu_write32(&regs->bgnd,
DCU_BGND_R(0) | DCU_BGND_G(0) | DCU_BGND_B(0));
dcu_write32(&regs->mode,
DCU_MODE_BLEND_ITER(2) |
DCU_MODE_RASTER_EN);
dcu_write32(&regs->threshold,
DCU_THRESHOLD_LS_BF_VS(0x3) |
DCU_THRESHOLD_OUT_BUF_HIGH(0x78) |
DCU_THRESHOLD_OUT_BUF_LOW(0));
mode = dcu_read32(&regs->mode);
dcu_write32(&regs->mode, mode | DCU_MODE_NORMAL);
layer_ctrldesc_init(*fbinfo, 0, pixel_format);
div = dcu_set_pixel_clock(fbinfo->var.pixclock);
dcu_write32(&regs->div_ratio, (div - 1));
dcu_write32(&regs->update_mode, DCU_UPDATE_MODE_READREG);
return 0;
}
ulong board_get_usable_ram_top(ulong total_size)
{
return gd->ram_top - CONFIG_VIDEO_FSL_DCU_MAX_FB_SIZE_MB;
}
int fsl_probe_common(struct fb_info *fbinfo, unsigned int *win_x,
unsigned int *win_y)
{
const char *options;
unsigned int depth = 0, freq = 0;
struct fb_videomode *fsl_dcu_mode_db = &fsl_dcu_mode_480_272;
if (!video_get_video_mode(win_x, win_y, &depth, &freq,
&options))
return -EINVAL;
/* Find the monitor port, which is a required option */
if (!options)
return -EINVAL;
if (strncmp(options, "monitor=", 8) != 0)
return -EINVAL;
switch (RESOLUTION(*win_x, *win_y)) {
case RESOLUTION(480, 272):
fsl_dcu_mode_db = &fsl_dcu_mode_480_272;
break;
case RESOLUTION(640, 480):
if (!strncmp(options, "monitor=hdmi", 12))
fsl_dcu_mode_db = &fsl_dcu_cea_mode_640_480;
else
fsl_dcu_mode_db = &fsl_dcu_mode_640_480;
break;
case RESOLUTION(800, 480):
fsl_dcu_mode_db = &fsl_dcu_mode_800_480;
break;
case RESOLUTION(1024, 600):
fsl_dcu_mode_db = &fsl_dcu_mode_1024_600;
break;
default:
printf("unsupported resolution %ux%u\n",
*win_x, *win_y);
}
fbinfo->var.xres = fsl_dcu_mode_db->xres;
fbinfo->var.yres = fsl_dcu_mode_db->yres;
fbinfo->var.bits_per_pixel = 32;
fbinfo->var.pixclock = fsl_dcu_mode_db->pixclock;
fbinfo->var.left_margin = fsl_dcu_mode_db->left_margin;
fbinfo->var.right_margin = fsl_dcu_mode_db->right_margin;
fbinfo->var.upper_margin = fsl_dcu_mode_db->upper_margin;
fbinfo->var.lower_margin = fsl_dcu_mode_db->lower_margin;
fbinfo->var.hsync_len = fsl_dcu_mode_db->hsync_len;
fbinfo->var.vsync_len = fsl_dcu_mode_db->vsync_len;
fbinfo->var.sync = fsl_dcu_mode_db->sync;
fbinfo->var.vmode = fsl_dcu_mode_db->vmode;
fbinfo->fix.line_length = fbinfo->var.xres *
fbinfo->var.bits_per_pixel / 8;
return platform_dcu_init(fbinfo, *win_x, *win_y,
options + 8, fsl_dcu_mode_db);
}
#ifndef CONFIG_DM_VIDEO
static struct fb_info info;
#if defined(CONFIG_OF_BOARD_SETUP)
int fsl_dcu_fixedfb_setup(void *blob)
{
u64 start, size;
int ret;
start = gd->bd->bi_dram[0].start;
size = gd->bd->bi_dram[0].size - info.screen_size;
/*
* Align size on section size (1 MiB).
*/
size &= 0xfff00000;
ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
if (ret) {
eprintf("Cannot setup fb: Error reserving memory\n");
return ret;
}
return 0;
}
#endif
void *video_hw_init(void)
{
static GraphicDevice ctfb;
if (fsl_probe_common(&info, &ctfb.winSizeX, &ctfb.winSizeY) < 0)
return NULL;
ctfb.frameAdrs = (unsigned int)info.screen_base;
ctfb.plnSizeX = ctfb.winSizeX;
ctfb.plnSizeY = ctfb.winSizeY;
ctfb.gdfBytesPP = 4;
ctfb.gdfIndex = GDF_32BIT_X888RGB;
ctfb.memSize = info.screen_size;
return &ctfb;
}
#else /* ifndef CONFIG_DM_VIDEO */
static int fsl_dcu_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct fb_info fbinfo = { 0 };
unsigned int win_x;
unsigned int win_y;
u32 fb_start, fb_end;
int ret = 0;
fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
fb_end = plat->base + plat->size;
fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
fbinfo.screen_base = (char *)fb_start;
fbinfo.screen_size = plat->size;
ret = fsl_probe_common(&fbinfo, &win_x, &win_y);
if (ret < 0)
return ret;
uc_priv->bpix = VIDEO_BPP32;
uc_priv->xsize = win_x;
uc_priv->ysize = win_y;
/* Enable dcache for the frame buffer */
mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
DCACHE_WRITEBACK);
video_set_flush_dcache(dev, true);
return ret;
}
static int fsl_dcu_video_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
unsigned int win_x;
unsigned int win_y;
unsigned int depth = 0, freq = 0;
const char *options;
int ret = 0;
ret = video_get_video_mode(&win_x, &win_y, &depth, &freq, &options);
if (ret < 0)
return ret;
plat->size = win_x * win_y * 32;
return 0;
}
static const struct udevice_id fsl_dcu_video_ids[] = {
{ .compatible = "fsl,vf610-dcu" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(fsl_dcu_video) = {
.name = "fsl_dcu_video",
.id = UCLASS_VIDEO,
.of_match = fsl_dcu_video_ids,
.bind = fsl_dcu_video_bind,
.probe = fsl_dcu_video_probe,
.flags = DM_FLAG_PRE_RELOC,
};
#endif /* ifndef CONFIG_DM_VIDEO */
@@ -0,0 +1,416 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2007, 2010-2011 Freescale Semiconductor, Inc.
* Authors: York Sun <yorksun@freescale.com>
* Timur Tabi <timur@freescale.com>
*
* FSL DIU Framebuffer driver
*/
#include <common.h>
#include <malloc.h>
#include <asm/io.h>
#include "videomodes.h"
#include <video_fb.h>
#include <fsl_diu_fb.h>
#include <linux/list.h>
#include <linux/fb.h>
/* This setting is used for the ifm pdm360ng with PRIMEVIEW PM070WL3 */
static struct fb_videomode fsl_diu_mode_800_480 = {
.name = "800x480-60",
.refresh = 60,
.xres = 800,
.yres = 480,
.pixclock = 31250,
.left_margin = 86,
.right_margin = 42,
.upper_margin = 33,
.lower_margin = 10,
.hsync_len = 128,
.vsync_len = 2,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED
};
/* For the SHARP LQ084S3LG01, used on the P1022DS board */
static struct fb_videomode fsl_diu_mode_800_600 = {
.name = "800x600-60",
.refresh = 60,
.xres = 800,
.yres = 600,
.pixclock = 25000,
.left_margin = 88,
.right_margin = 40,
.upper_margin = 23,
.lower_margin = 1,
.hsync_len = 128,
.vsync_len = 4,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
};
/*
* These parameters give default parameters
* for video output 1024x768,
* FIXME - change timing to proper amounts
* hsync 31.5kHz, vsync 60Hz
*/
static struct fb_videomode fsl_diu_mode_1024_768 = {
.name = "1024x768-60",
.refresh = 60,
.xres = 1024,
.yres = 768,
.pixclock = 15385,
.left_margin = 160,
.right_margin = 24,
.upper_margin = 29,
.lower_margin = 3,
.hsync_len = 136,
.vsync_len = 6,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
};
static struct fb_videomode fsl_diu_mode_1280_1024 = {
.name = "1280x1024-60",
.refresh = 60,
.xres = 1280,
.yres = 1024,
.pixclock = 9375,
.left_margin = 38,
.right_margin = 128,
.upper_margin = 2,
.lower_margin = 7,
.hsync_len = 216,
.vsync_len = 37,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
};
static struct fb_videomode fsl_diu_mode_1280_720 = {
.name = "1280x720-60",
.refresh = 60,
.xres = 1280,
.yres = 720,
.pixclock = 13426,
.left_margin = 192,
.right_margin = 64,
.upper_margin = 22,
.lower_margin = 1,
.hsync_len = 136,
.vsync_len = 3,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
};
static struct fb_videomode fsl_diu_mode_1920_1080 = {
.name = "1920x1080-60",
.refresh = 60,
.xres = 1920,
.yres = 1080,
.pixclock = 5787,
.left_margin = 328,
.right_margin = 120,
.upper_margin = 34,
.lower_margin = 1,
.hsync_len = 208,
.vsync_len = 3,
.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED
};
/*
* These are the fields of area descriptor(in DDR memory) for every plane
*/
struct diu_ad {
/* Word 0(32-bit) in DDR memory */
__le32 pix_fmt; /* hard coding pixel format */
/* Word 1(32-bit) in DDR memory */
__le32 addr;
/* Word 2(32-bit) in DDR memory */
__le32 src_size_g_alpha;
/* Word 3(32-bit) in DDR memory */
__le32 aoi_size;
/* Word 4(32-bit) in DDR memory */
__le32 offset_xyi;
/* Word 5(32-bit) in DDR memory */
__le32 offset_xyd;
/* Word 6(32-bit) in DDR memory */
__le32 ckmax_r:8;
__le32 ckmax_g:8;
__le32 ckmax_b:8;
__le32 res9:8;
/* Word 7(32-bit) in DDR memory */
__le32 ckmin_r:8;
__le32 ckmin_g:8;
__le32 ckmin_b:8;
__le32 res10:8;
/* Word 8(32-bit) in DDR memory */
__le32 next_ad;
/* Word 9(32-bit) in DDR memory, just for 64-bit aligned */
__le32 res[3];
} __attribute__ ((packed));
/*
* DIU register map
*/
struct diu {
__be32 desc[3];
__be32 gamma;
__be32 pallete;
__be32 cursor;
__be32 curs_pos;
__be32 diu_mode;
__be32 bgnd;
__be32 bgnd_wb;
__be32 disp_size;
__be32 wb_size;
__be32 wb_mem_addr;
__be32 hsyn_para;
__be32 vsyn_para;
__be32 syn_pol;
__be32 thresholds;
__be32 int_status;
__be32 int_mask;
__be32 colorbar[8];
__be32 filling;
__be32 plut;
} __attribute__ ((packed));
struct diu_addr {
void *vaddr; /* Virtual address */
u32 paddr; /* 32-bit physical address */
unsigned int offset; /* Alignment offset */
};
static struct fb_info info;
/*
* Align to 64-bit(8-byte), 32-byte, etc.
*/
static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
{
u32 offset, ssize;
u32 mask;
ssize = size + bytes_align;
buf->vaddr = malloc(ssize);
if (!buf->vaddr)
return -1;
memset(buf->vaddr, 0, ssize);
mask = bytes_align - 1;
offset = (u32)buf->vaddr & mask;
if (offset) {
buf->offset = bytes_align - offset;
buf->vaddr += offset;
} else
buf->offset = 0;
buf->paddr = virt_to_phys(buf->vaddr);
return 0;
}
/*
* Allocate a framebuffer and an Area Descriptor that points to it. Both
* are created in the same memory block. The Area Descriptor is updated to
* point to the framebuffer memory. Memory is aligned as needed.
*/
static struct diu_ad *allocate_fb(unsigned int xres, unsigned int yres,
unsigned int depth, char **fb)
{
unsigned long size = xres * yres * depth;
struct diu_addr addr;
struct diu_ad *ad;
size_t ad_size = roundup(sizeof(struct diu_ad), 32);
/*
* Allocate a memory block that holds the Area Descriptor and the
* frame buffer right behind it. To keep the code simple, everything
* is aligned on a 32-byte address.
*/
if (allocate_buf(&addr, ad_size + size, 32) < 0)
return NULL;
ad = addr.vaddr;
ad->addr = cpu_to_le32(addr.paddr + ad_size);
ad->aoi_size = cpu_to_le32((yres << 16) | xres);
ad->src_size_g_alpha = cpu_to_le32((yres << 12) | xres);
ad->offset_xyi = 0;
ad->offset_xyd = 0;
if (fb)
*fb = addr.vaddr + ad_size;
return ad;
}
int fsl_diu_init(u16 xres, u16 yres, u32 pixel_format, int gamma_fix)
{
struct fb_videomode *fsl_diu_mode_db;
struct diu_ad *ad;
struct diu *hw = (struct diu *)CONFIG_SYS_DIU_ADDR;
u8 *gamma_table_base;
unsigned int i, j;
struct diu_addr gamma;
struct diu_addr cursor;
/* Convert the X,Y resolution pair into a single number */
#define RESOLUTION(x, y) (((u32)(x) << 16) | (y))
switch (RESOLUTION(xres, yres)) {
case RESOLUTION(800, 480):
fsl_diu_mode_db = &fsl_diu_mode_800_480;
break;
case RESOLUTION(800, 600):
fsl_diu_mode_db = &fsl_diu_mode_800_600;
break;
case RESOLUTION(1024, 768):
fsl_diu_mode_db = &fsl_diu_mode_1024_768;
break;
case RESOLUTION(1280, 1024):
fsl_diu_mode_db = &fsl_diu_mode_1280_1024;
break;
case RESOLUTION(1280, 720):
fsl_diu_mode_db = &fsl_diu_mode_1280_720;
break;
case RESOLUTION(1920, 1080):
fsl_diu_mode_db = &fsl_diu_mode_1920_1080;
break;
default:
printf("DIU: Unsupported resolution %ux%u\n", xres, yres);
return -1;
}
/* read mode info */
info.var.xres = fsl_diu_mode_db->xres;
info.var.yres = fsl_diu_mode_db->yres;
info.var.bits_per_pixel = 32;
info.var.pixclock = fsl_diu_mode_db->pixclock;
info.var.left_margin = fsl_diu_mode_db->left_margin;
info.var.right_margin = fsl_diu_mode_db->right_margin;
info.var.upper_margin = fsl_diu_mode_db->upper_margin;
info.var.lower_margin = fsl_diu_mode_db->lower_margin;
info.var.hsync_len = fsl_diu_mode_db->hsync_len;
info.var.vsync_len = fsl_diu_mode_db->vsync_len;
info.var.sync = fsl_diu_mode_db->sync;
info.var.vmode = fsl_diu_mode_db->vmode;
info.fix.line_length = info.var.xres * info.var.bits_per_pixel / 8;
/* Memory allocation for framebuffer */
info.screen_size =
info.var.xres * info.var.yres * (info.var.bits_per_pixel / 8);
ad = allocate_fb(info.var.xres, info.var.yres,
info.var.bits_per_pixel / 8, &info.screen_base);
if (!ad) {
printf("DIU: Out of memory\n");
return -1;
}
ad->pix_fmt = pixel_format;
/* Disable chroma keying function */
ad->ckmax_r = 0;
ad->ckmax_g = 0;
ad->ckmax_b = 0;
ad->ckmin_r = 255;
ad->ckmin_g = 255;
ad->ckmin_b = 255;
/* Initialize the gamma table */
if (allocate_buf(&gamma, 256 * 3, 32) < 0) {
printf("DIU: Out of memory\n");
return -1;
}
gamma_table_base = gamma.vaddr;
for (i = 0; i <= 2; i++)
for (j = 0; j < 256; j++)
*gamma_table_base++ = j;
if (gamma_fix == 1) { /* fix the gamma */
gamma_table_base = gamma.vaddr;
for (i = 0; i < 256 * 3; i++) {
gamma_table_base[i] = (gamma_table_base[i] << 2)
| ((gamma_table_base[i] >> 6) & 0x03);
}
}
/* Initialize the cursor */
if (allocate_buf(&cursor, 32 * 32 * 2, 32) < 0) {
printf("DIU: Can't alloc cursor data\n");
return -1;
}
/* Program DIU registers */
out_be32(&hw->diu_mode, 0); /* Temporarily disable the DIU */
out_be32(&hw->gamma, gamma.paddr);
out_be32(&hw->cursor, cursor.paddr);
out_be32(&hw->bgnd, 0x007F7F7F);
out_be32(&hw->disp_size, info.var.yres << 16 | info.var.xres);
out_be32(&hw->hsyn_para, info.var.left_margin << 22 |
info.var.hsync_len << 11 |
info.var.right_margin);
out_be32(&hw->vsyn_para, info.var.upper_margin << 22 |
info.var.vsync_len << 11 |
info.var.lower_margin);
/* Pixel Clock configuration */
diu_set_pixel_clock(info.var.pixclock);
/* Set the frame buffers */
out_be32(&hw->desc[0], virt_to_phys(ad));
out_be32(&hw->desc[1], 0);
out_be32(&hw->desc[2], 0);
/* Enable the DIU, set display to all three planes */
out_be32(&hw->diu_mode, 1);
return 0;
}
void *video_hw_init(void)
{
static GraphicDevice ctfb;
const char *options;
unsigned int depth = 0, freq = 0;
if (!video_get_video_mode(&ctfb.winSizeX, &ctfb.winSizeY, &depth, &freq,
&options))
return NULL;
/* Find the monitor port, which is a required option */
if (!options)
return NULL;
if (strncmp(options, "monitor=", 8) != 0)
return NULL;
if (platform_diu_init(ctfb.winSizeX, ctfb.winSizeY, options + 8) < 0)
return NULL;
/* fill in Graphic device struct */
sprintf(ctfb.modeIdent, "%ix%ix%i %ikHz %iHz",
ctfb.winSizeX, ctfb.winSizeY, depth, 64, freq);
ctfb.frameAdrs = (unsigned int)info.screen_base;
ctfb.plnSizeX = ctfb.winSizeX;
ctfb.plnSizeY = ctfb.winSizeY;
ctfb.gdfBytesPP = 4;
ctfb.gdfIndex = GDF_32BIT_X888RGB;
ctfb.isaBase = 0;
ctfb.pciBase = 0;
ctfb.memSize = info.screen_size;
/* Cursor Start Address */
ctfb.dprBase = 0;
ctfb.vprBase = 0;
ctfb.cprBase = 0;
return &ctfb;
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Hitachi tx18d42vm LVDS LCD panel driver
*
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*/
#include <common.h>
#include <asm/gpio.h>
#include <errno.h>
/*
* Very simple write only SPI support, this does not use the generic SPI infra
* because that assumes R/W SPI, requiring a MISO pin. Also the necessary glue
* code alone would be larger then this minimal version.
*/
static void lcd_panel_spi_write(int cs, int clk, int mosi,
unsigned int data, int bits)
{
int i, offset;
gpio_direction_output(cs, 0);
for (i = 0; i < bits; i++) {
gpio_direction_output(clk, 0);
offset = (bits - 1) - i;
gpio_direction_output(mosi, (data >> offset) & 1);
udelay(2);
gpio_direction_output(clk, 1);
udelay(2);
}
gpio_direction_output(cs, 1);
udelay(2);
}
int hitachi_tx18d42vm_init(void)
{
const u16 init_data[] = {
0x0029, /* reset */
0x0025, /* standby */
0x0840, /* enable normally black */
0x0430, /* enable FRC/dither */
0x385f, /* enter test mode(1) */
0x3ca4, /* enter test mode(2) */
0x3409, /* enable SDRRS, enlarge OE width */
0x4041, /* adopt 2 line / 1 dot */
};
int i, cs, clk, mosi, ret = 0;
cs = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS);
clk = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK);
mosi = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI);
if (cs == -1 || clk == -1 || mosi == 1) {
printf("Error tx18d42vm spi gpio config is invalid\n");
return -EINVAL;
}
if (gpio_request(cs, "tx18d42vm-spi-cs") != 0 ||
gpio_request(clk, "tx18d42vm-spi-clk") != 0 ||
gpio_request(mosi, "tx18d42vm-spi-mosi") != 0) {
printf("Error cannot request tx18d42vm spi gpios\n");
ret = -EBUSY;
goto out;
}
for (i = 0; i < ARRAY_SIZE(init_data); i++)
lcd_panel_spi_write(cs, clk, mosi, init_data[i], 16);
mdelay(50); /* All the tx18d42vm drivers have a delay here ? */
lcd_panel_spi_write(cs, clk, mosi, 0x00ad, 16); /* display on */
out:
gpio_free(mosi);
gpio_free(clk);
gpio_free(cs);
return ret;
}
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Hitachi tx18d42vm LVDS LCD panel driver
*
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*/
void hitachi_tx18d42vm_init(void);
@@ -0,0 +1,361 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*/
#ifndef _I915_REG_H_
#define _I915_REG_H_
/* Hotplug control (945+ only) */
#define PORT_HOTPLUG_EN 0x61110
#define HDMIB_HOTPLUG_INT_EN (1 << 29)
#define DPB_HOTPLUG_INT_EN (1 << 29)
#define HDMIC_HOTPLUG_INT_EN (1 << 28)
#define DPC_HOTPLUG_INT_EN (1 << 28)
#define HDMID_HOTPLUG_INT_EN (1 << 27)
#define DPD_HOTPLUG_INT_EN (1 << 27)
#define SDVOB_HOTPLUG_INT_EN (1 << 26)
#define SDVOC_HOTPLUG_INT_EN (1 << 25)
#define TV_HOTPLUG_INT_EN (1 << 18)
#define CRT_HOTPLUG_INT_EN (1 << 9)
#define CRT_HOTPLUG_FORCE_DETECT (1 << 3)
#define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8)
/* must use period 64 on GM45 according to docs */
#define CRT_HOTPLUG_ACTIVATION_PERIOD_64 (1 << 8)
#define CRT_HOTPLUG_DAC_ON_TIME_2M (0 << 7)
#define CRT_HOTPLUG_DAC_ON_TIME_4M (1 << 7)
#define CRT_HOTPLUG_VOLTAGE_COMPARE_40 (0 << 5)
#define CRT_HOTPLUG_VOLTAGE_COMPARE_50 (1 << 5)
#define CRT_HOTPLUG_VOLTAGE_COMPARE_60 (2 << 5)
#define CRT_HOTPLUG_VOLTAGE_COMPARE_70 (3 << 5)
#define CRT_HOTPLUG_VOLTAGE_COMPARE_MASK (3 << 5)
#define CRT_HOTPLUG_DETECT_DELAY_1G (0 << 4)
#define CRT_HOTPLUG_DETECT_DELAY_2G (1 << 4)
#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2)
#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2)
/* Backlight control */
#define BLC_PWM_CTL2 0x61250 /* 965+ only */
#define BLM_PWM_ENABLE (1 << 31)
#define BLM_COMBINATION_MODE (1 << 30) /* gen4 only */
#define BLM_PIPE_SELECT (1 << 29)
#define BLM_PIPE_SELECT_IVB (3 << 29)
#define BLM_PIPE_A (0 << 29)
#define BLM_PIPE_B (1 << 29)
#define BLM_PIPE_C (2 << 29) /* ivb + */
#define BLM_PIPE(pipe) ((pipe) << 29)
#define BLM_POLARITY_I965 (1 << 28) /* gen4 only */
#define BLM_PHASE_IN_INTERUPT_STATUS (1 << 26)
#define BLM_PHASE_IN_ENABLE (1 << 25)
#define BLM_PHASE_IN_INTERUPT_ENABL (1 << 24)
#define BLM_PHASE_IN_TIME_BASE_SHIFT (16)
#define BLM_PHASE_IN_TIME_BASE_MASK (0xff << 16)
#define BLM_PHASE_IN_COUNT_SHIFT (8)
#define BLM_PHASE_IN_COUNT_MASK (0xff << 8)
#define BLM_PHASE_IN_INCR_SHIFT (0)
#define BLM_PHASE_IN_INCR_MASK (0xff << 0)
#define BLC_PWM_CTL 0x61254
/*
* This is the most significant 15 bits of the number of backlight cycles in a
* complete cycle of the modulated backlight control.
*
* The actual value is this field multiplied by two.
*/
#define BACKLIGHT_MODULATION_FREQ_SHIFT (17)
#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17)
#define BLM_LEGACY_MODE (1 << 16) /* gen2 only */
/*
* This is the number of cycles out of the backlight modulation cycle for which
* the backlight is on.
*
* This field must be no greater than the number of cycles in the complete
* backlight modulation cycle.
*/
#define BACKLIGHT_DUTY_CYCLE_SHIFT (0)
#define BACKLIGHT_DUTY_CYCLE_MASK (0xffff)
#define BACKLIGHT_DUTY_CYCLE_MASK_PNV (0xfffe)
#define BLM_POLARITY_PNV (1 << 0) /* pnv only */
#define BLC_HIST_CTL 0x61260
/*
* New registers for PCH-split platforms. Safe where new bits show up, the
* register layout machtes with gen4 BLC_PWM_CTL[12]
*/
#define BLC_PWM_CPU_CTL2 0x48250
#define BLC_PWM2_ENABLE (1<<31)
#define BLC_PWM_CPU_CTL 0x48254
#define BLM_HIST_CTL 0x48260
#define ENH_HIST_ENABLE (1<<31)
#define ENH_MODIF_TBL_ENABLE (1<<30)
#define ENH_PIPE_A_SELECT (0<<29)
#define ENH_PIPE_B_SELECT (1<<29)
#define ENH_PIPE(pipe) _PIPE(pipe, ENH_PIPE_A_SELECT, ENH_PIPE_B_SELECT)
#define HIST_MODE_YUV (0<<24)
#define HIST_MODE_HSV (1<<24)
#define ENH_MODE_DIRECT (0<<13)
#define ENH_MODE_ADDITIVE (1<<13)
#define ENH_MODE_MULTIPLICATIVE (2<<13)
#define BIN_REGISTER_SET (1<<11)
#define ENH_NUM_BINS 32
#define BLM_HIST_ENH 0x48264
#define BLM_HIST_GUARD_BAND 0x48268
#define BLM_HIST_INTR_ENABLE (1<<31)
#define BLM_HIST_EVENT_STATUS (1<<30)
#define BLM_HIST_INTR_DELAY_MASK (0xFF<<22)
#define BLM_HIST_INTR_DELAY_SHIFT 22
/*
* PCH CTL1 is totally different, all but the below bits are reserved. CTL2 is
* like the normal CTL from gen4 and earlier. Hooray for confusing naming.
*/
#define BLC_PWM_PCH_CTL1 0xc8250
#define BLM_PCH_PWM_ENABLE (1 << 31)
#define BLM_PCH_OVERRIDE_ENABLE (1 << 30)
#define BLM_PCH_POLARITY (1 << 29)
#define BLC_PWM_PCH_CTL2 0xc8254
/* digital port hotplug */
#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */
#define PORTD_HOTPLUG_ENABLE (1 << 20)
#define PORTD_PULSE_DURATION_2ms (0)
#define PORTD_PULSE_DURATION_4_5ms (1 << 18)
#define PORTD_PULSE_DURATION_6ms (2 << 18)
#define PORTD_PULSE_DURATION_100ms (3 << 18)
#define PORTD_PULSE_DURATION_MASK (3 << 18)
#define PORTD_HOTPLUG_NO_DETECT (0)
#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16)
#define PORTD_HOTPLUG_LONG_DETECT (1 << 17)
#define PORTC_HOTPLUG_ENABLE (1 << 12)
#define PORTC_PULSE_DURATION_2ms (0)
#define PORTC_PULSE_DURATION_4_5ms (1 << 10)
#define PORTC_PULSE_DURATION_6ms (2 << 10)
#define PORTC_PULSE_DURATION_100ms (3 << 10)
#define PORTC_PULSE_DURATION_MASK (3 << 10)
#define PORTC_HOTPLUG_NO_DETECT (0)
#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8)
#define PORTC_HOTPLUG_LONG_DETECT (1 << 9)
#define PORTB_HOTPLUG_ENABLE (1 << 4)
#define PORTB_PULSE_DURATION_2ms (0)
#define PORTB_PULSE_DURATION_4_5ms (1 << 2)
#define PORTB_PULSE_DURATION_6ms (2 << 2)
#define PORTB_PULSE_DURATION_100ms (3 << 2)
#define PORTB_PULSE_DURATION_MASK (3 << 2)
#define PORTB_HOTPLUG_NO_DETECT (0)
#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0)
#define PORTB_HOTPLUG_LONG_DETECT (1 << 1)
#define PCH_GPIOA 0xc5010
#define PCH_GPIOB 0xc5014
#define PCH_GPIOC 0xc5018
#define PCH_GPIOD 0xc501c
#define PCH_GPIOE 0xc5020
#define PCH_GPIOF 0xc5024
#define PCH_GMBUS0 0xc5100
#define PCH_GMBUS1 0xc5104
#define PCH_GMBUS2 0xc5108
#define PCH_GMBUS3 0xc510c
#define PCH_GMBUS4 0xc5110
#define PCH_GMBUS5 0xc5120
#define _PCH_DPLL_A 0xc6014
#define _PCH_DPLL_B 0xc6018
#define _PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B)
#define _PCH_FPA0 0xc6040
#define FP_CB_TUNE (0x3<<22)
#define _PCH_FPA1 0xc6044
#define _PCH_FPB0 0xc6048
#define _PCH_FPB1 0xc604c
#define _PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0)
#define _PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1)
#define PCH_DPLL_TEST 0xc606c
#define PCH_DREF_CONTROL 0xC6200
#define DREF_CONTROL_MASK 0x7fc3
#define DREF_CPU_SOURCE_OUTPUT_DISABLE (0<<13)
#define DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD (2<<13)
#define DREF_CPU_SOURCE_OUTPUT_NONSPREAD (3<<13)
#define DREF_CPU_SOURCE_OUTPUT_MASK (3<<13)
#define DREF_SSC_SOURCE_DISABLE (0<<11)
#define DREF_SSC_SOURCE_ENABLE (2<<11)
#define DREF_SSC_SOURCE_MASK (3<<11)
#define DREF_NONSPREAD_SOURCE_DISABLE (0<<9)
#define DREF_NONSPREAD_CK505_ENABLE (1<<9)
#define DREF_NONSPREAD_SOURCE_ENABLE (2<<9)
#define DREF_NONSPREAD_SOURCE_MASK (3<<9)
#define DREF_SUPERSPREAD_SOURCE_DISABLE (0<<7)
#define DREF_SUPERSPREAD_SOURCE_ENABLE (2<<7)
#define DREF_SUPERSPREAD_SOURCE_MASK (3<<7)
#define DREF_SSC4_DOWNSPREAD (0<<6)
#define DREF_SSC4_CENTERSPREAD (1<<6)
#define DREF_SSC1_DISABLE (0<<1)
#define DREF_SSC1_ENABLE (1<<1)
#define DREF_SSC4_DISABLE (0)
#define DREF_SSC4_ENABLE (1)
#define PCH_RAWCLK_FREQ 0xc6204
#define FDL_TP1_TIMER_SHIFT 12
#define FDL_TP1_TIMER_MASK (3<<12)
#define FDL_TP2_TIMER_SHIFT 10
#define FDL_TP2_TIMER_MASK (3<<10)
#define RAWCLK_FREQ_MASK 0x3ff
#define PCH_DPLL_TMR_CFG 0xc6208
#define PCH_SSC4_PARMS 0xc6210
#define PCH_SSC4_AUX_PARMS 0xc6214
#define PCH_DPLL_SEL 0xc7000
#define TRANSA_DPLL_ENABLE (1<<3)
#define TRANSA_DPLLB_SEL (1<<0)
#define TRANSA_DPLLA_SEL 0
#define TRANSB_DPLL_ENABLE (1<<7)
#define TRANSB_DPLLB_SEL (1<<4)
#define TRANSB_DPLLA_SEL (0)
#define TRANSC_DPLL_ENABLE (1<<11)
#define TRANSC_DPLLB_SEL (1<<8)
#define TRANSC_DPLLA_SEL (0)
/* transcoder */
#define _TRANS_HTOTAL_A 0xe0000
#define TRANS_HTOTAL_SHIFT 16
#define TRANS_HACTIVE_SHIFT 0
#define _TRANS_HBLANK_A 0xe0004
#define TRANS_HBLANK_END_SHIFT 16
#define TRANS_HBLANK_START_SHIFT 0
#define _TRANS_HSYNC_A 0xe0008
#define TRANS_HSYNC_END_SHIFT 16
#define TRANS_HSYNC_START_SHIFT 0
#define _TRANS_VTOTAL_A 0xe000c
#define TRANS_VTOTAL_SHIFT 16
#define TRANS_VACTIVE_SHIFT 0
#define _TRANS_VBLANK_A 0xe0010
#define TRANS_VBLANK_END_SHIFT 16
#define TRANS_VBLANK_START_SHIFT 0
#define _TRANS_VSYNC_A 0xe0014
#define TRANS_VSYNC_END_SHIFT 16
#define TRANS_VSYNC_START_SHIFT 0
#define _TRANS_VSYNCSHIFT_A 0xe0028
#define _TRANSA_DATA_M1 0xe0030
#define _TRANSA_DATA_N1 0xe0034
#define _TRANSA_DATA_M2 0xe0038
#define _TRANSA_DATA_N2 0xe003c
#define _TRANSA_DP_LINK_M1 0xe0040
#define _TRANSA_DP_LINK_N1 0xe0044
#define _TRANSA_DP_LINK_M2 0xe0048
#define _TRANSA_DP_LINK_N2 0xe004c
/* Per-transcoder DIP controls */
#define _VIDEO_DIP_CTL_A 0xe0200
#define _VIDEO_DIP_DATA_A 0xe0208
#define _VIDEO_DIP_GCP_A 0xe0210
#define _VIDEO_DIP_CTL_B 0xe1200
#define _VIDEO_DIP_DATA_B 0xe1208
#define _VIDEO_DIP_GCP_B 0xe1210
#define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B)
#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
#define VLV_VIDEO_DIP_CTL_A 0x60200
#define VLV_VIDEO_DIP_DATA_A 0x60208
#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A 0x60210
#define VLV_VIDEO_DIP_CTL_B 0x61170
#define VLV_VIDEO_DIP_DATA_B 0x61174
#define VLV_VIDEO_DIP_GDCP_PAYLOAD_B 0x61178
#define VLV_TVIDEO_DIP_CTL(pipe) \
_PIPE(pipe, VLV_VIDEO_DIP_CTL_A, VLV_VIDEO_DIP_CTL_B)
#define VLV_TVIDEO_DIP_DATA(pipe) \
_PIPE(pipe, VLV_VIDEO_DIP_DATA_A, VLV_VIDEO_DIP_DATA_B)
#define VLV_TVIDEO_DIP_GCP(pipe) \
_PIPE(pipe, VLV_VIDEO_DIP_GDCP_PAYLOAD_A, VLV_VIDEO_DIP_GDCP_PAYLOAD_B)
/* vlv has 2 sets of panel control regs. */
#define PIPEA_PP_STATUS 0x61200
#define PIPEA_PP_CONTROL 0x61204
#define PIPEA_PP_ON_DELAYS 0x61208
#define PIPEA_PP_OFF_DELAYS 0x6120c
#define PIPEA_PP_DIVISOR 0x61210
#define PIPEB_PP_STATUS 0x61300
#define PIPEB_PP_CONTROL 0x61304
#define PIPEB_PP_ON_DELAYS 0x61308
#define PIPEB_PP_OFF_DELAYS 0x6130c
#define PIPEB_PP_DIVISOR 0x61310
#define PCH_PP_STATUS 0xc7200
#define PCH_PP_CONTROL 0xc7204
#define PANEL_UNLOCK_REGS (0xabcd << 16)
#define PANEL_UNLOCK_MASK (0xffff << 16)
#define EDP_FORCE_VDD (1 << 3)
#define EDP_BLC_ENABLE (1 << 2)
#define PANEL_POWER_RESET (1 << 1)
#define PANEL_POWER_OFF (0 << 0)
#define PANEL_POWER_ON (1 << 0)
#define PCH_PP_ON_DELAYS 0xc7208
#define PANEL_PORT_SELECT_MASK (3 << 30)
#define PANEL_PORT_SELECT_LVDS (0 << 30)
#define PANEL_PORT_SELECT_DPA (1 << 30)
#define EDP_PANEL (1 << 30)
#define PANEL_PORT_SELECT_DPC (2 << 30)
#define PANEL_PORT_SELECT_DPD (3 << 30)
#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000)
#define PANEL_POWER_UP_DELAY_SHIFT 16
#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff)
#define PANEL_LIGHT_ON_DELAY_SHIFT 0
#define PCH_PP_OFF_DELAYS 0xc720c
#define PANEL_POWER_PORT_SELECT_MASK (0x3 << 30)
#define PANEL_POWER_PORT_LVDS (0 << 30)
#define PANEL_POWER_PORT_DP_A (1 << 30)
#define PANEL_POWER_PORT_DP_C (2 << 30)
#define PANEL_POWER_PORT_DP_D (3 << 30)
#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000)
#define PANEL_POWER_DOWN_DELAY_SHIFT 16
#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff)
#define PANEL_LIGHT_OFF_DELAY_SHIFT 0
#define PCH_PP_DIVISOR 0xc7210
#define PP_REFERENCE_DIVIDER_MASK (0xffffff00)
#define PP_REFERENCE_DIVIDER_SHIFT 8
#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f)
#define PANEL_POWER_CYCLE_DELAY_SHIFT 0
#define PCH_DP_B 0xe4100
#define PCH_DPB_AUX_CH_CTL 0xe4110
#define PCH_DPB_AUX_CH_DATA1 0xe4114
#define PCH_DPB_AUX_CH_DATA2 0xe4118
#define PCH_DPB_AUX_CH_DATA3 0xe411c
#define PCH_DPB_AUX_CH_DATA4 0xe4120
#define PCH_DPB_AUX_CH_DATA5 0xe4124
#define PCH_DP_C 0xe4200
#define PCH_DPC_AUX_CH_CTL 0xe4210
#define PCH_DPC_AUX_CH_DATA1 0xe4214
#define PCH_DPC_AUX_CH_DATA2 0xe4218
#define PCH_DPC_AUX_CH_DATA3 0xe421c
#define PCH_DPC_AUX_CH_DATA4 0xe4220
#define PCH_DPC_AUX_CH_DATA5 0xe4224
#define PCH_DP_D 0xe4300
#define PCH_DPD_AUX_CH_CTL 0xe4310
#define PCH_DPD_AUX_CH_DATA1 0xe4314
#define PCH_DPD_AUX_CH_DATA2 0xe4318
#define PCH_DPD_AUX_CH_DATA3 0xe431c
#define PCH_DPD_AUX_CH_DATA4 0xe4320
#define PCH_DPD_AUX_CH_DATA5 0xe4324
#endif /* _I915_REG_H_ */
@@ -0,0 +1,341 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2017
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*
* based on the gdsys osd driver, which is
*
* (C) Copyright 2010
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.de
*/
#include <common.h>
#include <display.h>
#include <dm.h>
#include <regmap.h>
#include <video_osd.h>
#include <asm/gpio.h>
static const uint MAX_X_CHARS = 53;
static const uint MAX_Y_CHARS = 26;
static const uint MAX_VIDEOMEM_WIDTH = 64;
static const uint MAX_VIDEOMEM_HEIGHT = 32;
static const uint CHAR_WIDTH = 12;
static const uint CHAR_HEIGHT = 18;
static const u16 BASE_WIDTH_MASK = 0x3f00;
static const uint BASE_WIDTH_SHIFT = 8;
static const u16 BASE_HEIGTH_MASK = 0x001f;
static const uint BASE_HEIGTH_SHIFT;
struct ihs_video_out_regs {
/* Device version register */
u16 versions;
/* Device feature register */
u16 features;
/* Device control register */
u16 control;
/* Register controlling screen size */
u16 xy_size;
/* Register controlling screen scaling */
u16 xy_scale;
/* Register controlling screen x position */
u16 x_pos;
/* Register controlling screen y position */
u16 y_pos;
};
#define ihs_video_out_set(map, member, val) \
regmap_range_set(map, 1, struct ihs_video_out_regs, member, val)
#define ihs_video_out_get(map, member, valp) \
regmap_range_get(map, 1, struct ihs_video_out_regs, member, valp)
enum {
CONTROL_FILTER_BLACK = (0 << 0),
CONTROL_FILTER_ORIGINAL = (1 << 0),
CONTROL_FILTER_DARKER = (2 << 0),
CONTROL_FILTER_GRAY = (3 << 0),
CONTROL_MODE_PASSTHROUGH = (0 << 3),
CONTROL_MODE_OSD = (1 << 3),
CONTROL_MODE_AUTO = (2 << 3),
CONTROL_MODE_OFF = (3 << 3),
CONTROL_ENABLE_OFF = (0 << 6),
CONTROL_ENABLE_ON = (1 << 6),
};
struct ihs_video_out_priv {
/* Register map for OSD device */
struct regmap *map;
/* Pointer to video memory */
u16 *vidmem;
/* Display width in text columns */
uint base_width;
/* Display height in text rows */
uint base_height;
/* x-resolution of the display in pixels */
uint res_x;
/* y-resolution of the display in pixels */
uint res_y;
/* OSD's sync mode (resolution + frequency) */
int sync_src;
/* The display port output for this OSD */
struct udevice *video_tx;
/* The pixel clock generator for the display */
struct udevice *clk_gen;
};
static const struct udevice_id ihs_video_out_ids[] = {
{ .compatible = "gdsys,ihs_video_out" },
{ }
};
/**
* set_control() - Set the control register to a given value
*
* The current value of sync_src is preserved by the function automatically.
*
* @dev: the OSD device whose control register to set
* @value: the 16-bit value to write to the control register
* Return: 0
*/
static int set_control(struct udevice *dev, u16 value)
{
struct ihs_video_out_priv *priv = dev_get_priv(dev);
if (priv->sync_src)
value |= ((priv->sync_src & 0x7) << 8);
ihs_video_out_set(priv->map, control, value);
return 0;
}
int ihs_video_out_get_info(struct udevice *dev, struct video_osd_info *info)
{
struct ihs_video_out_priv *priv = dev_get_priv(dev);
u16 versions;
ihs_video_out_get(priv->map, versions, &versions);
info->width = priv->base_width;
info->height = priv->base_height;
info->major_version = versions / 100;
info->minor_version = versions % 100;
return 0;
}
int ihs_video_out_set_mem(struct udevice *dev, uint col, uint row, u8 *buf,
size_t buflen, uint count)
{
struct ihs_video_out_priv *priv = dev_get_priv(dev);
int res;
uint offset;
uint k, rep;
u16 data;
/* Repetitions (controlled via count parmeter) */
for (rep = 0; rep < count; ++rep) {
offset = row * priv->base_width + col + rep * (buflen / 2);
/* Write a single buffer copy */
for (k = 0; k < buflen / 2; ++k) {
uint max_size = priv->base_width * priv->base_height;
if (offset + k >= max_size) {
debug("%s: Write would be out of OSD bounds\n",
dev->name);
return -E2BIG;
}
data = buf[2 * k + 1] + 256 * buf[2 * k];
out_le16(priv->vidmem + offset + k, data);
}
}
res = set_control(dev, CONTROL_FILTER_ORIGINAL |
CONTROL_MODE_OSD |
CONTROL_ENABLE_ON);
if (res) {
debug("%s: Could not set control register\n", dev->name);
return res;
}
return 0;
}
/**
* div2_u16() - Approximately divide a 16-bit number by 2
*
* @val: The 16-bit value to divide by two
* Return: The approximate division of val by two
*/
static inline u16 div2_u16(u16 val)
{
return (32767 * val) / 65535;
}
int ihs_video_out_set_size(struct udevice *dev, uint col, uint row)
{
struct ihs_video_out_priv *priv = dev_get_priv(dev);
if (!col || col > MAX_VIDEOMEM_WIDTH || col > MAX_X_CHARS ||
!row || row > MAX_VIDEOMEM_HEIGHT || row > MAX_Y_CHARS) {
debug("%s: Desired OSD size invalid\n", dev->name);
return -EINVAL;
}
ihs_video_out_set(priv->map, xy_size, ((col - 1) << 8) | (row - 1));
/* Center OSD on screen */
ihs_video_out_set(priv->map, x_pos,
div2_u16(priv->res_x - CHAR_WIDTH * col));
ihs_video_out_set(priv->map, y_pos,
div2_u16(priv->res_y - CHAR_HEIGHT * row));
return 0;
}
int ihs_video_out_print(struct udevice *dev, uint col, uint row, ulong color,
char *text)
{
int res;
u8 buffer[2 * MAX_VIDEOMEM_WIDTH];
uint k;
uint charcount = strlen(text);
uint len = min(charcount, 2 * MAX_VIDEOMEM_WIDTH);
for (k = 0; k < len; ++k) {
buffer[2 * k] = text[k];
buffer[2 * k + 1] = color;
}
res = ihs_video_out_set_mem(dev, col, row, buffer, 2 * len, 1);
if (res < 0) {
debug("%s: Could not write to video memory\n", dev->name);
return res;
}
return 0;
}
static const struct video_osd_ops ihs_video_out_ops = {
.get_info = ihs_video_out_get_info,
.set_mem = ihs_video_out_set_mem,
.set_size = ihs_video_out_set_size,
.print = ihs_video_out_print,
};
int ihs_video_out_probe(struct udevice *dev)
{
struct ihs_video_out_priv *priv = dev_get_priv(dev);
struct ofnode_phandle_args phandle_args;
const char *mode;
u16 features;
struct display_timing timing;
int res;
res = regmap_init_mem(dev_ofnode(dev), &priv->map);
if (res) {
debug("%s: Could not initialize regmap (err = %d)\n", dev->name,
res);
return res;
}
/* Range with index 2 is video memory */
priv->vidmem = regmap_get_range(priv->map, 2);
mode = dev_read_string(dev, "mode");
if (!mode) {
debug("%s: Could not read mode property\n", dev->name);
return -EINVAL;
}
if (!strcmp(mode, "1024_768_60")) {
priv->sync_src = 2;
priv->res_x = 1024;
priv->res_y = 768;
timing.hactive.typ = 1024;
timing.vactive.typ = 768;
} else if (!strcmp(mode, "720_400_70")) {
priv->sync_src = 1;
priv->res_x = 720;
priv->res_y = 400;
timing.hactive.typ = 720;
timing.vactive.typ = 400;
} else {
priv->sync_src = 0;
priv->res_x = 640;
priv->res_y = 480;
timing.hactive.typ = 640;
timing.vactive.typ = 480;
}
ihs_video_out_get(priv->map, features, &features);
res = set_control(dev, CONTROL_FILTER_ORIGINAL |
CONTROL_MODE_OSD |
CONTROL_ENABLE_OFF);
if (res) {
debug("%s: Could not set control register (err = %d)\n",
dev->name, res);
return res;
}
priv->base_width = ((features & BASE_WIDTH_MASK)
>> BASE_WIDTH_SHIFT) + 1;
priv->base_height = ((features & BASE_HEIGTH_MASK)
>> BASE_HEIGTH_SHIFT) + 1;
res = dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0,
&phandle_args);
if (res) {
debug("%s: Could not get clk_gen node (err = %d)\n",
dev->name, res);
return -EINVAL;
}
res = uclass_get_device_by_ofnode(UCLASS_CLK, phandle_args.node,
&priv->clk_gen);
if (res) {
debug("%s: Could not get clk_gen dev (err = %d)\n",
dev->name, res);
return -EINVAL;
}
res = dev_read_phandle_with_args(dev, "video_tx", NULL, 0, 0,
&phandle_args);
if (res) {
debug("%s: Could not get video_tx (err = %d)\n",
dev->name, res);
return -EINVAL;
}
res = uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node,
&priv->video_tx);
if (res) {
debug("%s: Could not get video_tx dev (err = %d)\n",
dev->name, res);
return -EINVAL;
}
res = display_enable(priv->video_tx, 8, &timing);
if (res && res != -EIO) { /* Ignore missing DP sink error */
debug("%s: Could not enable the display (err = %d)\n",
dev->name, res);
return res;
}
return 0;
}
U_BOOT_DRIVER(ihs_video_out_drv) = {
.name = "ihs_video_out_drv",
.id = UCLASS_VIDEO_OSD,
.ops = &ihs_video_out_ops,
.of_match = ihs_video_out_ids,
.probe = ihs_video_out_probe,
.priv_auto_alloc_size = sizeof(struct ihs_video_out_priv),
};
@@ -0,0 +1,8 @@
config VIDEO_IPUV3
bool "i.MX IPUv3 Core video support"
depends on (VIDEO || DM_VIDEO) && (MX5 || MX6)
help
This enables framebuffer driver for i.MX processors working
on the IPUv3(Image Processing Unit) internal graphic processor.
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-y += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o
@@ -0,0 +1,268 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Porting to u-boot:
*
* (C) Copyright 2010
* Stefano Babic, DENX Software Engineering, sbabic@denx.de
*
* Linux IPU driver for MX51:
*
* (C) Copyright 2005-2010 Freescale Semiconductor, Inc.
*/
#ifndef __ASM_ARCH_IPU_H__
#define __ASM_ARCH_IPU_H__
#include <linux/types.h>
#include <ipu_pixfmt.h>
#define IDMA_CHAN_INVALID 0xFF
#define HIGH_RESOLUTION_WIDTH 1024
struct clk {
const char *name;
int id;
/* Source clock this clk depends on */
struct clk *parent;
/* Secondary clock to enable/disable with this clock */
struct clk *secondary;
/* Current clock rate */
unsigned long rate;
/* Reference count of clock enable/disable */
__s8 usecount;
/* Register bit position for clock's enable/disable control. */
u8 enable_shift;
/* Register address for clock's enable/disable control. */
void *enable_reg;
u32 flags;
/*
* Function ptr to recalculate the clock's rate based on parent
* clock's rate
*/
void (*recalc) (struct clk *);
/*
* Function ptr to set the clock to a new rate. The rate must match a
* supported rate returned from round_rate. Leave blank if clock is not
* programmable
*/
int (*set_rate) (struct clk *, unsigned long);
/*
* Function ptr to round the requested clock rate to the nearest
* supported rate that is less than or equal to the requested rate.
*/
unsigned long (*round_rate) (struct clk *, unsigned long);
/*
* Function ptr to enable the clock. Leave blank if clock can not
* be gated.
*/
int (*enable) (struct clk *);
/*
* Function ptr to disable the clock. Leave blank if clock can not
* be gated.
*/
void (*disable) (struct clk *);
/* Function ptr to set the parent clock of the clock. */
int (*set_parent) (struct clk *, struct clk *);
};
/*
* Enumeration of Synchronous (Memory-less) panel types
*/
typedef enum {
IPU_PANEL_SHARP_TFT,
IPU_PANEL_TFT,
} ipu_panel_t;
/*
* IPU Driver channels definitions.
* Note these are different from IDMA channels
*/
#define IPU_MAX_CH 32
#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \
((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24))
#define IPU_CHAN_ID(ch) (ch >> 24)
#define IPU_CHAN_ALT(ch) (ch & 0x02000000)
#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F)
#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F)
#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F)
#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F))
#define NO_DMA 0x3F
#define ALT 1
/*
* Enumeration of IPU logical channels. An IPU logical channel is defined as a
* combination of an input (memory to IPU), output (IPU to memory), and/or
* secondary input IDMA channels and in some cases an Image Converter task.
* Some channels consist of only an input or output.
*/
typedef enum {
CHAN_NONE = -1,
MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
} ipu_channel_t;
/*
* Enumeration of types of buffers for a logical channel.
*/
typedef enum {
IPU_OUTPUT_BUFFER = 0, /*< Buffer for output from IPU */
IPU_ALPHA_IN_BUFFER = 1, /*< Buffer for input to IPU */
IPU_GRAPH_IN_BUFFER = 2, /*< Buffer for input to IPU */
IPU_VIDEO_IN_BUFFER = 3, /*< Buffer for input to IPU */
IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER,
IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER,
} ipu_buffer_t;
#define IPU_PANEL_SERIAL 1
#define IPU_PANEL_PARALLEL 2
struct ipu_channel {
u8 video_in_dma;
u8 alpha_in_dma;
u8 graph_in_dma;
u8 out_dma;
};
enum ipu_dmfc_type {
DMFC_NORMAL = 0,
DMFC_HIGH_RESOLUTION_DC,
DMFC_HIGH_RESOLUTION_DP,
DMFC_HIGH_RESOLUTION_ONLY_DP,
};
/*
* Union of initialization parameters for a logical channel.
*/
typedef union {
struct {
uint32_t di;
unsigned char interlaced;
} mem_dc_sync;
struct {
uint32_t temp;
} mem_sdc_fg;
struct {
uint32_t di;
unsigned char interlaced;
uint32_t in_pixel_fmt;
uint32_t out_pixel_fmt;
unsigned char alpha_chan_en;
} mem_dp_bg_sync;
struct {
uint32_t temp;
} mem_sdc_bg;
struct {
uint32_t di;
unsigned char interlaced;
uint32_t in_pixel_fmt;
uint32_t out_pixel_fmt;
unsigned char alpha_chan_en;
} mem_dp_fg_sync;
} ipu_channel_params_t;
/*
* Enumeration of IPU interrupts.
*/
enum ipu_irq_line {
IPU_IRQ_DP_SF_END = 448 + 3,
IPU_IRQ_DC_FC_1 = 448 + 9,
};
/*
* Bitfield of Display Interface signal polarities.
*/
typedef struct {
unsigned datamask_en:1;
unsigned ext_clk:1;
unsigned interlaced:1;
unsigned odd_field_first:1;
unsigned clksel_en:1;
unsigned clkidle_en:1;
unsigned data_pol:1; /* true = inverted */
unsigned clk_pol:1; /* true = rising edge */
unsigned enable_pol:1;
unsigned Hsync_pol:1; /* true = active high */
unsigned Vsync_pol:1;
} ipu_di_signal_cfg_t;
typedef enum {
RGB,
YCbCr,
YUV
} ipu_color_space_t;
/* Common IPU API */
int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params);
void ipu_uninit_channel(ipu_channel_t channel);
int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
uint32_t pixel_fmt,
uint16_t width, uint16_t height,
uint32_t stride,
dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
uint32_t u_offset, uint32_t v_offset);
int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum, dma_addr_t phyaddr);
int32_t ipu_is_channel_busy(ipu_channel_t channel);
void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum);
int32_t ipu_enable_channel(ipu_channel_t channel);
int32_t ipu_disable_channel(ipu_channel_t channel);
int32_t ipu_init_sync_panel(int disp,
uint32_t pixel_clk,
uint16_t width, uint16_t height,
uint32_t pixel_fmt,
uint16_t h_start_width, uint16_t h_sync_width,
uint16_t h_end_width, uint16_t v_start_width,
uint16_t v_sync_width, uint16_t v_end_width,
uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig);
int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable,
uint8_t alpha);
int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable,
uint32_t colorKey);
uint32_t bytes_per_pixel(uint32_t fmt);
void clk_enable(struct clk *clk);
void clk_disable(struct clk *clk);
u32 clk_get_rate(struct clk *clk);
int clk_set_rate(struct clk *clk, unsigned long rate);
long clk_round_rate(struct clk *clk, unsigned long rate);
int clk_set_parent(struct clk *clk, struct clk *parent);
int clk_get_usecount(struct clk *clk);
struct clk *clk_get_parent(struct clk *clk);
void ipu_dump_registers(void);
int ipu_probe(void);
bool ipu_clk_enabled(void);
void ipu_dmfc_init(int dmfc_type, int first);
void ipu_init_dc_mappings(void);
void ipu_dmfc_set_wait4eot(int dma_chan, int width);
void ipu_dc_init(int dc_chan, int di, unsigned char interlaced);
void ipu_dc_uninit(int dc_chan);
void ipu_dp_dc_enable(ipu_channel_t channel);
int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
uint32_t out_pixel_fmt);
void ipu_dp_uninit(ipu_channel_t channel);
void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap);
ipu_color_space_t format_to_colorspace(uint32_t fmt);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,415 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Porting to u-boot:
*
* (C) Copyright 2010
* Stefano Babic, DENX Software Engineering, sbabic@denx.de
*
* Linux IPU driver for MX51:
*
* (C) Copyright 2005-2009 Freescale Semiconductor, Inc.
*/
#ifndef __IPU_REGS_INCLUDED__
#define __IPU_REGS_INCLUDED__
#define IPU_DISP0_BASE 0x00000000
#define IPU_MCU_T_DEFAULT 8
#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25)
#define IPU_CM_REG_BASE 0x00000000
#define IPU_STAT_REG_BASE 0x00000200
#define IPU_IDMAC_REG_BASE 0x00008000
#define IPU_ISP_REG_BASE 0x00010000
#define IPU_DP_REG_BASE 0x00018000
#define IPU_IC_REG_BASE 0x00020000
#define IPU_IRT_REG_BASE 0x00028000
#define IPU_CSI0_REG_BASE 0x00030000
#define IPU_CSI1_REG_BASE 0x00038000
#define IPU_DI0_REG_BASE 0x00040000
#define IPU_DI1_REG_BASE 0x00048000
#define IPU_SMFC_REG_BASE 0x00050000
#define IPU_DC_REG_BASE 0x00058000
#define IPU_DMFC_REG_BASE 0x00060000
#define IPU_VDI_REG_BASE 0x00680000
#if defined(CONFIG_MX51) || defined(CONFIG_MX53)
#define IPU_CPMEM_REG_BASE 0x01000000
#define IPU_LUT_REG_BASE 0x01020000
#define IPU_SRM_REG_BASE 0x01040000
#define IPU_TPM_REG_BASE 0x01060000
#define IPU_DC_TMPL_REG_BASE 0x01080000
#define IPU_ISP_TBPR_REG_BASE 0x010C0000
#elif defined(CONFIG_MX6)
#define IPU_CPMEM_REG_BASE 0x00100000
#define IPU_LUT_REG_BASE 0x00120000
#define IPU_SRM_REG_BASE 0x00140000
#define IPU_TPM_REG_BASE 0x00160000
#define IPU_DC_TMPL_REG_BASE 0x00180000
#define IPU_ISP_TBPR_REG_BASE 0x001C0000
#endif
#define IPU_CTRL_BASE_ADDR (IPU_SOC_BASE_ADDR + IPU_SOC_OFFSET)
extern u32 *ipu_dc_tmpl_reg;
#define DC_EVT_NF 0
#define DC_EVT_NL 1
#define DC_EVT_EOF 2
#define DC_EVT_NFIELD 3
#define DC_EVT_EOL 4
#define DC_EVT_EOFIELD 5
#define DC_EVT_NEW_ADDR 6
#define DC_EVT_NEW_CHAN 7
#define DC_EVT_NEW_DATA 8
#define DC_EVT_NEW_ADDR_W_0 0
#define DC_EVT_NEW_ADDR_W_1 1
#define DC_EVT_NEW_CHAN_W_0 2
#define DC_EVT_NEW_CHAN_W_1 3
#define DC_EVT_NEW_DATA_W_0 4
#define DC_EVT_NEW_DATA_W_1 5
#define DC_EVT_NEW_ADDR_R_0 6
#define DC_EVT_NEW_ADDR_R_1 7
#define DC_EVT_NEW_CHAN_R_0 8
#define DC_EVT_NEW_CHAN_R_1 9
#define DC_EVT_NEW_DATA_R_0 10
#define DC_EVT_NEW_DATA_R_1 11
/* Software reset for ipu */
#define SW_IPU_RST 8
enum {
IPU_CONF_DP_EN = 0x00000020,
IPU_CONF_DI0_EN = 0x00000040,
IPU_CONF_DI1_EN = 0x00000080,
IPU_CONF_DMFC_EN = 0x00000400,
IPU_CONF_DC_EN = 0x00000200,
DI0_COUNTER_RELEASE = 0x01000000,
DI1_COUNTER_RELEASE = 0x02000000,
DI_DW_GEN_ACCESS_SIZE_OFFSET = 24,
DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16,
DI_GEN_DI_CLK_EXT = 0x100000,
DI_GEN_POLARITY_1 = 0x00000001,
DI_GEN_POLARITY_2 = 0x00000002,
DI_GEN_POLARITY_3 = 0x00000004,
DI_GEN_POLARITY_4 = 0x00000008,
DI_GEN_POLARITY_5 = 0x00000010,
DI_GEN_POLARITY_6 = 0x00000020,
DI_GEN_POLARITY_7 = 0x00000040,
DI_GEN_POLARITY_8 = 0x00000080,
DI_GEN_POL_CLK = 0x20000,
DI_POL_DRDY_DATA_POLARITY = 0x00000080,
DI_POL_DRDY_POLARITY_15 = 0x00000010,
DI_VSYNC_SEL_OFFSET = 13,
DC_WR_CH_CONF_FIELD_MODE = 0x00000200,
DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5,
DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0,
DC_WR_CH_CONF_PROG_DI_ID = 0x00000004,
DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3,
DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018,
DP_COM_CONF_FG_EN = 0x00000001,
DP_COM_CONF_GWSEL = 0x00000002,
DP_COM_CONF_GWAM = 0x00000004,
DP_COM_CONF_GWCKE = 0x00000008,
DP_COM_CONF_CSC_DEF_MASK = 0x00000300,
DP_COM_CONF_CSC_DEF_OFFSET = 8,
DP_COM_CONF_CSC_DEF_FG = 0x00000300,
DP_COM_CONF_CSC_DEF_BG = 0x00000200,
DP_COM_CONF_CSC_DEF_BOTH = 0x00000100,
DP_COM_CONF_GAMMA_EN = 0x00001000,
DP_COM_CONF_GAMMA_YUV_EN = 0x00002000,
};
enum di_pins {
DI_PIN11 = 0,
DI_PIN12 = 1,
DI_PIN13 = 2,
DI_PIN14 = 3,
DI_PIN15 = 4,
DI_PIN16 = 5,
DI_PIN17 = 6,
DI_PIN_CS = 7,
DI_PIN_SER_CLK = 0,
DI_PIN_SER_RS = 1,
};
enum di_sync_wave {
DI_SYNC_NONE = -1,
DI_SYNC_CLK = 0,
DI_SYNC_INT_HSYNC = 1,
DI_SYNC_HSYNC = 2,
DI_SYNC_VSYNC = 3,
DI_SYNC_DE = 5,
};
struct ipu_cm {
u32 conf;
u32 sisg_ctrl0;
u32 sisg_ctrl1;
u32 sisg_set[6];
u32 sisg_clear[6];
u32 int_ctrl[15];
u32 sdma_event[10];
u32 srm_pri1;
u32 srm_pri2;
u32 fs_proc_flow[3];
u32 fs_disp_flow[2];
u32 skip;
u32 disp_alt_conf;
u32 disp_gen;
u32 disp_alt[4];
u32 snoop;
u32 mem_rst;
u32 pm;
u32 gpr;
u32 reserved0[26];
u32 ch_db_mode_sel[2];
u32 reserved1[4];
u32 alt_ch_db_mode_sel[2];
u32 reserved2[2];
u32 ch_trb_mode_sel[2];
};
struct ipu_idmac {
u32 conf;
u32 ch_en[2];
u32 sep_alpha;
u32 alt_sep_alpha;
u32 ch_pri[2];
u32 wm_en[2];
u32 lock_en[2];
u32 sub_addr[5];
u32 bndm_en[2];
u32 sc_cord[2];
u32 reserved[44];
u32 ch_busy[2];
};
struct ipu_com_async {
u32 com_conf_async;
u32 graph_wind_ctrl_async;
u32 fg_pos_async;
u32 cur_pos_async;
u32 cur_map_async;
u32 gamma_c_async[8];
u32 gamma_s_async[4];
u32 dp_csca_async[4];
u32 dp_csc_async[2];
};
struct ipu_dp {
u32 com_conf_sync;
u32 graph_wind_ctrl_sync;
u32 fg_pos_sync;
u32 cur_pos_sync;
u32 cur_map_sync;
u32 gamma_c_sync[8];
u32 gamma_s_sync[4];
u32 csca_sync[4];
u32 csc_sync[2];
u32 cur_pos_alt;
struct ipu_com_async async[2];
};
struct ipu_di {
u32 general;
u32 bs_clkgen0;
u32 bs_clkgen1;
u32 sw_gen0[9];
u32 sw_gen1[9];
u32 sync_as;
u32 dw_gen[12];
u32 dw_set[48];
u32 stp_rep[4];
u32 stp_rep9;
u32 ser_conf;
u32 ssc;
u32 pol;
u32 aw0;
u32 aw1;
u32 scr_conf;
u32 stat;
};
struct ipu_stat {
u32 int_stat[15];
u32 cur_buf[2];
u32 alt_cur_buf_0;
u32 alt_cur_buf_1;
u32 srm_stat;
u32 proc_task_stat;
u32 disp_task_stat;
u32 triple_cur_buf[4];
u32 ch_buf0_rdy[2];
u32 ch_buf1_rdy[2];
u32 alt_ch_buf0_rdy[2];
u32 alt_ch_buf1_rdy[2];
u32 ch_buf2_rdy[2];
};
struct ipu_dc_ch {
u32 wr_ch_conf;
u32 wr_ch_addr;
u32 rl[5];
};
struct ipu_dc {
struct ipu_dc_ch dc_ch0_1_2[3];
u32 cmd_ch_conf_3;
u32 cmd_ch_conf_4;
struct ipu_dc_ch dc_ch5_6[2];
struct ipu_dc_ch dc_ch8;
u32 rl6_ch_8;
struct ipu_dc_ch dc_ch9;
u32 rl6_ch_9;
u32 gen;
u32 disp_conf1[4];
u32 disp_conf2[4];
u32 di0_conf[2];
u32 di1_conf[2];
u32 dc_map_ptr[15];
u32 dc_map_val[12];
u32 udge[16];
u32 lla[2];
u32 r_lla[2];
u32 wr_ch_addr_5_alt;
u32 stat;
};
struct ipu_dmfc {
u32 rd_chan;
u32 wr_chan;
u32 wr_chan_def;
u32 dp_chan;
u32 dp_chan_def;
u32 general[2];
u32 ic_ctrl;
u32 wr_chan_alt;
u32 wr_chan_def_alt;
u32 general1_alt;
u32 stat;
};
#define IPU_CM_REG ((struct ipu_cm *)(IPU_CTRL_BASE_ADDR + \
IPU_CM_REG_BASE))
#define IPU_CONF (&IPU_CM_REG->conf)
#define IPU_SRM_PRI1 (&IPU_CM_REG->srm_pri1)
#define IPU_SRM_PRI2 (&IPU_CM_REG->srm_pri2)
#define IPU_FS_PROC_FLOW1 (&IPU_CM_REG->fs_proc_flow[0])
#define IPU_FS_PROC_FLOW2 (&IPU_CM_REG->fs_proc_flow[1])
#define IPU_FS_PROC_FLOW3 (&IPU_CM_REG->fs_proc_flow[2])
#define IPU_FS_DISP_FLOW1 (&IPU_CM_REG->fs_disp_flow[0])
#define IPU_DISP_GEN (&IPU_CM_REG->disp_gen)
#define IPU_MEM_RST (&IPU_CM_REG->mem_rst)
#define IPU_GPR (&IPU_CM_REG->gpr)
#define IPU_CHA_DB_MODE_SEL(ch) (&IPU_CM_REG->ch_db_mode_sel[ch / 32])
#define IPU_STAT ((struct ipu_stat *)(IPU_CTRL_BASE_ADDR + \
IPU_STAT_REG_BASE))
#define IPU_INT_STAT(n) (&IPU_STAT->int_stat[(n) - 1])
#define IPU_CHA_CUR_BUF(ch) (&IPU_STAT->cur_buf[ch / 32])
#define IPU_CHA_BUF0_RDY(ch) (&IPU_STAT->ch_buf0_rdy[ch / 32])
#define IPU_CHA_BUF1_RDY(ch) (&IPU_STAT->ch_buf1_rdy[ch / 32])
#define IPUIRQ_2_STATREG(irq) (IPU_INT_STAT(1) + ((irq) / 32))
#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F))
#define IPU_INT_CTRL(n) (&IPU_CM_REG->int_ctrl[(n) - 1])
#define IDMAC_REG ((struct ipu_idmac *)(IPU_CTRL_BASE_ADDR + \
IPU_IDMAC_REG_BASE))
#define IDMAC_CONF (&IDMAC_REG->conf)
#define IDMAC_CHA_EN(ch) (&IDMAC_REG->ch_en[ch / 32])
#define IDMAC_CHA_PRI(ch) (&IDMAC_REG->ch_pri[ch / 32])
#define DI_REG(di) ((struct ipu_di *)(IPU_CTRL_BASE_ADDR + \
((di == 1) ? IPU_DI1_REG_BASE : \
IPU_DI0_REG_BASE)))
#define DI_GENERAL(di) (&DI_REG(di)->general)
#define DI_BS_CLKGEN0(di) (&DI_REG(di)->bs_clkgen0)
#define DI_BS_CLKGEN1(di) (&DI_REG(di)->bs_clkgen1)
#define DI_SW_GEN0(di, gen) (&DI_REG(di)->sw_gen0[gen - 1])
#define DI_SW_GEN1(di, gen) (&DI_REG(di)->sw_gen1[gen - 1])
#define DI_STP_REP(di, gen) (&DI_REG(di)->stp_rep[(gen - 1) / 2])
#define DI_STP_REP9(di) (&DI_REG(di)->stp_rep9)
#define DI_SYNC_AS_GEN(di) (&DI_REG(di)->sync_as)
#define DI_DW_GEN(di, gen) (&DI_REG(di)->dw_gen[gen])
#define DI_DW_SET(di, gen, set) (&DI_REG(di)->dw_set[gen + 12 * set])
#define DI_POL(di) (&DI_REG(di)->pol)
#define DI_SCR_CONF(di) (&DI_REG(di)->scr_conf)
#define DMFC_REG ((struct ipu_dmfc *)(IPU_CTRL_BASE_ADDR + \
IPU_DMFC_REG_BASE))
#define DMFC_WR_CHAN (&DMFC_REG->wr_chan)
#define DMFC_WR_CHAN_DEF (&DMFC_REG->wr_chan_def)
#define DMFC_DP_CHAN (&DMFC_REG->dp_chan)
#define DMFC_DP_CHAN_DEF (&DMFC_REG->dp_chan_def)
#define DMFC_GENERAL1 (&DMFC_REG->general[0])
#define DMFC_IC_CTRL (&DMFC_REG->ic_ctrl)
#define DC_REG ((struct ipu_dc *)(IPU_CTRL_BASE_ADDR + \
IPU_DC_REG_BASE))
#define DC_MAP_CONF_PTR(n) (&DC_REG->dc_map_ptr[n / 2])
#define DC_MAP_CONF_VAL(n) (&DC_REG->dc_map_val[n / 2])
static inline struct ipu_dc_ch *dc_ch_offset(int ch)
{
switch (ch) {
case 0:
case 1:
case 2:
return &DC_REG->dc_ch0_1_2[ch];
case 5:
case 6:
return &DC_REG->dc_ch5_6[ch - 5];
case 8:
return &DC_REG->dc_ch8;
case 9:
return &DC_REG->dc_ch9;
default:
printf("%s: invalid channel %d\n", __func__, ch);
return NULL;
}
}
#define DC_RL_CH(ch, evt) (&dc_ch_offset(ch)->rl[evt / 2])
#define DC_WR_CH_CONF(ch) (&dc_ch_offset(ch)->wr_ch_conf)
#define DC_WR_CH_ADDR(ch) (&dc_ch_offset(ch)->wr_ch_addr)
#define DC_WR_CH_CONF_1 DC_WR_CH_CONF(1)
#define DC_WR_CH_CONF_5 DC_WR_CH_CONF(5)
#define DC_GEN (&DC_REG->gen)
#define DC_DISP_CONF2(disp) (&DC_REG->disp_conf2[disp])
#define DC_STAT (&DC_REG->stat)
#define DP_SYNC 0
#define DP_ASYNC0 0x60
#define DP_ASYNC1 0xBC
#define DP_REG ((struct ipu_dp *)(IPU_CTRL_BASE_ADDR + \
IPU_DP_REG_BASE))
#define DP_COM_CONF() (&DP_REG->com_conf_sync)
#define DP_GRAPH_WIND_CTRL() (&DP_REG->graph_wind_ctrl_sync)
#define DP_CSC_A_0() (&DP_REG->csca_sync[0])
#define DP_CSC_A_1() (&DP_REG->csca_sync[1])
#define DP_CSC_A_2() (&DP_REG->csca_sync[2])
#define DP_CSC_A_3() (&DP_REG->csca_sync[3])
#define DP_CSC_0() (&DP_REG->csc_sync[0])
#define DP_CSC_1() (&DP_REG->csc_sync[1])
/* DC template opcodes */
#define WROD(lf) (0x18 | (lf << 1))
#endif
@@ -0,0 +1,721 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Porting to u-boot:
*
* (C) Copyright 2010
* Stefano Babic, DENX Software Engineering, sbabic@denx.de
*
* MX51 Linux framebuffer:
*
* (C) Copyright 2004-2010 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <linux/errno.h>
#include <asm/global_data.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/fb.h>
#include <asm/io.h>
#include <asm/mach-imx/video.h>
#include <malloc.h>
#include <video_fb.h>
#include "../videomodes.h"
#include "ipu.h"
#include "mxcfb.h"
#include "ipu_regs.h"
#include "display.h"
#include <panel.h>
#include <dm.h>
#include <video.h>
DECLARE_GLOBAL_DATA_PTR;
static int mxcfb_map_video_memory(struct fb_info *fbi);
static int mxcfb_unmap_video_memory(struct fb_info *fbi);
/* graphics setup */
static GraphicDevice panel;
static struct fb_videomode const *gmode;
static uint8_t gdisp;
static uint32_t gpixfmt;
static void fb_videomode_to_var(struct fb_var_screeninfo *var,
const struct fb_videomode *mode)
{
var->xres = mode->xres;
var->yres = mode->yres;
var->xres_virtual = mode->xres;
var->yres_virtual = mode->yres;
var->xoffset = 0;
var->yoffset = 0;
var->pixclock = mode->pixclock;
var->left_margin = mode->left_margin;
var->right_margin = mode->right_margin;
var->upper_margin = mode->upper_margin;
var->lower_margin = mode->lower_margin;
var->hsync_len = mode->hsync_len;
var->vsync_len = mode->vsync_len;
var->sync = mode->sync;
var->vmode = mode->vmode & FB_VMODE_MASK;
}
/*
* Structure containing the MXC specific framebuffer information.
*/
struct mxcfb_info {
int blank;
ipu_channel_t ipu_ch;
int ipu_di;
u32 ipu_di_pix_fmt;
unsigned char overlay;
unsigned char alpha_chan_en;
dma_addr_t alpha_phy_addr0;
dma_addr_t alpha_phy_addr1;
void *alpha_virt_addr0;
void *alpha_virt_addr1;
uint32_t alpha_mem_len;
uint32_t cur_ipu_buf;
uint32_t cur_ipu_alpha_buf;
u32 pseudo_palette[16];
};
enum {
BOTH_ON,
SRC_ON,
TGT_ON,
BOTH_OFF
};
static unsigned long default_bpp = 16;
static unsigned char g_dp_in_use;
static struct fb_info *mxcfb_info[3];
static int ext_clk_used;
static uint32_t bpp_to_pixfmt(struct fb_info *fbi)
{
uint32_t pixfmt = 0;
debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel);
if (fbi->var.nonstd)
return fbi->var.nonstd;
switch (fbi->var.bits_per_pixel) {
case 24:
pixfmt = IPU_PIX_FMT_BGR24;
break;
case 32:
pixfmt = IPU_PIX_FMT_BGR32;
break;
case 16:
pixfmt = IPU_PIX_FMT_RGB565;
break;
}
return pixfmt;
}
/*
* Set fixed framebuffer parameters based on variable settings.
*
* @param info framebuffer information pointer
*/
static int mxcfb_set_fix(struct fb_info *info)
{
struct fb_fix_screeninfo *fix = &info->fix;
struct fb_var_screeninfo *var = &info->var;
fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
fix->type = FB_TYPE_PACKED_PIXELS;
fix->accel = FB_ACCEL_NONE;
fix->visual = FB_VISUAL_TRUECOLOR;
fix->xpanstep = 1;
fix->ypanstep = 1;
return 0;
}
static int setup_disp_channel1(struct fb_info *fbi)
{
ipu_channel_params_t params;
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
memset(&params, 0, sizeof(params));
params.mem_dp_bg_sync.di = mxc_fbi->ipu_di;
debug("%s called\n", __func__);
/*
* Assuming interlaced means yuv output, below setting also
* valid for mem_dc_sync. FG should have the same vmode as BG.
*/
if (fbi->var.vmode & FB_VMODE_INTERLACED) {
params.mem_dp_bg_sync.interlaced = 1;
params.mem_dp_bg_sync.out_pixel_fmt =
IPU_PIX_FMT_YUV444;
} else {
if (mxc_fbi->ipu_di_pix_fmt) {
params.mem_dp_bg_sync.out_pixel_fmt =
mxc_fbi->ipu_di_pix_fmt;
} else {
params.mem_dp_bg_sync.out_pixel_fmt =
IPU_PIX_FMT_RGB666;
}
}
params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
if (mxc_fbi->alpha_chan_en)
params.mem_dp_bg_sync.alpha_chan_en = 1;
ipu_init_channel(mxc_fbi->ipu_ch, &params);
return 0;
}
static int setup_disp_channel2(struct fb_info *fbi)
{
int retval = 0;
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
mxc_fbi->cur_ipu_buf = 1;
if (mxc_fbi->alpha_chan_en)
mxc_fbi->cur_ipu_alpha_buf = 1;
fbi->var.xoffset = fbi->var.yoffset = 0;
debug("%s: %x %d %d %d %lx %lx\n",
__func__,
mxc_fbi->ipu_ch,
fbi->var.xres,
fbi->var.yres,
fbi->fix.line_length,
fbi->fix.smem_start,
fbi->fix.smem_start +
(fbi->fix.line_length * fbi->var.yres));
retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
bpp_to_pixfmt(fbi),
fbi->var.xres, fbi->var.yres,
fbi->fix.line_length,
fbi->fix.smem_start +
(fbi->fix.line_length * fbi->var.yres),
fbi->fix.smem_start,
0, 0);
if (retval)
printf("ipu_init_channel_buffer error %d\n", retval);
return retval;
}
/*
* Set framebuffer parameters and change the operating mode.
*
* @param info framebuffer information pointer
*/
static int mxcfb_set_par(struct fb_info *fbi)
{
int retval = 0;
u32 mem_len;
ipu_di_signal_cfg_t sig_cfg;
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
uint32_t out_pixel_fmt;
ipu_disable_channel(mxc_fbi->ipu_ch);
ipu_uninit_channel(mxc_fbi->ipu_ch);
mxcfb_set_fix(fbi);
mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) {
if (fbi->fix.smem_start)
mxcfb_unmap_video_memory(fbi);
if (mxcfb_map_video_memory(fbi) < 0)
return -ENOMEM;
}
setup_disp_channel1(fbi);
memset(&sig_cfg, 0, sizeof(sig_cfg));
if (fbi->var.vmode & FB_VMODE_INTERLACED) {
sig_cfg.interlaced = 1;
out_pixel_fmt = IPU_PIX_FMT_YUV444;
} else {
if (mxc_fbi->ipu_di_pix_fmt)
out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
else
out_pixel_fmt = IPU_PIX_FMT_RGB666;
}
if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
sig_cfg.odd_field_first = 1;
if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used)
sig_cfg.ext_clk = 1;
if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
sig_cfg.Hsync_pol = 1;
if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
sig_cfg.Vsync_pol = 1;
if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
sig_cfg.clk_pol = 1;
if (fbi->var.sync & FB_SYNC_DATA_INVERT)
sig_cfg.data_pol = 1;
if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
sig_cfg.enable_pol = 1;
if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
sig_cfg.clkidle_en = 1;
debug("pixclock = %lu Hz\n", PICOS2KHZ(fbi->var.pixclock) * 1000UL);
if (ipu_init_sync_panel(mxc_fbi->ipu_di,
(PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
fbi->var.xres, fbi->var.yres,
out_pixel_fmt,
fbi->var.left_margin,
fbi->var.hsync_len,
fbi->var.right_margin,
fbi->var.upper_margin,
fbi->var.vsync_len,
fbi->var.lower_margin,
0, sig_cfg) != 0) {
puts("mxcfb: Error initializing panel.\n");
return -EINVAL;
}
retval = setup_disp_channel2(fbi);
if (retval)
return retval;
if (mxc_fbi->blank == FB_BLANK_UNBLANK)
ipu_enable_channel(mxc_fbi->ipu_ch);
return retval;
}
/*
* Check framebuffer variable parameters and adjust to valid values.
*
* @param var framebuffer variable parameters
*
* @param info framebuffer information pointer
*/
static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
u32 vtotal;
u32 htotal;
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
(var->bits_per_pixel != 16) && (var->bits_per_pixel != 8))
var->bits_per_pixel = default_bpp;
switch (var->bits_per_pixel) {
case 8:
var->red.length = 3;
var->red.offset = 5;
var->red.msb_right = 0;
var->green.length = 3;
var->green.offset = 2;
var->green.msb_right = 0;
var->blue.length = 2;
var->blue.offset = 0;
var->blue.msb_right = 0;
var->transp.length = 0;
var->transp.offset = 0;
var->transp.msb_right = 0;
break;
case 16:
var->red.length = 5;
var->red.offset = 11;
var->red.msb_right = 0;
var->green.length = 6;
var->green.offset = 5;
var->green.msb_right = 0;
var->blue.length = 5;
var->blue.offset = 0;
var->blue.msb_right = 0;
var->transp.length = 0;
var->transp.offset = 0;
var->transp.msb_right = 0;
break;
case 24:
var->red.length = 8;
var->red.offset = 16;
var->red.msb_right = 0;
var->green.length = 8;
var->green.offset = 8;
var->green.msb_right = 0;
var->blue.length = 8;
var->blue.offset = 0;
var->blue.msb_right = 0;
var->transp.length = 0;
var->transp.offset = 0;
var->transp.msb_right = 0;
break;
case 32:
var->red.length = 8;
var->red.offset = 16;
var->red.msb_right = 0;
var->green.length = 8;
var->green.offset = 8;
var->green.msb_right = 0;
var->blue.length = 8;
var->blue.offset = 0;
var->blue.msb_right = 0;
var->transp.length = 8;
var->transp.offset = 24;
var->transp.msb_right = 0;
break;
}
if (var->pixclock < 1000) {
htotal = var->xres + var->right_margin + var->hsync_len +
var->left_margin;
vtotal = var->yres + var->lower_margin + var->vsync_len +
var->upper_margin;
var->pixclock = (vtotal * htotal * 6UL) / 100UL;
var->pixclock = KHZ2PICOS(var->pixclock);
printf("pixclock set for 60Hz refresh = %u ps\n",
var->pixclock);
}
var->height = -1;
var->width = -1;
var->grayscale = 0;
return 0;
}
static int mxcfb_map_video_memory(struct fb_info *fbi)
{
if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) {
fbi->fix.smem_len = fbi->var.yres_virtual *
fbi->fix.line_length;
}
fbi->fix.smem_len = roundup(fbi->fix.smem_len, ARCH_DMA_MINALIGN);
#if CONFIG_IS_ENABLED(DM_VIDEO)
fbi->screen_base = (char *)gd->video_bottom;
#else
fbi->screen_base = (char *)memalign(ARCH_DMA_MINALIGN,
fbi->fix.smem_len);
#endif
fbi->fix.smem_start = (unsigned long)fbi->screen_base;
if (fbi->screen_base == 0) {
puts("Unable to allocate framebuffer memory\n");
fbi->fix.smem_len = 0;
fbi->fix.smem_start = 0;
return -EBUSY;
}
debug("allocated fb @ paddr=0x%08X, size=%d.\n",
(uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
fbi->screen_size = fbi->fix.smem_len;
#if CONFIG_IS_ENABLED(VIDEO)
gd->fb_base = fbi->fix.smem_start;
#endif
/* Clear the screen */
memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
return 0;
}
static int mxcfb_unmap_video_memory(struct fb_info *fbi)
{
fbi->screen_base = 0;
fbi->fix.smem_start = 0;
fbi->fix.smem_len = 0;
return 0;
}
/*
* Initializes the framebuffer information pointer. After allocating
* sufficient memory for the framebuffer structure, the fields are
* filled with custom information passed in from the configurable
* structures. This includes information such as bits per pixel,
* color maps, screen width/height and RGBA offsets.
*
* @return Framebuffer structure initialized with our information
*/
static struct fb_info *mxcfb_init_fbinfo(void)
{
#define BYTES_PER_LONG 4
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
char *p;
int size = sizeof(struct mxcfb_info) + PADDING +
sizeof(struct fb_info);
debug("%s: %d %d %d %d\n",
__func__,
PADDING,
size,
sizeof(struct mxcfb_info),
sizeof(struct fb_info));
/*
* Allocate sufficient memory for the fb structure
*/
p = malloc(size);
if (!p)
return NULL;
memset(p, 0, size);
fbi = (struct fb_info *)p;
fbi->par = p + sizeof(struct fb_info) + PADDING;
mxcfbi = (struct mxcfb_info *)fbi->par;
debug("Framebuffer structures at: fbi=0x%x mxcfbi=0x%x\n",
(unsigned int)fbi, (unsigned int)mxcfbi);
fbi->var.activate = FB_ACTIVATE_NOW;
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->pseudo_palette = mxcfbi->pseudo_palette;
return fbi;
}
/*
* Probe routine for the framebuffer driver. It is called during the
* driver binding process. The following functions are performed in
* this routine: Framebuffer initialization, Memory allocation and
* mapping, Framebuffer registration, IPU initialization.
*
* @return Appropriate error code to the kernel common code
*/
static int mxcfb_probe(u32 interface_pix_fmt, uint8_t disp,
struct fb_videomode const *mode)
{
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
int ret = 0;
/*
* Initialize FB structures
*/
fbi = mxcfb_init_fbinfo();
if (!fbi) {
ret = -ENOMEM;
goto err0;
}
mxcfbi = (struct mxcfb_info *)fbi->par;
if (!g_dp_in_use) {
mxcfbi->ipu_ch = MEM_BG_SYNC;
mxcfbi->blank = FB_BLANK_UNBLANK;
} else {
mxcfbi->ipu_ch = MEM_DC_SYNC;
mxcfbi->blank = FB_BLANK_POWERDOWN;
}
mxcfbi->ipu_di = disp;
ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80);
ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0);
strcpy(fbi->fix.id, "DISP3 BG");
g_dp_in_use = 1;
mxcfb_info[mxcfbi->ipu_di] = fbi;
/* Need dummy values until real panel is configured */
mxcfbi->ipu_di_pix_fmt = interface_pix_fmt;
fb_videomode_to_var(&fbi->var, mode);
fbi->var.bits_per_pixel = 16;
fbi->fix.line_length = fbi->var.xres * (fbi->var.bits_per_pixel / 8);
fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length;
mxcfb_check_var(&fbi->var, fbi);
/* Default Y virtual size is 2x panel size */
fbi->var.yres_virtual = fbi->var.yres * 2;
mxcfb_set_fix(fbi);
/* allocate fb first */
if (mxcfb_map_video_memory(fbi) < 0)
return -ENOMEM;
mxcfb_set_par(fbi);
panel.winSizeX = mode->xres;
panel.winSizeY = mode->yres;
panel.plnSizeX = mode->xres;
panel.plnSizeY = mode->yres;
panel.frameAdrs = (u32)fbi->screen_base;
panel.memSize = fbi->screen_size;
panel.gdfBytesPP = 2;
panel.gdfIndex = GDF_16BIT_565RGB;
ipu_dump_registers();
return 0;
err0:
return ret;
}
void ipuv3_fb_shutdown(void)
{
int i;
struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT;
if (!ipu_clk_enabled())
return;
for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) {
struct fb_info *fbi = mxcfb_info[i];
if (fbi) {
struct mxcfb_info *mxc_fbi = fbi->par;
ipu_disable_channel(mxc_fbi->ipu_ch);
ipu_uninit_channel(mxc_fbi->ipu_ch);
}
}
for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) {
__raw_writel(__raw_readl(&stat->int_stat[i]),
&stat->int_stat[i]);
}
}
void *video_hw_init(void)
{
int ret;
ret = ipu_probe();
if (ret)
puts("Error initializing IPU\n");
ret = mxcfb_probe(gpixfmt, gdisp, gmode);
debug("Framebuffer at 0x%x\n", (unsigned int)panel.frameAdrs);
gd->fb_base = panel.frameAdrs;
return (void *)&panel;
}
int ipuv3_fb_init(struct fb_videomode const *mode,
uint8_t disp,
uint32_t pixfmt)
{
gmode = mode;
gdisp = disp;
gpixfmt = pixfmt;
return 0;
}
#if CONFIG_IS_ENABLED(DM_VIDEO)
enum {
/* Maximum display size we support */
LCD_MAX_WIDTH = 1920,
LCD_MAX_HEIGHT = 1080,
LCD_MAX_LOG2_BPP = VIDEO_BPP16,
};
static int ipuv3_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
#if defined(CONFIG_DISPLAY)
struct udevice *disp_dev;
#endif
struct udevice *panel_dev;
u32 fb_start, fb_end;
int ret;
debug("%s() plat: base 0x%lx, size 0x%x\n",
__func__, plat->base, plat->size);
ret = ipu_probe();
if (ret)
return ret;
ret = ipu_displays_init();
if (ret < 0)
return ret;
ret = mxcfb_probe(gpixfmt, gdisp, gmode);
if (ret < 0)
return ret;
#if defined(CONFIG_DISPLAY)
ret = uclass_first_device(UCLASS_DISPLAY, &disp_dev);
if (disp_dev) {
ret = display_enable(disp_dev, 16, NULL);
if (ret < 0)
return ret;
}
#endif
ret = uclass_get_device(UCLASS_PANEL, 0, &panel_dev);
if (panel_dev)
panel_enable_backlight(panel_dev);
uc_priv->xsize = gmode->xres;
uc_priv->ysize = gmode->yres;
uc_priv->bpix = LCD_MAX_LOG2_BPP;
/* Enable dcache for the frame buffer */
fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
fb_end = plat->base + plat->size;
fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
DCACHE_WRITEBACK);
video_set_flush_dcache(dev, true);
gd->fb_base = fb_start;
return 0;
}
struct ipuv3_video_priv {
ulong regs;
};
static int ipuv3_video_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
(1 << VIDEO_BPP32) / 8;
return 0;
}
static const struct udevice_id ipuv3_video_ids[] = {
{ .compatible = "fsl,imx6q-ipu" },
{ .compatible = "fsl,imx53-ipu" },
{ }
};
U_BOOT_DRIVER(ipuv3_video) = {
.name = "ipuv3_video",
.id = UCLASS_VIDEO,
.of_match = ipuv3_video_ids,
.bind = ipuv3_video_bind,
.probe = ipuv3_video_probe,
.priv_auto_alloc_size = sizeof(struct ipuv3_video_priv),
.flags = DM_FLAG_PRE_RELOC,
};
#endif /* CONFIG_DM_VIDEO */
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Porting to u-boot:
*
* (C) Copyright 2010
* Stefano Babic, DENX Software Engineering, sbabic@denx.de
*
* Linux IPU driver for MX51:
*
* (C) Copyright 2004-2009 Freescale Semiconductor, Inc.
*/
#ifndef __ASM_ARCH_MXCFB_H__
#define __ASM_ARCH_MXCFB_H__
#define FB_SYNC_OE_LOW_ACT 0x80000000
#define FB_SYNC_CLK_LAT_FALL 0x40000000
#define FB_SYNC_DATA_INVERT 0x20000000
#define FB_SYNC_CLK_IDLE_EN 0x10000000
#define FB_SYNC_SHARP_MODE 0x08000000
#define FB_SYNC_SWAP_RGB 0x04000000
struct mxcfb_gbl_alpha {
int enable;
int alpha;
};
struct mxcfb_loc_alpha {
int enable;
int alpha_in_pixel;
unsigned long alpha_phy_addr0;
unsigned long alpha_phy_addr1;
};
struct mxcfb_color_key {
int enable;
__u32 color_key;
};
struct mxcfb_pos {
__u16 x;
__u16 y;
};
struct mxcfb_gamma {
int enable;
int constk[16];
int slopek[16];
};
#endif
@@ -0,0 +1,788 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 Google, Inc
*/
#include <common.h>
#include <bios_emul.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <pci_rom.h>
#include <vbe.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/arch/pch.h>
#include <asm/arch/sandybridge.h>
DECLARE_GLOBAL_DATA_PTR;
struct gt_powermeter {
u16 reg;
u32 value;
};
/* These are magic values - unfortunately the meaning is unknown */
static const struct gt_powermeter snb_pm_gt1[] = {
{ 0xa200, 0xcc000000 },
{ 0xa204, 0x07000040 },
{ 0xa208, 0x0000fe00 },
{ 0xa20c, 0x00000000 },
{ 0xa210, 0x17000000 },
{ 0xa214, 0x00000021 },
{ 0xa218, 0x0817fe19 },
{ 0xa21c, 0x00000000 },
{ 0xa220, 0x00000000 },
{ 0xa224, 0xcc000000 },
{ 0xa228, 0x07000040 },
{ 0xa22c, 0x0000fe00 },
{ 0xa230, 0x00000000 },
{ 0xa234, 0x17000000 },
{ 0xa238, 0x00000021 },
{ 0xa23c, 0x0817fe19 },
{ 0xa240, 0x00000000 },
{ 0xa244, 0x00000000 },
{ 0xa248, 0x8000421e },
{ 0 }
};
static const struct gt_powermeter snb_pm_gt2[] = {
{ 0xa200, 0x330000a6 },
{ 0xa204, 0x402d0031 },
{ 0xa208, 0x00165f83 },
{ 0xa20c, 0xf1000000 },
{ 0xa210, 0x00000000 },
{ 0xa214, 0x00160016 },
{ 0xa218, 0x002a002b },
{ 0xa21c, 0x00000000 },
{ 0xa220, 0x00000000 },
{ 0xa224, 0x330000a6 },
{ 0xa228, 0x402d0031 },
{ 0xa22c, 0x00165f83 },
{ 0xa230, 0xf1000000 },
{ 0xa234, 0x00000000 },
{ 0xa238, 0x00160016 },
{ 0xa23c, 0x002a002b },
{ 0xa240, 0x00000000 },
{ 0xa244, 0x00000000 },
{ 0xa248, 0x8000421e },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt1[] = {
{ 0xa800, 0x00000000 },
{ 0xa804, 0x00021c00 },
{ 0xa808, 0x00000403 },
{ 0xa80c, 0x02001700 },
{ 0xa810, 0x05000200 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00690500 },
{ 0xa81c, 0x0000007f },
{ 0xa820, 0x01002501 },
{ 0xa824, 0x00000300 },
{ 0xa828, 0x01000331 },
{ 0xa82c, 0x0000000c },
{ 0xa830, 0x00010016 },
{ 0xa834, 0x01100101 },
{ 0xa838, 0x00010103 },
{ 0xa83c, 0x00041300 },
{ 0xa840, 0x00000b30 },
{ 0xa844, 0x00000000 },
{ 0xa848, 0x7f000000 },
{ 0xa84c, 0x05000008 },
{ 0xa850, 0x00000001 },
{ 0xa854, 0x00000004 },
{ 0xa858, 0x00000007 },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00010000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00001c00 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x06000000 },
{ 0xa910, 0x09000200 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00590000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x04002501 },
{ 0xa924, 0x00000100 },
{ 0xa928, 0x03000410 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00020000 },
{ 0xa934, 0x02070106 },
{ 0xa938, 0x00010100 },
{ 0xa93c, 0x00401c00 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x10000e00 },
{ 0xa94c, 0x02000004 },
{ 0xa950, 0x00000001 },
{ 0xa954, 0x00000004 },
{ 0xa960, 0x00060000 },
{ 0xaa3c, 0x00001c00 },
{ 0xaa54, 0x00000004 },
{ 0xaa60, 0x00060000 },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt2_17w[] = {
{ 0xa800, 0x20000000 },
{ 0xa804, 0x000e3800 },
{ 0xa808, 0x00000806 },
{ 0xa80c, 0x0c002f00 },
{ 0xa810, 0x0c000800 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00d20d00 },
{ 0xa81c, 0x000000ff },
{ 0xa820, 0x03004b02 },
{ 0xa824, 0x00000600 },
{ 0xa828, 0x07000773 },
{ 0xa82c, 0x00000000 },
{ 0xa830, 0x00020032 },
{ 0xa834, 0x1520040d },
{ 0xa838, 0x00020105 },
{ 0xa83c, 0x00083700 },
{ 0xa840, 0x000016ff },
{ 0xa844, 0x00000000 },
{ 0xa848, 0xff000000 },
{ 0xa84c, 0x0a000010 },
{ 0xa850, 0x00000002 },
{ 0xa854, 0x00000008 },
{ 0xa858, 0x0000000f },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00020000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00003800 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x0c000000 },
{ 0xa910, 0x12000800 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00b20000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x08004b02 },
{ 0xa924, 0x00000300 },
{ 0xa928, 0x01000820 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00030000 },
{ 0xa934, 0x15150406 },
{ 0xa938, 0x00020300 },
{ 0xa93c, 0x00903900 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x20001b00 },
{ 0xa94c, 0x0a000010 },
{ 0xa950, 0x00000000 },
{ 0xa954, 0x00000008 },
{ 0xa960, 0x00110000 },
{ 0xaa3c, 0x00003900 },
{ 0xaa54, 0x00000008 },
{ 0xaa60, 0x00110000 },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt2_35w[] = {
{ 0xa800, 0x00000000 },
{ 0xa804, 0x00030400 },
{ 0xa808, 0x00000806 },
{ 0xa80c, 0x0c002f00 },
{ 0xa810, 0x0c000300 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00d20d00 },
{ 0xa81c, 0x000000ff },
{ 0xa820, 0x03004b02 },
{ 0xa824, 0x00000600 },
{ 0xa828, 0x07000773 },
{ 0xa82c, 0x00000000 },
{ 0xa830, 0x00020032 },
{ 0xa834, 0x1520040d },
{ 0xa838, 0x00020105 },
{ 0xa83c, 0x00083700 },
{ 0xa840, 0x000016ff },
{ 0xa844, 0x00000000 },
{ 0xa848, 0xff000000 },
{ 0xa84c, 0x0a000010 },
{ 0xa850, 0x00000001 },
{ 0xa854, 0x00000008 },
{ 0xa858, 0x00000008 },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00020000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00003800 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x0c000000 },
{ 0xa910, 0x12000800 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00b20000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x08004b02 },
{ 0xa924, 0x00000300 },
{ 0xa928, 0x01000820 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00030000 },
{ 0xa934, 0x15150406 },
{ 0xa938, 0x00020300 },
{ 0xa93c, 0x00903900 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x20001b00 },
{ 0xa94c, 0x0a000010 },
{ 0xa950, 0x00000000 },
{ 0xa954, 0x00000008 },
{ 0xa960, 0x00110000 },
{ 0xaa3c, 0x00003900 },
{ 0xaa54, 0x00000008 },
{ 0xaa60, 0x00110000 },
{ 0 }
};
static inline u32 gtt_read(void *bar, u32 reg)
{
return readl(bar + reg);
}
static inline void gtt_write(void *bar, u32 reg, u32 data)
{
writel(data, bar + reg);
}
static void gtt_write_powermeter(void *bar, const struct gt_powermeter *pm)
{
for (; pm && pm->reg; pm++)
gtt_write(bar, pm->reg, pm->value);
}
#define GTT_RETRY 1000
static int gtt_poll(void *bar, u32 reg, u32 mask, u32 value)
{
unsigned try = GTT_RETRY;
u32 data;
while (try--) {
data = gtt_read(bar, reg);
if ((data & mask) == value)
return 1;
udelay(10);
}
printf("GT init timeout\n");
return 0;
}
static int gma_pm_init_pre_vbios(void *gtt_bar, int rev)
{
u32 reg32;
debug("GT Power Management Init, silicon = %#x\n", rev);
if (rev < IVB_STEP_C0) {
/* 1: Enable force wake */
gtt_write(gtt_bar, 0xa18c, 0x00000001);
gtt_poll(gtt_bar, 0x130090, (1 << 0), (1 << 0));
} else {
gtt_write(gtt_bar, 0xa180, 1 << 5);
gtt_write(gtt_bar, 0xa188, 0xffff0001);
gtt_poll(gtt_bar, 0x130040, (1 << 0), (1 << 0));
}
if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
/* 1d: Set GTT+0x42004 [15:14]=11 (SnB C1+) */
reg32 = gtt_read(gtt_bar, 0x42004);
reg32 |= (1 << 14) | (1 << 15);
gtt_write(gtt_bar, 0x42004, reg32);
}
if (rev >= IVB_STEP_A0) {
/* Display Reset Acknowledge Settings */
reg32 = gtt_read(gtt_bar, 0x45010);
reg32 |= (1 << 1) | (1 << 0);
gtt_write(gtt_bar, 0x45010, reg32);
}
/* 2: Get GT SKU from GTT+0x911c[13] */
reg32 = gtt_read(gtt_bar, 0x911c);
if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
if (reg32 & (1 << 13)) {
debug("SNB GT1 Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, snb_pm_gt1);
} else {
debug("SNB GT2 Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, snb_pm_gt2);
}
} else {
u32 unit = readl(MCHBAR_REG(0x5938)) & 0xf;
if (reg32 & (1 << 13)) {
/* GT1 SKU */
debug("IVB GT1 Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt1);
} else {
/* GT2 SKU */
u32 tdp = readl(MCHBAR_REG(0x5930)) & 0x7fff;
tdp /= (1 << unit);
if (tdp <= 17) {
/* <=17W ULV */
debug("IVB GT2 17W Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt2_17w);
} else if ((tdp >= 25) && (tdp <= 35)) {
/* 25W-35W */
debug("IVB GT2 25W-35W Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt2_35w);
} else {
/* All others */
debug("IVB GT2 35W Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt2_35w);
}
}
}
/* 3: Gear ratio map */
gtt_write(gtt_bar, 0xa004, 0x00000010);
/* 4: GFXPAUSE */
gtt_write(gtt_bar, 0xa000, 0x00070020);
/* 5: Dynamic EU trip control */
gtt_write(gtt_bar, 0xa080, 0x00000004);
/* 6: ECO bits */
reg32 = gtt_read(gtt_bar, 0xa180);
reg32 |= (1 << 26) | (1 << 31);
/* (bit 20=1 for SNB step D1+ / IVB A0+) */
if (rev >= SNB_STEP_D1)
reg32 |= (1 << 20);
gtt_write(gtt_bar, 0xa180, reg32);
/* 6a: for SnB step D2+ only */
if (((rev & BASE_REV_MASK) == BASE_REV_SNB) &&
(rev >= SNB_STEP_D2)) {
reg32 = gtt_read(gtt_bar, 0x9400);
reg32 |= (1 << 7);
gtt_write(gtt_bar, 0x9400, reg32);
reg32 = gtt_read(gtt_bar, 0x941c);
reg32 &= 0xf;
reg32 |= (1 << 1);
gtt_write(gtt_bar, 0x941c, reg32);
gtt_poll(gtt_bar, 0x941c, (1 << 1), (0 << 1));
}
if ((rev & BASE_REV_MASK) == BASE_REV_IVB) {
reg32 = gtt_read(gtt_bar, 0x907c);
reg32 |= (1 << 16);
gtt_write(gtt_bar, 0x907c, reg32);
/* 6b: Clocking reset controls */
gtt_write(gtt_bar, 0x9424, 0x00000001);
} else {
/* 6b: Clocking reset controls */
gtt_write(gtt_bar, 0x9424, 0x00000000);
}
/* 7 */
if (gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31))) {
gtt_write(gtt_bar, 0x138128, 0x00000029); /* Mailbox Data */
/* Mailbox Cmd for RC6 VID */
gtt_write(gtt_bar, 0x138124, 0x80000004);
if (gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31)))
gtt_write(gtt_bar, 0x138124, 0x8000000a);
gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31));
}
/* 8 */
gtt_write(gtt_bar, 0xa090, 0x00000000); /* RC Control */
gtt_write(gtt_bar, 0xa098, 0x03e80000); /* RC1e Wake Rate Limit */
gtt_write(gtt_bar, 0xa09c, 0x0028001e); /* RC6/6p Wake Rate Limit */
gtt_write(gtt_bar, 0xa0a0, 0x0000001e); /* RC6pp Wake Rate Limit */
gtt_write(gtt_bar, 0xa0a8, 0x0001e848); /* RC Evaluation Interval */
gtt_write(gtt_bar, 0xa0ac, 0x00000019); /* RC Idle Hysteresis */
/* 9 */
gtt_write(gtt_bar, 0x2054, 0x0000000a); /* Render Idle Max Count */
gtt_write(gtt_bar, 0x12054, 0x0000000a); /* Video Idle Max Count */
gtt_write(gtt_bar, 0x22054, 0x0000000a); /* Blitter Idle Max Count */
/* 10 */
gtt_write(gtt_bar, 0xa0b0, 0x00000000); /* Unblock Ack to Busy */
gtt_write(gtt_bar, 0xa0b4, 0x000003e8); /* RC1e Threshold */
gtt_write(gtt_bar, 0xa0b8, 0x0000c350); /* RC6 Threshold */
gtt_write(gtt_bar, 0xa0bc, 0x000186a0); /* RC6p Threshold */
gtt_write(gtt_bar, 0xa0c0, 0x0000fa00); /* RC6pp Threshold */
/* 11 */
gtt_write(gtt_bar, 0xa010, 0x000f4240); /* RP Down Timeout */
gtt_write(gtt_bar, 0xa014, 0x12060000); /* RP Interrupt Limits */
gtt_write(gtt_bar, 0xa02c, 0x00015f90); /* RP Up Threshold */
gtt_write(gtt_bar, 0xa030, 0x000186a0); /* RP Down Threshold */
gtt_write(gtt_bar, 0xa068, 0x000186a0); /* RP Up EI */
gtt_write(gtt_bar, 0xa06c, 0x000493e0); /* RP Down EI */
gtt_write(gtt_bar, 0xa070, 0x0000000a); /* RP Idle Hysteresis */
/* 11a: Enable Render Standby (RC6) */
if ((rev & BASE_REV_MASK) == BASE_REV_IVB) {
/*
* IvyBridge should also support DeepRenderStandby.
*
* Unfortunately it does not work reliably on all SKUs so
* disable it here and it can be enabled by the kernel.
*/
gtt_write(gtt_bar, 0xa090, 0x88040000); /* HW RC Control */
} else {
gtt_write(gtt_bar, 0xa090, 0x88040000); /* HW RC Control */
}
/* 12: Normal Frequency Request */
/* RPNFREQ_VAL comes from MCHBAR 0x5998 23:16 (8 bits!? use 7) */
reg32 = readl(MCHBAR_REG(0x5998));
reg32 >>= 16;
reg32 &= 0xef;
reg32 <<= 25;
gtt_write(gtt_bar, 0xa008, reg32);
/* 13: RP Control */
gtt_write(gtt_bar, 0xa024, 0x00000592);
/* 14: Enable PM Interrupts */
gtt_write(gtt_bar, 0x4402c, 0x03000076);
/* Clear 0x6c024 [8:6] */
reg32 = gtt_read(gtt_bar, 0x6c024);
reg32 &= ~0x000001c0;
gtt_write(gtt_bar, 0x6c024, reg32);
return 0;
}
static int gma_pm_init_post_vbios(struct udevice *dev, int rev, void *gtt_bar)
{
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
u32 reg32, cycle_delay;
debug("GT Power Management Init (post VBIOS)\n");
/* 15: Deassert Force Wake */
if (rev < IVB_STEP_C0) {
gtt_write(gtt_bar, 0xa18c, gtt_read(gtt_bar, 0xa18c) & ~1);
gtt_poll(gtt_bar, 0x130090, (1 << 0), (0 << 0));
} else {
gtt_write(gtt_bar, 0xa188, 0x1fffe);
if (gtt_poll(gtt_bar, 0x130040, (1 << 0), (0 << 0))) {
gtt_write(gtt_bar, 0xa188,
gtt_read(gtt_bar, 0xa188) | 1);
}
}
/* 16: SW RC Control */
gtt_write(gtt_bar, 0xa094, 0x00060000);
/* Setup Digital Port Hotplug */
reg32 = gtt_read(gtt_bar, 0xc4030);
if (!reg32) {
u32 dp_hotplug[3];
if (fdtdec_get_int_array(blob, node, "intel,dp_hotplug",
dp_hotplug, ARRAY_SIZE(dp_hotplug)))
return -EINVAL;
reg32 = (dp_hotplug[0] & 0x7) << 2;
reg32 |= (dp_hotplug[0] & 0x7) << 10;
reg32 |= (dp_hotplug[0] & 0x7) << 18;
gtt_write(gtt_bar, 0xc4030, reg32);
}
/* Setup Panel Power On Delays */
reg32 = gtt_read(gtt_bar, 0xc7208);
if (!reg32) {
reg32 = (unsigned)fdtdec_get_int(blob, node,
"panel-port-select", 0) << 30;
reg32 |= fdtdec_get_int(blob, node, "panel-power-up-delay", 0)
<< 16;
reg32 |= fdtdec_get_int(blob, node,
"panel-power-backlight-on-delay", 0);
gtt_write(gtt_bar, 0xc7208, reg32);
}
/* Setup Panel Power Off Delays */
reg32 = gtt_read(gtt_bar, 0xc720c);
if (!reg32) {
reg32 = fdtdec_get_int(blob, node, "panel-power-down-delay", 0)
<< 16;
reg32 |= fdtdec_get_int(blob, node,
"panel-power-backlight-off-delay", 0);
gtt_write(gtt_bar, 0xc720c, reg32);
}
/* Setup Panel Power Cycle Delay */
cycle_delay = fdtdec_get_int(blob, node,
"intel,panel-power-cycle-delay", 0);
if (cycle_delay) {
reg32 = gtt_read(gtt_bar, 0xc7210);
reg32 &= ~0xff;
reg32 |= cycle_delay;
gtt_write(gtt_bar, 0xc7210, reg32);
}
/* Enable Backlight if needed */
reg32 = fdtdec_get_int(blob, node, "intel,cpu-backlight", 0);
if (reg32) {
gtt_write(gtt_bar, 0x48250, (1 << 31));
gtt_write(gtt_bar, 0x48254, reg32);
}
reg32 = fdtdec_get_int(blob, node, "intel,pch-backlight", 0);
if (reg32) {
gtt_write(gtt_bar, 0xc8250, (1 << 31));
gtt_write(gtt_bar, 0xc8254, reg32);
}
return 0;
}
/*
* Some vga option roms are used for several chipsets but they only have one
* PCI ID in their header. If we encounter such an option rom, we need to do
* the mapping ourselves.
*/
uint32_t board_map_oprom_vendev(uint32_t vendev)
{
switch (vendev) {
case 0x80860102: /* GT1 Desktop */
case 0x8086010a: /* GT1 Server */
case 0x80860112: /* GT2 Desktop */
case 0x80860116: /* GT2 Mobile */
case 0x80860122: /* GT2 Desktop >=1.3GHz */
case 0x80860126: /* GT2 Mobile >=1.3GHz */
case 0x80860156: /* IVB */
case 0x80860166: /* IVB */
return 0x80860106; /* GT1 Mobile */
}
return vendev;
}
static int int15_handler(void)
{
int res = 0;
debug("%s: INT15 function %04x!\n", __func__, M.x86.R_AX);
switch (M.x86.R_AX) {
case 0x5f34:
/*
* Set Panel Fitting Hook:
* bit 2 = Graphics Stretching
* bit 1 = Text Stretching
* bit 0 = Centering (do not set with bit1 or bit2)
* 0 = video bios default
*/
M.x86.R_AX = 0x005f;
M.x86.R_CL = 0x00; /* Use video bios default */
res = 1;
break;
case 0x5f35:
/*
* Boot Display Device Hook:
* bit 0 = CRT
* bit 1 = TV (eDP)
* bit 2 = EFP
* bit 3 = LFP
* bit 4 = CRT2
* bit 5 = TV2 (eDP)
* bit 6 = EFP2
* bit 7 = LFP2
*/
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000; /* Use video bios default */
res = 1;
break;
case 0x5f51:
/*
* Hook to select active LFP configuration:
* 00h = No LVDS, VBIOS does not enable LVDS
* 01h = Int-LVDS, LFP driven by integrated LVDS decoder
* 02h = SVDO-LVDS, LFP driven by SVDO decoder
* 03h = eDP, LFP Driven by Int-DisplayPort encoder
*/
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0003; /* eDP */
res = 1;
break;
case 0x5f70:
switch (M.x86.R_CH) {
case 0:
/* Get Mux */
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000;
res = 1;
break;
case 1:
/* Set Mux */
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000;
res = 1;
break;
case 2:
/* Get SG/Non-SG mode */
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000;
res = 1;
break;
default:
/* Interrupt was not handled */
debug("Unknown INT15 5f70 function: 0x%02x\n",
M.x86.R_CH);
break;
}
break;
case 0x5fac:
res = 1;
break;
default:
debug("Unknown INT15 function %04x!\n", M.x86.R_AX);
break;
}
return res;
}
static void sandybridge_setup_graphics(struct udevice *dev,
struct udevice *video_dev)
{
u32 reg32;
u16 reg16;
u8 reg8;
dm_pci_read_config16(video_dev, PCI_DEVICE_ID, &reg16);
switch (reg16) {
case 0x0102: /* GT1 Desktop */
case 0x0106: /* GT1 Mobile */
case 0x010a: /* GT1 Server */
case 0x0112: /* GT2 Desktop */
case 0x0116: /* GT2 Mobile */
case 0x0122: /* GT2 Desktop >=1.3GHz */
case 0x0126: /* GT2 Mobile >=1.3GHz */
case 0x0156: /* IvyBridge */
case 0x0166: /* IvyBridge */
break;
default:
debug("Graphics not supported by this CPU/chipset\n");
return;
}
debug("Initialising Graphics\n");
/* Setup IGD memory by setting GGC[7:3] = 1 for 32MB */
dm_pci_read_config16(dev, GGC, &reg16);
reg16 &= ~0x00f8;
reg16 |= 1 << 3;
/* Program GTT memory by setting GGC[9:8] = 2MB */
reg16 &= ~0x0300;
reg16 |= 2 << 8;
/* Enable VGA decode */
reg16 &= ~0x0002;
dm_pci_write_config16(dev, GGC, reg16);
/* Enable 256MB aperture */
dm_pci_read_config8(video_dev, MSAC, &reg8);
reg8 &= ~0x06;
reg8 |= 0x02;
dm_pci_write_config8(video_dev, MSAC, reg8);
/* Erratum workarounds */
reg32 = readl(MCHBAR_REG(0x5f00));
reg32 |= (1 << 9) | (1 << 10);
writel(reg32, MCHBAR_REG(0x5f00));
/* Enable SA Clock Gating */
reg32 = readl(MCHBAR_REG(0x5f00));
writel(reg32 | 1, MCHBAR_REG(0x5f00));
/* GPU RC6 workaround for sighting 366252 */
reg32 = readl(MCHBAR_REG(0x5d14));
reg32 |= (1 << 31);
writel(reg32, MCHBAR_REG(0x5d14));
/* VLW */
reg32 = readl(MCHBAR_REG(0x6120));
reg32 &= ~(1 << 0);
writel(reg32, MCHBAR_REG(0x6120));
reg32 = readl(MCHBAR_REG(0x5418));
reg32 |= (1 << 4) | (1 << 5);
writel(reg32, MCHBAR_REG(0x5418));
}
static int gma_func0_init(struct udevice *dev)
{
struct udevice *nbridge;
void *gtt_bar;
ulong base;
u32 reg32;
int ret;
int rev;
/* Enable PCH Display Port */
writew(0x0010, RCB_REG(DISPBDF));
setbits_le32(RCB_REG(FD2), PCH_ENABLE_DBDF);
ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &nbridge);
if (ret)
return ret;
rev = bridge_silicon_revision(nbridge);
sandybridge_setup_graphics(nbridge, dev);
/* IGD needs to be Bus Master */
dm_pci_read_config32(dev, PCI_COMMAND, &reg32);
reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
dm_pci_write_config32(dev, PCI_COMMAND, reg32);
/* Use write-combining for the graphics memory, 256MB */
base = dm_pci_read_bar32(dev, 2);
mtrr_add_request(MTRR_TYPE_WRCOMB, base, 256 << 20);
mtrr_commit(true);
gtt_bar = (void *)(ulong)dm_pci_read_bar32(dev, 0);
debug("GT bar %p\n", gtt_bar);
ret = gma_pm_init_pre_vbios(gtt_bar, rev);
if (ret)
return ret;
return rev;
}
static int bd82x6x_video_probe(struct udevice *dev)
{
void *gtt_bar;
int ret, rev;
rev = gma_func0_init(dev);
if (rev < 0)
return rev;
ret = vbe_setup_video(dev, int15_handler);
if (ret)
return ret;
/* Post VBIOS init */
gtt_bar = (void *)(ulong)dm_pci_read_bar32(dev, 0);
ret = gma_pm_init_post_vbios(dev, rev, gtt_bar);
if (ret)
return ret;
return 0;
}
static const struct udevice_id bd82x6x_video_ids[] = {
{ .compatible = "intel,gma" },
{ }
};
U_BOOT_DRIVER(bd82x6x_video) = {
.name = "bd82x6x_video",
.id = UCLASS_VIDEO,
.of_match = bd82x6x_video_ids,
.probe = bd82x6x_video_probe,
};
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* ld9040 AMOLED LCD panel driver.
*
* Copyright (C) 2012 Samsung Electronics
* Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <spi.h>
static const unsigned char SEQ_USER_SETTING[] = {
0xF0, 0x5A, 0x5A
};
static const unsigned char SEQ_ELVSS_ON[] = {
0xB1, 0x0D, 0x00, 0x16,
};
static const unsigned char SEQ_GTCON[] = {
0xF7, 0x09, 0x00, 0x00,
};
static const unsigned char SEQ_PANEL_CONDITION[] = {
0xF8, 0x05, 0x65, 0x96, 0x71, 0x7D, 0x19, 0x3B,
0x0D, 0x19, 0x7E, 0x0D, 0xE2, 0x00, 0x00, 0x7E,
0x7D, 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02,
};
static const unsigned char SEQ_GAMMA_SET1[] = {
0xF9, 0x00, 0xA7, 0xB4, 0xAE, 0xBF, 0x00, 0x91,
0x00, 0xB2, 0xB4, 0xAA, 0xBB, 0x00, 0xAC, 0x00,
0xB3, 0xB1, 0xAA, 0xBC, 0x00, 0xB3,
};
static const unsigned char SEQ_GAMMA_CTRL[] = {
0xFB, 0x02, 0x5A,
};
static const unsigned char SEQ_DISPCTL[] = {
0xF2, 0x02, 0x08, 0x08, 0x10, 0x10,
};
static const unsigned char SEQ_MANPWR[] = {
0xB0, 0x04,
};
static const unsigned char SEQ_PWR_CTRL[] = {
0xF4, 0x0A, 0x87, 0x25, 0x6A, 0x44, 0x02, 0x88,
};
static const unsigned char SEQ_SLPOUT[] = {
0x11,
};
static const unsigned char SEQ_DISPON[] = {
0x29,
};
static const unsigned char SEQ_DISPOFF[] = {
0x28,
};
static void ld9040_spi_write(const unsigned char *wbuf, unsigned int size_cmd)
{
int i = 0;
/*
* Data are transmitted in 9-bit words:
* the first bit is command/parameter, the other are the value.
* The value's LSB is shifted to MSB position, to be sent as 9th bit
*/
unsigned int data_out = 0, data_in = 0;
for (i = 0; i < size_cmd; i++) {
data_out = wbuf[i] >> 1;
if (i != 0)
data_out += 0x0080;
if (wbuf[i] & 0x01)
data_out += 0x8000;
spi_xfer(NULL, 9, &data_out, &data_in, SPI_XFER_BEGIN);
}
}
void ld9040_cfg_ldo(void)
{
udelay(10);
ld9040_spi_write(SEQ_USER_SETTING,
ARRAY_SIZE(SEQ_USER_SETTING));
ld9040_spi_write(SEQ_PANEL_CONDITION,
ARRAY_SIZE(SEQ_PANEL_CONDITION));
ld9040_spi_write(SEQ_DISPCTL, ARRAY_SIZE(SEQ_DISPCTL));
ld9040_spi_write(SEQ_MANPWR, ARRAY_SIZE(SEQ_MANPWR));
ld9040_spi_write(SEQ_PWR_CTRL, ARRAY_SIZE(SEQ_PWR_CTRL));
ld9040_spi_write(SEQ_ELVSS_ON, ARRAY_SIZE(SEQ_ELVSS_ON));
ld9040_spi_write(SEQ_GTCON, ARRAY_SIZE(SEQ_GTCON));
ld9040_spi_write(SEQ_GAMMA_SET1, ARRAY_SIZE(SEQ_GAMMA_SET1));
ld9040_spi_write(SEQ_GAMMA_CTRL, ARRAY_SIZE(SEQ_GAMMA_CTRL));
ld9040_spi_write(SEQ_SLPOUT, ARRAY_SIZE(SEQ_SLPOUT));
udelay(120);
}
void ld9040_enable_ldo(unsigned int onoff)
{
if (onoff)
ld9040_spi_write(SEQ_DISPON, ARRAY_SIZE(SEQ_DISPON));
else
ld9040_spi_write(SEQ_DISPOFF, ARRAY_SIZE(SEQ_DISPOFF));
}
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0
/*
* LCD: LG4573, TFT 4.3", 480x800, RGB24
* LCD initialization via SPI
*
*/
#include <common.h>
#include <errno.h>
#include <spi.h>
#define PWR_ON_DELAY_MSECS 120
static int lb043wv_spi_write_u16(struct spi_slave *spi, u16 val)
{
unsigned long flags = SPI_XFER_BEGIN;
unsigned short buf16 = htons(val);
int ret = 0;
flags |= SPI_XFER_END;
ret = spi_xfer(spi, 16, &buf16, NULL, flags);
if (ret)
debug("%s: Failed to send: %d\n", __func__, ret);
return ret;
}
static void lb043wv_spi_write_u16_array(struct spi_slave *spi, u16 *buff,
int size)
{
int i;
for (i = 0; i < size; i++)
lb043wv_spi_write_u16(spi, buff[i]);
}
static void lb043wv_display_mode_settings(struct spi_slave *spi)
{
static u16 display_mode_settings[] = {
0x703A,
0x7270,
0x70B1,
0x7208,
0x723B,
0x720F,
0x70B2,
0x7200,
0x72C8,
0x70B3,
0x7200,
0x70B4,
0x7200,
0x70B5,
0x7242,
0x7210,
0x7210,
0x7200,
0x7220,
0x70B6,
0x720B,
0x720F,
0x723C,
0x7213,
0x7213,
0x72E8,
0x70B7,
0x7246,
0x7206,
0x720C,
0x7200,
0x7200,
};
debug("transfer display mode settings\n");
lb043wv_spi_write_u16_array(spi, display_mode_settings,
ARRAY_SIZE(display_mode_settings));
}
static void lb043wv_power_settings(struct spi_slave *spi)
{
static u16 power_settings[] = {
0x70C0,
0x7201,
0x7211,
0x70C3,
0x7207,
0x7203,
0x7204,
0x7204,
0x7204,
0x70C4,
0x7212,
0x7224,
0x7218,
0x7218,
0x7202,
0x7249,
0x70C5,
0x726F,
0x70C6,
0x7241,
0x7263,
};
debug("transfer power settings\n");
lb043wv_spi_write_u16_array(spi, power_settings,
ARRAY_SIZE(power_settings));
}
static void lb043wv_gamma_settings(struct spi_slave *spi)
{
static u16 gamma_settings[] = {
0x70D0,
0x7203,
0x7207,
0x7273,
0x7235,
0x7200,
0x7201,
0x7220,
0x7200,
0x7203,
0x70D1,
0x7203,
0x7207,
0x7273,
0x7235,
0x7200,
0x7201,
0x7220,
0x7200,
0x7203,
0x70D2,
0x7203,
0x7207,
0x7273,
0x7235,
0x7200,
0x7201,
0x7220,
0x7200,
0x7203,
0x70D3,
0x7203,
0x7207,
0x7273,
0x7235,
0x7200,
0x7201,
0x7220,
0x7200,
0x7203,
0x70D4,
0x7203,
0x7207,
0x7273,
0x7235,
0x7200,
0x7201,
0x7220,
0x7200,
0x7203,
0x70D5,
0x7203,
0x7207,
0x7273,
0x7235,
0x7200,
0x7201,
0x7220,
0x7200,
0x7203,
};
debug("transfer gamma settings\n");
lb043wv_spi_write_u16_array(spi, gamma_settings,
ARRAY_SIZE(gamma_settings));
}
static void lb043wv_display_on(struct spi_slave *spi)
{
static u16 sleep_out = 0x7011;
static u16 display_on = 0x7029;
lb043wv_spi_write_u16(spi, sleep_out);
mdelay(PWR_ON_DELAY_MSECS);
lb043wv_spi_write_u16(spi, display_on);
}
int lg4573_spi_startup(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
struct spi_slave *spi;
int ret;
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
if (!spi) {
debug("%s: Failed to set up slave\n", __func__);
return -1;
}
ret = spi_claim_bus(spi);
if (ret) {
debug("%s: Failed to claim SPI bus: %d\n", __func__, ret);
goto err_claim_bus;
}
lb043wv_display_mode_settings(spi);
lb043wv_power_settings(spi);
lb043wv_gamma_settings(spi);
lb043wv_display_on(spi);
return 0;
err_claim_bus:
spi_free_slave(spi);
return -1;
}
static int do_lgset(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
lg4573_spi_startup(CONFIG_LG4573_BUS, CONFIG_LG4573_CS, 10000000,
SPI_MODE_0);
return 0;
}
U_BOOT_CMD(
lgset, 2, 1, do_lgset,
"set lgdisplay",
""
);
@@ -0,0 +1,341 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* logicore_dp_dpcd.h
*
* DPCD interface definition for XILINX LogiCore DisplayPort v6.1
* based on Xilinx dp_v3_1 driver sources
*
* (C) Copyright 2016
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
*/
#ifndef __GDSYS_LOGICORE_DP_DPCD_H__
#define __GDSYS_LOGICORE_DP_DPCD_H__
/* receiver capability field */
#define DPCD_REV 0x00000
#define DPCD_MAX_LINK_RATE 0x00001
#define DPCD_MAX_LANE_COUNT 0x00002
#define DPCD_MAX_DOWNSPREAD 0x00003
#define DPCD_NORP_PWR_V_CAP 0x00004
#define DPCD_DOWNSP_PRESENT 0x00005
#define DPCD_ML_CH_CODING_CAP 0x00006
#define DPCD_DOWNSP_COUNT_MSA_OUI 0x00007
#define DPCD_RX_PORT0_CAP_0 0x00008
#define DPCD_RX_PORT0_CAP_1 0x00009
#define DPCD_RX_PORT1_CAP_0 0x0000A
#define DPCD_RX_PORT1_CAP_1 0x0000B
#define DPCD_I2C_SPEED_CTL_CAP 0x0000C
#define DPCD_EDP_CFG_CAP 0x0000D
#define DPCD_TRAIN_AUX_RD_INTERVAL 0x0000E
#define DPCD_ADAPTER_CAP 0x0000F
#define DPCD_FAUX_CAP 0x00020
#define DPCD_MSTM_CAP 0x00021
#define DPCD_NUM_AUDIO_EPS 0x00022
#define DPCD_AV_GRANULARITY 0x00023
#define DPCD_AUD_DEC_LAT_7_0 0x00024
#define DPCD_AUD_DEC_LAT_15_8 0x00025
#define DPCD_AUD_PP_LAT_7_0 0x00026
#define DPCD_AUD_PP_LAT_15_8 0x00027
#define DPCD_VID_INTER_LAT 0x00028
#define DPCD_VID_PROG_LAT 0x00029
#define DPCD_REP_LAT 0x0002A
#define DPCD_AUD_DEL_INS_7_0 0x0002B
#define DPCD_AUD_DEL_INS_15_8 0x0002C
#define DPCD_AUD_DEL_INS_23_16 0x0002D
#define DPCD_GUID 0x00030
#define DPCD_RX_GTC_VALUE_7_0 0x00054
#define DPCD_RX_GTC_VALUE_15_8 0x00055
#define DPCD_RX_GTC_VALUE_23_16 0x00056
#define DPCD_RX_GTC_VALUE_31_24 0x00057
#define DPCD_RX_GTC_MSTR_REQ 0x00058
#define DPCD_RX_GTC_FREQ_LOCK_DONE 0x00059
#define DPCD_DOWNSP_0_CAP 0x00080
#define DPCD_DOWNSP_1_CAP 0x00081
#define DPCD_DOWNSP_2_CAP 0x00082
#define DPCD_DOWNSP_3_CAP 0x00083
#define DPCD_DOWNSP_0_DET_CAP 0x00080
#define DPCD_DOWNSP_1_DET_CAP 0x00084
#define DPCD_DOWNSP_2_DET_CAP 0x00088
#define DPCD_DOWNSP_3_DET_CAP 0x0008C
/* link configuration field */
#define DPCD_LINK_BW_SET 0x00100
#define DPCD_LANE_COUNT_SET 0x00101
#define DPCD_TP_SET 0x00102
#define DPCD_TRAINING_LANE0_SET 0x00103
#define DPCD_TRAINING_LANE1_SET 0x00104
#define DPCD_TRAINING_LANE2_SET 0x00105
#define DPCD_TRAINING_LANE3_SET 0x00106
#define DPCD_DOWNSPREAD_CTRL 0x00107
#define DPCD_ML_CH_CODING_SET 0x00108
#define DPCD_I2C_SPEED_CTL_SET 0x00109
#define DPCD_EDP_CFG_SET 0x0010A
#define DPCD_LINK_QUAL_LANE0_SET 0x0010B
#define DPCD_LINK_QUAL_LANE1_SET 0x0010C
#define DPCD_LINK_QUAL_LANE2_SET 0x0010D
#define DPCD_LINK_QUAL_LANE3_SET 0x0010E
#define DPCD_TRAINING_LANE0_1_SET2 0x0010F
#define DPCD_TRAINING_LANE2_3_SET2 0x00110
#define DPCD_MSTM_CTRL 0x00111
#define DPCD_AUDIO_DELAY_7_0 0x00112
#define DPCD_AUDIO_DELAY_15_8 0x00113
#define DPCD_AUDIO_DELAY_23_6 0x00114
#define DPCD_UPSTREAM_DEVICE_DP_PWR_NEED 0x00118
#define DPCD_FAUX_MODE_CTRL 0x00120
#define DPCD_FAUX_FORWARD_CH_DRIVE_SET 0x00121
#define DPCD_BACK_CH_STATUS 0x00122
#define DPCD_FAUX_BACK_CH_SYMBOL_ERROR_COUNT 0x00123
#define DPCD_FAUX_BACK_CH_TRAINING_PATTERN_TIME 0x00125
#define DPCD_TX_GTC_VALUE_7_0 0x00154
#define DPCD_TX_GTC_VALUE_15_8 0x00155
#define DPCD_TX_GTC_VALUE_23_16 0x00156
#define DPCD_TX_GTC_VALUE_31_24 0x00157
#define DPCD_RX_GTC_VALUE_PHASE_SKEW_EN 0x00158
#define DPCD_TX_GTC_FREQ_LOCK_DONE 0x00159
#define DPCD_ADAPTER_CTRL 0x001A0
#define DPCD_BRANCH_DEVICE_CTRL 0x001A1
#define DPCD_PAYLOAD_ALLOCATE_SET 0x001C0
#define DPCD_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x001C1
#define DPCD_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x001C2
/* link/sink status field */
#define DPCD_SINK_COUNT 0x00200
#define DPCD_DEVICE_SERVICE_IRQ 0x00201
#define DPCD_STATUS_LANE_0_1 0x00202
#define DPCD_STATUS_LANE_2_3 0x00203
#define DPCD_LANE_ALIGN_STATUS_UPDATED 0x00204
#define DPCD_SINK_STATUS 0x00205
#define DPCD_ADJ_REQ_LANE_0_1 0x00206
#define DPCD_ADJ_REQ_LANE_2_3 0x00207
#define DPCD_TRAINING_SCORE_LANE_0 0x00208
#define DPCD_TRAINING_SCORE_LANE_1 0x00209
#define DPCD_TRAINING_SCORE_LANE_2 0x0020A
#define DPCD_TRAINING_SCORE_LANE_3 0x0020B
#define DPCD_ADJ_REQ_PC2 0x0020C
#define DPCD_FAUX_FORWARD_CH_SYMBOL_ERROR_COUNT 0x0020D
#define DPCD_SYMBOL_ERROR_COUNT_LANE_0 0x00210
#define DPCD_SYMBOL_ERROR_COUNT_LANE_1 0x00212
#define DPCD_SYMBOL_ERROR_COUNT_LANE_2 0x00214
#define DPCD_SYMBOL_ERROR_COUNT_LANE_3 0x00216
/* automated testing sub-field */
#define DPCD_FAUX_FORWARD_CH_STATUS 0x00280
#define DPCD_FAUX_BACK_CH_DRIVE_SET 0x00281
#define DPCD_FAUX_BACK_CH_SYM_ERR_COUNT_CTRL 0x00282
#define DPCD_PAYLOAD_TABLE_UPDATE_STATUS 0x002C0
#define DPCD_VC_PAYLOAD_ID_SLOT(slotnum) \
(DPCD_PAYLOAD_TABLE_UPDATE_STATUS + slotnum)
/* sink control field */
#define DPCD_SET_POWER_DP_PWR_VOLTAGE 0x00600
/* sideband message buffers */
#define DPCD_DOWN_REQ 0x01000
#define DPCD_UP_REP 0x01200
#define DPCD_DOWN_REP 0x01400
#define DPCD_UP_REQ 0x01600
/* event status indicator field */
#define DPCD_SINK_COUNT_ESI 0x02002
#define DPCD_SINK_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x02003
#define DPCD_SINK_DEVICE_SERVICE_IRQ_VECTOR_ESI1 0x02004
#define DPCD_SINK_LINK_SERVICE_IRQ_VECTOR_ESI0 0x02005
#define DPCD_SINK_LANE0_1_STATUS 0x0200C
#define DPCD_SINK_LANE2_3_STATUS 0x0200D
#define DPCD_SINK_ALIGN_STATUS_UPDATED_ESI 0x0200E
#define DPCD_SINK_STATUS_ESI 0x0200F
/*
* field addresses and sizes.
*/
#define DPCD_RECEIVER_CAP_FIELD_START DPCD_REV
#define DPCD_RECEIVER_CAP_FIELD_SIZE 0x100
#define DPCD_LINK_CFG_FIELD_START DPCD_LINK_BW_SET
#define DPCD_LINK_CFG_FIELD_SIZE 0x100
#define DPCD_LINK_SINK_STATUS_FIELD_START DPCD_SINK_COUNT
#define DPCD_LINK_SINK_STATUS_FIELD_SIZE 0x17
/* 0x00000: DPCD_REV */
#define DPCD_REV_MNR_MASK 0x0F
#define DPCD_REV_MJR_MASK 0xF0
#define DPCD_REV_MJR_SHIFT 4
/* 0x00001: MAX_LINK_RATE */
#define DPCD_MAX_LINK_RATE_162GBPS 0x06
#define DPCD_MAX_LINK_RATE_270GBPS 0x0A
#define DPCD_MAX_LINK_RATE_540GBPS 0x14
/* 0x00002: MAX_LANE_COUNT */
#define DPCD_MAX_LANE_COUNT_MASK 0x1F
#define DPCD_MAX_LANE_COUNT_1 0x01
#define DPCD_MAX_LANE_COUNT_2 0x02
#define DPCD_MAX_LANE_COUNT_4 0x04
#define DPCD_TPS3_SUPPORT_MASK 0x40
#define DPCD_ENHANCED_FRAME_SUPPORT_MASK 0x80
/* 0x00003: MAX_DOWNSPREAD */
#define DPCD_MAX_DOWNSPREAD_MASK 0x01
#define DPCD_NO_AUX_HANDSHAKE_LINK_TRAIN_MASK 0x40
/* 0x00005: DOWNSP_PRESENT */
#define DPCD_DOWNSP_PRESENT_MASK 0x01
#define DPCD_DOWNSP_TYPE_MASK 0x06
#define DPCD_DOWNSP_TYPE_SHIFT 1
#define DPCD_DOWNSP_TYPE_DP 0x0
#define DPCD_DOWNSP_TYPE_AVGA_ADVII 0x1
#define DPCD_DOWNSP_TYPE_DVI_HDMI_DPPP 0x2
#define DPCD_DOWNSP_TYPE_OTHERS 0x3
#define DPCD_DOWNSP_FORMAT_CONV_MASK 0x08
#define DPCD_DOWNSP_DCAP_INFO_AVAIL_MASK 0x10
/* 0x00006, 0x00108: ML_CH_CODING_SUPPORT, ML_CH_CODING_SET */
#define DPCD_ML_CH_CODING_MASK 0x01
/* 0x00007: DOWNSP_COUNT_MSA_OUI */
#define DPCD_DOWNSP_COUNT_MASK 0x0F
#define DPCD_MSA_TIMING_PAR_IGNORED_MASK 0x40
#define DPCD_OUI_SUPPORT_MASK 0x80
/* 0x00008, 0x0000A: RX_PORT[0-1]_CAP_0 */
#define DPCD_RX_PORTX_CAP_0_LOCAL_EDID_PRESENT_MASK 0x02
#define DPCD_RX_PORTX_CAP_0_ASSOC_TO_PRECEDING_PORT_MASK 0x04
/* 0x0000C, 0x00109: I2C_SPEED_CTL_CAP, I2C_SPEED_CTL_SET */
#define DPCD_I2C_SPEED_CTL_NONE 0x00
#define DPCD_I2C_SPEED_CTL_1KBIPS 0x01
#define DPCD_I2C_SPEED_CTL_5KBIPS 0x02
#define DPCD_I2C_SPEED_CTL_10KBIPS 0x04
#define DPCD_I2C_SPEED_CTL_100KBIPS 0x08
#define DPCD_I2C_SPEED_CTL_400KBIPS 0x10
#define DPCD_I2C_SPEED_CTL_1MBIPS 0x20
/* 0x0000E: TRAIN_AUX_RD_INTERVAL */
#define DPCD_TRAIN_AUX_RD_INT_100_400US 0x00
#define DPCD_TRAIN_AUX_RD_INT_4MS 0x01
#define DPCD_TRAIN_AUX_RD_INT_8MS 0x02
#define DPCD_TRAIN_AUX_RD_INT_12MS 0x03
#define DPCD_TRAIN_AUX_RD_INT_16MS 0x04
/* 0x00020: DPCD_FAUX_CAP */
#define DPCD_FAUX_CAP_MASK 0x01
/* 0x00021: MSTM_CAP */
#define DPCD_MST_CAP_MASK 0x01
/* 0x00080, 0x00081|4, 0x00082|8, 0x00083|C: DOWNSP_X_(DET_)CAP */
#define DPCD_DOWNSP_X_CAP_TYPE_MASK 0x07
#define DPCD_DOWNSP_X_CAP_TYPE_DP 0x0
#define DPCD_DOWNSP_X_CAP_TYPE_AVGA 0x1
#define DPCD_DOWNSP_X_CAP_TYPE_DVI 0x2
#define DPCD_DOWNSP_X_CAP_TYPE_HDMI 0x3
#define DPCD_DOWNSP_X_CAP_TYPE_OTHERS 0x4
#define DPCD_DOWNSP_X_CAP_TYPE_DPPP 0x5
#define DPCD_DOWNSP_X_CAP_HPD_MASK 0x80
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_MASK 0xF0
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_SHIFT 4
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_720_480_I_60 0x1
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_720_480_I_50 0x2
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1920_1080_I_60 0x3
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1920_1080_I_50 0x4
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1280_720_P_60 0x5
#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1280_720_P_50 0x7
/* 0x00082, 0x00086, 0x0008A, 0x0008E: DOWNSP_X_(DET_)CAP2 */
#define DPCD_DOWNSP_X_DCAP_MAX_BPC_MASK 0x03
#define DPCD_DOWNSP_X_DCAP_MAX_BPC_8 0x0
#define DPCD_DOWNSP_X_DCAP_MAX_BPC_10 0x1
#define DPCD_DOWNSP_X_DCAP_MAX_BPC_12 0x2
#define DPCD_DOWNSP_X_DCAP_MAX_BPC_16 0x3
/* 0x00082, 0x00086, 0x0008A, 0x0008E: DOWNSP_X_(DET_)CAP2 */
#define DPCD_DOWNSP_X_DCAP_HDMI_DPPP_FS2FP_MASK 0x01
#define DPCD_DOWNSP_X_DCAP_DVI_DL_MASK 0x02
#define DPCD_DOWNSP_X_DCAP_DVI_HCD_MASK 0x04
/* link configuration field masks, shifts, and register values */
/* 0x00100: DPCD_LINK_BW_SET */
#define DPCD_LINK_BW_SET_162GBPS 0x06
#define DPCD_LINK_BW_SET_270GBPS 0x0A
#define DPCD_LINK_BW_SET_540GBPS 0x14
/* 0x00101: LANE_COUNT_SET */
#define DPCD_LANE_COUNT_SET_MASK 0x1F
#define DPCD_LANE_COUNT_SET_1 0x01
#define DPCD_LANE_COUNT_SET_2 0x02
#define DPCD_LANE_COUNT_SET_4 0x04
#define DPCD_ENHANCED_FRAME_EN_MASK 0x80
/* 0x00102: TP_SET */
#define DPCD_TP_SEL_MASK 0x03
#define DPCD_TP_SEL_OFF 0x0
#define DPCD_TP_SEL_TP1 0x1
#define DPCD_TP_SEL_TP2 0x2
#define DPCD_TP_SEL_TP3 0x3
#define DPCD_TP_SET_LQP_MASK 0x06
#define DPCD_TP_SET_LQP_SHIFT 2
#define DPCD_TP_SET_LQP_OFF 0x0
#define DPCD_TP_SET_LQP_D102_TEST 0x1
#define DPCD_TP_SET_LQP_SER_MES 0x2
#define DPCD_TP_SET_LQP_PRBS7 0x3
#define DPCD_TP_SET_REC_CLK_OUT_EN_MASK 0x10
#define DPCD_TP_SET_SCRAMB_DIS_MASK 0x20
#define DPCD_TP_SET_SE_COUNT_SEL_MASK 0xC0
#define DPCD_TP_SET_SE_COUNT_SEL_SHIFT 6
#define DPCD_TP_SET_SE_COUNT_SEL_DE_ISE 0x0
#define DPCD_TP_SET_SE_COUNT_SEL_DE 0x1
#define DPCD_TP_SET_SE_COUNT_SEL_ISE 0x2
/* 0x00103-0x00106: TRAINING_LANE[0-3]_SET */
#define DPCD_TRAINING_LANEX_SET_VS_MASK 0x03
#define DPCD_TRAINING_LANEX_SET_MAX_VS_MASK 0x04
#define DPCD_TRAINING_LANEX_SET_PE_MASK 0x18
#define DPCD_TRAINING_LANEX_SET_PE_SHIFT 3
#define DPCD_TRAINING_LANEX_SET_MAX_PE_MASK 0x20
/* 0x00107: DOWNSPREAD_CTRL */
#define DPCD_SPREAD_AMP_MASK 0x10
#define DPCD_MSA_TIMING_PAR_IGNORED_EN_MASK 0x80
/* 0x00108: ML_CH_CODING_SET - Same as 0x00006: ML_CH_CODING_SUPPORT */
/* 0x00109: I2C_SPEED_CTL_SET - Same as 0x0000C: I2C_SPEED_CTL_CAP */
/* 0x0010F-0x00110: TRAINING_LANE[0_1-2_3]_SET2 */
#define DPCD_TRAINING_LANE_0_2_SET_PC2_MASK 0x03
#define DPCD_TRAINING_LANE_0_2_SET_MAX_PC2_MASK 0x04
#define DPCD_TRAINING_LANE_1_3_SET_PC2_MASK 0x30
#define DPCD_TRAINING_LANE_1_3_SET_PC2_SHIFT 4
#define DPCD_TRAINING_LANE_1_3_SET_MAX_PC2_MASK 0x40
/* 0x00111: MSTM_CTRL */
#define DPCD_MST_EN_MASK 0x01
#define DPCD_UP_REQ_EN_MASK 0x02
#define DPCD_UP_IS_SRC_MASK 0x03
/* link/sink status field masks, shifts, and register values */
/* 0x00200: SINK_COUNT */
#define DPCD_SINK_COUNT_LOW_MASK 0x3F
#define DPCD_SINK_CP_READY_MASK 0x40
#define DPCD_SINK_COUNT_HIGH_MASK 0x80
#define DPCD_SINK_COUNT_HIGH_LOW_SHIFT 1
/* 0x00202: STATUS_LANE_0_1 */
#define DPCD_STATUS_LANE_0_CR_DONE_MASK 0x01
#define DPCD_STATUS_LANE_0_CE_DONE_MASK 0x02
#define DPCD_STATUS_LANE_0_SL_DONE_MASK 0x04
#define DPCD_STATUS_LANE_1_CR_DONE_MASK 0x10
#define DPCD_STATUS_LANE_1_CE_DONE_MASK 0x20
#define DPCD_STATUS_LANE_1_SL_DONE_MASK 0x40
/* 0x00202: STATUS_LANE_2_3 */
#define DPCD_STATUS_LANE_2_CR_DONE_MASK 0x01
#define DPCD_STATUS_LANE_2_CE_DONE_MASK 0x02
#define DPCD_STATUS_LANE_2_SL_DONE_MASK 0x04
#define DPCD_STATUS_LANE_3_CR_DONE_MASK 0x10
#define DPCD_STATUS_LANE_3_CE_DONE_MASK 0x20
#define DPCD_STATUS_LANE_3_SL_DONE_MASK 0x40
/* 0x00204: LANE_ALIGN_STATUS_UPDATED */
#define DPCD_LANE_ALIGN_STATUS_UPDATED_IA_DONE_MASK \
0x01
#define DPCD_LANE_ALIGN_STATUS_UPDATED_DOWNSP_STATUS_CHANGED_MASK \
0x40
#define DPCD_LANE_ALIGN_STATUS_UPDATED_LINK_STATUS_UPDATED_MASK \
0x80
/* 0x00205: SINK_STATUS */
#define DPCD_SINK_STATUS_RX_PORT0_SYNC_STATUS_MASK 0x01
#define DPCD_SINK_STATUS_RX_PORT1_SYNC_STATUS_MASK 0x02
/* 0x00206, 0x00207: ADJ_REQ_LANE_[0,2]_[1,3] */
#define DPCD_ADJ_REQ_LANE_0_2_VS_MASK 0x03
#define DPCD_ADJ_REQ_LANE_0_2_PE_MASK 0x0C
#define DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT 2
#define DPCD_ADJ_REQ_LANE_1_3_VS_MASK 0x30
#define DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT 4
#define DPCD_ADJ_REQ_LANE_1_3_PE_MASK 0xC0
#define DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT 6
/* 0x0020C: ADJ_REQ_PC2 */
#define DPCD_ADJ_REQ_PC2_LANE_0_MASK 0x03
#define DPCD_ADJ_REQ_PC2_LANE_1_MASK 0x0C
#define DPCD_ADJ_REQ_PC2_LANE_1_SHIFT 2
#define DPCD_ADJ_REQ_PC2_LANE_2_MASK 0x30
#define DPCD_ADJ_REQ_PC2_LANE_2_SHIFT 4
#define DPCD_ADJ_REQ_PC2_LANE_3_MASK 0xC0
#define DPCD_ADJ_REQ_PC2_LANE_3_SHIFT 6
#endif /* __GDSYS_LOGICORE_DP_DPCD_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* logicore_dp_tx.h
*
* Driver for XILINX LogiCore DisplayPort v6.1 TX (Source)
*
* (C) Copyright 2016
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
*/
#ifndef __GDSYS_LOGICORE_DP_TX_H__
#define __GDSYS_LOGICORE_DP_TX_H__
/*
* struct logicore_dp_tx_msa - Main Stream Attributes (MSA)
* @pixel_clock_hz: The pixel clock of the stream (in Hz)
* @bits_per_color: Number of bits per color component
* @h_active: Horizontal active resolution (pixels)
* @h_start: Horizontal blank start (in pixels)
* @h_sync_polarity: Horizontal sync polarity
* (0 = negative | 1 = positive)
* @h_sync_width: Horizontal sync width (pixels)
* @h_total: Horizontal total (pixels)
* @v_active: Vertical active resolution (lines)
* @v_start: Vertical blank start (in lines).
* @v_sync_polarity: Vertical sync polarity
* (0 = negative | 1 = positive)
* @v_sync_width: Vertical sync width (lines)
* @v_total: Vertical total (lines)
* @override_user_pixel_width: If true, the value stored for user_pixel_width
* will be used as the pixel width.
* @user_pixel_width: The width of the user data input port.
*
* This is a stripped down version of struct main_stream_attributes that
* contains only the parameters that are not set by cfg_msa_recalculate()
*/
struct logicore_dp_tx_msa {
u32 pixel_clock_hz;
u32 bits_per_color;
u16 h_active;
u32 h_start;
bool h_sync_polarity;
u16 h_sync_width;
u16 h_total;
u16 v_active;
u32 v_start;
bool v_sync_polarity;
u16 v_sync_width;
u16 v_total;
bool override_user_pixel_width;
u32 user_pixel_width;
};
#endif /* __GDSYS_LOGICORE_DP_TX_H__ */
@@ -0,0 +1,396 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* logicore_dp_tx_regif.h
*
* Register interface definition for XILINX LogiCore DisplayPort v6.1 TX
* (Source) based on Xilinx dp_v3_1 driver sources
*
* (C) Copyright 2016
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
*/
#ifndef __GDSYS_LOGICORE_DP_TX_REGIF_H__
#define __GDSYS_LOGICORE_DP_TX_REGIF_H__
enum {
/* link configuration field */
REG_LINK_BW_SET = 0x000,
REG_LANE_COUNT_SET = 0x004,
REG_ENHANCED_FRAME_EN = 0x008,
REG_TRAINING_PATTERN_SET = 0x00C,
REG_LINK_QUAL_PATTERN_SET = 0x010,
REG_SCRAMBLING_DISABLE = 0x014,
REG_DOWNSPREAD_CTRL = 0x018,
REG_SOFT_RESET = 0x01C,
};
enum {
/* core enables */
REG_ENABLE = 0x080,
REG_ENABLE_MAIN_STREAM = 0x084,
REG_ENABLE_SEC_STREAM = 0x088,
REG_FORCE_SCRAMBLER_RESET = 0x0C0,
REG_MST_CONFIG = 0x0D0,
REG_LINE_RESET_DISABLE = 0x0F0,
};
enum {
/* core ID */
REG_VERSION = 0x0F8,
REG_CORE_ID = 0x0FC,
};
enum {
/* AUX channel interface */
REG_AUX_CMD = 0x100,
REG_AUX_WRITE_FIFO = 0x104,
REG_AUX_ADDRESS = 0x108,
REG_AUX_CLK_DIVIDER = 0x10C,
REG_USER_FIFO_OVERFLOW = 0x110,
REG_INTERRUPT_SIG_STATE = 0x130,
REG_AUX_REPLY_DATA = 0x134,
REG_AUX_REPLY_CODE = 0x138,
REG_AUX_REPLY_COUNT = 0x13C,
REG_INTERRUPT_STATUS = 0x140,
REG_INTERRUPT_MASK = 0x144,
REG_REPLY_DATA_COUNT = 0x148,
REG_REPLY_STATUS = 0x14C,
REG_HPD_DURATION = 0x150,
};
enum {
/* main stream attributes for SST / MST STREAM1 */
REG_STREAM1_MSA_START = 0x180,
REG_MAIN_STREAM_HTOTAL = 0x180,
REG_MAIN_STREAM_VTOTAL = 0x184,
REG_MAIN_STREAM_POLARITY = 0x188,
REG_MAIN_STREAM_HSWIDTH = 0x18C,
REG_MAIN_STREAM_VSWIDTH = 0x190,
REG_MAIN_STREAM_HRES = 0x194,
REG_MAIN_STREAM_VRES = 0x198,
REG_MAIN_STREAM_HSTART = 0x19C,
REG_MAIN_STREAM_VSTART = 0x1A0,
REG_MAIN_STREAM_MISC0 = 0x1A4,
REG_MAIN_STREAM_MISC1 = 0x1A8,
REG_M_VID = 0x1AC,
REG_TU_SIZE = 0x1B0,
REG_N_VID = 0x1B4,
REG_USER_PIXEL_WIDTH = 0x1B8,
REG_USER_DATA_COUNT_PER_LANE = 0x1BC,
REG_MAIN_STREAM_INTERLACED = 0x1C0,
REG_MIN_BYTES_PER_TU = 0x1C4,
REG_FRAC_BYTES_PER_TU = 0x1C8,
REG_INIT_WAIT = 0x1CC,
REG_STREAM1 = 0x1D0,
REG_STREAM2 = 0x1D4,
REG_STREAM3 = 0x1D8,
REG_STREAM4 = 0x1DC,
};
enum {
/* PHY configuration status */
REG_PHY_CONFIG = 0x200,
REG_PHY_VOLTAGE_DIFF_LANE_0 = 0x220,
REG_PHY_VOLTAGE_DIFF_LANE_1 = 0x224,
REG_PHY_VOLTAGE_DIFF_LANE_2 = 0x228,
REG_PHY_VOLTAGE_DIFF_LANE_3 = 0x22C,
REG_PHY_TRANSMIT_PRBS7 = 0x230,
REG_PHY_CLOCK_SELECT = 0x234,
REG_PHY_POWER_DOWN = 0x238,
REG_PHY_PRECURSOR_LANE_0 = 0x23C,
REG_PHY_PRECURSOR_LANE_1 = 0x240,
REG_PHY_PRECURSOR_LANE_2 = 0x244,
REG_PHY_PRECURSOR_LANE_3 = 0x248,
REG_PHY_POSTCURSOR_LANE_0 = 0x24C,
REG_PHY_POSTCURSOR_LANE_1 = 0x250,
REG_PHY_POSTCURSOR_LANE_2 = 0x254,
REG_PHY_POSTCURSOR_LANE_3 = 0x258,
REG_PHY_STATUS = 0x280,
REG_GT_DRP_COMMAND = 0x2A0,
REG_GT_DRP_READ_DATA = 0x2A4,
REG_GT_DRP_CHANNEL_STATUS = 0x2A8,
};
enum {
/* DisplayPort audio */
REG_AUDIO_CONTROL = 0x300,
REG_AUDIO_CHANNELS = 0x304,
REG_AUDIO_INFO_DATA = 0x308,
REG_AUDIO_MAUD = 0x328,
REG_AUDIO_NAUD = 0x32C,
REG_AUDIO_EXT_DATA = 0x330,
};
enum {
/* HDCP */
REG_HDCP_ENABLE = 0x400,
};
enum {
/* main stream attributes for MST STREAM2, 3, and 4 */
REG_STREAM2_MSA_START = 0x500,
REG_STREAM3_MSA_START = 0x550,
REG_STREAM4_MSA_START = 0x5A0,
REG_VC_PAYLOAD_BUFFER_ADDR = 0x800,
};
enum {
LINK_BW_SET_162GBPS = 0x06,
LINK_BW_SET_270GBPS = 0x0A,
LINK_BW_SET_540GBPS = 0x14,
};
enum {
LANE_COUNT_SET_1 = 0x1,
LANE_COUNT_SET_2 = 0x2,
LANE_COUNT_SET_4 = 0x4,
};
enum {
TRAINING_PATTERN_SET_OFF = 0x0,
/* training pattern 1 used for clock recovery */
TRAINING_PATTERN_SET_TP1 = 0x1,
/* training pattern 2 used for channel equalization */
TRAINING_PATTERN_SET_TP2 = 0x2,
/*
* training pattern 3 used for channel equalization for cores with DP
* v1.2
*/
TRAINING_PATTERN_SET_TP3 = 0x3,
};
enum {
LINK_QUAL_PATTERN_SET_OFF = 0x0,
/* D10.2 unscrambled test pattern transmitted */
LINK_QUAL_PATTERN_SET_D102_TEST = 0x1,
/* symbol error rate measurement pattern transmitted */
LINK_QUAL_PATTERN_SET_SER_MES = 0x2,
/* pseudo random bit sequence 7 transmitted */
LINK_QUAL_PATTERN_SET_PRBS7 = 0x3,
};
enum {
SOFT_RESET_VIDEO_STREAM1_MASK = 0x00000001,
SOFT_RESET_VIDEO_STREAM2_MASK = 0x00000002,
SOFT_RESET_VIDEO_STREAM3_MASK = 0x00000004,
SOFT_RESET_VIDEO_STREAM4_MASK = 0x00000008,
SOFT_RESET_AUX_MASK = 0x00000080,
SOFT_RESET_VIDEO_STREAM_ALL_MASK = 0x0000000F,
};
enum {
MST_CONFIG_MST_EN_MASK = 0x00000001,
};
enum {
LINE_RESET_DISABLE_MASK = 0x1,
};
#define AUX_CMD_NBYTES_TRANSFER_MASK 0x0000000F
#define AUX_CMD_SHIFT 8
#define AUX_CMD_MASK 0x00000F00
enum {
AUX_CMD_I2C_WRITE = 0x0,
AUX_CMD_I2C_READ = 0x1,
AUX_CMD_I2C_WRITE_STATUS = 0x2,
AUX_CMD_I2C_WRITE_MOT = 0x4,
AUX_CMD_I2C_READ_MOT = 0x5,
AUX_CMD_I2C_WRITE_STATUS_MOT = 0x6,
AUX_CMD_WRITE = 0x8,
AUX_CMD_READ = 0x9,
};
#define AUX_CLK_DIVIDER_VAL_MASK 0x00FF
#define AUX_CLK_DIVIDER_AUX_SIG_WIDTH_FILT_SHIFT 8
#define AUX_CLK_DIVIDER_AUX_SIG_WIDTH_FILT_MASK 0xFF00
enum {
INTERRUPT_SIG_STATE_HPD_STATE_MASK = 0x00000001,
INTERRUPT_SIG_STATE_REQUEST_STATE_MASK = 0x00000002,
INTERRUPT_SIG_STATE_REPLY_STATE_MASK = 0x00000004,
INTERRUPT_SIG_STATE_REPLY_TIMEOUT_MASK = 0x00000008,
};
enum {
AUX_REPLY_CODE_ACK = 0x0,
AUX_REPLY_CODE_I2C_ACK = 0x0,
AUX_REPLY_CODE_NACK = 0x1,
AUX_REPLY_CODE_DEFER = 0x2,
AUX_REPLY_CODE_I2C_NACK = 0x4,
AUX_REPLY_CODE_I2C_DEFER = 0x8,
};
enum {
INTERRUPT_STATUS_HPD_IRQ_MASK = 0x00000001,
INTERRUPT_STATUS_HPD_EVENT_MASK = 0x00000002,
INTERRUPT_STATUS_REPLY_RECEIVED_MASK = 0x00000004,
INTERRUPT_STATUS_REPLY_TIMEOUT_MASK = 0x00000008,
INTERRUPT_STATUS_HPD_PULSE_DETECTED_MASK = 0x00000010,
INTERRUPT_STATUS_EXT_PKT_TXD_MASK = 0x00000020,
};
enum {
INTERRUPT_MASK_HPD_IRQ_MASK = 0x00000001,
INTERRUPT_MASK_HPD_EVENT_MASK = 0x00000002,
INTERRUPT_MASK_REPLY_RECEIVED_MASK = 0x00000004,
INTERRUPT_MASK_REPLY_TIMEOUT_MASK = 0x00000008,
INTERRUPT_MASK_HPD_PULSE_DETECTED_MASK = 0x00000010,
INTERRUPT_MASK_EXT_PKT_TXD_MASK = 0x00000020,
};
#define REPLY_STATUS_REPLY_STATUS_STATE_SHIFT 4
#define REPLY_STATUS_REPLY_STATUS_STATE_MASK 0x00000FF0
enum {
REPLY_STATUS_REPLY_RECEIVED_MASK = 0x00000001,
REPLY_STATUS_REPLY_IN_PROGRESS_MASK = 0x00000002,
REPLY_STATUS_REQUEST_IN_PROGRESS_MASK = 0x00000004,
REPLY_STATUS_REPLY_ERROR_MASK = 0x00000008,
};
#define MAIN_STREAMX_POLARITY_VSYNC_POL_SHIFT 1
enum {
MAIN_STREAMX_POLARITY_HSYNC_POL_MASK = 0x00000001,
MAIN_STREAMX_POLARITY_VSYNC_POL_MASK = 0x00000002,
};
enum {
MAIN_STREAMX_MISC0_SYNC_CLK_MASK = 0x00000001,
};
#define MAIN_STREAMX_MISC0_COMPONENT_FORMAT_SHIFT 1
#define MAIN_STREAMX_MISC0_COMPONENT_FORMAT_MASK 0x00000006
enum {
MAIN_STREAMX_MISC0_COMPONENT_FORMAT_RGB = 0x0,
MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR422 = 0x1,
MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR444 = 0x2,
};
#define MAIN_STREAMX_MISC0_DYNAMIC_RANGE_SHIFT 3
#define MAIN_STREAMX_MISC0_DYNAMIC_RANGE_MASK 0x00000008
#define MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_SHIFT 4
#define MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_MASK 0x00000010
#define MAIN_STREAMX_MISC0_BDC_SHIFT 5
#define MAIN_STREAMX_MISC0_BDC_MASK 0x000000E0
enum {
MAIN_STREAMX_MISC0_BDC_6BPC = 0x0,
MAIN_STREAMX_MISC0_BDC_8BPC = 0x1,
MAIN_STREAMX_MISC0_BDC_10BPC = 0x2,
MAIN_STREAMX_MISC0_BDC_12BPC = 0x3,
MAIN_STREAMX_MISC0_BDC_16BPC = 0x4,
};
enum {
PHY_CONFIG_PHY_RESET_ENABLE_MASK = 0x0000000,
PHY_CONFIG_PHY_RESET_MASK = 0x0000001,
PHY_CONFIG_GTTX_RESET_MASK = 0x0000002,
PHY_CONFIG_GT_ALL_RESET_MASK = 0x0000003,
PHY_CONFIG_TX_PHY_PMA_RESET_MASK = 0x0000100,
PHY_CONFIG_TX_PHY_PCS_RESET_MASK = 0x0000200,
PHY_CONFIG_TX_PHY_POLARITY_MASK = 0x0000800,
PHY_CONFIG_TX_PHY_PRBSFORCEERR_MASK = 0x0001000,
PHY_CONFIG_TX_PHY_POLARITY_IND_LANE_MASK = 0x0010000,
PHY_CONFIG_TX_PHY_POLARITY_LANE0_MASK = 0x0020000,
PHY_CONFIG_TX_PHY_POLARITY_LANE1_MASK = 0x0040000,
PHY_CONFIG_TX_PHY_POLARITY_LANE2_MASK = 0x0080000,
PHY_CONFIG_TX_PHY_POLARITY_LANE3_MASK = 0x0100000,
PHY_CONFIG_TX_PHY_8B10BEN_MASK = 0x0200000,
};
#define PHY_CONFIG_TX_PHY_LOOPBACK_SHIFT 13
#define PHY_CONFIG_TX_PHY_LOOPBACK_MASK 0x000E000
enum {
PHY_CLOCK_SELECT_162GBPS = 0x1,
PHY_CLOCK_SELECT_270GBPS = 0x3,
PHY_CLOCK_SELECT_540GBPS = 0x5,
};
enum {
VS_LEVEL_0 = 0x2,
VS_LEVEL_1 = 0x5,
VS_LEVEL_2 = 0x8,
VS_LEVEL_3 = 0xF,
VS_LEVEL_OFFSET = 0x4,
};
enum {
PE_LEVEL_0 = 0x00,
PE_LEVEL_1 = 0x0E,
PE_LEVEL_2 = 0x14,
PE_LEVEL_3 = 0x1B,
};
enum {
PHY_STATUS_RESET_LANE_2_3_DONE_SHIFT = 2,
PHY_STATUS_TX_ERROR_LANE_0_SHIFT = 18,
PHY_STATUS_TX_BUFFER_STATUS_LANE_1_SHIFT = 20,
PHY_STATUS_TX_ERROR_LANE_1_SHIFT = 22,
PHY_STATUS_TX_BUFFER_STATUS_LANE_0_SHIFT = 16,
PHY_STATUS_TX_BUFFER_STATUS_LANE_2_SHIFT = 24,
PHY_STATUS_TX_ERROR_LANE_2_SHIFT = 26,
PHY_STATUS_TX_BUFFER_STATUS_LANE_3_SHIFT = 28,
PHY_STATUS_TX_ERROR_LANE_3_SHIFT = 30,
};
enum {
PHY_STATUS_RESET_LANE_0_DONE_MASK = 0x00000001,
PHY_STATUS_RESET_LANE_1_DONE_MASK = 0x00000002,
PHY_STATUS_RESET_LANE_2_3_DONE_MASK = 0x0000000C,
PHY_STATUS_PLL_LANE0_1_LOCK_MASK = 0x00000010,
PHY_STATUS_PLL_LANE2_3_LOCK_MASK = 0x00000020,
PHY_STATUS_PLL_FABRIC_LOCK_MASK = 0x00000040,
PHY_STATUS_TX_BUFFER_STATUS_LANE_0_MASK = 0x00030000,
PHY_STATUS_TX_ERROR_LANE_0_MASK = 0x000C0000,
PHY_STATUS_TX_BUFFER_STATUS_LANE_1_MASK = 0x00300000,
PHY_STATUS_TX_ERROR_LANE_1_MASK = 0x00C00000,
PHY_STATUS_TX_BUFFER_STATUS_LANE_2_MASK = 0x03000000,
PHY_STATUS_TX_ERROR_LANE_2_MASK = 0x0C000000,
PHY_STATUS_TX_BUFFER_STATUS_LANE_3_MASK = 0x30000000,
PHY_STATUS_TX_ERROR_LANE_3_MASK = 0xC0000000,
};
#define PHY_STATUS_LANE_0_READY_MASK \
(PHY_STATUS_RESET_LANE_0_DONE_MASK | \
PHY_STATUS_PLL_LANE0_1_LOCK_MASK)
#define PHY_STATUS_LANES_0_1_READY_MASK \
(PHY_STATUS_LANE_0_READY_MASK | \
PHY_STATUS_RESET_LANE_1_DONE_MASK)
/*
* PHY_STATUS_ALL_LANES_READY_MASK seems to be missing lanes 0 and 1 in
* Xilinx dp_v3_0 implementation
*/
#define PHY_STATUS_ALL_LANES_READY_MASK \
(PHY_STATUS_LANES_0_1_READY_MASK | \
PHY_STATUS_RESET_LANE_2_3_DONE_MASK | \
PHY_STATUS_PLL_LANE2_3_LOCK_MASK)
/**
* phy_status_lanes_ready_mask() - Generate phy status ready mask
* @lane_count: Number of lanes for which to generate a mask
*
* Return: The generated phy status ready mask
*/
static inline u32 phy_status_lanes_ready_mask(u8 lane_count)
{
if (lane_count > 2)
return PHY_STATUS_ALL_LANES_READY_MASK;
if (lane_count == 2)
return PHY_STATUS_LANES_0_1_READY_MASK;
return PHY_STATUS_LANE_0_READY_MASK;
}
#define GT_DRP_COMMAND_DRP_ADDR_MASK 0x000F
#define GT_DRP_COMMAND_DRP_RW_CMD_MASK 0x0080
#define GT_DRP_COMMAND_DRP_W_DATA_SHIFT 16
#define GT_DRP_COMMAND_DRP_W_DATA_MASK 0xFF00
#define HDCP_ENABLE_BYPASS_DISABLE_MASK 0x0001
#endif /* __GDSYS_LOGICORE_DP_TX_REGIF_H__ */
@@ -0,0 +1,405 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2016-2018 ARM Ltd.
* Author: Liviu Dudau <liviu.dudau@foss.arm.com>
*
*/
#define DEBUG
#include <common.h>
#include <video.h>
#include <dm.h>
#ifdef CONFIG_DISPLAY
#include <display.h>
#endif
#include <fdtdec.h>
#include <asm/io.h>
#include <os.h>
#include <fdt_support.h>
#include <clk.h>
#include <linux/sizes.h>
#define MALIDP_CORE_ID 0x0018
#define MALIDP_REG_BG_COLOR 0x0044
#define MALIDP_LAYER_LV1 0x0100
#define MALIDP_DC_STATUS 0xc000
#define MALIDP_DC_CONTROL 0xc010
#define MALIDP_DC_CFG_VALID 0xc014
/* offsets inside the modesetting register block */
#define MALIDP_H_INTERVALS 0x0000
#define MALIDP_V_INTERVALS 0x0004
#define MALIDP_SYNC_CONTROL 0x0008
#define MALIDP_HV_ACTIVESIZE 0x000c
#define MALIDP_OUTPUT_DEPTH 0x001c
/* offsets inside the layer register block */
#define MALIDP_LAYER_FORMAT 0x0000
#define MALIDP_LAYER_CONTROL 0x0004
#define MALIDP_LAYER_IN_SIZE 0x000c
#define MALIDP_LAYER_CMP_SIZE 0x0010
#define MALIDP_LAYER_STRIDE 0x0018
#define MALIDP_LAYER_PTR_LOW 0x0024
#define MALIDP_LAYER_PTR_HIGH 0x0028
/* offsets inside the IRQ control blocks */
#define MALIDP_REG_MASKIRQ 0x0008
#define MALIDP_REG_CLEARIRQ 0x000c
#define M1BITS 0x0001
#define M2BITS 0x0003
#define M4BITS 0x000f
#define M8BITS 0x00ff
#define M10BITS 0x03ff
#define M12BITS 0x0fff
#define M13BITS 0x1fff
#define M16BITS 0xffff
#define M17BITS 0x1ffff
#define MALIDP_H_FRONTPORCH(x) (((x) & M12BITS) << 0)
#define MALIDP_H_BACKPORCH(x) (((x) & M10BITS) << 16)
#define MALIDP_V_FRONTPORCH(x) (((x) & M12BITS) << 0)
#define MALIDP_V_BACKPORCH(x) (((x) & M8BITS) << 16)
#define MALIDP_H_SYNCWIDTH(x) (((x) & M10BITS) << 0)
#define MALIDP_V_SYNCWIDTH(x) (((x) & M8BITS) << 16)
#define MALIDP_H_ACTIVE(x) (((x) & M13BITS) << 0)
#define MALIDP_V_ACTIVE(x) (((x) & M13BITS) << 16)
#define MALIDP_CMP_V_SIZE(x) (((x) & M13BITS) << 16)
#define MALIDP_CMP_H_SIZE(x) (((x) & M13BITS) << 0)
#define MALIDP_IN_V_SIZE(x) (((x) & M13BITS) << 16)
#define MALIDP_IN_H_SIZE(x) (((x) & M13BITS) << 0)
#define MALIDP_DC_CM_CONTROL(x) ((x) & M1BITS) << 16, 1 << 16
#define MALIDP_DC_STATUS_GET_CM(reg) (((reg) >> 16) & M1BITS)
#define MALIDP_FORMAT_ARGB8888 0x08
#define MALIDP_DEFAULT_BG_R 0x0
#define MALIDP_DEFAULT_BG_G 0x0
#define MALIDP_DEFAULT_BG_B 0x0
#define MALIDP_PRODUCT_ID(core_id) ((u32)(core_id) >> 16)
#define MALIDP500 0x500
DECLARE_GLOBAL_DATA_PTR;
struct malidp_priv {
phys_addr_t base_addr;
phys_addr_t dc_status_addr;
phys_addr_t dc_control_addr;
phys_addr_t cval_addr;
struct udevice *display; /* display device attached */
struct clk aclk;
struct clk pxlclk;
u16 modeset_regs_offset;
u8 config_bit_shift;
u8 clear_irq; /* offset for IRQ clear register */
};
static const struct video_ops malidp_ops = {
};
static int malidp_get_hwid(phys_addr_t base_addr)
{
int hwid;
/*
* reading from the old CORE_ID offset will always
* return 0x5000000 on DP500
*/
hwid = readl(base_addr + MALIDP_CORE_ID);
if (MALIDP_PRODUCT_ID(hwid) == MALIDP500)
return hwid;
/* otherwise try the other gen CORE_ID offset */
hwid = readl(base_addr + MALIDP_DC_STATUS + MALIDP_CORE_ID);
return hwid;
}
/*
* wait for config mode bit setup to be acted upon by the hardware
*/
static int malidp_wait_configdone(struct malidp_priv *malidp)
{
u32 status, tries = 300;
while (tries--) {
status = readl(malidp->dc_status_addr);
if ((status >> malidp->config_bit_shift) & 1)
break;
udelay(500);
}
if (!tries)
return -ETIMEDOUT;
return 0;
}
/*
* signal the hardware to enter configuration mode
*/
static int malidp_enter_config(struct malidp_priv *malidp)
{
setbits_le32(malidp->dc_control_addr, 1 << malidp->config_bit_shift);
return malidp_wait_configdone(malidp);
}
/*
* signal the hardware to exit configuration mode
*/
static int malidp_leave_config(struct malidp_priv *malidp)
{
clrbits_le32(malidp->dc_control_addr, 1 << malidp->config_bit_shift);
return malidp_wait_configdone(malidp);
}
static void malidp_setup_timings(struct malidp_priv *malidp,
struct display_timing *timings)
{
u32 val = MALIDP_H_SYNCWIDTH(timings->hsync_len.typ) |
MALIDP_V_SYNCWIDTH(timings->vsync_len.typ);
writel(val, malidp->base_addr + malidp->modeset_regs_offset +
MALIDP_SYNC_CONTROL);
val = MALIDP_H_BACKPORCH(timings->hback_porch.typ) |
MALIDP_H_FRONTPORCH(timings->hfront_porch.typ);
writel(val, malidp->base_addr + malidp->modeset_regs_offset +
MALIDP_H_INTERVALS);
val = MALIDP_V_BACKPORCH(timings->vback_porch.typ) |
MALIDP_V_FRONTPORCH(timings->vfront_porch.typ);
writel(val, malidp->base_addr + malidp->modeset_regs_offset +
MALIDP_V_INTERVALS);
val = MALIDP_H_ACTIVE(timings->hactive.typ) |
MALIDP_V_ACTIVE(timings->vactive.typ);
writel(val, malidp->base_addr + malidp->modeset_regs_offset +
MALIDP_HV_ACTIVESIZE);
/* default output bit-depth per colour is 8 bits */
writel(0x080808, malidp->base_addr + malidp->modeset_regs_offset +
MALIDP_OUTPUT_DEPTH);
}
static int malidp_setup_mode(struct malidp_priv *malidp,
struct display_timing *timings)
{
int err;
if (clk_set_rate(&malidp->pxlclk, timings->pixelclock.typ) == 0)
return -EIO;
malidp_setup_timings(malidp, timings);
err = display_enable(malidp->display, 8, timings);
if (err)
printf("display_enable failed with %d\n", err);
return err;
}
static void malidp_setup_layer(struct malidp_priv *malidp,
struct display_timing *timings,
u32 layer_offset, phys_addr_t fb_addr)
{
u32 val;
/* setup the base layer's pixel format to A8R8G8B8 */
writel(MALIDP_FORMAT_ARGB8888, malidp->base_addr + layer_offset +
MALIDP_LAYER_FORMAT);
/* setup layer composition size */
val = MALIDP_CMP_V_SIZE(timings->vactive.typ) |
MALIDP_CMP_H_SIZE(timings->hactive.typ);
writel(val, malidp->base_addr + layer_offset +
MALIDP_LAYER_CMP_SIZE);
/* setup layer input size */
val = MALIDP_IN_V_SIZE(timings->vactive.typ) |
MALIDP_IN_H_SIZE(timings->hactive.typ);
writel(val, malidp->base_addr + layer_offset + MALIDP_LAYER_IN_SIZE);
/* setup layer stride in bytes */
writel(timings->hactive.typ << 2, malidp->base_addr + layer_offset +
MALIDP_LAYER_STRIDE);
/* set framebuffer address */
writel(lower_32_bits(fb_addr), malidp->base_addr + layer_offset +
MALIDP_LAYER_PTR_LOW);
writel(upper_32_bits(fb_addr), malidp->base_addr + layer_offset +
MALIDP_LAYER_PTR_HIGH);
/* enable layer */
setbits_le32(malidp->base_addr + layer_offset +
MALIDP_LAYER_CONTROL, 1);
}
static void malidp_set_configvalid(struct malidp_priv *malidp)
{
setbits_le32(malidp->cval_addr, 1);
}
static int malidp_update_timings_from_edid(struct udevice *dev,
struct display_timing *timings)
{
#ifdef CONFIG_DISPLAY
struct malidp_priv *priv = dev_get_priv(dev);
struct udevice *disp_dev;
int err;
err = uclass_first_device(UCLASS_DISPLAY, &disp_dev);
if (err)
return err;
priv->display = disp_dev;
err = display_read_timing(disp_dev, timings);
if (err)
return err;
#endif
return 0;
}
static int malidp_probe(struct udevice *dev)
{
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
ofnode framebuffer = ofnode_find_subnode(dev_ofnode(dev), "framebuffer");
struct malidp_priv *priv = dev_get_priv(dev);
struct display_timing timings;
phys_addr_t fb_base, fb_size;
const char *format;
u32 value;
int err;
if (!ofnode_valid(framebuffer))
return -EINVAL;
err = clk_get_by_name(dev, "pxlclk", &priv->pxlclk);
if (err) {
dev_err(dev, "failed to get pixel clock\n");
return err;
}
err = clk_get_by_name(dev, "aclk", &priv->aclk);
if (err) {
dev_err(dev, "failed to get AXI clock\n");
goto fail_aclk;
}
err = ofnode_decode_display_timing(dev_ofnode(dev), 1, &timings);
if (err) {
dev_err(dev, "failed to get any display timings\n");
goto fail_timings;
}
err = malidp_update_timings_from_edid(dev, &timings);
if (err) {
printf("malidp_update_timings_from_edid failed: %d\n", err);
goto fail_timings;
}
fb_base = ofnode_get_addr_size(framebuffer, "reg", &fb_size);
if (fb_base != FDT_ADDR_T_NONE) {
uc_plat->base = fb_base;
uc_plat->size = fb_size;
} else {
printf("cannot get address size for framebuffer\n");
}
err = ofnode_read_u32(framebuffer, "width", &value);
if (err)
goto fail_timings;
uc_priv->xsize = (ushort)value;
err = ofnode_read_u32(framebuffer, "height", &value);
if (err)
goto fail_timings;
uc_priv->ysize = (ushort)value;
format = ofnode_read_string(framebuffer, "format");
if (!format) {
err = -EINVAL;
goto fail_timings;
} else if (!strncmp(format, "a8r8g8b8", 8)) {
uc_priv->bpix = VIDEO_BPP32;
}
uc_priv->rot = 0;
priv->base_addr = (phys_addr_t)dev_read_addr(dev);
clk_enable(&priv->pxlclk);
clk_enable(&priv->aclk);
value = malidp_get_hwid(priv->base_addr);
printf("Display: Arm Mali DP%3x r%dp%d\n", MALIDP_PRODUCT_ID(value),
(value >> 12) & 0xf, (value >> 8) & 0xf);
if (MALIDP_PRODUCT_ID(value) == MALIDP500) {
/* DP500 is special */
priv->modeset_regs_offset = 0x28;
priv->dc_status_addr = priv->base_addr;
priv->dc_control_addr = priv->base_addr + 0xc;
priv->cval_addr = priv->base_addr + 0xf00;
priv->config_bit_shift = 17;
priv->clear_irq = 0;
} else {
priv->modeset_regs_offset = 0x30;
priv->dc_status_addr = priv->base_addr + MALIDP_DC_STATUS;
priv->dc_control_addr = priv->base_addr + MALIDP_DC_CONTROL;
priv->cval_addr = priv->base_addr + MALIDP_DC_CFG_VALID;
priv->config_bit_shift = 16;
priv->clear_irq = MALIDP_REG_CLEARIRQ;
}
/* enter config mode */
err = malidp_enter_config(priv);
if (err)
return err;
/* disable interrupts */
writel(0, priv->dc_status_addr + MALIDP_REG_MASKIRQ);
writel(0xffffffff, priv->dc_status_addr + priv->clear_irq);
err = malidp_setup_mode(priv, &timings);
if (err)
goto fail_timings;
malidp_setup_layer(priv, &timings, MALIDP_LAYER_LV1,
(phys_addr_t)uc_plat->base);
err = malidp_leave_config(priv);
if (err)
goto fail_timings;
malidp_set_configvalid(priv);
return 0;
fail_timings:
clk_free(&priv->aclk);
fail_aclk:
clk_free(&priv->pxlclk);
return err;
}
static int malidp_bind(struct udevice *dev)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
/* choose max possible size: 2K x 2K, XRGB888 framebuffer */
uc_plat->size = 4 * 2048 * 2048;
return 0;
}
static const struct udevice_id malidp_ids[] = {
{ .compatible = "arm,mali-dp500" },
{ .compatible = "arm,mali-dp550" },
{ .compatible = "arm,mali-dp650" },
{ }
};
U_BOOT_DRIVER(mali_dp) = {
.name = "mali_dp",
.id = UCLASS_VIDEO,
.of_match = malidp_ids,
.bind = malidp_bind,
.probe = malidp_probe,
.priv_auto_alloc_size = sizeof(struct malidp_priv),
.ops = &malidp_ops,
};
@@ -0,0 +1,485 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007
* DENX Software Engineering, Anatolij Gustschin, agust@denx.de
*/
/*
* mb862xx.c - Graphic interface for Fujitsu CoralP/Lime
* PCI and video mode code was derived from smiLynxEM driver.
*/
#include <common.h>
#include <asm/io.h>
#include <env.h>
#include <pci.h>
#include <video_fb.h>
#include "videomodes.h"
#include <mb862xx.h>
#if defined(CONFIG_POST)
#include <post.h>
#endif
/*
* Graphic Device
*/
GraphicDevice mb862xx;
/*
* 32MB external RAM - 256K Chip MMIO = 0x1FC0000 ;
*/
#define VIDEO_MEM_SIZE 0x01FC0000
#if defined(CONFIG_PCI)
#if defined(CONFIG_VIDEO_CORALP)
static struct pci_device_id supported[] = {
{ PCI_VENDOR_ID_FUJITSU, PCI_DEVICE_ID_CORAL_P },
{ PCI_VENDOR_ID_FUJITSU, PCI_DEVICE_ID_CORAL_PA },
{ }
};
/* Internal clock frequency divider table, index is mode number */
unsigned int fr_div[] = { 0x00000f00, 0x00000900, 0x00000500 };
#endif
#endif
#if defined(CONFIG_VIDEO_CORALP)
#define rd_io in32r
#define wr_io out32r
#else
#define rd_io(addr) in_be32((volatile unsigned *)(addr))
#define wr_io(addr, val) out_be32((volatile unsigned *)(addr), (val))
#endif
#define HOST_RD_REG(off) rd_io((dev->frameAdrs + GC_HOST_BASE + (off)))
#define HOST_WR_REG(off, val) wr_io((dev->frameAdrs + GC_HOST_BASE + (off)), \
(val))
#define DISP_RD_REG(off) rd_io((dev->frameAdrs + GC_DISP_BASE + (off)))
#define DISP_WR_REG(off, val) wr_io((dev->frameAdrs + GC_DISP_BASE + (off)), \
(val))
#define DE_RD_REG(off) rd_io((dev->dprBase + (off)))
#define DE_WR_REG(off, val) wr_io((dev->dprBase + (off)), (val))
#if defined(CONFIG_VIDEO_CORALP)
#define DE_WR_FIFO(val) wr_io((dev->dprBase + (GC_GEO_FIFO)), (val))
#else
#define DE_WR_FIFO(val) wr_io((dev->dprBase + (GC_FIFO)), (val))
#endif
#define L0PAL_WR_REG(idx, val) wr_io((dev->frameAdrs + \
(GC_DISP_BASE | GC_L0PAL0) + \
((idx) << 2)), (val))
#if defined(CONFIG_VIDEO_MB862xx_ACCEL)
static void gdc_sw_reset (void)
{
GraphicDevice *dev = &mb862xx;
HOST_WR_REG (GC_SRST, 0x1);
udelay (500);
video_hw_init ();
}
static void de_wait (void)
{
GraphicDevice *dev = &mb862xx;
int lc = 0x10000;
/*
* Sync with software writes to framebuffer,
* try to reset if engine locked
*/
while (DE_RD_REG (GC_CTR) & 0x00000131)
if (lc-- < 0) {
gdc_sw_reset ();
puts ("gdc reset done after drawing engine lock.\n");
break;
}
}
static void de_wait_slots (int slots)
{
GraphicDevice *dev = &mb862xx;
int lc = 0x10000;
/* Wait for free fifo slots */
while (DE_RD_REG (GC_IFCNT) < slots)
if (lc-- < 0) {
gdc_sw_reset ();
puts ("gdc reset done after drawing engine lock.\n");
break;
}
}
#endif
#if !defined(CONFIG_VIDEO_CORALP)
static void board_disp_init (void)
{
GraphicDevice *dev = &mb862xx;
const gdc_regs *regs = board_get_regs ();
while (regs->index) {
DISP_WR_REG (regs->index, regs->value);
regs++;
}
}
#endif
/*
* Init drawing engine if accel enabled.
* Also clears visible framebuffer.
*/
static void de_init (void)
{
GraphicDevice *dev = &mb862xx;
#if defined(CONFIG_VIDEO_MB862xx_ACCEL)
int cf = (dev->gdfBytesPP == 1) ? 0x0000 : 0x8000;
dev->dprBase = dev->frameAdrs + GC_DRAW_BASE;
/* Setup mode and fbbase, xres, fg, bg */
de_wait_slots (2);
DE_WR_FIFO (0xf1010108);
DE_WR_FIFO (cf | 0x0300);
DE_WR_REG (GC_FBR, 0x0);
DE_WR_REG (GC_XRES, dev->winSizeX);
DE_WR_REG (GC_FC, 0x0);
DE_WR_REG (GC_BC, 0x0);
/* Reset clipping */
DE_WR_REG (GC_CXMIN, 0x0);
DE_WR_REG (GC_CXMAX, dev->winSizeX);
DE_WR_REG (GC_CYMIN, 0x0);
DE_WR_REG (GC_CYMAX, dev->winSizeY);
/* Clear framebuffer using drawing engine */
de_wait_slots (3);
DE_WR_FIFO (0x09410000);
DE_WR_FIFO (0x00000000);
DE_WR_FIFO (dev->winSizeY << 16 | dev->winSizeX);
/* sync with SW access to framebuffer */
de_wait ();
#else
unsigned int i, *p;
i = dev->winSizeX * dev->winSizeY;
p = (unsigned int *)dev->frameAdrs;
while (i--)
*p++ = 0;
#endif
}
#if defined(CONFIG_VIDEO_CORALP)
/* use CCF and MMR parameters for Coral-P Eval. Board as default */
#ifndef CONFIG_SYS_MB862xx_CCF
#define CONFIG_SYS_MB862xx_CCF 0x00090000
#endif
#ifndef CONFIG_SYS_MB862xx_MMR
#define CONFIG_SYS_MB862xx_MMR 0x11d7fa13
#endif
unsigned int pci_video_init (void)
{
GraphicDevice *dev = &mb862xx;
pci_dev_t devbusfn;
u16 device;
if ((devbusfn = pci_find_devices (supported, 0)) < 0) {
puts("controller not present\n");
return 0;
}
/* PCI setup */
pci_write_config_dword (devbusfn, PCI_COMMAND,
(PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &dev->frameAdrs);
dev->frameAdrs = pci_mem_to_phys (devbusfn, dev->frameAdrs);
if (dev->frameAdrs == 0) {
puts ("PCI config: failed to get base address\n");
return 0;
}
dev->pciBase = dev->frameAdrs;
puts("Coral-");
pci_read_config_word(devbusfn, PCI_DEVICE_ID, &device);
switch (device) {
case PCI_DEVICE_ID_CORAL_P:
puts("P\n");
break;
case PCI_DEVICE_ID_CORAL_PA:
puts("PA\n");
break;
default:
puts("Unknown\n");
return 0;
}
/* Setup clocks and memory mode for Coral-P(A) */
HOST_WR_REG(GC_CCF, CONFIG_SYS_MB862xx_CCF);
udelay (200);
HOST_WR_REG(GC_MMR, CONFIG_SYS_MB862xx_MMR);
udelay (100);
return dev->frameAdrs;
}
unsigned int card_init (void)
{
GraphicDevice *dev = &mb862xx;
unsigned int cf, videomode, div = 0;
unsigned long t1, hsync, vsync;
char *penv;
int tmp, i, bpp;
struct ctfb_res_modes *res_mode;
struct ctfb_res_modes var_mode;
memset (dev, 0, sizeof (GraphicDevice));
if (!pci_video_init ())
return 0;
tmp = 0;
videomode = 0x310;
/* get video mode via environment */
penv = env_get("videomode");
if (penv) {
/* decide if it is a string */
if (penv[0] <= '9') {
videomode = (int) simple_strtoul (penv, NULL, 16);
tmp = 1;
}
} else {
tmp = 1;
}
if (tmp) {
/* parameter are vesa modes, search params */
for (i = 0; i < VESA_MODES_COUNT; i++) {
if (vesa_modes[i].vesanr == videomode)
break;
}
if (i == VESA_MODES_COUNT) {
printf ("\tno VESA Mode found, fallback to mode 0x%x\n",
videomode);
i = 0;
}
res_mode = (struct ctfb_res_modes *)
&res_mode_init[vesa_modes[i].resindex];
if (vesa_modes[i].resindex > 2) {
puts ("\tUnsupported resolution, using default\n");
bpp = vesa_modes[1].bits_per_pixel;
div = fr_div[1];
}
bpp = vesa_modes[i].bits_per_pixel;
div = fr_div[vesa_modes[i].resindex];
} else {
res_mode = (struct ctfb_res_modes *) &var_mode;
bpp = video_get_params (res_mode, penv);
}
/* calculate hsync and vsync freq (info only) */
t1 = (res_mode->left_margin + res_mode->xres +
res_mode->right_margin + res_mode->hsync_len) / 8;
t1 *= 8;
t1 *= res_mode->pixclock;
t1 /= 1000;
hsync = 1000000000L / t1;
t1 *= (res_mode->upper_margin + res_mode->yres +
res_mode->lower_margin + res_mode->vsync_len);
t1 /= 1000;
vsync = 1000000000L / t1;
/* fill in Graphic device struct */
sprintf (dev->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres,
res_mode->yres, bpp, (hsync / 1000), (vsync / 1000));
printf ("\t%s\n", dev->modeIdent);
dev->winSizeX = res_mode->xres;
dev->winSizeY = res_mode->yres;
dev->memSize = VIDEO_MEM_SIZE;
switch (bpp) {
case 8:
dev->gdfIndex = GDF__8BIT_INDEX;
dev->gdfBytesPP = 1;
break;
case 15:
case 16:
dev->gdfIndex = GDF_15BIT_555RGB;
dev->gdfBytesPP = 2;
break;
default:
printf ("\t%d bpp configured, but only 8,15 and 16 supported\n",
bpp);
puts ("\tfallback to 15bpp\n");
dev->gdfIndex = GDF_15BIT_555RGB;
dev->gdfBytesPP = 2;
}
/* Setup dot clock (internal pll, division rate) */
DISP_WR_REG (GC_DCM1, div);
/* L0 init */
cf = (dev->gdfBytesPP == 1) ? 0x00000000 : 0x80000000;
DISP_WR_REG (GC_L0M, ((dev->winSizeX * dev->gdfBytesPP) / 64) << 16 |
(dev->winSizeY - 1) | cf);
DISP_WR_REG (GC_L0OA0, 0x0);
DISP_WR_REG (GC_L0DA0, 0x0);
DISP_WR_REG (GC_L0DY_L0DX, 0x0);
DISP_WR_REG (GC_L0EM, 0x0);
DISP_WR_REG (GC_L0WY_L0WX, 0x0);
DISP_WR_REG (GC_L0WH_L0WW, (dev->winSizeY - 1) << 16 | dev->winSizeX);
/* Display timing init */
DISP_WR_REG (GC_HTP_A, (dev->winSizeX +
res_mode->left_margin +
res_mode->right_margin +
res_mode->hsync_len - 1) << 16);
DISP_WR_REG (GC_HDB_HDP_A, (dev->winSizeX - 1) << 16 |
(dev->winSizeX - 1));
DISP_WR_REG (GC_VSW_HSW_HSP_A, (res_mode->vsync_len - 1) << 24 |
(res_mode->hsync_len - 1) << 16 |
(dev->winSizeX +
res_mode->right_margin - 1));
DISP_WR_REG (GC_VTR_A, (dev->winSizeY + res_mode->lower_margin +
res_mode->upper_margin +
res_mode->vsync_len - 1) << 16);
DISP_WR_REG (GC_VDP_VSP_A, (dev->winSizeY-1) << 16 |
(dev->winSizeY +
res_mode->lower_margin - 1));
DISP_WR_REG (GC_WY_WX, 0x0);
DISP_WR_REG (GC_WH_WW, dev->winSizeY << 16 | dev->winSizeX);
/* Display enable, L0 layer */
DISP_WR_REG (GC_DCM1, 0x80010000 | div);
return dev->frameAdrs;
}
#endif
#if !defined(CONFIG_VIDEO_CORALP)
int mb862xx_probe(unsigned int addr)
{
GraphicDevice *dev = &mb862xx;
unsigned int reg;
dev->frameAdrs = addr;
dev->dprBase = dev->frameAdrs + GC_DRAW_BASE;
/* Try to access GDC ID/Revision registers */
reg = HOST_RD_REG (GC_CID);
reg = HOST_RD_REG (GC_CID);
if (reg == 0x303) {
reg = DE_RD_REG(GC_REV);
reg = DE_RD_REG(GC_REV);
if ((reg & ~0xff) == 0x20050100)
return MB862XX_TYPE_LIME;
}
return 0;
}
#endif
void *video_hw_init (void)
{
GraphicDevice *dev = &mb862xx;
puts ("Video: Fujitsu ");
memset (dev, 0, sizeof (GraphicDevice));
#if defined(CONFIG_VIDEO_CORALP)
if (card_init () == 0)
return NULL;
#else
/*
* Preliminary init of the onboard graphic controller,
* retrieve base address
*/
if ((dev->frameAdrs = board_video_init ()) == 0) {
puts ("Controller not found!\n");
return NULL;
} else {
puts ("Lime\n");
/* Set Change of Clock Frequency Register */
HOST_WR_REG (GC_CCF, CONFIG_SYS_MB862xx_CCF);
/* Delay required */
udelay(300);
/* Set Memory I/F Mode Register) */
HOST_WR_REG (GC_MMR, CONFIG_SYS_MB862xx_MMR);
}
#endif
de_init ();
#if !defined(CONFIG_VIDEO_CORALP)
board_disp_init ();
#endif
#if (defined(CONFIG_LWMON5) || \
defined(CONFIG_SOCRATES)) && !(CONFIG_POST & CONFIG_SYS_POST_SYSMON)
/* Lamp on */
board_backlight_switch (1);
#endif
return dev;
}
/*
* Set a RGB color in the LUT
*/
void video_set_lut (unsigned int index, unsigned char r,
unsigned char g, unsigned char b)
{
GraphicDevice *dev = &mb862xx;
L0PAL_WR_REG (index, (r << 16) | (g << 8) | (b));
}
#if defined(CONFIG_VIDEO_MB862xx_ACCEL)
/*
* Drawing engine Fill and BitBlt screen region
*/
void video_hw_rectfill (unsigned int bpp, unsigned int dst_x,
unsigned int dst_y, unsigned int dim_x,
unsigned int dim_y, unsigned int color)
{
GraphicDevice *dev = &mb862xx;
de_wait_slots (3);
DE_WR_REG (GC_FC, color);
DE_WR_FIFO (0x09410000);
DE_WR_FIFO ((dst_y << 16) | dst_x);
DE_WR_FIFO ((dim_y << 16) | dim_x);
de_wait ();
}
void video_hw_bitblt (unsigned int bpp, unsigned int src_x,
unsigned int src_y, unsigned int dst_x,
unsigned int dst_y, unsigned int width,
unsigned int height)
{
GraphicDevice *dev = &mb862xx;
unsigned int ctrl = 0x0d000000L;
if (src_x >= dst_x && src_y >= dst_y)
ctrl |= 0x00440000L;
else if (src_x >= dst_x && src_y <= dst_y)
ctrl |= 0x00460000L;
else if (src_x <= dst_x && src_y >= dst_y)
ctrl |= 0x00450000L;
else
ctrl |= 0x00470000L;
de_wait_slots (4);
DE_WR_FIFO (ctrl);
DE_WR_FIFO ((src_y << 16) | src_x);
DE_WR_FIFO ((dst_y << 16) | dst_x);
DE_WR_FIFO ((height << 16) | width);
de_wait (); /* sync */
}
#endif
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2018 BayLibre, SAS
#
# Author: Neil Armstrong <narmstrong@baylibre.com>
config VIDEO_MESON
bool "Enable Amlogic Meson video support"
depends on DM_VIDEO
select DISPLAY
help
Enable Amlogic Meson Video Processing Unit video support.
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2018 BayLibre, SAS
#
# Author: Neil Armstrong <narmstrong@baylibre.com>
obj-$(CONFIG_VIDEO_MESON) = meson_vpu.o meson_vpu_init.o meson_canvas.o
obj-$(CONFIG_VIDEO_MESON) += meson_plane.o meson_venc.o meson_vclk.o
obj-$(CONFIG_VIDEO_MESON) += meson_dw_hdmi.o simplefb_common.o ../dw_hdmi.o
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson Video Processing Unit driver
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include "meson_vpu.h"
/* DMC Registers */
#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
#define CANVAS_WIDTH_LBIT 29
#define CANVAS_WIDTH_LWID 3
#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
#define CANVAS_WIDTH_HBIT 0
#define CANVAS_HEIGHT_BIT 9
#define CANVAS_BLKMODE_BIT 24
#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
#define CANVAS_LUT_WR_EN (0x2 << 8)
#define CANVAS_LUT_RD_EN (0x1 << 8)
void meson_canvas_setup(struct meson_vpu_priv *priv,
u32 canvas_index, u32 addr,
u32 stride, u32 height,
unsigned int wrap,
unsigned int blkmode)
{
dmc_write(DMC_CAV_LUT_DATAL,
(((addr + 7) >> 3)) |
(((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
dmc_write(DMC_CAV_LUT_DATAH,
((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
CANVAS_WIDTH_HBIT) |
(height << CANVAS_HEIGHT_BIT) |
(wrap << 22) |
(blkmode << CANVAS_BLKMODE_BIT));
dmc_write(DMC_CAV_LUT_ADDR,
CANVAS_LUT_WR_EN | canvas_index);
/* Force a read-back to make sure everything is flushed. */
dmc_read(DMC_CAV_LUT_DATAH);
}
@@ -0,0 +1,508 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 BayLibre, SAS
* Author: Jorge Ramirez-Ortiz <jramirez@baylibre.com>
*/
#include <common.h>
#include <display.h>
#include <dm.h>
#include <edid.h>
#include <asm/io.h>
#include <dw_hdmi.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <power/regulator.h>
#include <clk.h>
#include <linux/delay.h>
#include <reset.h>
#include <media_bus_format.h>
#include "meson_dw_hdmi.h"
#include "meson_vpu.h"
/* TOP Block Communication Channel */
#define HDMITX_TOP_ADDR_REG 0x0
#define HDMITX_TOP_DATA_REG 0x4
#define HDMITX_TOP_CTRL_REG 0x8
#define HDMITX_TOP_G12A_OFFSET 0x8000
/* Controller Communication Channel */
#define HDMITX_DWC_ADDR_REG 0x10
#define HDMITX_DWC_DATA_REG 0x14
#define HDMITX_DWC_CTRL_REG 0x18
/* HHI Registers */
#define HHI_MEM_PD_REG0 0x100 /* 0x40 */
#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */
#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */
#define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */
#define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */
#define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */
#define HHI_HDMI_PHY_CNTL4 0x3b0 /* 0xec */
#define HHI_HDMI_PHY_CNTL5 0x3b4 /* 0xed */
struct meson_dw_hdmi {
struct udevice *dev;
struct dw_hdmi hdmi;
void __iomem *hhi_base;
};
enum hdmi_compatible {
HDMI_COMPATIBLE_GXBB = 0,
HDMI_COMPATIBLE_GXL = 1,
HDMI_COMPATIBLE_GXM = 2,
HDMI_COMPATIBLE_G12A = 3,
};
static inline bool meson_hdmi_is_compatible(struct meson_dw_hdmi *priv,
enum hdmi_compatible family)
{
enum hdmi_compatible compat = dev_get_driver_data(priv->dev);
return compat == family;
}
static unsigned int dw_hdmi_top_read(struct dw_hdmi *hdmi, unsigned int addr)
{
struct meson_dw_hdmi *priv = container_of(hdmi, struct meson_dw_hdmi,
hdmi);
unsigned int data;
if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
return readl(hdmi->ioaddr +
HDMITX_TOP_G12A_OFFSET + (addr << 2));
/* ADDR must be written twice */
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
/* Read needs a second DATA read */
data = readl(hdmi->ioaddr + HDMITX_TOP_DATA_REG);
data = readl(hdmi->ioaddr + HDMITX_TOP_DATA_REG);
return data;
}
static inline void dw_hdmi_top_write(struct dw_hdmi *hdmi,
unsigned int addr, unsigned int data)
{
struct meson_dw_hdmi *priv = container_of(hdmi, struct meson_dw_hdmi,
hdmi);
if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A)) {
writel(data, hdmi->ioaddr +
HDMITX_TOP_G12A_OFFSET + (addr << 2));
return;
}
/* ADDR must be written twice */
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
/* Write needs single DATA write */
writel(data, hdmi->ioaddr + HDMITX_TOP_DATA_REG);
}
static inline void dw_hdmi_top_write_bits(struct dw_hdmi *hdmi,
unsigned int addr,
unsigned int mask,
unsigned int val)
{
unsigned int data = dw_hdmi_top_read(hdmi, addr);
data &= ~mask;
data |= val;
dw_hdmi_top_write(hdmi, addr, data);
}
static u8 dw_hdmi_dwc_read(struct dw_hdmi *hdmi, int addr)
{
unsigned int data;
/* ADDR must be written twice */
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
/* Read needs a second DATA read */
data = readl(hdmi->ioaddr + HDMITX_DWC_DATA_REG);
data = readl(hdmi->ioaddr + HDMITX_DWC_DATA_REG);
return data;
}
static inline void dw_hdmi_dwc_write(struct dw_hdmi *hdmi, u8 data, int addr)
{
/* ADDR must be written twice */
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
/* Write needs single DATA write */
writel(data, hdmi->ioaddr + HDMITX_DWC_DATA_REG);
}
static inline void dw_hdmi_dwc_write_bits(struct dw_hdmi *hdmi,
unsigned int addr,
unsigned int mask,
unsigned int val)
{
u8 data = dw_hdmi_dwc_read(hdmi, addr);
data &= ~mask;
data |= val;
dw_hdmi_dwc_write(hdmi, data, addr);
}
static inline void dw_hdmi_hhi_write(struct meson_dw_hdmi *priv,
unsigned int addr, unsigned int data)
{
hhi_write(addr, data);
}
__attribute__((unused))
static unsigned int dw_hdmi_hhi_read(struct meson_dw_hdmi *priv,
unsigned int addr)
{
return hhi_read(addr);
}
static inline void dw_hdmi_hhi_update_bits(struct meson_dw_hdmi *priv,
unsigned int addr,
unsigned int mask,
unsigned int val)
{
hhi_update_bits(addr, mask, val);
}
static int meson_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
{
#if defined DEBUG
struct display_timing timing;
int panel_bits_per_colour;
#endif
struct meson_dw_hdmi *priv = dev_get_priv(dev);
int ret;
ret = dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
#if defined DEBUG
if (!ret)
return ret;
edid_print_info((struct edid1_info *)buf);
edid_get_timing(buf, ret, &timing, &panel_bits_per_colour);
debug("Display timing:\n");
debug(" hactive %04d, hfrontp %04d, hbackp %04d hsync %04d\n"
" vactive %04d, vfrontp %04d, vbackp %04d vsync %04d\n",
timing.hactive.typ, timing.hfront_porch.typ,
timing.hback_porch.typ, timing.hsync_len.typ,
timing.vactive.typ, timing.vfront_porch.typ,
timing.vback_porch.typ, timing.vsync_len.typ);
debug(" flags: ");
if (timing.flags & DISPLAY_FLAGS_INTERLACED)
debug("interlaced ");
if (timing.flags & DISPLAY_FLAGS_DOUBLESCAN)
debug("doublescan ");
if (timing.flags & DISPLAY_FLAGS_DOUBLECLK)
debug("doubleclk ");
if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
debug("hsync_low ");
if (timing.flags & DISPLAY_FLAGS_HSYNC_HIGH)
debug("hsync_high ");
if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
debug("vsync_low ");
if (timing.flags & DISPLAY_FLAGS_VSYNC_HIGH)
debug("vsync_high ");
debug("\n");
#endif
return ret;
}
static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *priv)
{
/* Enable and software reset */
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, 0xf, 0xf);
mdelay(2);
/* Enable and unreset */
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, 0xf, 0xe);
mdelay(2);
}
static void meson_dw_hdmi_phy_setup_mode(struct meson_dw_hdmi *priv,
uint pixel_clock)
{
pixel_clock = pixel_clock / 1000;
if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXL) ||
meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXM)) {
if (pixel_clock >= 371250) {
/* 5.94Gbps, 3.7125Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x333d3282);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2136315b);
} else if (pixel_clock >= 297000) {
/* 2.97Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33303382);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2036315b);
} else if (pixel_clock >= 148500) {
/* 1.485Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33303362);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2016315b);
} else {
/* 742.5Mbps, and below */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33604142);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x0016315b);
}
} else if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXBB)) {
if (pixel_clock >= 371250) {
/* 5.94Gbps, 3.7125Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33353245);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2100115b);
} else if (pixel_clock >= 297000) {
/* 2.97Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33634283);
hhi_write(HHI_HDMI_PHY_CNTL3, 0xb000115b);
} else {
/* 1.485Gbps, and below */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33632122);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2000115b);
}
} else if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A)) {
if (pixel_clock >= 371250) {
/* 5.94Gbps, 3.7125Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x37eb65c4);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
hhi_write(HHI_HDMI_PHY_CNTL5, 0x0000080b);
} else if (pixel_clock >= 297000) {
/* 2.97Gbps */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33eb6262);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
hhi_write(HHI_HDMI_PHY_CNTL5, 0x00000003);
} else {
/* 1.485Gbps, and below */
hhi_write(HHI_HDMI_PHY_CNTL0, 0x33eb4242);
hhi_write(HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
hhi_write(HHI_HDMI_PHY_CNTL5, 0x00000003);
}
}
}
static int meson_dw_hdmi_phy_init(struct dw_hdmi *hdmi, uint pixel_clock)
{
struct meson_dw_hdmi *priv = container_of(hdmi, struct meson_dw_hdmi,
hdmi);
/* Enable clocks */
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
/* Bring HDMITX MEM output of power down */
dw_hdmi_hhi_update_bits(priv, HHI_MEM_PD_REG0, 0xff << 8, 0);
/* Bring out of reset */
dw_hdmi_top_write(hdmi, HDMITX_TOP_SW_RESET, 0);
/* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */
dw_hdmi_top_write_bits(hdmi, HDMITX_TOP_CLK_CNTL, 0x3, 0x3);
dw_hdmi_top_write_bits(hdmi, HDMITX_TOP_CLK_CNTL, 0x3 << 4, 0x3 << 4);
/* Enable normal output to PHY */
dw_hdmi_top_write(hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
/* TMDS pattern setup (TOFIX pattern for 4k2k scrambling) */
dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0x001f001f);
dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, 0x001f001f);
/* Load TMDS pattern */
dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
mdelay(20);
dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
/* Setup PHY parameters */
meson_dw_hdmi_phy_setup_mode(priv, pixel_clock);
/* Setup PHY */
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1,
0xffff << 16, 0x0390 << 16);
/* BIT_INVERT */
if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXL) ||
meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXM) ||
meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, BIT(17), 0);
else
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1,
BIT(17), BIT(17));
/* Disable clock, fifo, fifo_wr */
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, 0xf, 0);
mdelay(100);
/* Reset PHY 3 times in a row */
meson_dw_hdmi_phy_reset(priv);
meson_dw_hdmi_phy_reset(priv);
meson_dw_hdmi_phy_reset(priv);
return 0;
}
static int meson_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
const struct display_timing *edid)
{
struct meson_dw_hdmi *priv = dev_get_priv(dev);
/* will back into meson_dw_hdmi_phy_init */
return dw_hdmi_enable(&priv->hdmi, edid);
}
static int meson_dw_hdmi_wait_hpd(struct dw_hdmi *hdmi)
{
int i;
/* Poll 1 second for HPD signal */
for (i = 0; i < 10; ++i) {
if (dw_hdmi_top_read(hdmi, HDMITX_TOP_STAT0))
return 0;
mdelay(100);
}
return -ETIMEDOUT;
}
static int meson_dw_hdmi_probe(struct udevice *dev)
{
struct meson_dw_hdmi *priv = dev_get_priv(dev);
struct reset_ctl_bulk resets;
struct clk_bulk clocks;
struct udevice *supply;
int ret;
priv->dev = dev;
priv->hdmi.ioaddr = (ulong)dev_remap_addr_index(dev, 0);
if (!priv->hdmi.ioaddr)
return -EINVAL;
priv->hhi_base = dev_remap_addr_index(dev, 1);
if (!priv->hhi_base)
return -EINVAL;
priv->hdmi.hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
priv->hdmi.hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
priv->hdmi.phy_set = meson_dw_hdmi_phy_init;
if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
priv->hdmi.reg_io_width = 1;
else {
priv->hdmi.write_reg = dw_hdmi_dwc_write;
priv->hdmi.read_reg = dw_hdmi_dwc_read;
}
priv->hdmi.i2c_clk_high = 0x67;
priv->hdmi.i2c_clk_low = 0x78;
#if CONFIG_IS_ENABLED(DM_REGULATOR)
ret = device_get_supply_regulator(dev, "hdmi-supply", &supply);
if (ret && ret != -ENOENT) {
pr_err("Failed to get HDMI regulator\n");
return ret;
}
if (!ret) {
ret = regulator_set_enable(supply, true);
if (ret)
return ret;
}
#endif
uclass_get_device_by_phandle(UCLASS_I2C, dev, "ddc-i2c-bus",
&priv->hdmi.ddc_bus);
ret = reset_get_bulk(dev, &resets);
if (ret)
return ret;
ret = clk_get_bulk(dev, &clocks);
if (ret)
return ret;
ret = clk_enable_bulk(&clocks);
if (ret)
return ret;
/* Enable clocks */
dw_hdmi_hhi_update_bits(priv, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
/* Bring HDMITX MEM output of power down */
dw_hdmi_hhi_update_bits(priv, HHI_MEM_PD_REG0, 0xff << 8, 0);
/* Reset HDMITX APB & TX & PHY: cycle needed for EDID */
ret = reset_deassert_bulk(&resets);
if (ret)
return ret;
ret = reset_assert_bulk(&resets);
if (ret)
return ret;
ret = reset_deassert_bulk(&resets);
if (ret)
return ret;
if (!meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A)) {
/* Enable APB3 fail on error */
writel_bits(BIT(15), BIT(15),
priv->hdmi.ioaddr + HDMITX_TOP_CTRL_REG);
writel_bits(BIT(15), BIT(15),
priv->hdmi.ioaddr + HDMITX_DWC_CTRL_REG);
}
/* Bring out of reset */
dw_hdmi_top_write(&priv->hdmi, HDMITX_TOP_SW_RESET, 0);
mdelay(20);
dw_hdmi_top_write(&priv->hdmi, HDMITX_TOP_CLK_CNTL, 0xff);
dw_hdmi_init(&priv->hdmi);
dw_hdmi_phy_init(&priv->hdmi);
/* wait for connector */
ret = meson_dw_hdmi_wait_hpd(&priv->hdmi);
if (ret)
debug("hdmi can not get hpd signal\n");
return ret;
}
static bool meson_dw_hdmi_mode_valid(struct udevice *dev,
const struct display_timing *timing)
{
return meson_venc_hdmi_supported_mode(timing);
}
static const struct dm_display_ops meson_dw_hdmi_ops = {
.read_edid = meson_dw_hdmi_read_edid,
.enable = meson_dw_hdmi_enable,
.mode_valid = meson_dw_hdmi_mode_valid,
};
static const struct udevice_id meson_dw_hdmi_ids[] = {
{ .compatible = "amlogic,meson-gxbb-dw-hdmi",
.data = HDMI_COMPATIBLE_GXBB },
{ .compatible = "amlogic,meson-gxl-dw-hdmi",
.data = HDMI_COMPATIBLE_GXL },
{ .compatible = "amlogic,meson-gxm-dw-hdmi",
.data = HDMI_COMPATIBLE_GXM },
{ .compatible = "amlogic,meson-g12a-dw-hdmi",
.data = HDMI_COMPATIBLE_G12A },
{ }
};
U_BOOT_DRIVER(meson_dw_hdmi) = {
.name = "meson_dw_hdmi",
.id = UCLASS_DISPLAY,
.of_match = meson_dw_hdmi_ids,
.ops = &meson_dw_hdmi_ops,
.probe = meson_dw_hdmi_probe,
.priv_auto_alloc_size = sizeof(struct meson_dw_hdmi),
};
@@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*/
#ifndef __MESON_DW_HDMI_H
#define __MESON_DW_HDMI_H
/*
* Bit 7 RW Reserved. Default 1.
* Bit 6 RW Reserved. Default 1.
* Bit 5 RW Reserved. Default 1.
* Bit 4 RW sw_reset_phyif: PHY interface. 1=Apply reset; 0=Release from reset.
* Default 1.
* Bit 3 RW sw_reset_intr: interrupt module. 1=Apply reset;
* 0=Release from reset.
* Default 1.
* Bit 2 RW sw_reset_mem: KSV/REVOC mem. 1=Apply reset; 0=Release from reset.
* Default 1.
* Bit 1 RW sw_reset_rnd: random number interface to HDCP. 1=Apply reset;
* 0=Release from reset. Default 1.
* Bit 0 RW sw_reset_core: connects to IP's ~irstz. 1=Apply reset;
* 0=Release from reset. Default 1.
*/
#define HDMITX_TOP_SW_RESET (0x000)
/*
* Bit 12 RW i2s_ws_inv:1=Invert i2s_ws; 0=No invert. Default 0.
* Bit 11 RW i2s_clk_inv: 1=Invert i2s_clk; 0=No invert. Default 0.
* Bit 10 RW spdif_clk_inv: 1=Invert spdif_clk; 0=No invert. Default 0.
* Bit 9 RW tmds_clk_inv: 1=Invert tmds_clk; 0=No invert. Default 0.
* Bit 8 RW pixel_clk_inv: 1=Invert pixel_clk; 0=No invert. Default 0.
* Bit 4 RW cec_clk_en: 1=enable cec_clk; 0=disable. Default 0.
* Bit 3 RW i2s_clk_en: 1=enable i2s_clk; 0=disable. Default 0.
* Bit 2 RW spdif_clk_en: 1=enable spdif_clk; 0=disable. Default 0.
* Bit 1 RW tmds_clk_en: 1=enable tmds_clk; 0=disable. Default 0.
* Bit 0 RW pixel_clk_en: 1=enable pixel_clk; 0=disable. Default 0.
*/
#define HDMITX_TOP_CLK_CNTL (0x001)
/*
* Bit 11: 0 RW hpd_valid_width: filter out width <= M*1024. Default 0.
* Bit 15:12 RW hpd_glitch_width: filter out glitch <= N. Default 0.
*/
#define HDMITX_TOP_HPD_FILTER (0x002)
/*
* intr_maskn: MASK_N, one bit per interrupt source.
* 1=Enable interrupt source; 0=Disable interrupt source. Default 0.
* [ 4] hdcp22_rndnum_err
* [ 3] nonce_rfrsh_rise
* [ 2] hpd_fall_intr
* [ 1] hpd_rise_intr
* [ 0] core_intr
*/
#define HDMITX_TOP_INTR_MASKN (0x003)
/*
* Bit 30: 0 RW intr_stat: For each bit, write 1 to manually set the interrupt
* bit, read back the interrupt status.
* Bit 31 R IP interrupt status
* Bit 2 RW hpd_fall
* Bit 1 RW hpd_rise
* Bit 0 RW IP interrupt
*/
#define HDMITX_TOP_INTR_STAT (0x004)
/*
* [4] hdcp22_rndnum_err
* [3] nonce_rfrsh_rise
* [2] hpd_fall
* [1] hpd_rise
* [0] core_intr_rise
*/
#define HDMITX_TOP_INTR_STAT_CLR (0x005)
#define HDMITX_TOP_INTR_CORE BIT(0)
#define HDMITX_TOP_INTR_HPD_RISE BIT(1)
#define HDMITX_TOP_INTR_HPD_FALL BIT(2)
/* Bit 14:12 RW tmds_sel: 3'b000=Output zero; 3'b001=Output normal TMDS data;
* 3'b010=Output PRBS data; 3'b100=Output shift pattern. Default 0.
* Bit 11: 9 RW shift_pttn_repeat: 0=New pattern every clk cycle; 1=New pattern
* every 2 clk cycles; ...; 7=New pattern every 8 clk cycles. Default 0.
* Bit 8 RW shift_pttn_en: 1= Enable shift pattern generator; 0=Disable.
* Default 0.
* Bit 4: 3 RW prbs_pttn_mode: 0=PRBS11; 1=PRBS15; 2=PRBS7; 3=PRBS31. Default 0.
* Bit 2: 1 RW prbs_pttn_width: 0=idle; 1=output 8-bit pattern;
* 2=Output 1-bit pattern; 3=output 10-bit pattern. Default 0.
* Bit 0 RW prbs_pttn_en: 1=Enable PRBS generator; 0=Disable. Default 0.
*/
#define HDMITX_TOP_BIST_CNTL (0x006)
/* Bit 29:20 RW shift_pttn_data[59:50]. Default 0. */
/* Bit 19:10 RW shift_pttn_data[69:60]. Default 0. */
/* Bit 9: 0 RW shift_pttn_data[79:70]. Default 0. */
#define HDMITX_TOP_SHIFT_PTTN_012 (0x007)
/* Bit 29:20 RW shift_pttn_data[29:20]. Default 0. */
/* Bit 19:10 RW shift_pttn_data[39:30]. Default 0. */
/* Bit 9: 0 RW shift_pttn_data[49:40]. Default 0. */
#define HDMITX_TOP_SHIFT_PTTN_345 (0x008)
/* Bit 19:10 RW shift_pttn_data[ 9: 0]. Default 0. */
/* Bit 9: 0 RW shift_pttn_data[19:10]. Default 0. */
#define HDMITX_TOP_SHIFT_PTTN_67 (0x009)
/* Bit 25:16 RW tmds_clk_pttn[19:10]. Default 0. */
/* Bit 9: 0 RW tmds_clk_pttn[ 9: 0]. Default 0. */
#define HDMITX_TOP_TMDS_CLK_PTTN_01 (0x00A)
/* Bit 25:16 RW tmds_clk_pttn[39:30]. Default 0. */
/* Bit 9: 0 RW tmds_clk_pttn[29:20]. Default 0. */
#define HDMITX_TOP_TMDS_CLK_PTTN_23 (0x00B)
/* Bit 1 RW shift_tmds_clk_pttn:1=Enable shifting clk pattern,
* used when TMDS CLK rate = TMDS character rate /4. Default 0.
* Bit 0 R Reserved. Default 0.
* [ 1] shift_tmds_clk_pttn
* [ 0] load_tmds_clk_pttn
*/
#define HDMITX_TOP_TMDS_CLK_PTTN_CNTL (0x00C)
/* Bit 0 RW revocmem_wr_fail: Read back 1 to indicate Host write REVOC MEM
* failure, write 1 to clear the failure flag. Default 0.
*/
#define HDMITX_TOP_REVOCMEM_STAT (0x00D)
/* Bit 0 R filtered HPD status. */
#define HDMITX_TOP_STAT0 (0x00E)
#endif /* __MESON_DW_HDMI_H */
@@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson Video Processing Unit driver
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include "meson_vpu.h"
/* OSDx_BLKx_CFG */
#define OSD_CANVAS_SEL 16
#define OSD_ENDIANNESS_LE BIT(15)
#define OSD_ENDIANNESS_BE (0)
#define OSD_BLK_MODE_422 (0x03 << 8)
#define OSD_BLK_MODE_16 (0x04 << 8)
#define OSD_BLK_MODE_32 (0x05 << 8)
#define OSD_BLK_MODE_24 (0x07 << 8)
#define OSD_OUTPUT_COLOR_RGB BIT(7)
#define OSD_OUTPUT_COLOR_YUV (0)
#define OSD_COLOR_MATRIX_32_RGBA (0x00 << 2)
#define OSD_COLOR_MATRIX_32_ARGB (0x01 << 2)
#define OSD_COLOR_MATRIX_32_ABGR (0x02 << 2)
#define OSD_COLOR_MATRIX_32_BGRA (0x03 << 2)
#define OSD_COLOR_MATRIX_24_RGB (0x00 << 2)
#define OSD_COLOR_MATRIX_16_RGB655 (0x00 << 2)
#define OSD_COLOR_MATRIX_16_RGB565 (0x04 << 2)
#define OSD_INTERLACE_ENABLED BIT(1)
#define OSD_INTERLACE_ODD BIT(0)
#define OSD_INTERLACE_EVEN (0)
/* OSDx_CTRL_STAT */
#define OSD_ENABLE BIT(21)
#define OSD_BLK0_ENABLE BIT(0)
#define OSD_GLOBAL_ALPHA_SHIFT 12
/* OSDx_CTRL_STAT2 */
#define OSD_REPLACE_EN BIT(14)
#define OSD_REPLACE_SHIFT 6
/*
* When the output is interlaced, the OSD must switch between
* each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0
* at each vsync.
* But the vertical scaler can provide such funtionnality if
* is configured for 2:1 scaling with interlace options enabled.
*/
static void meson_vpp_setup_interlace_vscaler_osd1(struct meson_vpu_priv *priv,
struct video_priv *uc_priv)
{
writel(BIT(3) /* Enable scaler */ |
BIT(2), /* Select OSD1 */
priv->io_base + _REG(VPP_OSD_SC_CTRL0));
writel(((uc_priv->xsize - 1) << 16) | (uc_priv->ysize - 1),
priv->io_base + _REG(VPP_OSD_SCI_WH_M1));
/* 2:1 scaling */
writel((0 << 16) | uc_priv->xsize,
priv->io_base + _REG(VPP_OSD_SCO_H_START_END));
writel(((0 >> 1) << 16) | (uc_priv->ysize >> 1),
priv->io_base + _REG(VPP_OSD_SCO_V_START_END));
/* 2:1 scaling values */
writel(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE));
writel(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP));
writel(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
writel((4 << 0) /* osd_vsc_bank_length */ |
(4 << 3) /* osd_vsc_top_ini_rcv_num0 */ |
(1 << 8) /* osd_vsc_top_rpt_p0_num0 */ |
(6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ |
(2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ |
BIT(23) /* osd_prog_interlace */ |
BIT(24), /* Enable vertical scaler */
priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
}
static void
meson_vpp_disable_interlace_vscaler_osd1(struct meson_vpu_priv *priv)
{
writel(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
writel(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
writel(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
}
void meson_vpu_setup_plane(struct udevice *dev, bool is_interlaced)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct meson_vpu_priv *priv = dev_get_priv(dev);
u32 osd1_ctrl_stat;
u32 osd1_blk0_cfg[5];
bool osd1_interlace;
unsigned int src_x1, src_x2, src_y1, src_y2;
unsigned int dest_x1, dest_x2, dest_y1, dest_y2;
dest_x1 = src_x1 = 0;
dest_x2 = src_x2 = uc_priv->xsize;
dest_y1 = src_y1 = 0;
dest_y2 = src_y2 = uc_priv->ysize;
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
/* VD1 Preblend vertical start/end */
writel(FIELD_PREP(GENMASK(11, 0), 2303),
priv->io_base + _REG(VPP_PREBLEND_VD1_V_START_END));
/* Setup Blender */
writel(uc_priv->xsize |
uc_priv->ysize << 16,
priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
writel(0 << 16 |
(uc_priv->xsize - 1),
priv->io_base + _REG(VPP_OSD1_BLD_H_SCOPE));
writel(0 << 16 |
(uc_priv->ysize - 1),
priv->io_base + _REG(VPP_OSD1_BLD_V_SCOPE));
writel(uc_priv->xsize << 16 |
uc_priv->ysize,
priv->io_base + _REG(VPP_OUT_H_V_SIZE));
} else {
/* Enable VPP Postblend */
writel(uc_priv->xsize,
priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
writel_bits(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
priv->io_base + _REG(VPP_MISC));
}
/* uc_plat->base is the framebuffer */
/* Enable OSD and BLK0, set max global alpha */
osd1_ctrl_stat = OSD_ENABLE | (0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
OSD_BLK0_ENABLE;
/* Set up BLK0 to point to the right canvas */
osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) |
OSD_ENDIANNESS_LE);
/* On GXBB, Use the old non-HDR RGB2YUV converter */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB;
/* For XRGB, replace the pixel's alpha by 0xFF */
writel_bits(OSD_REPLACE_EN, OSD_REPLACE_EN,
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 |
OSD_COLOR_MATRIX_32_ARGB;
if (is_interlaced) {
osd1_interlace = true;
dest_y1 /= 2;
dest_y2 /= 2;
} else {
osd1_interlace = false;
}
/*
* The format of these registers is (x2 << 16 | x1),
* where x2 is exclusive.
* e.g. +30x1920 would be (1919 << 16) | 30
*/
osd1_blk0_cfg[1] = ((src_x2 - 1) << 16) | src_x1;
osd1_blk0_cfg[2] = ((src_y2 - 1) << 16) | src_y1;
osd1_blk0_cfg[3] = ((dest_x2 - 1) << 16) | dest_x1;
osd1_blk0_cfg[4] = ((dest_y2 - 1) << 16) | dest_y1;
writel(osd1_ctrl_stat, priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
writel(osd1_blk0_cfg[0], priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0));
writel(osd1_blk0_cfg[1], priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1));
writel(osd1_blk0_cfg[2], priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2));
writel(osd1_blk0_cfg[3], priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3));
writel(osd1_blk0_cfg[4], priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4));
/* If output is interlace, make use of the Scaler */
if (osd1_interlace)
meson_vpp_setup_interlace_vscaler_osd1(priv, uc_priv);
else
meson_vpp_disable_interlace_vscaler_osd1(priv);
meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
uc_plat->base, uc_priv->xsize * 4,
uc_priv->ysize, MESON_CANVAS_WRAP_NONE,
MESON_CANVAS_BLKMODE_LINEAR);
/* Enable OSD1 */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
writel(((dest_x2 - 1) << 16) | dest_x1,
priv->io_base + _REG(VIU_OSD_BLEND_DIN0_SCOPE_H));
writel(((dest_y2 - 1) << 16) | dest_y1,
priv->io_base + _REG(VIU_OSD_BLEND_DIN0_SCOPE_V));
writel(uc_priv->xsize << 16 | uc_priv->ysize,
priv->io_base + _REG(VIU_OSD_BLEND_BLEND0_SIZE));
writel(uc_priv->xsize << 16 | uc_priv->ysize,
priv->io_base + _REG(VIU_OSD_BLEND_BLEND1_SIZE));
writel_bits(3 << 8, 3 << 8,
priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
} else
writel_bits(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
priv->io_base + _REG(VPP_MISC));
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson Video Processing Unit driver
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include "meson_vpu.h"
#include <efi_loader.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <fdt_support.h>
#include <linux/sizes.h>
#include <asm/arch/mem.h>
#include "meson_registers.h"
#include "simplefb_common.h"
#define MESON_VPU_OVERSCAN SZ_64K
/* Static variable for use in meson_vpu_rsv_fb() */
static struct meson_framebuffer {
u64 base;
u64 fb_size;
unsigned int xsize;
unsigned int ysize;
bool is_cvbs;
} meson_fb = { 0 };
static int meson_vpu_setup_mode(struct udevice *dev, struct udevice *disp)
{
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct display_timing timing;
bool is_cvbs = false;
int ret = 0;
if (disp) {
ret = display_read_timing(disp, &timing);
if (ret) {
debug("%s: Failed to read timings\n", __func__);
goto cvbs;
}
uc_priv->xsize = timing.hactive.typ;
uc_priv->ysize = timing.vactive.typ;
ret = display_enable(disp, 0, &timing);
if (ret)
goto cvbs;
} else {
cvbs:
/* CVBS has a fixed 720x480i (NTSC) and 720x576i (PAL) */
is_cvbs = true;
timing.flags = DISPLAY_FLAGS_INTERLACED;
uc_priv->xsize = 720;
uc_priv->ysize = 576;
}
uc_priv->bpix = VPU_MAX_LOG2_BPP;
meson_fb.is_cvbs = is_cvbs;
meson_fb.xsize = uc_priv->xsize;
meson_fb.ysize = uc_priv->ysize;
/* Move the framebuffer to the end of addressable ram */
meson_fb.fb_size = ALIGN(meson_fb.xsize * meson_fb.ysize *
((1 << VPU_MAX_LOG2_BPP) / 8) +
MESON_VPU_OVERSCAN, EFI_PAGE_SIZE);
meson_fb.base = gd->bd->bi_dram[0].start +
gd->bd->bi_dram[0].size - meson_fb.fb_size;
/* Override the framebuffer address */
uc_plat->base = meson_fb.base;
meson_vpu_setup_plane(dev, timing.flags & DISPLAY_FLAGS_INTERLACED);
meson_vpu_setup_venc(dev, &timing, is_cvbs);
meson_vpu_setup_vclk(dev, &timing, is_cvbs);
video_set_flush_dcache(dev, 1);
return 0;
}
static const struct udevice_id meson_vpu_ids[] = {
{ .compatible = "amlogic,meson-gxbb-vpu", .data = VPU_COMPATIBLE_GXBB },
{ .compatible = "amlogic,meson-gxl-vpu", .data = VPU_COMPATIBLE_GXL },
{ .compatible = "amlogic,meson-gxm-vpu", .data = VPU_COMPATIBLE_GXM },
{ .compatible = "amlogic,meson-g12a-vpu", .data = VPU_COMPATIBLE_G12A },
{ }
};
static int meson_vpu_probe(struct udevice *dev)
{
struct meson_vpu_priv *priv = dev_get_priv(dev);
struct udevice *disp;
int ret;
/* Before relocation we don't need to do anything */
if (!(gd->flags & GD_FLG_RELOC))
return 0;
priv->dev = dev;
priv->io_base = dev_remap_addr_index(dev, 0);
if (!priv->io_base)
return -EINVAL;
priv->hhi_base = dev_remap_addr_index(dev, 1);
if (!priv->hhi_base)
return -EINVAL;
priv->dmc_base = dev_remap_addr_index(dev, 2);
if (!priv->dmc_base)
return -EINVAL;
meson_vpu_init(dev);
/* probe the display */
ret = uclass_get_device(UCLASS_DISPLAY, 0, &disp);
return meson_vpu_setup_mode(dev, ret ? NULL : disp);
}
static int meson_vpu_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
plat->size = VPU_MAX_WIDTH * VPU_MAX_HEIGHT *
(1 << VPU_MAX_LOG2_BPP) / 8;
return 0;
}
#if defined(CONFIG_VIDEO_DT_SIMPLEFB)
static void meson_vpu_setup_simplefb(void *fdt)
{
const char *pipeline = NULL;
u64 mem_start, mem_size;
int offset, ret;
if (meson_fb.is_cvbs)
pipeline = "vpu-cvbs";
else
pipeline = "vpu-hdmi";
offset = meson_simplefb_fdt_match(fdt, pipeline);
if (offset < 0) {
eprintf("Cannot setup simplefb: node not found\n");
/* If simplefb is missing, add it as reserved memory */
meson_board_add_reserved_memory(fdt, meson_fb.base,
meson_fb.fb_size);
return;
}
/*
* SimpleFB will try to iomap the framebuffer, so we can't use
* fdt_add_mem_rsv on the memory area. Instead, the FB is stored
* at the end of the RAM and we strip this portion from the kernel
* allowed region
*/
mem_start = gd->bd->bi_dram[0].start;
mem_size = gd->bd->bi_dram[0].size - meson_fb.fb_size;
ret = fdt_fixup_memory_banks(fdt, &mem_start, &mem_size, 1);
if (ret) {
eprintf("Cannot setup simplefb: Error reserving memory\n");
return;
}
ret = fdt_setup_simplefb_node(fdt, offset, meson_fb.base,
meson_fb.xsize, meson_fb.ysize,
meson_fb.xsize * 4, "x8r8g8b8");
if (ret)
eprintf("Cannot setup simplefb: Error setting properties\n");
}
#endif
void meson_vpu_rsv_fb(void *fdt)
{
if (!meson_fb.base || !meson_fb.xsize || !meson_fb.ysize)
return;
#if defined(CONFIG_EFI_LOADER)
efi_add_memory_map(meson_fb.base, meson_fb.fb_size >> EFI_PAGE_SHIFT,
EFI_RESERVED_MEMORY_TYPE, false);
#endif
#if defined(CONFIG_VIDEO_DT_SIMPLEFB)
meson_vpu_setup_simplefb(fdt);
#endif
}
U_BOOT_DRIVER(meson_vpu) = {
.name = "meson_vpu",
.id = UCLASS_VIDEO,
.of_match = meson_vpu_ids,
.probe = meson_vpu_probe,
.bind = meson_vpu_bind,
.priv_auto_alloc_size = sizeof(struct meson_vpu_priv),
.flags = DM_FLAG_PRE_RELOC,
};
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Amlogic Meson Video Processing Unit driver
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#ifndef __MESON_VPU_H__
#define __MESON_VPU_H__
#include <common.h>
#include <dm.h>
#include <video.h>
#include <display.h>
#include <linux/io.h>
#include <linux/bitfield.h>
#include "meson_registers.h"
enum {
/* Maximum size we support */
VPU_MAX_WIDTH = 3840,
VPU_MAX_HEIGHT = 2160,
VPU_MAX_LOG2_BPP = VIDEO_BPP32,
};
enum vpu_compatible {
VPU_COMPATIBLE_GXBB = 0,
VPU_COMPATIBLE_GXL = 1,
VPU_COMPATIBLE_GXM = 2,
VPU_COMPATIBLE_G12A = 3,
};
struct meson_vpu_priv {
struct udevice *dev;
void __iomem *io_base;
void __iomem *hhi_base;
void __iomem *dmc_base;
};
static inline bool meson_vpu_is_compatible(struct meson_vpu_priv *priv,
enum vpu_compatible family)
{
enum vpu_compatible compat = dev_get_driver_data(priv->dev);
return compat == family;
}
#define hhi_update_bits(offset, mask, value) \
writel_bits(mask, value, priv->hhi_base + offset)
#define hhi_write(offset, value) \
writel(value, priv->hhi_base + offset)
#define hhi_read(offset) \
readl(priv->hhi_base + offset)
#define dmc_update_bits(offset, mask, value) \
writel_bits(mask, value, priv->dmc_base + offset)
#define dmc_write(offset, value) \
writel(value, priv->dmc_base + offset)
#define dmc_read(offset) \
readl(priv->dmc_base + offset)
#define MESON_CANVAS_ID_OSD1 0x4e
/* Canvas configuration. */
#define MESON_CANVAS_WRAP_NONE 0x00
#define MESON_CANVAS_WRAP_X 0x01
#define MESON_CANVAS_WRAP_Y 0x02
#define MESON_CANVAS_BLKMODE_LINEAR 0x00
#define MESON_CANVAS_BLKMODE_32x32 0x01
#define MESON_CANVAS_BLKMODE_64x64 0x02
void meson_canvas_setup(struct meson_vpu_priv *priv,
u32 canvas_index, u32 addr,
u32 stride, u32 height,
unsigned int wrap,
unsigned int blkmode);
/* Mux VIU/VPP to ENCI */
#define MESON_VIU_VPP_MUX_ENCI 0x5
/* Mux VIU/VPP to ENCP */
#define MESON_VIU_VPP_MUX_ENCP 0xA
void meson_vpp_setup_mux(struct meson_vpu_priv *priv, unsigned int mux);
void meson_vpu_init(struct udevice *dev);
void meson_vpu_setup_plane(struct udevice *dev, bool is_interlaced);
bool meson_venc_hdmi_supported_mode(const struct display_timing *mode);
void meson_vpu_setup_venc(struct udevice *dev,
const struct display_timing *mode, bool is_cvbs);
bool meson_vclk_dmt_supported_freq(struct meson_vpu_priv *priv,
unsigned int freq);
void meson_vpu_setup_vclk(struct udevice *dev,
const struct display_timing *mode, bool is_cvbs);
#endif
@@ -0,0 +1,557 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson Video Processing Unit driver
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#define DEBUG
#include "meson_vpu.h"
/* HHI Registers */
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */
#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */
/* OSDx_CTRL_STAT2 */
#define OSD_REPLACE_EN BIT(14)
#define OSD_REPLACE_SHIFT 6
void meson_vpp_setup_mux(struct meson_vpu_priv *priv, unsigned int mux)
{
writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL));
}
static unsigned int vpp_filter_coefs_4point_bspline[] = {
0x15561500, 0x14561600, 0x13561700, 0x12561800,
0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00,
0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200,
0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700,
0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01,
0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201,
0x05473301, 0x05463401, 0x04453601, 0x04433702,
0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02,
0x033d3d03
};
static void meson_vpp_write_scaling_filter_coefs(struct meson_vpu_priv *priv,
const unsigned int *coefs,
bool is_horizontal)
{
int i;
writel(is_horizontal ? VPP_SCALE_HORIZONTAL_COEF : 0,
priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX));
for (i = 0; i < 33; i++)
writel(coefs[i],
priv->io_base + _REG(VPP_OSD_SCALE_COEF));
}
static const u32 vpp_filter_coefs_bicubic[] = {
0x00800000, 0x007f0100, 0xff7f0200, 0xfe7f0300,
0xfd7e0500, 0xfc7e0600, 0xfb7d0800, 0xfb7c0900,
0xfa7b0b00, 0xfa7a0dff, 0xf9790fff, 0xf97711ff,
0xf87613ff, 0xf87416fe, 0xf87218fe, 0xf8701afe,
0xf76f1dfd, 0xf76d1ffd, 0xf76b21fd, 0xf76824fd,
0xf76627fc, 0xf76429fc, 0xf7612cfc, 0xf75f2ffb,
0xf75d31fb, 0xf75a34fb, 0xf75837fa, 0xf7553afa,
0xf8523cfa, 0xf8503ff9, 0xf84d42f9, 0xf84a45f9,
0xf84848f8
};
static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_vpu_priv *priv,
const unsigned int *coefs,
bool is_horizontal)
{
int i;
writel(is_horizontal ? VPP_SCALE_HORIZONTAL_COEF : 0,
priv->io_base + _REG(VPP_SCALE_COEF_IDX));
for (i = 0; i < 33; i++)
writel(coefs[i],
priv->io_base + _REG(VPP_SCALE_COEF));
}
/* OSD csc defines */
enum viu_matrix_sel_e {
VIU_MATRIX_OSD_EOTF = 0,
VIU_MATRIX_OSD,
};
enum viu_lut_sel_e {
VIU_LUT_OSD_EOTF = 0,
VIU_LUT_OSD_OETF,
};
#define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
#define MATRIX_5X3_COEF_SIZE 24
#define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
#define EOTF_COEFF_SIZE 10
#define EOTF_COEFF_RIGHTSHIFT 1
static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
0, 0, 0, /* pre offset */
COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
0, 0, 0, /* 10'/11'/12' */
0, 0, 0, /* 20'/21'/22' */
64, 512, 512, /* offset */
0, 0, 0 /* mode, right_shift, clip_en */
};
/* eotf matrix: bypass */
static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
EOTF_COEFF_RIGHTSHIFT /* right shift */
};
static void meson_viu_set_g12a_osd1_matrix(struct meson_vpu_priv *priv,
int *m, bool csc_on)
{
/* VPP WRAP OSD1 matrix */
writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1));
writel(m[2] & 0xfff,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2));
writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01));
writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10));
writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
writel((m[11] & 0x1fff) << 16,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1));
writel(m[20] & 0xfff,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2));
writel_bits(BIT(0), csc_on ? BIT(0) : 0,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
}
static void meson_viu_set_osd_matrix(struct meson_vpu_priv *priv,
enum viu_matrix_sel_e m_select,
int *m, bool csc_on)
{
if (m_select == VIU_MATRIX_OSD) {
/* osd matrix, VIU_MATRIX_0 */
writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
writel(m[2] & 0xfff,
priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
if (m[21]) {
writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
priv->io_base +
_REG(VIU_OSD1_MATRIX_COEF22_30));
writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
priv->io_base +
_REG(VIU_OSD1_MATRIX_COEF31_32));
writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
priv->io_base +
_REG(VIU_OSD1_MATRIX_COEF40_41));
writel(m[17] & 0x1fff, priv->io_base +
_REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
} else {
writel((m[11] & 0x1fff) << 16, priv->io_base +
_REG(VIU_OSD1_MATRIX_COEF22_30));
}
writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
writel(m[20] & 0xfff,
priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
writel_bits(3 << 30, m[21] << 30,
priv->io_base +
_REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
writel_bits(7 << 16, m[22] << 16,
priv->io_base +
_REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
/* 23 reserved for clipping control */
writel_bits(BIT(0), csc_on ? BIT(0) : 0,
priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
writel_bits(BIT(1), 0,
priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
} else if (m_select == VIU_MATRIX_OSD_EOTF) {
int i;
/* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
for (i = 0; i < 5; i++)
writel(((m[i * 2] & 0x1fff) << 16) |
(m[i * 2 + 1] & 0x1fff), priv->io_base +
_REG(VIU_OSD1_EOTF_CTL + i + 1));
writel_bits(BIT(30), csc_on ? BIT(30) : 0,
priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
writel_bits(BIT(31), csc_on ? BIT(31) : 0,
priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
}
}
#define OSD_EOTF_LUT_SIZE 33
#define OSD_OETF_LUT_SIZE 41
static void meson_viu_set_osd_lut(struct meson_vpu_priv *priv,
enum viu_lut_sel_e lut_sel,
unsigned int *r_map, unsigned int *g_map,
unsigned int *b_map,
bool csc_on)
{
unsigned int addr_port;
unsigned int data_port;
unsigned int ctrl_port;
int i;
if (lut_sel == VIU_LUT_OSD_EOTF) {
addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
ctrl_port = VIU_OSD1_EOTF_CTL;
} else if (lut_sel == VIU_LUT_OSD_OETF) {
addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
ctrl_port = VIU_OSD1_OETF_CTL;
} else {
return;
}
if (lut_sel == VIU_LUT_OSD_OETF) {
writel(0, priv->io_base + _REG(addr_port));
for (i = 0; i < 20; i++)
writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
priv->io_base + _REG(data_port));
writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
priv->io_base + _REG(data_port));
for (i = 0; i < 20; i++)
writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
priv->io_base + _REG(data_port));
for (i = 0; i < 20; i++)
writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
priv->io_base + _REG(data_port));
writel(b_map[OSD_OETF_LUT_SIZE - 1],
priv->io_base + _REG(data_port));
if (csc_on)
writel_bits(0x7 << 29, 7 << 29,
priv->io_base + _REG(ctrl_port));
else
writel_bits(0x7 << 29, 0,
priv->io_base + _REG(ctrl_port));
} else if (lut_sel == VIU_LUT_OSD_EOTF) {
writel(0, priv->io_base + _REG(addr_port));
for (i = 0; i < 20; i++)
writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
priv->io_base + _REG(data_port));
writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
priv->io_base + _REG(data_port));
for (i = 0; i < 20; i++)
writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
priv->io_base + _REG(data_port));
for (i = 0; i < 20; i++)
writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
priv->io_base + _REG(data_port));
writel(b_map[OSD_EOTF_LUT_SIZE - 1],
priv->io_base + _REG(data_port));
if (csc_on)
writel_bits(7 << 27, 7 << 27,
priv->io_base + _REG(ctrl_port));
else
writel_bits(7 << 27, 0,
priv->io_base + _REG(ctrl_port));
writel_bits(BIT(31), BIT(31),
priv->io_base + _REG(ctrl_port));
}
}
/* eotf lut: linear */
static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
0x0000, 0x0200, 0x0400, 0x0600,
0x0800, 0x0a00, 0x0c00, 0x0e00,
0x1000, 0x1200, 0x1400, 0x1600,
0x1800, 0x1a00, 0x1c00, 0x1e00,
0x2000, 0x2200, 0x2400, 0x2600,
0x2800, 0x2a00, 0x2c00, 0x2e00,
0x3000, 0x3200, 0x3400, 0x3600,
0x3800, 0x3a00, 0x3c00, 0x3e00,
0x4000
};
/* osd oetf lut: linear */
static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
0, 0, 0, 0,
0, 32, 64, 96,
128, 160, 196, 224,
256, 288, 320, 352,
384, 416, 448, 480,
512, 544, 576, 608,
640, 672, 704, 736,
768, 800, 832, 864,
896, 928, 960, 992,
1023, 1023, 1023, 1023,
1023
};
static void meson_viu_load_matrix(struct meson_vpu_priv *priv)
{
/* eotf lut bypass */
meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
eotf_33_linear_mapping, /* R */
eotf_33_linear_mapping, /* G */
eotf_33_linear_mapping, /* B */
false);
/* eotf matrix bypass */
meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
eotf_bypass_coeff,
false);
/* oetf lut bypass */
meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
oetf_41_linear_mapping, /* R */
oetf_41_linear_mapping, /* G */
oetf_41_linear_mapping, /* B */
false);
/* osd matrix RGB709 to YUV709 limit */
meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
RGB709_to_YUV709l_coeff,
true);
}
static inline uint32_t meson_viu_osd_burst_length_reg(uint32_t length)
{
u32 val = (((length & 0x80) % 24) / 12);
return (((val & 0x3) << 10) | (((val & 0x4) >> 2) << 31));
}
void meson_vpu_init(struct udevice *dev)
{
struct meson_vpu_priv *priv = dev_get_priv(dev);
u32 reg;
/*
* Slave dc0 and dc5 connected to master port 1.
* By default other slaves are connected to master port 0.
*/
reg = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
writel(reg, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
/* Slave dc0 connected to master port 1 */
reg = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
writel(reg, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
/* Slave dc4 and dc7 connected to master port 1 */
reg = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
writel(reg, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
/* Slave dc1 connected to master port 1 */
reg = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
writel(reg, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
/* Disable CVBS VDAC */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
hhi_write(HHI_VDAC_CNTL0_G12A, 0);
hhi_write(HHI_VDAC_CNTL1_G12A, 8);
} else {
hhi_write(HHI_VDAC_CNTL0, 0);
hhi_write(HHI_VDAC_CNTL1, 8);
}
/* Power Down Dacs */
writel(0xff, priv->io_base + _REG(VENC_VDAC_SETTING));
/* Disable HDMI PHY */
hhi_write(HHI_HDMI_PHY_CNTL0, 0);
/* Disable HDMI */
writel_bits(VPU_HDMI_ENCI_DATA_TO_HDMI |
VPU_HDMI_ENCP_DATA_TO_HDMI, 0,
priv->io_base + _REG(VPU_HDMI_SETTING));
/* Disable all encoders */
writel(0, priv->io_base + _REG(ENCI_VIDEO_EN));
writel(0, priv->io_base + _REG(ENCP_VIDEO_EN));
writel(0, priv->io_base + _REG(ENCL_VIDEO_EN));
/* Disable VSync IRQ */
writel(0, priv->io_base + _REG(VENC_INTCTRL));
/* set dummy data default YUV black */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
writel(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1));
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) {
writel_bits(0xff << 16, 0xff << 16,
priv->io_base + _REG(VIU_MISC_CTRL1));
writel(VPP_PPS_DUMMY_DATA_MODE,
priv->io_base + _REG(VPP_DOLBY_CTRL));
writel(0x1020080,
priv->io_base + _REG(VPP_DUMMY_DATA1));
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
writel(0xf, priv->io_base + _REG(DOLBY_PATH_CTRL));
/* Initialize vpu fifo control registers */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
writel(VPP_OFIFO_SIZE_DEFAULT,
priv->io_base + _REG(VPP_OFIFO_SIZE));
else
writel_bits(VPP_OFIFO_SIZE_MASK, 0x77f,
priv->io_base + _REG(VPP_OFIFO_SIZE));
writel(VPP_POSTBLEND_HOLD_LINES(4) | VPP_PREBLEND_HOLD_LINES(4),
priv->io_base + _REG(VPP_HOLD_LINES));
if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
/* Turn off preblend */
writel_bits(VPP_PREBLEND_ENABLE, 0,
priv->io_base + _REG(VPP_MISC));
/* Turn off POSTBLEND */
writel_bits(VPP_POSTBLEND_ENABLE, 0,
priv->io_base + _REG(VPP_MISC));
/* Force all planes off */
writel_bits(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND |
VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND |
VPP_VD1_PREBLEND | VPP_VD2_PREBLEND, 0,
priv->io_base + _REG(VPP_MISC));
/* Setup default VD settings */
writel(4096,
priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END));
writel(4096,
priv->io_base + _REG(VPP_BLEND_VD2_H_START_END));
}
/* Disable Scalers */
writel(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
writel(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
writel(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
writel(VPP_VSC_BANK_LENGTH(4) | VPP_HSC_BANK_LENGTH(4) |
VPP_SC_VD_EN_ENABLE,
priv->io_base + _REG(VPP_SC_MISC));
/* Enable minus black level for vadj1 */
writel(VPP_MINUS_BLACK_LVL_VADJ1_ENABLE,
priv->io_base + _REG(VPP_VADJ_CTRL));
/* Write in the proper filter coefficients. */
meson_vpp_write_scaling_filter_coefs(priv,
vpp_filter_coefs_4point_bspline, false);
meson_vpp_write_scaling_filter_coefs(priv,
vpp_filter_coefs_4point_bspline, true);
/* Write the VD proper filter coefficients. */
meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic,
false);
meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic,
true);
/* Disable OSDs */
writel_bits(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0,
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
writel_bits(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0,
priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
/* On GXL/GXM, Use the 10bit HDR conversion matrix */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
meson_viu_load_matrix(priv);
else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
true);
/* Initialize OSD1 fifo control register */
reg = VIU_OSD_DDR_PRIORITY_URGENT |
VIU_OSD_HOLD_FIFO_LINES(4) |
VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */
VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */
VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
reg |= meson_viu_osd_burst_length_reg(32);
else
reg |= meson_viu_osd_burst_length_reg(64);
writel(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
writel(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
/* Set OSD alpha replace value */
writel_bits(0xff << OSD_REPLACE_SHIFT,
0xff << OSD_REPLACE_SHIFT,
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
writel_bits(0xff << OSD_REPLACE_SHIFT,
0xff << OSD_REPLACE_SHIFT,
priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
/* Disable VD1 AFBC */
/* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 and afbc vd1 set=0*/
writel_bits(VIU_CTRL0_VD1_AFBC_MASK, 0,
priv->io_base + _REG(VIU_MISC_CTRL0));
writel(0, priv->io_base + _REG(AFBC_ENABLE));
writel(0x00FF00C0,
priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE));
writel(0x00FF00C0,
priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
writel(VIU_OSD_BLEND_REORDER(0, 1) |
VIU_OSD_BLEND_REORDER(1, 0) |
VIU_OSD_BLEND_REORDER(2, 0) |
VIU_OSD_BLEND_REORDER(3, 0) |
VIU_OSD_BLEND_DIN_EN(1) |
VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 |
VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 |
VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 |
VIU_OSD_BLEND_BLEN2_PREMULT_EN(1) |
VIU_OSD_BLEND_HOLD_LINES(4),
priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
writel(OSD_BLEND_PATH_SEL_ENABLE,
priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
writel(OSD_BLEND_PATH_SEL_ENABLE,
priv->io_base + _REG(OSD2_BLEND_SRC_CTRL));
writel(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
writel(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
writel(0, priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0));
writel(0, priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA));
writel_bits(DOLBY_BYPASS_EN(0xc), DOLBY_BYPASS_EN(0xc),
priv->io_base + _REG(DOLBY_PATH_CTRL));
}
}
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Common code for Amlogic SimpleFB with pipeline.
*
* (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
* (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
* (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
*/
#include <fdtdec.h>
int meson_simplefb_fdt_match(void *blob, const char *pipeline)
{
int offset, ret;
/* Find a prefilled simpefb node, matching out pipeline config */
offset = fdt_node_offset_by_compatible(blob, -1,
"amlogic,simple-framebuffer");
while (offset >= 0) {
ret = fdt_stringlist_search(blob, offset, "amlogic,pipeline",
pipeline);
if (ret == 0)
break;
offset = fdt_node_offset_by_compatible(blob, offset,
"amlogic,simple-framebuffer");
}
return offset;
}
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
*/
#ifndef __SIMPLEFB_COMMON_H
#define __SIMPLEFB_COMMON_H
/**
* meson_simplefb_fdt_match() - match a meson simplefb node
*
* Match a meson simplefb device node with a specified pipeline, and
* return its offset.
*
* @blob: device tree blob
* @pipeline: display pipeline
* @return device node offset in blob, or negative values if failed
*/
int meson_simplefb_fdt_match(void *blob, const char *pipeline);
#endif
@@ -0,0 +1,828 @@
// SPDX-License-Identifier: GPL-2.0
/*
* MIPI DSI Bus
*
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
* Copyright (C) 2019 STMicroelectronics - All Rights Reserved
* Andrzej Hajda <a.hajda@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Mipi_dsi.c contains a set of dsi helpers.
* This file is inspired from the drm helper file drivers/gpu/drm/drm_mipi_dsi.c
* (kernel linux).
*
*/
#include <common.h>
#include <clk.h>
#include <display.h>
#include <dm.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
/**
* DOC: dsi helpers
*
* These functions contain some common logic and helpers to deal with MIPI DSI
* peripherals.
*
* Helpers are provided for a number of standard MIPI DSI command as well as a
* subset of the MIPI DCS command set.
*/
/**
* mipi_dsi_attach - attach a DSI device to its DSI host
* @dsi: DSI peripheral
*/
int mipi_dsi_attach(struct mipi_dsi_device *dsi)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->attach)
return -ENOSYS;
return ops->attach(dsi->host, dsi);
}
EXPORT_SYMBOL(mipi_dsi_attach);
/**
* mipi_dsi_detach - detach a DSI device from its DSI host
* @dsi: DSI peripheral
*/
int mipi_dsi_detach(struct mipi_dsi_device *dsi)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->detach)
return -ENOSYS;
return ops->detach(dsi->host, dsi);
}
EXPORT_SYMBOL(mipi_dsi_detach);
/**
* mipi_dsi_device_transfer - transfer message to a DSI device
* @dsi: DSI peripheral
* @msg: message
*/
static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
struct mipi_dsi_msg *msg)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->transfer)
return -ENOSYS;
if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
msg->flags |= MIPI_DSI_MSG_USE_LPM;
return ops->transfer(dsi->host, msg);
}
/**
* mipi_dsi_packet_format_is_short - check if a packet is of the short format
* @type: MIPI DSI data type of the packet
*
* Return: true if the packet for the given data type is a short packet, false
* otherwise.
*/
bool mipi_dsi_packet_format_is_short(u8 type)
{
switch (type) {
case MIPI_DSI_V_SYNC_START:
case MIPI_DSI_V_SYNC_END:
case MIPI_DSI_H_SYNC_START:
case MIPI_DSI_H_SYNC_END:
case MIPI_DSI_END_OF_TRANSMISSION:
case MIPI_DSI_COLOR_MODE_OFF:
case MIPI_DSI_COLOR_MODE_ON:
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
case MIPI_DSI_TURN_ON_PERIPHERAL:
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_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_READ:
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
return true;
}
return false;
}
EXPORT_SYMBOL(mipi_dsi_packet_format_is_short);
/**
* mipi_dsi_packet_format_is_long - check if a packet is of the long format
* @type: MIPI DSI data type of the packet
*
* Return: true if the packet for the given data type is a long packet, false
* otherwise.
*/
bool mipi_dsi_packet_format_is_long(u8 type)
{
switch (type) {
case MIPI_DSI_NULL_PACKET:
case MIPI_DSI_BLANKING_PACKET:
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
case MIPI_DSI_PACKED_PIXEL_STREAM_30:
case MIPI_DSI_PACKED_PIXEL_STREAM_36:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
return true;
}
return false;
}
EXPORT_SYMBOL(mipi_dsi_packet_format_is_long);
/**
* mipi_dsi_create_packet - create a packet from a message according to the
* DSI protocol
* @packet: pointer to a DSI packet structure
* @msg: message to translate into a packet
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg)
{
if (!packet || !msg)
return -EINVAL;
/* do some minimum sanity checking */
if (!mipi_dsi_packet_format_is_short(msg->type) &&
!mipi_dsi_packet_format_is_long(msg->type))
return -EINVAL;
if (msg->channel > 3)
return -EINVAL;
memset(packet, 0, sizeof(*packet));
packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
/* TODO: compute ECC if hardware support is not available */
/*
* Long write packets contain the word count in header bytes 1 and 2.
* The payload follows the header and is word count bytes long.
*
* Short write packets encode up to two parameters in header bytes 1
* and 2.
*/
if (mipi_dsi_packet_format_is_long(msg->type)) {
packet->header[1] = (msg->tx_len >> 0) & 0xff;
packet->header[2] = (msg->tx_len >> 8) & 0xff;
packet->payload_length = msg->tx_len;
packet->payload = msg->tx_buf;
} else {
const u8 *tx = msg->tx_buf;
packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
}
packet->size = sizeof(packet->header) + packet->payload_length;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_create_packet);
/**
* mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SHUTDOWN_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
};
int ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral);
/**
* mipi_dsi_turn_on_peripheral() - sends a Turn On Peripheral command
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_TURN_ON_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
};
int ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral);
/*
* mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
* the payload in a long packet transmitted from the peripheral back to the
* host processor
* @dsi: DSI peripheral device
* @value: the maximum size of the payload
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value)
{
u8 tx[2] = { value & 0xff, value >> 8 };
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
.tx_len = sizeof(tx),
.tx_buf = tx,
};
int ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
/**
* mipi_dsi_generic_write() - transmit data using a generic write packet
* @dsi: DSI peripheral device
* @payload: buffer containing the payload
* @size: size of payload buffer
*
* This function will automatically choose the right data type depending on
* the payload length.
*
* Return: The number of bytes transmitted on success or a negative error code
* on failure.
*/
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_buf = payload,
.tx_len = size
};
switch (size) {
case 0:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
break;
default:
msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
break;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_generic_write);
/**
* mipi_dsi_generic_read() - receive data using a generic read packet
* @dsi: DSI peripheral device
* @params: buffer containing the request parameters
* @num_params: number of request parameters
* @data: buffer in which to return the received data
* @size: size of receive buffer
*
* This function will automatically choose the right data type depending on
* the number of parameters passed in.
*
* Return: The number of bytes successfully read or a negative error code on
* failure.
*/
ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
size_t num_params, void *data, size_t size)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_len = num_params,
.tx_buf = params,
.rx_len = size,
.rx_buf = data
};
switch (num_params) {
case 0:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
break;
default:
return -EINVAL;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_generic_read);
/**
* mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
* @dsi: DSI peripheral device
* @data: buffer containing data to be transmitted
* @len: size of transmission buffer
*
* This function will automatically choose the right data type depending on
* the command payload length.
*
* Return: The number of bytes successfully transmitted or a negative error
* code on failure.
*/
ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
const void *data, size_t len)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_buf = data,
.tx_len = len
};
switch (len) {
case 0:
return -EINVAL;
case 1:
msg.type = MIPI_DSI_DCS_SHORT_WRITE;
break;
case 2:
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
break;
default:
msg.type = MIPI_DSI_DCS_LONG_WRITE;
break;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer);
/**
* mipi_dsi_dcs_write() - send DCS write command
* @dsi: DSI peripheral device
* @cmd: DCS command
* @data: buffer containing the command payload
* @len: command payload length
*
* This function will automatically choose the right data type depending on
* the command payload length.
*
* Return: The number of bytes successfully transmitted or a negative error
* code on failure.
*/
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
const void *data, size_t len)
{
ssize_t err;
size_t size;
u8 *tx;
if (len > 0) {
size = 1 + len;
tx = kmalloc(size, GFP_KERNEL);
if (!tx)
return -ENOMEM;
/* concatenate the DCS command byte and the payload */
tx[0] = cmd;
memcpy(&tx[1], data, len);
} else {
tx = &cmd;
size = 1;
}
err = mipi_dsi_dcs_write_buffer(dsi, tx, size);
if (len > 0)
kfree(tx);
return err;
}
EXPORT_SYMBOL(mipi_dsi_dcs_write);
/**
* mipi_dsi_dcs_read() - send DCS read request command
* @dsi: DSI peripheral device
* @cmd: DCS command
* @data: buffer in which to receive data
* @len: size of receive buffer
*
* Return: The number of bytes read or a negative error code on failure.
*/
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
size_t len)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_DCS_READ,
.tx_buf = &cmd,
.tx_len = 1,
.rx_buf = data,
.rx_len = len
};
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_dcs_read);
/**
* mipi_dsi_dcs_nop() - send DCS nop packet
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_nop);
/**
* mipi_dsi_dcs_soft_reset() - perform a software reset of the display module
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
/**
* mipi_dsi_dcs_get_power_mode() - query the display module's current power
* mode
* @dsi: DSI peripheral device
* @mode: return location for the current power mode
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
sizeof(*mode));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
/**
* mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image
* data used by the interface
* @dsi: DSI peripheral device
* @format: return location for the pixel format
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format,
sizeof(*format));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format);
/**
* mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
* display module except interface communication
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode);
/**
* mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
* module
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode);
/**
* mipi_dsi_dcs_set_display_off() - stop displaying the image data on the
* display device
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off);
/**
* mipi_dsi_dcs_set_display_on() - start displaying the image data on the
* display device
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
/**
* mipi_dsi_dcs_set_column_address() - define the column extent of the frame
* memory accessed by the host processor
* @dsi: DSI peripheral device
* @start: first column of frame memory
* @end: last column of frame memory
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
u16 end)
{
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
sizeof(payload));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_column_address);
/**
* mipi_dsi_dcs_set_page_address() - define the page extent of the frame
* memory accessed by the host processor
* @dsi: DSI peripheral device
* @start: first page of frame memory
* @end: last page of frame memory
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
u16 end)
{
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
sizeof(payload));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address);
/**
* mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
* output signal on the TE signal line
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off);
/**
* mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
* output signal on the TE signal line.
* @dsi: DSI peripheral device
* @mode: the Tearing Effect Output Line mode
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
enum mipi_dsi_dcs_tear_mode mode)
{
u8 value = mode;
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
sizeof(value));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
/**
* mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
* data used by the interface
* @dsi: DSI peripheral device
* @format: pixel format
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
sizeof(format));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
/**
* mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for
* the Tearing Effect output signal of the display module
* @dsi: DSI peripheral device
* @scanline: scanline to use as trigger
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline)
{
u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, scanline >> 8,
scanline & 0xff };
ssize_t err;
err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
/**
* mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the
* display
* @dsi: DSI peripheral device
* @brightness: brightness value
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
u16 brightness)
{
u8 payload[2] = { brightness & 0xff, brightness >> 8 };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
payload, sizeof(payload));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness);
/**
* mipi_dsi_dcs_get_display_brightness() - gets the current brightness value
* of the display
* @dsi: DSI peripheral device
* @brightness: brightness value
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
u16 *brightness)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
brightness, sizeof(*brightness));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness);
@@ -0,0 +1,594 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Video driver for Marvell Armada XP SoC
*
* Initialization of LCD interface and setup of SPLASH screen image
*/
#include <common.h>
#include <dm.h>
#include <video.h>
#include <linux/mbus.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#define MVEBU_LCD_WIN_CONTROL(w) (0xf000 + ((w) << 4))
#define MVEBU_LCD_WIN_BASE(w) (0xf004 + ((w) << 4))
#define MVEBU_LCD_WIN_REMAP(w) (0xf00c + ((w) << 4))
#define MVEBU_LCD_CFG_DMA_START_ADDR_0 0x00cc
#define MVEBU_LCD_CFG_DMA_START_ADDR_1 0x00dc
#define MVEBU_LCD_CFG_GRA_START_ADDR0 0x00f4
#define MVEBU_LCD_CFG_GRA_START_ADDR1 0x00f8
#define MVEBU_LCD_CFG_GRA_PITCH 0x00fc
#define MVEBU_LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100
#define MVEBU_LCD_SPU_GRA_HPXL_VLN 0x0104
#define MVEBU_LCD_SPU_GZM_HPXL_VLN 0x0108
#define MVEBU_LCD_SPU_HWC_OVSA_HPXL_VLN 0x010c
#define MVEBU_LCD_SPU_HWC_HPXL_VLN 0x0110
#define MVEBU_LCD_SPUT_V_H_TOTAL 0x0114
#define MVEBU_LCD_SPU_V_H_ACTIVE 0x0118
#define MVEBU_LCD_SPU_H_PORCH 0x011c
#define MVEBU_LCD_SPU_V_PORCH 0x0120
#define MVEBU_LCD_SPU_BLANKCOLOR 0x0124
#define MVEBU_LCD_SPU_ALPHA_COLOR1 0x0128
#define MVEBU_LCD_SPU_ALPHA_COLOR2 0x012c
#define MVEBU_LCD_SPU_COLORKEY_Y 0x0130
#define MVEBU_LCD_SPU_COLORKEY_U 0x0134
#define MVEBU_LCD_SPU_COLORKEY_V 0x0138
#define MVEBU_LCD_CFG_RDREG4F 0x013c
#define MVEBU_LCD_SPU_SPI_RXDATA 0x0140
#define MVEBU_LCD_SPU_ISA_RXDATA 0x0144
#define MVEBU_LCD_SPU_DBG_ISA 0x0148
#define MVEBU_LCD_SPU_HWC_RDDAT 0x0158
#define MVEBU_LCD_SPU_GAMMA_RDDAT 0x015c
#define MVEBU_LCD_SPU_PALETTE_RDDAT 0x0160
#define MVEBU_LCD_SPU_IOPAD_IN 0x0178
#define MVEBU_LCD_FRAME_COUNT 0x017c
#define MVEBU_LCD_SPU_DMA_CTRL0 0x0190
#define MVEBU_LCD_SPU_DMA_CTRL1 0x0194
#define MVEBU_LCD_SPU_SRAM_CTRL 0x0198
#define MVEBU_LCD_SPU_SRAM_WRDAT 0x019c
#define MVEBU_LCD_SPU_SRAM_PARA0 0x01a0
#define MVEBU_LCD_SPU_SRAM_PARA1 0x01a4
#define MVEBU_LCD_CFG_SCLK_DIV 0x01a8
#define MVEBU_LCD_SPU_CONTRAST 0x01ac
#define MVEBU_LCD_SPU_SATURATION 0x01b0
#define MVEBU_LCD_SPU_CBSH_HUE 0x01b4
#define MVEBU_LCD_SPU_DUMB_CTRL 0x01b8
#define MVEBU_LCD_SPU_IOPAD_CONTROL 0x01bc
#define MVEBU_LCD_SPU_IRQ_ENA_2 0x01d8
#define MVEBU_LCD_SPU_IRQ_ISR_2 0x01dc
#define MVEBU_LCD_SPU_IRQ_ENA 0x01c0
#define MVEBU_LCD_SPU_IRQ_ISR 0x01c4
#define MVEBU_LCD_ADLL_CTRL 0x01c8
#define MVEBU_LCD_CLK_DIS 0x01cc
#define MVEBU_LCD_VGA_HVSYNC_DELAY 0x01d4
#define MVEBU_LCD_CLK_CFG_0 0xf0a0
#define MVEBU_LCD_CLK_CFG_1 0xf0a4
#define MVEBU_LCD_LVDS_CLK_CFG 0xf0ac
#define MVEBU_LVDS_PADS_REG (MVEBU_SYSTEM_REG_BASE + 0xf0)
enum {
/* Maximum LCD size we support */
LCD_MAX_WIDTH = 640,
LCD_MAX_HEIGHT = 480,
LCD_MAX_LOG2_BPP = VIDEO_BPP16,
};
struct mvebu_lcd_info {
u32 fb_base;
int x_res;
int y_res;
int x_fp;
int y_fp;
int x_bp;
int y_bp;
};
struct mvebu_video_priv {
uintptr_t regs;
};
/* Setup Mbus Bridge Windows for LCD */
static void mvebu_lcd_conf_mbus_registers(uintptr_t regs)
{
const struct mbus_dram_target_info *dram;
int i;
dram = mvebu_mbus_dram_info();
/* Disable windows, set size/base/remap to 0 */
for (i = 0; i < 6; i++) {
writel(0, regs + MVEBU_LCD_WIN_CONTROL(i));
writel(0, regs + MVEBU_LCD_WIN_BASE(i));
writel(0, regs + MVEBU_LCD_WIN_REMAP(i));
}
/* Write LCD bridge window registers */
for (i = 0; i < dram->num_cs; i++) {
const struct mbus_dram_window *cs = dram->cs + i;
writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
regs + MVEBU_LCD_WIN_CONTROL(i));
writel(cs->base & 0xffff0000, regs + MVEBU_LCD_WIN_BASE(i));
}
}
/* Initialize LCD registers */
static void mvebu_lcd_register_init(struct mvebu_lcd_info *lcd_info,
uintptr_t regs)
{
/* Local variable for easier handling */
int x = lcd_info->x_res;
int y = lcd_info->y_res;
u32 val;
/* Setup Mbus Bridge Windows */
mvebu_lcd_conf_mbus_registers(regs);
/*
* Set LVDS Pads Control Register
* wr 0 182F0 FFE00000
*/
clrbits_le32(MVEBU_LVDS_PADS_REG, 0x1f << 16);
/*
* Set the LCD_CFG_GRA_START_ADDR0/1 Registers
* This is supposed to point to the "physical" memory at memory
* end (currently 1GB-64MB but also may be 2GB-64MB).
* See also the Window 0 settings!
*/
writel(lcd_info->fb_base, regs + MVEBU_LCD_CFG_GRA_START_ADDR0);
writel(lcd_info->fb_base, regs + MVEBU_LCD_CFG_GRA_START_ADDR1);
/*
* Set the LCD_CFG_GRA_PITCH Register
* Bits 31-28: Duty Cycle of Backlight. value/16=High (0x8=Mid Setting)
* Bits 25-16: Backlight divider from 32kHz Clock
* (here 16=0x10 for 1kHz)
* Bits 15-00: Line Length in Bytes
* 240*2 (for RGB1555)=480=0x1E0
*/
writel(0x80100000 + 2 * x, regs + MVEBU_LCD_CFG_GRA_PITCH);
/*
* Set the LCD_SPU_GRA_OVSA_HPXL_VLN Register
* Bits 31-16: Vertical start of graphical overlay on screen
* Bits 15-00: Horizontal start of graphical overlay on screen
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_GRA_OVSA_HPXL_VLN);
/*
* Set the LCD_SPU_GRA_HPXL_VLN Register
* Bits 31-16: Vertical size of graphical overlay 320=0x140
* Bits 15-00: Horizontal size of graphical overlay 240=0xF0
* Values before zooming
*/
writel((y << 16) | x, regs + MVEBU_LCD_SPU_GRA_HPXL_VLN);
/*
* Set the LCD_SPU_GZM_HPXL_VLN Register
* Bits 31-16: Vertical size of graphical overlay 320=0x140
* Bits 15-00: Horizontal size of graphical overlay 240=0xF0
* Values after zooming
*/
writel((y << 16) | x, regs + MVEBU_LCD_SPU_GZM_HPXL_VLN);
/*
* Set the LCD_SPU_HWC_OVSA_HPXL_VLN Register
* Bits 31-16: Vertical position of HW Cursor 320=0x140
* Bits 15-00: Horizontal position of HW Cursor 240=0xF0
*/
writel((y << 16) | x, regs + MVEBU_LCD_SPU_HWC_OVSA_HPXL_VLN);
/*
* Set the LCD_SPU_HWC_OVSA_HPXL_VLN Register
* Bits 31-16: Vertical size of HW Cursor
* Bits 15-00: Horizontal size of HW Cursor
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_HWC_HPXL_VLN);
/*
* Set the LCD_SPU_HWC_OVSA_HPXL_VLN Register
* Bits 31-16: Screen total vertical lines:
* VSYNC = 1
* Vertical Front Porch = 2
* Vertical Lines = 320
* Vertical Back Porch = 2
* SUM = 325 = 0x0145
* Bits 15-00: Screen total horizontal pixels:
* HSYNC = 1
* Horizontal Front Porch = 44
* Horizontal Lines = 240
* Horizontal Back Porch = 2
* SUM = 287 = 0x011F
* Note: For the display the backporch is between SYNC and
* the start of the pixels.
* This is not certain for the Marvell (!?)
*/
val = ((y + lcd_info->y_fp + lcd_info->y_bp + 1) << 16) |
(x + lcd_info->x_fp + lcd_info->x_bp + 1);
writel(val, regs + MVEBU_LCD_SPUT_V_H_TOTAL);
/*
* Set the LCD_SPU_V_H_ACTIVE Register
* Bits 31-16: Screen active vertical lines 320=0x140
* Bits 15-00: Screen active horizontakl pixels 240=0x00F0
*/
writel((y << 16) | x, regs + MVEBU_LCD_SPU_V_H_ACTIVE);
/*
* Set the LCD_SPU_H_PORCH Register
* Bits 31-16: Screen horizontal backporch 44=0x2c
* Bits 15-00: Screen horizontal frontporch 2=0x02
* Note: The terms "front" and "back" for the Marvell seem to be
* exactly opposite to the display.
*/
writel((lcd_info->x_fp << 16) | lcd_info->x_bp,
regs + MVEBU_LCD_SPU_H_PORCH);
/*
* Set the LCD_SPU_V_PORCH Register
* Bits 31-16: Screen vertical backporch 2=0x02
* Bits 15-00: Screen vertical frontporch 2=0x02
* Note: The terms "front" and "back" for the Marvell seem to be exactly
* opposite to the display.
*/
writel((lcd_info->y_fp << 16) | lcd_info->y_bp,
regs + MVEBU_LCD_SPU_V_PORCH);
/*
* Set the LCD_SPU_BLANKCOLOR Register
* This should be black = 0
* For tests this is magenta=00FF00FF
*/
writel(0x00FF00FF, regs + MVEBU_LCD_SPU_BLANKCOLOR);
/*
* Registers in the range of 0x0128 to 0x012C are colors for the cursor
* Registers in the range of 0x0130 to 0x0138 are colors for video
* color keying
*/
/*
* Set the LCD_SPU_RDREG4F Register
* Bits 31-12: Reservd
* Bit 11: SRAM Wait
* Bit 10: Smart display fast TX (must be 1)
* Bit 9: DMA Arbitration Video/Graphics overlay: 0=interleaved
* Bit 8: FIFO watermark for DMA: 0=disable
* Bits 07-00: Empty 8B FIFO entries to trigger DMA, default=0x80
*/
writel(0x00000780, regs + MVEBU_LCD_CFG_RDREG4F);
/*
* Set the LCD_SPU_DMACTRL 0 Register
* Bit 31: Disable overlay blending 1=disable
* Bit 30: Gamma correction enable, 0=disable
* Bit 29: Video Contrast/Saturation/Hue Adjust enable, 0=disable
* Bit 28: Color palette enable, 0=disable
* Bit 27: DMA AXI Arbiter, 1=default
* Bit 26: HW Cursor 1-bit mode
* Bit 25: HW Cursor or 1- or 2-bit mode
* Bit 24: HW Cursor enabled, 0=disable
* Bits 23-20: Graphics Memory Color Format: 0x1=RGB1555
* Bits 19-16: Video Memory Color Format: 0x1=RGB1555
* Bit 15: Memory Toggle between frame 0 and 1: 0=disable
* Bit 14: Graphics horizontal scaling enable: 0=disable
* Bit 13: Graphics test mode: 0=disable
* Bit 12: Graphics SWAP R and B: 0=disable
* Bit 11: Graphics SWAP U and V: 0=disable
* Bit 10: Graphics SWAP Y and U/V: 0=disable
* Bit 09: Graphic YUV to RGB Conversion: 0=disable
* Bit 08: Graphic Transfer: 1=enable
* Bit 07: Memory Toggle: 0=disable
* Bit 06: Video horizontal scaling enable: 0=disable
* Bit 05: Video test mode: 0=disable
* Bit 04: Video SWAP R and B: 0=disable
* Bit 03: Video SWAP U and V: 0=disable
* Bit 02: Video SWAP Y and U/V: 0=disable
* Bit 01: Video YUV to RGB Conversion: 0=disable
* Bit 00: Video Transfer: 0=disable
*/
writel(0x88111100, regs + MVEBU_LCD_SPU_DMA_CTRL0);
/*
* Set the LCD_SPU_DMA_CTRL1 Register
* Bit 31: Manual DMA Trigger = 0
* Bits 30-28: DMA Trigger Source: 0x2 VSYNC
* Bit 28: VSYNC_INV: 0=Rising Edge, 1=Falling Edge
* Bits 26-24: Color Key Mode: 0=disable
* Bit 23: Fill low bits: 0=fill with zeroes
* Bit 22: Reserved
* Bit 21: Gated Clock: 0=disable
* Bit 20: Power Save enable: 0=disable
* Bits 19-18: Reserved
* Bits 17-16: Configure Video/Graphic Path: 0x1: Graphic path alpha.
* Bits 15-08: Configure Alpha: 0x00.
* Bits 07-00: Reserved.
*/
writel(0x20010000, regs + MVEBU_LCD_SPU_DMA_CTRL1);
/*
* Set the LCD_SPU_SRAM_CTRL Register
* Reset to default = 0000C000
* Bits 15-14: SRAM control: init=0x3, Read=0, Write=2
* Bits 11-08: SRAM address ID: 0=gamma_yr, 1=gammy_ug, 2=gamma_vb,
* 3=palette, 15=cursor
*/
writel(0x0000C000, regs + MVEBU_LCD_SPU_SRAM_CTRL);
/*
* LCD_SPU_SRAM_WRDAT register: 019C
* LCD_SPU_SRAM_PARA0 register: 01A0
* LCD_SPU_SRAM_PARA1 register: 01A4 - Cursor control/Power settings
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_SRAM_PARA1);
/* Clock settings in the at 01A8 and in the range F0A0 see below */
/*
* Set LCD_SPU_CONTRAST
* Bits 31-16: Brightness sign ext. 8-bit value +255 to -255: default=0
* Bits 15-00: Contrast sign ext. 8-bit value +255 to -255: default=0
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_CONTRAST);
/*
* Set LCD_SPU_SATURATION
* Bits 31-16: Multiplier signed 4.12 fixed point value
* Bits 15-00: Saturation signed 4.12 fixed point value
*/
writel(0x10001000, regs + MVEBU_LCD_SPU_SATURATION);
/*
* Set LCD_SPU_HUE
* Bits 31-16: Sine signed 2.14 fixed point value
* Bits 15-00: Cosine signed 2.14 fixed point value
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_CBSH_HUE);
/*
* Set LCD_SPU_DUMB_CTRL
* Bits 31-28: LCD Type: 3=18 bit RGB | 6=24 bit RGB888
* Bits 27-12: Reserved
* Bit 11: LCD DMA Pipeline Enable: 1=Enable
* Bits 10-09: Reserved
* Bit 8: LCD GPIO pin (??)
* Bit 7: Reverse RGB
* Bit 6: Invert composite blank signal DE/EN (??)
* Bit 5: Invert composite sync signal
* Bit 4: Invert Pixel Valid Enable DE/EN (??)
* Bit 3: Invert VSYNC
* Bit 2: Invert HSYNC
* Bit 1: Invert Pixel Clock
* Bit 0: Enable LCD Panel: 1=Enable
* Question: Do we have to disable Smart and Dumb LCD
* and separately enable LVDS?
*/
writel(0x6000080F, regs + MVEBU_LCD_SPU_DUMB_CTRL);
/*
* Set LCD_SPU_IOPAD_CTRL
* Bits 31-20: Reserved
* Bits 19-18: Vertical Interpolation: 0=Disable
* Bits 17-16: Reserved
* Bit 15: Graphics Vertical Mirror enable: 0=disable
* Bit 14: Reserved
* Bit 13: Video Vertical Mirror enable: 0=disable
* Bit 12: Reserved
* Bit 11: Command Vertical Mirror enable: 0=disable
* Bit 10: Reserved
* Bits 09-08: YUV to RGB Color space conversion: 0 (Not used)
* Bits 07-04: AXI Bus Master: 0x4: no crossing of 4k boundary,
* 128 Bytes burst
* Bits 03-00: LCD pins: ??? 0=24-bit Dump panel ??
*/
writel(0x000000C0, regs + MVEBU_LCD_SPU_IOPAD_CONTROL);
/*
* Set SUP_IRQ_ENA_2: Disable all interrupts
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_IRQ_ENA_2);
/*
* Set SUP_IRQ_ENA: Disable all interrupts.
*/
writel(0x00000000, regs + MVEBU_LCD_SPU_IRQ_ENA);
/*
* Set up ADDL Control Register
* Bits 31-29: 0x0 = Fastest Delay Line (default)
* 0x3 = Slowest Delay Line (default)
* Bit 28: Calibration done status.
* Bit 27: Reserved
* Bit 26: Set Pixel Clock to ADDL output
* Bit 25: Reduce CAL Enable
* Bits 24-22: Manual calibration value.
* Bit 21: Manual calibration enable.
* Bit 20: Restart Auto Cal
* Bits 19-16: Calibration Threshold voltage, default= 0x2
* Bite 15-14: Reserved
* Bits 13-11: Divisor for ADDL Clock: 0x1=/2, 0x3=/8, 0x5=/16
* Bit 10: Power Down ADDL module, default = 1!
* Bits 09-08: Test point configuration: 0x2=Bias, 0x3=High-z
* Bit 07: Reset ADDL
* Bit 06: Invert ADLL Clock
* Bits 05-00: Delay taps, 0x3F=Half Cycle, 0x00=No delay
* Note: ADLL is used for a VGA interface with DAC - not used here
*/
writel(0x00000000, regs + MVEBU_LCD_ADLL_CTRL);
/*
* Set the LCD_CLK_DIS Register:
* Bits 3 and 4 must be 1
*/
writel(0x00000018, regs + MVEBU_LCD_CLK_DIS);
/*
* Set the LCD_VGA_HSYNC/VSYNC Delay Register:
* Bits 03-00: Sets the delay for the HSYNC and VSYNC signals
*/
writel(0x00000000, regs + MVEBU_LCD_VGA_HVSYNC_DELAY);
/*
* Clock registers
* See page 475 in the functional spec.
*/
/* Step 1 and 2: Disable the PLL */
/*
* Disable PLL, see "LCD Clock Configuration 1 Register" below
*/
writel(0x8FF40007, regs + MVEBU_LCD_CLK_CFG_1);
/*
* Powerdown, see "LCD Clock Configuration 0 Register" below
*/
writel(0x94000174, regs + MVEBU_LCD_CLK_CFG_0);
/*
* Set the LCD_CFG_SCLK_DIV Register
* This is set fix to 0x40000001 for the LVDS output:
* Bits 31-30: SCLCK Source: 0=AXIBus, 1=AHBus, 2=PLLDivider0
* Bits 15-01: Clock Divider: Bypass for LVDS=0x0001
* See page 475 in section 28.5.
*/
writel(0x80000001, regs + MVEBU_LCD_CFG_SCLK_DIV);
/*
* Set the LCD Clock Configuration 0 Register:
* Bit 31: Powerdown: 0=Power up
* Bits 30-29: Reserved
* Bits 28-26: PLL_KDIV: This encodes K
* K=16 => 0x5
* Bits 25-17: PLL_MDIV: This is M-1:
* M=1 => 0x0
* Bits 16-13: VCO band: 0x1 for 700-920MHz
* Bits 12-04: PLL_NDIV: This is N-1 and corresponds to R1_CTRL!
* N=28=0x1C => 0x1B
* Bits 03-00: R1_CTRL (for N=28 => 0x4)
*/
writel(0x940021B4, regs + MVEBU_LCD_CLK_CFG_0);
/*
* Set the LCD Clock Configuration 1 Register:
* Bits 31-19: Reserved
* Bit 18: Select PLL: Core PLL, 1=Dedicated PPL
* Bit 17: Clock Output Enable: 0=disable, 1=enable
* Bit 16: Select RefClk: 0=RefClk (25MHz), 1=External
* Bit 15: Half-Div, Device Clock by DIV+0.5*Half-Dev
* Bits 14-13: Reserved
* Bits 12-00: PLL Full Divider [Note: Assumed to be the Post-Divider
* M' for LVDS=7!]
*/
writel(0x8FF40007, regs + MVEBU_LCD_CLK_CFG_1);
/*
* Set the LVDS Clock Configuration Register:
* Bit 31: Clock Gating for the input clock to the LVDS
* Bit 30: LVDS Serializer enable: 1=Enabled
* Bits 29-11: Reserved
* Bit 11-08: LVDS Clock delay: 0x02 (default): by 2 pixel clock/7
* Bits 07-02: Reserved
* Bit 01: 24bbp Option: 0=Option_1,1=Option2
* Bit 00: 1=24bbp Panel: 0=18bpp Panel
* Note: Bits 0 and must be verified with the help of the
* Interface/display
*/
writel(0xC0000201, regs + MVEBU_LCD_LVDS_CLK_CFG);
/*
* Power up PLL (Clock Config 0)
*/
writel(0x140021B4, regs + MVEBU_LCD_CLK_CFG_0);
/* wait 10 ms */
mdelay(10);
/*
* Enable PLL (Clock Config 1)
*/
writel(0x8FF60007, regs + MVEBU_LCD_CLK_CFG_1);
}
static int mvebu_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct mvebu_video_priv *priv = dev_get_priv(dev);
struct mvebu_lcd_info lcd_info;
struct display_timing timings;
u32 fb_start, fb_end;
int ret;
priv->regs = dev_read_addr(dev);
if (priv->regs == FDT_ADDR_T_NONE) {
dev_err(dev, "failed to get LCD address\n");
return -ENXIO;
}
ret = ofnode_decode_display_timing(dev_ofnode(dev), 0, &timings);
if (ret) {
dev_err(dev, "failed to get any display timings\n");
return -EINVAL;
}
/* Use DT timing (resolution) in internal info struct */
lcd_info.fb_base = plat->base;
lcd_info.x_res = timings.hactive.typ;
lcd_info.x_fp = timings.hfront_porch.typ;
lcd_info.x_bp = timings.hback_porch.typ;
lcd_info.y_res = timings.vactive.typ;
lcd_info.y_fp = timings.vfront_porch.typ;
lcd_info.y_bp = timings.vback_porch.typ;
/* Initialize the LCD controller */
mvebu_lcd_register_init(&lcd_info, priv->regs);
/* Enable dcache for the frame buffer */
fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
fb_end = plat->base + plat->size;
fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
DCACHE_WRITEBACK);
video_set_flush_dcache(dev, true);
uc_priv->xsize = lcd_info.x_res;
uc_priv->ysize = lcd_info.y_res;
uc_priv->bpix = VIDEO_BPP16; /* Uses RGB555 format */
return 0;
}
static int mvebu_video_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
(1 << LCD_MAX_LOG2_BPP) / 8;
return 0;
}
static const struct udevice_id mvebu_video_ids[] = {
{ .compatible = "marvell,armada-xp-lcd" },
{ }
};
U_BOOT_DRIVER(mvebu_video) = {
.name = "mvebu_video",
.id = UCLASS_VIDEO,
.of_match = mvebu_video_ids,
.bind = mvebu_video_bind,
.probe = mvebu_video_probe,
.priv_auto_alloc_size = sizeof(struct mvebu_video_priv),
};
@@ -0,0 +1,904 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2009
* Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
* Copyright (C) 2011
* HALE electronic GmbH, <helmut.raiger@hale.at>
*/
#include <common.h>
#include <env.h>
#include <malloc.h>
#include <video_fb.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#include <linux/errno.h>
#include <asm/io.h>
#include "videomodes.h"
/* this might need panel specific set-up as-well */
#define IF_CONF 0
/* -------------- controller specific stuff -------------- */
/* IPU DMA Controller channel definitions. */
enum ipu_channel {
IDMAC_IC_0 = 0, /* IC (encoding task) to memory */
IDMAC_IC_1 = 1, /* IC (viewfinder task) to memory */
IDMAC_ADC_0 = 1,
IDMAC_IC_2 = 2,
IDMAC_ADC_1 = 2,
IDMAC_IC_3 = 3,
IDMAC_IC_4 = 4,
IDMAC_IC_5 = 5,
IDMAC_IC_6 = 6,
IDMAC_IC_7 = 7, /* IC (sensor data) to memory */
IDMAC_IC_8 = 8,
IDMAC_IC_9 = 9,
IDMAC_IC_10 = 10,
IDMAC_IC_11 = 11,
IDMAC_IC_12 = 12,
IDMAC_IC_13 = 13,
IDMAC_SDC_0 = 14, /* Background synchronous display data */
IDMAC_SDC_1 = 15, /* Foreground data (overlay) */
IDMAC_SDC_2 = 16,
IDMAC_SDC_3 = 17,
IDMAC_ADC_2 = 18,
IDMAC_ADC_3 = 19,
IDMAC_ADC_4 = 20,
IDMAC_ADC_5 = 21,
IDMAC_ADC_6 = 22,
IDMAC_ADC_7 = 23,
IDMAC_PF_0 = 24,
IDMAC_PF_1 = 25,
IDMAC_PF_2 = 26,
IDMAC_PF_3 = 27,
IDMAC_PF_4 = 28,
IDMAC_PF_5 = 29,
IDMAC_PF_6 = 30,
IDMAC_PF_7 = 31,
};
/* More formats can be copied from the Linux driver if needed */
enum pixel_fmt {
/* 2 bytes */
IPU_PIX_FMT_RGB565,
IPU_PIX_FMT_RGB666,
IPU_PIX_FMT_BGR666,
/* 3 bytes */
IPU_PIX_FMT_RGB24,
};
struct pixel_fmt_cfg {
u32 b0;
u32 b1;
u32 b2;
u32 acc;
};
static struct pixel_fmt_cfg fmt_cfg[] = {
[IPU_PIX_FMT_RGB24] = {
0x1600AAAA, 0x00E05555, 0x00070000, 3,
},
[IPU_PIX_FMT_RGB666] = {
0x0005000F, 0x000B000F, 0x0011000F, 1,
},
[IPU_PIX_FMT_BGR666] = {
0x0011000F, 0x000B000F, 0x0005000F, 1,
},
[IPU_PIX_FMT_RGB565] = {
0x0004003F, 0x000A000F, 0x000F003F, 1,
}
};
enum ipu_panel {
IPU_PANEL_SHARP_TFT,
IPU_PANEL_TFT,
};
/* IPU Common registers */
/* IPU_CONF and its bits already defined in imx-regs.h */
#define IPU_CHA_BUF0_RDY (0x04 + IPU_BASE)
#define IPU_CHA_BUF1_RDY (0x08 + IPU_BASE)
#define IPU_CHA_DB_MODE_SEL (0x0C + IPU_BASE)
#define IPU_CHA_CUR_BUF (0x10 + IPU_BASE)
#define IPU_FS_PROC_FLOW (0x14 + IPU_BASE)
#define IPU_FS_DISP_FLOW (0x18 + IPU_BASE)
#define IPU_TASKS_STAT (0x1C + IPU_BASE)
#define IPU_IMA_ADDR (0x20 + IPU_BASE)
#define IPU_IMA_DATA (0x24 + IPU_BASE)
#define IPU_INT_CTRL_1 (0x28 + IPU_BASE)
#define IPU_INT_CTRL_2 (0x2C + IPU_BASE)
#define IPU_INT_CTRL_3 (0x30 + IPU_BASE)
#define IPU_INT_CTRL_4 (0x34 + IPU_BASE)
#define IPU_INT_CTRL_5 (0x38 + IPU_BASE)
#define IPU_INT_STAT_1 (0x3C + IPU_BASE)
#define IPU_INT_STAT_2 (0x40 + IPU_BASE)
#define IPU_INT_STAT_3 (0x44 + IPU_BASE)
#define IPU_INT_STAT_4 (0x48 + IPU_BASE)
#define IPU_INT_STAT_5 (0x4C + IPU_BASE)
#define IPU_BRK_CTRL_1 (0x50 + IPU_BASE)
#define IPU_BRK_CTRL_2 (0x54 + IPU_BASE)
#define IPU_BRK_STAT (0x58 + IPU_BASE)
#define IPU_DIAGB_CTRL (0x5C + IPU_BASE)
/* Image Converter Registers */
#define IC_CONF (0x88 + IPU_BASE)
#define IC_PRP_ENC_RSC (0x8C + IPU_BASE)
#define IC_PRP_VF_RSC (0x90 + IPU_BASE)
#define IC_PP_RSC (0x94 + IPU_BASE)
#define IC_CMBP_1 (0x98 + IPU_BASE)
#define IC_CMBP_2 (0x9C + IPU_BASE)
#define PF_CONF (0xA0 + IPU_BASE)
#define IDMAC_CONF (0xA4 + IPU_BASE)
#define IDMAC_CHA_EN (0xA8 + IPU_BASE)
#define IDMAC_CHA_PRI (0xAC + IPU_BASE)
#define IDMAC_CHA_BUSY (0xB0 + IPU_BASE)
/* Image Converter Register bits */
#define IC_CONF_PRPENC_EN 0x00000001
#define IC_CONF_PRPENC_CSC1 0x00000002
#define IC_CONF_PRPENC_ROT_EN 0x00000004
#define IC_CONF_PRPVF_EN 0x00000100
#define IC_CONF_PRPVF_CSC1 0x00000200
#define IC_CONF_PRPVF_CSC2 0x00000400
#define IC_CONF_PRPVF_CMB 0x00000800
#define IC_CONF_PRPVF_ROT_EN 0x00001000
#define IC_CONF_PP_EN 0x00010000
#define IC_CONF_PP_CSC1 0x00020000
#define IC_CONF_PP_CSC2 0x00040000
#define IC_CONF_PP_CMB 0x00080000
#define IC_CONF_PP_ROT_EN 0x00100000
#define IC_CONF_IC_GLB_LOC_A 0x10000000
#define IC_CONF_KEY_COLOR_EN 0x20000000
#define IC_CONF_RWS_EN 0x40000000
#define IC_CONF_CSI_MEM_WR_EN 0x80000000
/* SDC Registers */
#define SDC_COM_CONF (0xB4 + IPU_BASE)
#define SDC_GW_CTRL (0xB8 + IPU_BASE)
#define SDC_FG_POS (0xBC + IPU_BASE)
#define SDC_BG_POS (0xC0 + IPU_BASE)
#define SDC_CUR_POS (0xC4 + IPU_BASE)
#define SDC_PWM_CTRL (0xC8 + IPU_BASE)
#define SDC_CUR_MAP (0xCC + IPU_BASE)
#define SDC_HOR_CONF (0xD0 + IPU_BASE)
#define SDC_VER_CONF (0xD4 + IPU_BASE)
#define SDC_SHARP_CONF_1 (0xD8 + IPU_BASE)
#define SDC_SHARP_CONF_2 (0xDC + IPU_BASE)
/* Register bits */
#define SDC_COM_TFT_COLOR 0x00000001UL
#define SDC_COM_FG_EN 0x00000010UL
#define SDC_COM_GWSEL 0x00000020UL
#define SDC_COM_GLB_A 0x00000040UL
#define SDC_COM_KEY_COLOR_G 0x00000080UL
#define SDC_COM_BG_EN 0x00000200UL
#define SDC_COM_SHARP 0x00001000UL
#define SDC_V_SYNC_WIDTH_L 0x00000001UL
/* Display Interface registers */
#define DI_DISP_IF_CONF (0x0124 + IPU_BASE)
#define DI_DISP_SIG_POL (0x0128 + IPU_BASE)
#define DI_SER_DISP1_CONF (0x012C + IPU_BASE)
#define DI_SER_DISP2_CONF (0x0130 + IPU_BASE)
#define DI_HSP_CLK_PER (0x0134 + IPU_BASE)
#define DI_DISP0_TIME_CONF_1 (0x0138 + IPU_BASE)
#define DI_DISP0_TIME_CONF_2 (0x013C + IPU_BASE)
#define DI_DISP0_TIME_CONF_3 (0x0140 + IPU_BASE)
#define DI_DISP1_TIME_CONF_1 (0x0144 + IPU_BASE)
#define DI_DISP1_TIME_CONF_2 (0x0148 + IPU_BASE)
#define DI_DISP1_TIME_CONF_3 (0x014C + IPU_BASE)
#define DI_DISP2_TIME_CONF_1 (0x0150 + IPU_BASE)
#define DI_DISP2_TIME_CONF_2 (0x0154 + IPU_BASE)
#define DI_DISP2_TIME_CONF_3 (0x0158 + IPU_BASE)
#define DI_DISP3_TIME_CONF (0x015C + IPU_BASE)
#define DI_DISP0_DB0_MAP (0x0160 + IPU_BASE)
#define DI_DISP0_DB1_MAP (0x0164 + IPU_BASE)
#define DI_DISP0_DB2_MAP (0x0168 + IPU_BASE)
#define DI_DISP0_CB0_MAP (0x016C + IPU_BASE)
#define DI_DISP0_CB1_MAP (0x0170 + IPU_BASE)
#define DI_DISP0_CB2_MAP (0x0174 + IPU_BASE)
#define DI_DISP1_DB0_MAP (0x0178 + IPU_BASE)
#define DI_DISP1_DB1_MAP (0x017C + IPU_BASE)
#define DI_DISP1_DB2_MAP (0x0180 + IPU_BASE)
#define DI_DISP1_CB0_MAP (0x0184 + IPU_BASE)
#define DI_DISP1_CB1_MAP (0x0188 + IPU_BASE)
#define DI_DISP1_CB2_MAP (0x018C + IPU_BASE)
#define DI_DISP2_DB0_MAP (0x0190 + IPU_BASE)
#define DI_DISP2_DB1_MAP (0x0194 + IPU_BASE)
#define DI_DISP2_DB2_MAP (0x0198 + IPU_BASE)
#define DI_DISP2_CB0_MAP (0x019C + IPU_BASE)
#define DI_DISP2_CB1_MAP (0x01A0 + IPU_BASE)
#define DI_DISP2_CB2_MAP (0x01A4 + IPU_BASE)
#define DI_DISP3_B0_MAP (0x01A8 + IPU_BASE)
#define DI_DISP3_B1_MAP (0x01AC + IPU_BASE)
#define DI_DISP3_B2_MAP (0x01B0 + IPU_BASE)
#define DI_DISP_ACC_CC (0x01B4 + IPU_BASE)
#define DI_DISP_LLA_CONF (0x01B8 + IPU_BASE)
#define DI_DISP_LLA_DATA (0x01BC + IPU_BASE)
/* DI_DISP_SIG_POL bits */
#define DI_D3_VSYNC_POL (1 << 28)
#define DI_D3_HSYNC_POL (1 << 27)
#define DI_D3_DRDY_SHARP_POL (1 << 26)
#define DI_D3_CLK_POL (1 << 25)
#define DI_D3_DATA_POL (1 << 24)
/* DI_DISP_IF_CONF bits */
#define DI_D3_CLK_IDLE (1 << 26)
#define DI_D3_CLK_SEL (1 << 25)
#define DI_D3_DATAMSK (1 << 24)
#define IOMUX_PADNUM_MASK 0x1ff
#define IOMUX_GPIONUM_SHIFT 9
#define IOMUX_GPIONUM_MASK (0xff << IOMUX_GPIONUM_SHIFT)
#define IOMUX_PIN(gpionum, padnum) ((padnum) & IOMUX_PADNUM_MASK)
#define IOMUX_MODE_L(pin, mode) IOMUX_MODE(((pin) + 0xc) ^ 3, mode)
struct chan_param_mem_planar {
/* Word 0 */
u32 xv:10;
u32 yv:10;
u32 xb:12;
u32 yb:12;
u32 res1:2;
u32 nsb:1;
u32 lnpb:6;
u32 ubo_l:11;
u32 ubo_h:15;
u32 vbo_l:17;
u32 vbo_h:9;
u32 res2:3;
u32 fw:12;
u32 fh_l:8;
u32 fh_h:4;
u32 res3:28;
/* Word 1 */
u32 eba0;
u32 eba1;
u32 bpp:3;
u32 sl:14;
u32 pfs:3;
u32 bam:3;
u32 res4:2;
u32 npb:6;
u32 res5:1;
u32 sat:2;
u32 res6:30;
} __attribute__ ((packed));
struct chan_param_mem_interleaved {
/* Word 0 */
u32 xv:10;
u32 yv:10;
u32 xb:12;
u32 yb:12;
u32 sce:1;
u32 res1:1;
u32 nsb:1;
u32 lnpb:6;
u32 sx:10;
u32 sy_l:1;
u32 sy_h:9;
u32 ns:10;
u32 sm:10;
u32 sdx_l:3;
u32 sdx_h:2;
u32 sdy:5;
u32 sdrx:1;
u32 sdry:1;
u32 sdr1:1;
u32 res2:2;
u32 fw:12;
u32 fh_l:8;
u32 fh_h:4;
u32 res3:28;
/* Word 1 */
u32 eba0;
u32 eba1;
u32 bpp:3;
u32 sl:14;
u32 pfs:3;
u32 bam:3;
u32 res4:2;
u32 npb:6;
u32 res5:1;
u32 sat:2;
u32 scc:1;
u32 ofs0:5;
u32 ofs1:5;
u32 ofs2:5;
u32 ofs3:5;
u32 wid0:3;
u32 wid1:3;
u32 wid2:3;
u32 wid3:3;
u32 dec_sel:1;
u32 res6:28;
} __attribute__ ((packed));
union chan_param_mem {
struct chan_param_mem_planar pp;
struct chan_param_mem_interleaved ip;
};
/* graphics setup */
static GraphicDevice panel;
static struct ctfb_res_modes *mode;
static struct ctfb_res_modes var_mode;
/*
* sdc_init_panel() - initialize a synchronous LCD panel.
* @width: width of panel in pixels.
* @height: height of panel in pixels.
* @di_setup: pixel format of the frame buffer
* @di_panel: either SHARP or normal TFT
* @return: 0 on success or negative error code on failure.
*/
static int sdc_init_panel(u16 width, u16 height,
enum pixel_fmt di_setup, enum ipu_panel di_panel)
{
u32 reg, div;
uint32_t old_conf;
int clock;
debug("%s(width=%d, height=%d)\n", __func__, width, height);
/* Init clocking, the IPU receives its clock from the hsp divder */
clock = mxc_get_clock(MXC_IPU_CLK);
if (clock < 0)
return -EACCES;
/* Init panel size and blanking periods */
reg = width + mode->left_margin + mode->right_margin - 1;
if (reg > 1023) {
printf("mx3fb: Display width too large, coerced to 1023!");
reg = 1023;
}
reg = ((mode->hsync_len - 1) << 26) | (reg << 16);
writel(reg, SDC_HOR_CONF);
reg = height + mode->upper_margin + mode->lower_margin - 1;
if (reg > 1023) {
printf("mx3fb: Display height too large, coerced to 1023!");
reg = 1023;
}
reg = ((mode->vsync_len - 1) << 26) | SDC_V_SYNC_WIDTH_L | (reg << 16);
writel(reg, SDC_VER_CONF);
switch (di_panel) {
case IPU_PANEL_SHARP_TFT:
writel(0x00FD0102L, SDC_SHARP_CONF_1);
writel(0x00F500F4L, SDC_SHARP_CONF_2);
writel(SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF);
/* TODO: probably IF_CONF must be adapted (see below)! */
break;
case IPU_PANEL_TFT:
writel(SDC_COM_TFT_COLOR, SDC_COM_CONF);
break;
default:
return -EINVAL;
}
/*
* Calculate divider: The fractional part is 4 bits so simply
* multiple by 2^4 to get it.
*
* Opposed to the kernel driver mode->pixclock is the time of one
* pixel in pico seconds, so:
* pixel_clk = 1e12 / mode->pixclock
* div = ipu_clk * 16 / pixel_clk
* leads to:
* div = ipu_clk * 16 / (1e12 / mode->pixclock)
* or:
* div = ipu_clk * 16 * mode->pixclock / 1e12
*
* To avoid integer overflows this is split into 2 shifts and
* one divide with sufficient accuracy:
* 16*1024*128*476837 = 0.9999996682e12
*/
div = ((clock/1024) * (mode->pixclock/128)) / 476837;
debug("hsp_clk is %d, div=%d\n", clock, div);
/* coerce to not less than 4.0, not more than 255.9375 */
if (div < 0x40)
div = 0x40;
else if (div > 0xFFF)
div = 0xFFF;
/* DISP3_IF_CLK_DOWN_WR is half the divider value and 2 less
* fraction bits. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR
* based on timing debug DISP3_IF_CLK_UP_WR is 0
*/
writel((((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF);
/* DI settings for display 3: clock idle (bit 26) during vsync */
old_conf = readl(DI_DISP_IF_CONF) & 0x78FFFFFF;
writel(old_conf | IF_CONF, DI_DISP_IF_CONF);
/* only set display 3 polarity bits */
old_conf = readl(DI_DISP_SIG_POL) & 0xE0FFFFFF;
writel(old_conf | mode->sync, DI_DISP_SIG_POL);
writel(fmt_cfg[di_setup].b0, DI_DISP3_B0_MAP);
writel(fmt_cfg[di_setup].b1, DI_DISP3_B1_MAP);
writel(fmt_cfg[di_setup].b2, DI_DISP3_B2_MAP);
writel(readl(DI_DISP_ACC_CC) |
((fmt_cfg[di_setup].acc - 1) << 12), DI_DISP_ACC_CC);
debug("DI_DISP_IF_CONF = 0x%08X\n", readl(DI_DISP_IF_CONF));
debug("DI_DISP_SIG_POL = 0x%08X\n", readl(DI_DISP_SIG_POL));
debug("DI_DISP3_TIME_CONF = 0x%08X\n", readl(DI_DISP3_TIME_CONF));
debug("SDC_HOR_CONF = 0x%08X\n", readl(SDC_HOR_CONF));
debug("SDC_VER_CONF = 0x%08X\n", readl(SDC_VER_CONF));
return 0;
}
static void ipu_ch_param_set_size(union chan_param_mem *params,
uint pixelfmt, uint16_t width,
uint16_t height, uint16_t stride)
{
debug("%s(pixelfmt=%d, width=%d, height=%d, stride=%d)\n",
__func__, pixelfmt, width, height, stride);
params->pp.fw = width - 1;
params->pp.fh_l = height - 1;
params->pp.fh_h = (height - 1) >> 8;
params->pp.sl = stride - 1;
/* See above, for further formats see the Linux driver */
switch (pixelfmt) {
case GDF_16BIT_565RGB:
params->ip.bpp = 2;
params->ip.pfs = 4;
params->ip.npb = 7;
params->ip.sat = 2; /* SAT = 32-bit access */
params->ip.ofs0 = 0; /* Red bit offset */
params->ip.ofs1 = 5; /* Green bit offset */
params->ip.ofs2 = 11; /* Blue bit offset */
params->ip.ofs3 = 16; /* Alpha bit offset */
params->ip.wid0 = 4; /* Red bit width - 1 */
params->ip.wid1 = 5; /* Green bit width - 1 */
params->ip.wid2 = 4; /* Blue bit width - 1 */
break;
case GDF_32BIT_X888RGB:
params->ip.bpp = 1; /* 24 BPP & RGB PFS */
params->ip.pfs = 4;
params->ip.npb = 7;
params->ip.sat = 2; /* SAT = 32-bit access */
params->ip.ofs0 = 16; /* Red bit offset */
params->ip.ofs1 = 8; /* Green bit offset */
params->ip.ofs2 = 0; /* Blue bit offset */
params->ip.ofs3 = 24; /* Alpha bit offset */
params->ip.wid0 = 7; /* Red bit width - 1 */
params->ip.wid1 = 7; /* Green bit width - 1 */
params->ip.wid2 = 7; /* Blue bit width - 1 */
break;
default:
printf("mx3fb: Pixel format not supported!\n");
break;
}
params->pp.nsb = 1;
}
static void ipu_ch_param_set_buffer(union chan_param_mem *params,
void *buf0, void *buf1)
{
params->pp.eba0 = (u32)buf0;
params->pp.eba1 = (u32)buf1;
}
static void ipu_write_param_mem(uint32_t addr, uint32_t *data,
uint32_t num_words)
{
for (; num_words > 0; num_words--) {
writel(addr, IPU_IMA_ADDR);
writel(*data++, IPU_IMA_DATA);
addr++;
if ((addr & 0x7) == 5) {
addr &= ~0x7; /* set to word 0 */
addr += 8; /* increment to next row */
}
}
}
static uint32_t dma_param_addr(enum ipu_channel channel)
{
/* Channel Parameter Memory */
return 0x10000 | (channel << 4);
}
static void ipu_init_channel_buffer(enum ipu_channel channel, void *fbmem)
{
union chan_param_mem params = {};
uint32_t reg;
uint32_t stride_bytes;
stride_bytes = (panel.plnSizeX * panel.gdfBytesPP + 3) & ~3;
debug("%s(channel=%d, fbmem=%p)\n", __func__, channel, fbmem);
/* Build parameter memory data for DMA channel */
ipu_ch_param_set_size(&params, panel.gdfIndex,
panel.plnSizeX, panel.plnSizeY, stride_bytes);
ipu_ch_param_set_buffer(&params, fbmem, NULL);
params.pp.bam = 0;
/* Some channels (rotation) have restriction on burst length */
switch (channel) {
case IDMAC_SDC_0:
/* In original code only IPU_PIX_FMT_RGB565 was setting burst */
params.pp.npb = 16 - 1;
break;
default:
break;
}
ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)&params, 10);
/* Disable double-buffering */
reg = readl(IPU_CHA_DB_MODE_SEL);
reg &= ~(1UL << channel);
writel(reg, IPU_CHA_DB_MODE_SEL);
}
static void ipu_channel_set_priority(enum ipu_channel channel,
int prio)
{
u32 reg = readl(IDMAC_CHA_PRI);
if (prio)
reg |= 1UL << channel;
else
reg &= ~(1UL << channel);
writel(reg, IDMAC_CHA_PRI);
}
/*
* ipu_enable_channel() - enable an IPU channel.
* @channel: channel ID.
* @return: 0 on success or negative error code on failure.
*/
static int ipu_enable_channel(enum ipu_channel channel)
{
uint32_t reg;
/* Reset to buffer 0 */
writel(1UL << channel, IPU_CHA_CUR_BUF);
switch (channel) {
case IDMAC_SDC_0:
ipu_channel_set_priority(channel, 1);
break;
default:
break;
}
reg = readl(IDMAC_CHA_EN);
writel(reg | (1UL << channel), IDMAC_CHA_EN);
return 0;
}
static int ipu_update_channel_buffer(enum ipu_channel channel, void *buf)
{
uint32_t reg;
reg = readl(IPU_CHA_BUF0_RDY);
if (reg & (1UL << channel))
return -EACCES;
/* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */
writel(dma_param_addr(channel) + 0x0008UL, IPU_IMA_ADDR);
writel((u32)buf, IPU_IMA_DATA);
return 0;
}
static int idmac_tx_submit(enum ipu_channel channel, void *buf)
{
int ret;
ipu_init_channel_buffer(channel, buf);
/* ipu_idmac.c::ipu_submit_channel_buffers() */
ret = ipu_update_channel_buffer(channel, buf);
if (ret < 0)
return ret;
/* ipu_idmac.c::ipu_select_buffer() */
/* Mark buffer 0 as ready. */
writel(1UL << channel, IPU_CHA_BUF0_RDY);
ret = ipu_enable_channel(channel);
return ret;
}
static void sdc_enable_channel(void *fbmem)
{
int ret;
u32 reg;
ret = idmac_tx_submit(IDMAC_SDC_0, fbmem);
/* mx3fb.c::sdc_fb_init() */
if (ret >= 0) {
reg = readl(SDC_COM_CONF);
writel(reg | SDC_COM_BG_EN, SDC_COM_CONF);
}
/*
* Attention! Without this msleep the channel keeps generating
* interrupts. Next sdc_set_brightness() is going to be called
* from mx3fb_blank().
*/
udelay(2000);
}
/*
* mx3fb_set_par() - set framebuffer parameters and change the operating mode.
* @return: 0 on success or negative error code on failure.
* TODO: currently only 666 and TFT as DI setup supported
*/
static int mx3fb_set_par(void)
{
int ret;
ret = sdc_init_panel(panel.plnSizeX, panel.plnSizeY,
IPU_PIX_FMT_RGB666, IPU_PANEL_TFT);
if (ret < 0)
return ret;
writel((mode->left_margin << 16) | mode->upper_margin, SDC_BG_POS);
return 0;
}
static void ll_disp3_enable(void *base)
{
u32 reg;
debug("%s(base=0x%x)\n", __func__, (u32) base);
/* pcm037.c::mxc_board_init() */
/* Display Interface #3 */
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD0, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD1, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD2, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD3, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD4, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD5, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD6, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD7, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD8, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD9, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD10, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD11, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD12, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD13, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD14, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD15, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD16, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD17, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_VSYNC3, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_HSYNC, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_FPSHIFT, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_DRDY0, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_REV, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_CONTRAST, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_SPL, MUX_CTL_FUNC));
mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_CLS, MUX_CTL_FUNC));
/* ipu_idmac.c::ipu_probe() */
/* Start the clock */
__REG(CCM_CGR1) = __REG(CCM_CGR1) | (3 << 22);
/* ipu_idmac.c::ipu_idmac_init() */
/* Service request counter to maximum - shouldn't be needed */
writel(0x00000070, IDMAC_CONF);
/* ipu_idmac.c::ipu_init_channel() */
/* Enable IPU sub modules */
reg = readl(IPU_CONF) | IPU_CONF_SDC_EN | IPU_CONF_DI_EN;
writel(reg, IPU_CONF);
/* mx3fb.c::init_fb_chan() */
/* set Display Interface clock period */
writel(0x00100010L, DI_HSP_CLK_PER);
/* Might need to trigger HSP clock change - see 44.3.3.8.5 */
/* mx3fb.c::sdc_set_brightness() */
/* This might be board-specific */
writel(0x03000000UL | 255 << 16, SDC_PWM_CTRL);
/* mx3fb.c::sdc_set_global_alpha() */
/* Use global - not per-pixel - Alpha-blending */
reg = readl(SDC_GW_CTRL) & 0x00FFFFFFL;
writel(reg | ((uint32_t) 0xff << 24), SDC_GW_CTRL);
reg = readl(SDC_COM_CONF);
writel(reg | SDC_COM_GLB_A, SDC_COM_CONF);
/* mx3fb.c::sdc_set_color_key() */
/* Disable colour-keying for background */
reg = readl(SDC_COM_CONF) &
~(SDC_COM_GWSEL | SDC_COM_KEY_COLOR_G);
writel(reg, SDC_COM_CONF);
mx3fb_set_par();
sdc_enable_channel(base);
/*
* Linux driver calls sdc_set_brightness() here again,
* once is enough for us
*/
debug("%s() done\n", __func__);
}
/* ------------------------ public part ------------------- */
ulong calc_fbsize(void)
{
return panel.plnSizeX * panel.plnSizeY * panel.gdfBytesPP;
}
/*
* The current implementation is only tested for GDF_16BIT_565RGB!
* It was switched from the original CONFIG_LCD setup to CONFIG_VIDEO,
* because the lcd code seemed loaded with color table stuff, that
* does not relate to most modern TFTs. cfb_console.c looks more
* straight forward.
* This is the environment setting for the original setup
* "unknown=video=ctfb:x:240,y:320,depth:16,mode:0,pclk:185925,le:9,ri:17,
* up:7,lo:10,hs:1,vs:1,sync:100663296,vmode:0"
* "videomode=unknown"
*
* Settings for VBEST VGG322403 display:
* "videomode=video=ctfb:x:320,y:240,depth:16,mode:0,pclk:156000,
* "le:20,ri:68,up:7,lo:29,hs:30,vs:3,sync:100663296,vmode:0"
*
* Settings for COM57H5M10XRC display:
* "videomode=video=ctfb:x:640,y:480,depth:16,mode:0,pclk:40000,
* "le:120,ri:40,up:35,lo:10,hs:30,vs:3,sync:100663296,vmode:0"
*/
void *video_hw_init(void)
{
char *penv;
u32 memsize;
unsigned long t1, hsynch, vsynch;
int bits_per_pixel, i, tmp, videomode;
tmp = 0;
puts("Video: ");
videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
/* get video mode via environment */
penv = env_get("videomode");
if (penv) {
/* decide if it is a string */
if (penv[0] <= '9') {
videomode = (int) simple_strtoul(penv, NULL, 16);
tmp = 1;
}
} else {
tmp = 1;
}
if (tmp) {
/* parameter are vesa modes */
/* search params */
for (i = 0; i < VESA_MODES_COUNT; i++) {
if (vesa_modes[i].vesanr == videomode)
break;
}
if (i == VESA_MODES_COUNT) {
printf("No VESA Mode found, switching to mode 0x%x ",
CONFIG_SYS_DEFAULT_VIDEO_MODE);
i = 0;
}
mode = (struct ctfb_res_modes *)
&res_mode_init[vesa_modes[i].resindex];
bits_per_pixel = vesa_modes[i].bits_per_pixel;
} else {
mode = (struct ctfb_res_modes *) &var_mode;
bits_per_pixel = video_get_params(mode, penv);
}
/* calculate hsynch and vsynch freq (info only) */
t1 = (mode->left_margin + mode->xres +
mode->right_margin + mode->hsync_len) / 8;
t1 *= 8;
t1 *= mode->pixclock;
t1 /= 1000;
hsynch = 1000000000L / t1;
t1 *= (mode->upper_margin + mode->yres +
mode->lower_margin + mode->vsync_len);
t1 /= 1000;
vsynch = 1000000000L / t1;
/* fill in Graphic device struct */
sprintf(panel.modeIdent, "%dx%dx%d %ldkHz %ldHz",
mode->xres, mode->yres,
bits_per_pixel, (hsynch / 1000), (vsynch / 1000));
printf("%s\n", panel.modeIdent);
panel.winSizeX = mode->xres;
panel.winSizeY = mode->yres;
panel.plnSizeX = mode->xres;
panel.plnSizeY = mode->yres;
switch (bits_per_pixel) {
case 24:
panel.gdfBytesPP = 4;
panel.gdfIndex = GDF_32BIT_X888RGB;
break;
case 16:
panel.gdfBytesPP = 2;
panel.gdfIndex = GDF_16BIT_565RGB;
break;
default:
panel.gdfBytesPP = 1;
panel.gdfIndex = GDF__8BIT_INDEX;
break;
}
/* set up Hardware */
memsize = calc_fbsize();
debug("%s() allocating %d bytes\n", __func__, memsize);
/* fill in missing Graphic device struct */
panel.frameAdrs = (u32) malloc(memsize);
if (panel.frameAdrs == 0) {
printf("%s() malloc(%d) failed\n", __func__, memsize);
return 0;
}
panel.memSize = memsize;
ll_disp3_enable((void *) panel.frameAdrs);
memset((void *) panel.frameAdrs, 0, memsize);
debug("%s() done, framebuffer at 0x%x, size=%d cleared\n",
__func__, panel.frameAdrs, memsize);
return (void *) &panel;
}
@@ -0,0 +1,434 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX23/i.MX28 LCDIF driver
*
* Copyright (C) 2011-2013 Marek Vasut <marex@denx.de>
*/
#include <common.h>
#include <dm.h>
#include <env.h>
#include <linux/errno.h>
#include <malloc.h>
#include <video.h>
#include <video_fb.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-imx/dma.h>
#include <asm/io.h>
#include "videomodes.h"
#define PS2KHZ(ps) (1000000000UL / (ps))
#define HZ2PS(hz) (1000000000UL / ((hz) / 1000))
#define BITS_PP 18
#define BYTES_PP 4
struct mxs_dma_desc desc;
/**
* mxsfb_system_setup() - Fine-tune LCDIF configuration
*
* This function is used to adjust the LCDIF configuration. This is usually
* needed when driving the controller in System-Mode to operate an 8080 or
* 6800 connected SmartLCD.
*/
__weak void mxsfb_system_setup(void)
{
}
/*
* ARIES M28EVK:
* setenv videomode
* video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066,
* le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0
*
* Freescale mx23evk/mx28evk with a Seiko 4.3'' WVGA panel:
* setenv videomode
* video=ctfb:x:800,y:480,depth:24,mode:0,pclk:29851,
* le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0
*/
static void mxs_lcd_init(u32 fb_addr, struct ctfb_res_modes *mode, int bpp)
{
struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
uint32_t word_len = 0, bus_width = 0;
uint8_t valid_data = 0;
/* Kick in the LCDIF clock */
mxs_set_lcdclk(MXS_LCDIF_BASE, PS2KHZ(mode->pixclock));
/* Restart the LCDIF block */
mxs_reset_block(&regs->hw_lcdif_ctrl_reg);
switch (bpp) {
case 24:
word_len = LCDIF_CTRL_WORD_LENGTH_24BIT;
bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT;
valid_data = 0x7;
break;
case 18:
word_len = LCDIF_CTRL_WORD_LENGTH_24BIT;
bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT;
valid_data = 0x7;
break;
case 16:
word_len = LCDIF_CTRL_WORD_LENGTH_16BIT;
bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT;
valid_data = 0xf;
break;
case 8:
word_len = LCDIF_CTRL_WORD_LENGTH_8BIT;
bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT;
valid_data = 0xf;
break;
}
writel(bus_width | word_len | LCDIF_CTRL_DOTCLK_MODE |
LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER,
&regs->hw_lcdif_ctrl);
writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET,
&regs->hw_lcdif_ctrl1);
mxsfb_system_setup();
writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres,
&regs->hw_lcdif_transfer_count);
writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL |
LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |
LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
mode->vsync_len, &regs->hw_lcdif_vdctrl0);
writel(mode->upper_margin + mode->lower_margin +
mode->vsync_len + mode->yres,
&regs->hw_lcdif_vdctrl1);
writel((mode->hsync_len << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) |
(mode->left_margin + mode->right_margin +
mode->hsync_len + mode->xres),
&regs->hw_lcdif_vdctrl2);
writel(((mode->left_margin + mode->hsync_len) <<
LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) |
(mode->upper_margin + mode->vsync_len),
&regs->hw_lcdif_vdctrl3);
writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres,
&regs->hw_lcdif_vdctrl4);
writel(fb_addr, &regs->hw_lcdif_cur_buf);
writel(fb_addr, &regs->hw_lcdif_next_buf);
/* Flush FIFO first */
writel(LCDIF_CTRL1_FIFO_CLEAR, &regs->hw_lcdif_ctrl1_set);
#ifndef CONFIG_VIDEO_MXS_MODE_SYSTEM
/* Sync signals ON */
setbits_le32(&regs->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON);
#endif
/* FIFO cleared */
writel(LCDIF_CTRL1_FIFO_CLEAR, &regs->hw_lcdif_ctrl1_clr);
/* RUN! */
writel(LCDIF_CTRL_RUN, &regs->hw_lcdif_ctrl_set);
}
static int mxs_probe_common(struct ctfb_res_modes *mode, int bpp, u32 fb)
{
/* Start framebuffer */
mxs_lcd_init(fb, mode, bpp);
#ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM
/*
* If the LCD runs in system mode, the LCD refresh has to be triggered
* manually by setting the RUN bit in HW_LCDIF_CTRL register. To avoid
* having to set this bit manually after every single change in the
* framebuffer memory, we set up specially crafted circular DMA, which
* sets the RUN bit, then waits until it gets cleared and repeats this
* infinitelly. This way, we get smooth continuous updates of the LCD.
*/
struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
memset(&desc, 0, sizeof(struct mxs_dma_desc));
desc.address = (dma_addr_t)&desc;
desc.cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
MXS_DMA_DESC_WAIT4END |
(1 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
desc.cmd.pio_words[0] = readl(&regs->hw_lcdif_ctrl) | LCDIF_CTRL_RUN;
desc.cmd.next = (uint32_t)&desc.cmd;
/* Execute the DMA chain. */
mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc);
#endif
return 0;
}
static int mxs_remove_common(u32 fb)
{
struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
int timeout = 1000000;
if (!fb)
return -EINVAL;
writel(fb, &regs->hw_lcdif_cur_buf_reg);
writel(fb, &regs->hw_lcdif_next_buf_reg);
writel(LCDIF_CTRL1_VSYNC_EDGE_IRQ, &regs->hw_lcdif_ctrl1_clr);
while (--timeout) {
if (readl(&regs->hw_lcdif_ctrl1_reg) &
LCDIF_CTRL1_VSYNC_EDGE_IRQ)
break;
udelay(1);
}
mxs_reset_block((struct mxs_register_32 *)&regs->hw_lcdif_ctrl_reg);
return 0;
}
#ifndef CONFIG_DM_VIDEO
static GraphicDevice panel;
void lcdif_power_down(void)
{
mxs_remove_common(panel.frameAdrs);
}
void *video_hw_init(void)
{
int bpp = -1;
int ret = 0;
char *penv;
void *fb = NULL;
struct ctfb_res_modes mode;
puts("Video: ");
/* Suck display configuration from "videomode" variable */
penv = env_get("videomode");
if (!penv) {
puts("MXSFB: 'videomode' variable not set!\n");
return NULL;
}
bpp = video_get_params(&mode, penv);
/* fill in Graphic device struct */
sprintf(panel.modeIdent, "%dx%dx%d", mode.xres, mode.yres, bpp);
panel.winSizeX = mode.xres;
panel.winSizeY = mode.yres;
panel.plnSizeX = mode.xres;
panel.plnSizeY = mode.yres;
switch (bpp) {
case 24:
case 18:
panel.gdfBytesPP = 4;
panel.gdfIndex = GDF_32BIT_X888RGB;
break;
case 16:
panel.gdfBytesPP = 2;
panel.gdfIndex = GDF_16BIT_565RGB;
break;
case 8:
panel.gdfBytesPP = 1;
panel.gdfIndex = GDF__8BIT_INDEX;
break;
default:
printf("MXSFB: Invalid BPP specified! (bpp = %i)\n", bpp);
return NULL;
}
panel.memSize = mode.xres * mode.yres * panel.gdfBytesPP;
/* Allocate framebuffer */
fb = memalign(ARCH_DMA_MINALIGN,
roundup(panel.memSize, ARCH_DMA_MINALIGN));
if (!fb) {
printf("MXSFB: Error allocating framebuffer!\n");
return NULL;
}
/* Wipe framebuffer */
memset(fb, 0, panel.memSize);
panel.frameAdrs = (u32)fb;
printf("%s\n", panel.modeIdent);
ret = mxs_probe_common(&mode, bpp, (u32)fb);
if (ret)
goto dealloc_fb;
return (void *)&panel;
dealloc_fb:
free(fb);
return NULL;
}
#else /* ifndef CONFIG_DM_VIDEO */
static int mxs_of_get_timings(struct udevice *dev,
struct display_timing *timings,
u32 *bpp)
{
int ret = 0;
u32 display_phandle;
ofnode display_node;
ret = ofnode_read_u32(dev_ofnode(dev), "display", &display_phandle);
if (ret) {
dev_err(dev, "required display property isn't provided\n");
return -EINVAL;
}
display_node = ofnode_get_by_phandle(display_phandle);
if (!ofnode_valid(display_node)) {
dev_err(dev, "failed to find display subnode\n");
return -EINVAL;
}
ret = ofnode_read_u32(display_node, "bits-per-pixel", bpp);
if (ret) {
dev_err(dev,
"required bits-per-pixel property isn't provided\n");
return -EINVAL;
}
ret = ofnode_decode_display_timing(display_node, 0, timings);
if (ret) {
dev_err(dev, "failed to get any display timings\n");
return -EINVAL;
}
return ret;
}
static int mxs_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct ctfb_res_modes mode;
struct display_timing timings;
u32 bpp = 0;
u32 fb_start, fb_end;
int ret;
debug("%s() plat: base 0x%lx, size 0x%x\n",
__func__, plat->base, plat->size);
ret = mxs_of_get_timings(dev, &timings, &bpp);
if (ret)
return ret;
mode.xres = timings.hactive.typ;
mode.yres = timings.vactive.typ;
mode.left_margin = timings.hback_porch.typ;
mode.right_margin = timings.hfront_porch.typ;
mode.upper_margin = timings.vback_porch.typ;
mode.lower_margin = timings.vfront_porch.typ;
mode.hsync_len = timings.hsync_len.typ;
mode.vsync_len = timings.vsync_len.typ;
mode.pixclock = HZ2PS(timings.pixelclock.typ);
ret = mxs_probe_common(&mode, bpp, plat->base);
if (ret)
return ret;
switch (bpp) {
case 32:
case 24:
case 18:
uc_priv->bpix = VIDEO_BPP32;
break;
case 16:
uc_priv->bpix = VIDEO_BPP16;
break;
case 8:
uc_priv->bpix = VIDEO_BPP8;
break;
default:
dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp);
return -EINVAL;
}
uc_priv->xsize = mode.xres;
uc_priv->ysize = mode.yres;
/* Enable dcache for the frame buffer */
fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
fb_end = plat->base + plat->size;
fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
DCACHE_WRITEBACK);
video_set_flush_dcache(dev, true);
gd->fb_base = plat->base;
return ret;
}
static int mxs_video_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct display_timing timings;
u32 bpp = 0;
u32 bytes_pp = 0;
int ret;
ret = mxs_of_get_timings(dev, &timings, &bpp);
if (ret)
return ret;
switch (bpp) {
case 32:
case 24:
case 18:
bytes_pp = 4;
break;
case 16:
bytes_pp = 2;
break;
case 8:
bytes_pp = 1;
break;
default:
dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp);
return -EINVAL;
}
plat->size = timings.hactive.typ * timings.vactive.typ * bytes_pp;
return 0;
}
static int mxs_video_remove(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
mxs_remove_common(plat->base);
return 0;
}
static const struct udevice_id mxs_video_ids[] = {
{ .compatible = "fsl,imx23-lcdif" },
{ .compatible = "fsl,imx28-lcdif" },
{ .compatible = "fsl,imx7ulp-lcdif" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(mxs_video) = {
.name = "mxs_video",
.id = UCLASS_VIDEO,
.of_match = mxs_video_ids,
.bind = mxs_video_bind,
.probe = mxs_video_probe,
.remove = mxs_video_remove,
.flags = DM_FLAG_PRE_RELOC,
};
#endif /* ifndef CONFIG_DM_VIDEO */
@@ -0,0 +1,167 @@
/*
* (C) Copyright 2010
* Texas Instruments, <www.ti.com>
* Syed Mohammed Khasim <khasim@ti.com>
*
* Referred to Linux Kernel DSS driver files for OMAP3 by
* Tomi Valkeinen from drivers/video/omap2/dss/
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation's version 2 and any
* later version the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/dss.h>
#include <video_fb.h>
/* Configure VENC for a given Mode (NTSC / PAL) */
void omap3_dss_venc_config(const struct venc_regs *venc_cfg,
u32 height, u32 width)
{
struct venc_regs *venc = (struct venc_regs *) OMAP3_VENC_BASE;
struct dss_regs *dss = (struct dss_regs *) OMAP3_DSS_BASE;
struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE;
writel(venc_cfg->status, &venc->status);
writel(venc_cfg->f_control, &venc->f_control);
writel(venc_cfg->vidout_ctrl, &venc->vidout_ctrl);
writel(venc_cfg->sync_ctrl, &venc->sync_ctrl);
writel(venc_cfg->llen, &venc->llen);
writel(venc_cfg->flens, &venc->flens);
writel(venc_cfg->hfltr_ctrl, &venc->hfltr_ctrl);
writel(venc_cfg->cc_carr_wss_carr, &venc->cc_carr_wss_carr);
writel(venc_cfg->c_phase, &venc->c_phase);
writel(venc_cfg->gain_u, &venc->gain_u);
writel(venc_cfg->gain_v, &venc->gain_v);
writel(venc_cfg->gain_y, &venc->gain_y);
writel(venc_cfg->black_level, &venc->black_level);
writel(venc_cfg->blank_level, &venc->blank_level);
writel(venc_cfg->x_color, &venc->x_color);
writel(venc_cfg->m_control, &venc->m_control);
writel(venc_cfg->bstamp_wss_data, &venc->bstamp_wss_data);
writel(venc_cfg->s_carr, &venc->s_carr);
writel(venc_cfg->line21, &venc->line21);
writel(venc_cfg->ln_sel, &venc->ln_sel);
writel(venc_cfg->l21__wc_ctl, &venc->l21__wc_ctl);
writel(venc_cfg->htrigger_vtrigger, &venc->htrigger_vtrigger);
writel(venc_cfg->savid__eavid, &venc->savid__eavid);
writel(venc_cfg->flen__fal, &venc->flen__fal);
writel(venc_cfg->lal__phase_reset, &venc->lal__phase_reset);
writel(venc_cfg->hs_int_start_stop_x, &venc->hs_int_start_stop_x);
writel(venc_cfg->hs_ext_start_stop_x, &venc->hs_ext_start_stop_x);
writel(venc_cfg->vs_int_start_x, &venc->vs_int_start_x);
writel(venc_cfg->vs_int_stop_x__vs_int_start_y,
&venc->vs_int_stop_x__vs_int_start_y);
writel(venc_cfg->vs_int_stop_y__vs_ext_start_x,
&venc->vs_int_stop_y__vs_ext_start_x);
writel(venc_cfg->vs_ext_stop_x__vs_ext_start_y,
&venc->vs_ext_stop_x__vs_ext_start_y);
writel(venc_cfg->vs_ext_stop_y, &venc->vs_ext_stop_y);
writel(venc_cfg->avid_start_stop_x, &venc->avid_start_stop_x);
writel(venc_cfg->avid_start_stop_y, &venc->avid_start_stop_y);
writel(venc_cfg->fid_int_start_x__fid_int_start_y,
&venc->fid_int_start_x__fid_int_start_y);
writel(venc_cfg->fid_int_offset_y__fid_ext_start_x,
&venc->fid_int_offset_y__fid_ext_start_x);
writel(venc_cfg->fid_ext_start_y__fid_ext_offset_y,
&venc->fid_ext_start_y__fid_ext_offset_y);
writel(venc_cfg->tvdetgp_int_start_stop_x,
&venc->tvdetgp_int_start_stop_x);
writel(venc_cfg->tvdetgp_int_start_stop_y,
&venc->tvdetgp_int_start_stop_y);
writel(venc_cfg->gen_ctrl, &venc->gen_ctrl);
writel(venc_cfg->output_control, &venc->output_control);
writel(venc_cfg->dac_b__dac_c, &venc->dac_b__dac_c);
/* Configure DSS for VENC Settings */
writel(VENC_CLK_ENABLE | DAC_DEMEN | DAC_POWERDN | VENC_OUT_SEL,
&dss->control);
/* Configure height and width for Digital out */
writel(height << DIG_LPP_SHIFT | width, &dispc->size_dig);
}
/* Configure Panel Specific Parameters */
void omap3_dss_panel_config(const struct panel_config *panel_cfg)
{
struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE;
struct dss_regs *dss = (struct dss_regs *) OMAP3_DSS_BASE;
writel(DSS_SOFTRESET, &dss->sysconfig);
while (!(readl(&dss->sysstatus) & DSS_RESETDONE))
;
writel(panel_cfg->timing_h, &dispc->timing_h);
writel(panel_cfg->timing_v, &dispc->timing_v);
writel(panel_cfg->pol_freq, &dispc->pol_freq);
writel(panel_cfg->divisor, &dispc->divisor);
writel(panel_cfg->lcd_size, &dispc->size_lcd);
writel(panel_cfg->load_mode << LOADMODE_SHIFT, &dispc->config);
writel(panel_cfg->panel_type << TFTSTN_SHIFT |
panel_cfg->data_lines << DATALINES_SHIFT, &dispc->control);
writel(panel_cfg->panel_color, &dispc->default_color0);
writel((u32) panel_cfg->frame_buffer, &dispc->gfx_ba0);
if (!panel_cfg->frame_buffer)
return;
writel(panel_cfg->gfx_format | GFX_ENABLE, &dispc->gfx_attributes);
writel(1, &dispc->gfx_row_inc);
writel(1, &dispc->gfx_pixel_inc);
writel(panel_cfg->lcd_size, &dispc->gfx_size);
}
/* Enable LCD and DIGITAL OUT in DSS */
void omap3_dss_enable(void)
{
struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE;
u32 l;
l = readl(&dispc->control);
l |= LCD_ENABLE | GO_LCD | DIG_ENABLE | GO_DIG | GP_OUT0 | GP_OUT1;
writel(l, &dispc->control);
}
#ifdef CONFIG_CFB_CONSOLE
int __board_video_init(void)
{
return -1;
}
int board_video_init(void)
__attribute__((weak, alias("__board_video_init")));
void *video_hw_init(void)
{
static GraphicDevice dssfb;
GraphicDevice *pGD = &dssfb;
struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE;
if (board_video_init() || !readl(&dispc->gfx_ba0))
return NULL;
pGD->winSizeX = (readl(&dispc->size_lcd) & 0x7FF) + 1;
pGD->winSizeY = ((readl(&dispc->size_lcd) >> 16) & 0x7FF) + 1;
pGD->gdfBytesPP = 4;
pGD->gdfIndex = GDF_32BIT_X888RGB;
pGD->frameAdrs = readl(&dispc->gfx_ba0);
return pGD;
}
#endif
@@ -0,0 +1,379 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 STMicroelectronics - All Rights Reserved
* Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
* Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
*
* This otm8009a panel driver is inspired from the Linux Kernel driver
* drivers/gpu/drm/panel/panel-orisetech-otm8009a.c.
*/
#include <common.h>
#include <backlight.h>
#include <dm.h>
#include <mipi_dsi.h>
#include <panel.h>
#include <asm/gpio.h>
#include <power/regulator.h>
#define OTM8009A_BACKLIGHT_DEFAULT 240
#define OTM8009A_BACKLIGHT_MAX 255
/* Manufacturer Command Set */
#define MCS_ADRSFT 0x0000 /* Address Shift Function */
#define MCS_PANSET 0xB3A6 /* Panel Type Setting */
#define MCS_SD_CTRL 0xC0A2 /* Source Driver Timing Setting */
#define MCS_P_DRV_M 0xC0B4 /* Panel Driving Mode */
#define MCS_OSC_ADJ 0xC181 /* Oscillator Adjustment for Idle/Normal mode */
#define MCS_RGB_VID_SET 0xC1A1 /* RGB Video Mode Setting */
#define MCS_SD_PCH_CTRL 0xC480 /* Source Driver Precharge Control */
#define MCS_NO_DOC1 0xC48A /* Command not documented */
#define MCS_PWR_CTRL1 0xC580 /* Power Control Setting 1 */
#define MCS_PWR_CTRL2 0xC590 /* Power Control Setting 2 for Normal Mode */
#define MCS_PWR_CTRL4 0xC5B0 /* Power Control Setting 4 for DC Voltage */
#define MCS_PANCTRLSET1 0xCB80 /* Panel Control Setting 1 */
#define MCS_PANCTRLSET2 0xCB90 /* Panel Control Setting 2 */
#define MCS_PANCTRLSET3 0xCBA0 /* Panel Control Setting 3 */
#define MCS_PANCTRLSET4 0xCBB0 /* Panel Control Setting 4 */
#define MCS_PANCTRLSET5 0xCBC0 /* Panel Control Setting 5 */
#define MCS_PANCTRLSET6 0xCBD0 /* Panel Control Setting 6 */
#define MCS_PANCTRLSET7 0xCBE0 /* Panel Control Setting 7 */
#define MCS_PANCTRLSET8 0xCBF0 /* Panel Control Setting 8 */
#define MCS_PANU2D1 0xCC80 /* Panel U2D Setting 1 */
#define MCS_PANU2D2 0xCC90 /* Panel U2D Setting 2 */
#define MCS_PANU2D3 0xCCA0 /* Panel U2D Setting 3 */
#define MCS_PAND2U1 0xCCB0 /* Panel D2U Setting 1 */
#define MCS_PAND2U2 0xCCC0 /* Panel D2U Setting 2 */
#define MCS_PAND2U3 0xCCD0 /* Panel D2U Setting 3 */
#define MCS_GOAVST 0xCE80 /* GOA VST Setting */
#define MCS_GOACLKA1 0xCEA0 /* GOA CLKA1 Setting */
#define MCS_GOACLKA3 0xCEB0 /* GOA CLKA3 Setting */
#define MCS_GOAECLK 0xCFC0 /* GOA ECLK Setting */
#define MCS_NO_DOC2 0xCFD0 /* Command not documented */
#define MCS_GVDDSET 0xD800 /* GVDD/NGVDD */
#define MCS_VCOMDC 0xD900 /* VCOM Voltage Setting */
#define MCS_GMCT2_2P 0xE100 /* Gamma Correction 2.2+ Setting */
#define MCS_GMCT2_2N 0xE200 /* Gamma Correction 2.2- Setting */
#define MCS_NO_DOC3 0xF5B6 /* Command not documented */
#define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */
#define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */
struct otm8009a_panel_priv {
struct udevice *reg;
struct gpio_desc reset;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
};
static const struct display_timing default_timing = {
.pixelclock.typ = 29700000,
.hactive.typ = 480,
.hfront_porch.typ = 98,
.hback_porch.typ = 98,
.hsync_len.typ = 32,
.vactive.typ = 800,
.vfront_porch.typ = 15,
.vback_porch.typ = 14,
.vsync_len.typ = 10,
};
static void otm8009a_dcs_write_buf(struct udevice *dev, const void *data,
size_t len)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
dev_err(dev, "mipi dsi dcs write buffer failed\n");
}
static void otm8009a_dcs_write_buf_hs(struct udevice *dev, const void *data,
size_t len)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
/* data will be sent in dsi hs mode (ie. no lpm) */
device->mode_flags &= ~MIPI_DSI_MODE_LPM;
if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
dev_err(dev, "mipi dsi dcs write buffer failed\n");
/* restore back the dsi lpm mode */
device->mode_flags |= MIPI_DSI_MODE_LPM;
}
#define dcs_write_seq(dev, seq...) \
({ \
static const u8 d[] = { seq }; \
otm8009a_dcs_write_buf(dev, d, ARRAY_SIZE(d)); \
})
#define dcs_write_seq_hs(dev, seq...) \
({ \
static const u8 d[] = { seq }; \
otm8009a_dcs_write_buf_hs(dev, d, ARRAY_SIZE(d)); \
})
#define dcs_write_cmd_at(dev, cmd, seq...) \
({ \
static const u16 c = cmd; \
struct udevice *device = dev; \
dcs_write_seq(device, MCS_ADRSFT, (c) & 0xFF); \
dcs_write_seq(device, (c) >> 8, seq); \
})
static int otm8009a_init_sequence(struct udevice *dev)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
int ret;
/* Enter CMD2 */
dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
/* Enter Orise Command2 */
dcs_write_cmd_at(dev, MCS_CMD2_ENA2, 0x80, 0x09);
dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL, 0x30);
mdelay(10);
dcs_write_cmd_at(dev, MCS_NO_DOC1, 0x40);
mdelay(10);
dcs_write_cmd_at(dev, MCS_PWR_CTRL4 + 1, 0xA9);
dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 1, 0x34);
dcs_write_cmd_at(dev, MCS_P_DRV_M, 0x50);
dcs_write_cmd_at(dev, MCS_VCOMDC, 0x4E);
dcs_write_cmd_at(dev, MCS_OSC_ADJ, 0x66); /* 65Hz */
dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 2, 0x01);
dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 5, 0x34);
dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 4, 0x33);
dcs_write_cmd_at(dev, MCS_GVDDSET, 0x79, 0x79);
dcs_write_cmd_at(dev, MCS_SD_CTRL + 1, 0x1B);
dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 2, 0x83);
dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL + 1, 0x83);
dcs_write_cmd_at(dev, MCS_RGB_VID_SET, 0x0E);
dcs_write_cmd_at(dev, MCS_PANSET, 0x00, 0x01);
dcs_write_cmd_at(dev, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
dcs_write_cmd_at(dev, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
0x01, 0x02, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_NO_DOC2, 0x00);
dcs_write_cmd_at(dev, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
4, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
dcs_write_cmd_at(dev, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
dcs_write_cmd_at(dev, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
0x00, 0x00, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
dcs_write_cmd_at(dev, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
0x00, 0x00, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
dcs_write_cmd_at(dev, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 1, 0x66);
dcs_write_cmd_at(dev, MCS_NO_DOC3, 0x06);
dcs_write_cmd_at(dev, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
0x01);
dcs_write_cmd_at(dev, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
0x01);
/* Exit CMD2 */
dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
ret = mipi_dsi_dcs_nop(device);
if (ret)
return ret;
ret = mipi_dsi_dcs_exit_sleep_mode(device);
if (ret)
return ret;
/* Wait for sleep out exit */
mdelay(120);
/* Default portrait 480x800 rgb24 */
dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
ret = mipi_dsi_dcs_set_column_address(device, 0,
default_timing.hactive.typ - 1);
if (ret)
return ret;
ret = mipi_dsi_dcs_set_page_address(device, 0,
default_timing.vactive.typ - 1);
if (ret)
return ret;
/* See otm8009a driver documentation for pixel format descriptions */
ret = mipi_dsi_dcs_set_pixel_format(device, MIPI_DCS_PIXEL_FMT_24BIT |
MIPI_DCS_PIXEL_FMT_24BIT << 4);
if (ret)
return ret;
/* Disable CABC feature */
dcs_write_seq(dev, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
ret = mipi_dsi_dcs_set_display_on(device);
if (ret)
return ret;
ret = mipi_dsi_dcs_nop(device);
if (ret)
return ret;
/* Send Command GRAM memory write (no parameters) */
dcs_write_seq(dev, MIPI_DCS_WRITE_MEMORY_START);
return 0;
}
static int otm8009a_panel_enable_backlight(struct udevice *dev)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
int ret;
ret = mipi_dsi_attach(device);
if (ret < 0)
return ret;
ret = otm8009a_init_sequence(dev);
if (ret)
return ret;
/*
* Power on the backlight with the requested brightness
* Note We can not use mipi_dsi_dcs_set_display_brightness()
* as otm8009a driver support only 8-bit brightness (1 param).
*/
dcs_write_seq(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
OTM8009A_BACKLIGHT_DEFAULT);
/* Update Brightness Control & Backlight */
dcs_write_seq(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
/* Update Brightness Control & Backlight */
dcs_write_seq_hs(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY);
/* Need to wait a few time before sending the first image */
mdelay(10);
return 0;
}
static int otm8009a_panel_get_display_timing(struct udevice *dev,
struct display_timing *timings)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
struct otm8009a_panel_priv *priv = dev_get_priv(dev);
memcpy(timings, &default_timing, sizeof(*timings));
/* fill characteristics of DSI data link */
device->lanes = priv->lanes;
device->format = priv->format;
device->mode_flags = priv->mode_flags;
return 0;
}
static int otm8009a_panel_ofdata_to_platdata(struct udevice *dev)
{
struct otm8009a_panel_priv *priv = dev_get_priv(dev);
int ret;
if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
ret = device_get_supply_regulator(dev, "power-supply",
&priv->reg);
if (ret && ret != -ENOENT) {
dev_err(dev, "Warning: cannot get power supply\n");
return ret;
}
}
ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
GPIOD_IS_OUT);
if (ret) {
dev_err(dev, "warning: cannot get reset GPIO\n");
if (ret != -ENOENT)
return ret;
}
return 0;
}
static int otm8009a_panel_probe(struct udevice *dev)
{
struct otm8009a_panel_priv *priv = dev_get_priv(dev);
int ret;
if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
dev_dbg(dev, "enable regulator '%s'\n", priv->reg->name);
ret = regulator_set_enable(priv->reg, true);
if (ret)
return ret;
}
/* reset panel */
dm_gpio_set_value(&priv->reset, true);
mdelay(1); /* >50us */
dm_gpio_set_value(&priv->reset, false);
mdelay(10); /* >5ms */
priv->lanes = 2;
priv->format = MIPI_DSI_FMT_RGB888;
priv->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
return 0;
}
static const struct panel_ops otm8009a_panel_ops = {
.enable_backlight = otm8009a_panel_enable_backlight,
.get_display_timing = otm8009a_panel_get_display_timing,
};
static const struct udevice_id otm8009a_panel_ids[] = {
{ .compatible = "orisetech,otm8009a" },
{ }
};
U_BOOT_DRIVER(otm8009a_panel) = {
.name = "otm8009a_panel",
.id = UCLASS_PANEL,
.of_match = otm8009a_panel_ids,
.ops = &otm8009a_panel_ops,
.ofdata_to_platdata = otm8009a_panel_ofdata_to_platdata,
.probe = otm8009a_panel_probe,
.platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat),
.priv_auto_alloc_size = sizeof(struct otm8009a_panel_priv),
};
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <panel.h>
int panel_enable_backlight(struct udevice *dev)
{
struct panel_ops *ops = panel_get_ops(dev);
if (!ops->enable_backlight)
return -ENOSYS;
return ops->enable_backlight(dev);
}
/**
* panel_set_backlight - Set brightness for the panel backlight
*
* @dev: Panel device containing the backlight to update
* @percent: Brightness value (0=off, 1=min brightness,
* 100=full brightness)
* @return 0 if OK, -ve on error
*/
int panel_set_backlight(struct udevice *dev, int percent)
{
struct panel_ops *ops = panel_get_ops(dev);
if (!ops->set_backlight)
return -ENOSYS;
return ops->set_backlight(dev, percent);
}
int panel_get_display_timing(struct udevice *dev,
struct display_timing *timings)
{
struct panel_ops *ops = panel_get_ops(dev);
if (!ops->get_display_timing)
return -ENOSYS;
return ops->get_display_timing(dev, timings);
}
UCLASS_DRIVER(panel) = {
.id = UCLASS_PANEL,
.name = "panel",
};
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
#include <common.h>
#include <dm.h>
#include <backlight.h>
#include <pwm.h>
#include <asm/gpio.h>
#include <power/regulator.h>
/**
* Private information for the PWM backlight
*
* If @num_levels is 0 then the levels are simple values with the backlight
* value going between the minimum (default 0) and the maximum (default 255).
* Otherwise the levels are an index into @levels (0..n-1).
*
* @reg: Regulator to enable to turn the backlight on (NULL if none)
* @enable, GPIO to set to enable the backlight (can be missing)
* @pwm: PWM to use to change the backlight brightness
* @channel: PWM channel to use
* @period_ns: Period of the backlight in nanoseconds
* @levels: Levels for the backlight, or NULL if not using indexed levels
* @num_levels: Number of levels
* @cur_level: Current level for the backlight (index or value)
* @default_level: Default level for the backlight (index or value)
* @min_level: Minimum level of the backlight (full off)
* @min_level: Maximum level of the backlight (full on)
* @enabled: true if backlight is enabled
*/
struct pwm_backlight_priv {
struct udevice *reg;
struct gpio_desc enable;
struct udevice *pwm;
uint channel;
uint period_ns;
/*
* the polarity of one PWM
* 0: normal polarity
* 1: inverted polarity
*/
bool polarity;
u32 *levels;
int num_levels;
uint default_level;
int cur_level;
uint min_level;
uint max_level;
bool enabled;
};
static int set_pwm(struct pwm_backlight_priv *priv)
{
uint duty_cycle;
int ret;
duty_cycle = priv->period_ns * (priv->cur_level - priv->min_level) /
(priv->max_level - priv->min_level + 1);
ret = pwm_set_config(priv->pwm, priv->channel, priv->period_ns,
duty_cycle);
if (ret)
return log_ret(ret);
ret = pwm_set_invert(priv->pwm, priv->channel, priv->polarity);
if (ret == -ENOSYS && !priv->polarity)
ret = 0;
return log_ret(ret);
}
static int enable_sequence(struct udevice *dev, int seq)
{
struct pwm_backlight_priv *priv = dev_get_priv(dev);
int ret;
switch (seq) {
case 0:
if (priv->reg) {
__maybe_unused struct dm_regulator_uclass_platdata
*plat;
plat = dev_get_uclass_platdata(priv->reg);
log_debug("Enable '%s', regulator '%s'/'%s'\n",
dev->name, priv->reg->name, plat->name);
ret = regulator_set_enable(priv->reg, true);
if (ret) {
log_debug("Cannot enable regulator for PWM '%s'\n",
dev->name);
return log_ret(ret);
}
mdelay(120);
}
break;
case 1:
mdelay(10);
dm_gpio_set_value(&priv->enable, 1);
break;
}
return 0;
}
static int pwm_backlight_enable(struct udevice *dev)
{
struct pwm_backlight_priv *priv = dev_get_priv(dev);
int ret;
ret = enable_sequence(dev, 0);
if (ret)
return log_ret(ret);
ret = set_pwm(priv);
if (ret)
return log_ret(ret);
ret = pwm_set_enable(priv->pwm, priv->channel, true);
if (ret)
return log_ret(ret);
ret = enable_sequence(dev, 1);
if (ret)
return log_ret(ret);
priv->enabled = true;
return 0;
}
static int pwm_backlight_set_brightness(struct udevice *dev, int percent)
{
struct pwm_backlight_priv *priv = dev_get_priv(dev);
bool disable = false;
int level;
int ret;
if (!priv->enabled) {
ret = enable_sequence(dev, 0);
if (ret)
return log_ret(ret);
}
if (percent == BACKLIGHT_OFF) {
disable = true;
percent = 0;
}
if (percent == BACKLIGHT_DEFAULT) {
level = priv->default_level;
} else {
if (priv->levels) {
level = priv->levels[percent * (priv->num_levels - 1)
/ 100];
} else {
level = priv->min_level +
(priv->max_level - priv->min_level) *
percent / 100;
}
}
priv->cur_level = level;
ret = set_pwm(priv);
if (ret)
return log_ret(ret);
if (!priv->enabled) {
ret = enable_sequence(dev, 1);
if (ret)
return log_ret(ret);
priv->enabled = true;
}
if (disable) {
dm_gpio_set_value(&priv->enable, 0);
if (priv->reg) {
ret = regulator_set_enable(priv->reg, false);
if (ret)
return log_ret(ret);
}
priv->enabled = false;
}
return 0;
}
static int pwm_backlight_ofdata_to_platdata(struct udevice *dev)
{
struct pwm_backlight_priv *priv = dev_get_priv(dev);
struct ofnode_phandle_args args;
int index, ret, count, len;
const u32 *cell;
log_debug("start\n");
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply", &priv->reg);
if (ret)
log_debug("Cannot get power supply: ret=%d\n", ret);
ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
GPIOD_IS_OUT);
if (ret) {
log_debug("Warning: cannot get enable GPIO: ret=%d\n", ret);
if (ret != -ENOENT)
return log_ret(ret);
}
ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0,
&args);
if (ret) {
log_debug("Cannot get PWM phandle: ret=%d\n", ret);
return log_ret(ret);
}
ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm);
if (ret) {
log_debug("Cannot get PWM: ret=%d\n", ret);
return log_ret(ret);
}
if (args.args_count < 2)
return log_msg_ret("Not enough arguments to pwm\n", -EINVAL);
priv->channel = args.args[0];
priv->period_ns = args.args[1];
if (args.args_count > 2)
priv->polarity = args.args[2];
index = dev_read_u32_default(dev, "default-brightness-level", 255);
cell = dev_read_prop(dev, "brightness-levels", &len);
count = len / sizeof(u32);
if (cell && count > index) {
priv->levels = malloc(len);
if (!priv->levels)
return log_ret(-ENOMEM);
dev_read_u32_array(dev, "brightness-levels", priv->levels,
count);
priv->num_levels = count;
priv->default_level = priv->levels[index];
priv->max_level = priv->levels[count - 1];
} else {
priv->default_level = index;
priv->max_level = 255;
}
priv->cur_level = priv->default_level;
log_debug("done\n");
return 0;
}
static int pwm_backlight_probe(struct udevice *dev)
{
return 0;
}
static const struct backlight_ops pwm_backlight_ops = {
.enable = pwm_backlight_enable,
.set_brightness = pwm_backlight_set_brightness,
};
static const struct udevice_id pwm_backlight_ids[] = {
{ .compatible = "pwm-backlight" },
{ }
};
U_BOOT_DRIVER(pwm_backlight) = {
.name = "pwm_backlight",
.id = UCLASS_PANEL_BACKLIGHT,
.of_match = pwm_backlight_ids,
.ops = &pwm_backlight_ops,
.ofdata_to_platdata = pwm_backlight_ofdata_to_platdata,
.probe = pwm_backlight_probe,
.priv_auto_alloc_size = sizeof(struct pwm_backlight_priv),
};
@@ -0,0 +1,614 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* PXA LCD Controller
*
* (C) Copyright 2001-2002
* Wolfgang Denk, DENX Software Engineering -- wd@denx.de
*/
/************************************************************************/
/* ** HEADER FILES */
/************************************************************************/
#include <common.h>
#include <asm/arch/pxa-regs.h>
#include <asm/io.h>
#include <lcd.h>
#include <linux/types.h>
#include <stdarg.h>
#include <stdio_dev.h>
/* #define DEBUG */
#ifdef CONFIG_LCD
/*----------------------------------------------------------------------*/
/*
* Define panel bpp, LCCR0, LCCR3 and panel_info video struct for
* your display.
*/
#ifdef CONFIG_PXA_VGA
/* LCD outputs connected to a video DAC */
# define LCD_BPP LCD_COLOR8
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x003008f8
# define REG_LCCR3 0x0300FF01
/* 640x480x16 @ 61 Hz */
vidinfo_t panel_info = {
.vl_col = 640,
.vl_row = 480,
.vl_width = 640,
.vl_height = 480,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_HIGH,
.vl_hsp = CONFIG_SYS_HIGH,
.vl_vsp = CONFIG_SYS_HIGH,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 0,
.vl_clor = 0,
.vl_tft = 1,
.vl_hpw = 40,
.vl_blw = 56,
.vl_elw = 56,
.vl_vpw = 20,
.vl_bfw = 8,
.vl_efw = 8,
};
#endif /* CONFIG_PXA_VIDEO */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_SHARP_LM8V31
# define LCD_BPP LCD_COLOR8
# define LCD_INVERT_COLORS /* Needed for colors to be correct, but why? */
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x0030087C
# define REG_LCCR3 0x0340FF08
vidinfo_t panel_info = {
.vl_col = 640,
.vl_row = 480,
.vl_width = 157,
.vl_height = 118,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_HIGH,
.vl_hsp = CONFIG_SYS_HIGH,
.vl_vsp = CONFIG_SYS_HIGH,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 1,
.vl_clor = 1,
.vl_tft = 0,
.vl_hpw = 1,
.vl_blw = 3,
.vl_elw = 3,
.vl_vpw = 1,
.vl_bfw = 0,
.vl_efw = 0,
};
#endif /* CONFIG_SHARP_LM8V31 */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_VOIPAC_LCD
# define LCD_BPP LCD_COLOR8
# define LCD_INVERT_COLORS
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x043008f8
# define REG_LCCR3 0x0340FF08
vidinfo_t panel_info = {
.vl_col = 640,
.vl_row = 480,
.vl_width = 157,
.vl_height = 118,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_HIGH,
.vl_hsp = CONFIG_SYS_HIGH,
.vl_vsp = CONFIG_SYS_HIGH,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 1,
.vl_clor = 1,
.vl_tft = 1,
.vl_hpw = 32,
.vl_blw = 144,
.vl_elw = 32,
.vl_vpw = 2,
.vl_bfw = 13,
.vl_efw = 30,
};
#endif /* CONFIG_VOIPAC_LCD */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_HITACHI_SX14
/* Hitachi SX14Q004-ZZA color STN LCD */
#define LCD_BPP LCD_COLOR8
/* you have to set lccr0 and lccr3 (including pcd) */
#define REG_LCCR0 0x00301079
#define REG_LCCR3 0x0340FF20
vidinfo_t panel_info = {
.vl_col = 320,
.vl_row = 240,
.vl_width = 167,
.vl_height = 109,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_HIGH,
.vl_hsp = CONFIG_SYS_HIGH,
.vl_vsp = CONFIG_SYS_HIGH,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 1,
.vl_splt = 0,
.vl_clor = 1,
.vl_tft = 0,
.vl_hpw = 1,
.vl_blw = 1,
.vl_elw = 1,
.vl_vpw = 7,
.vl_bfw = 0,
.vl_efw = 0,
};
#endif /* CONFIG_HITACHI_SX14 */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_LMS283GF05
# define LCD_BPP LCD_COLOR8
/*# define LCD_INVERT_COLORS*/
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x043008f8
# define REG_LCCR3 0x03b00009
vidinfo_t panel_info = {
.vl_col = 240,
.vl_row = 320,
.vl_rot = 3,
.vl_width = 240,
.vl_height = 320,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_LOW,
.vl_hsp = CONFIG_SYS_LOW,
.vl_vsp = CONFIG_SYS_LOW,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 1,
.vl_clor = 1,
.vl_tft = 1,
.vl_hpw = 4,
.vl_blw = 4,
.vl_elw = 8,
.vl_vpw = 4,
.vl_bfw = 4,
.vl_efw = 8,
};
#endif /* CONFIG_LMS283GF05 */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_ACX517AKN
# define LCD_BPP LCD_COLOR8
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x003008f9
# define REG_LCCR3 0x03700006
vidinfo_t panel_info = {
.vl_col = 320,
.vl_row = 320,
.vl_width = 320,
.vl_height = 320,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_LOW,
.vl_hsp = CONFIG_SYS_LOW,
.vl_vsp = CONFIG_SYS_LOW,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 1,
.vl_clor = 1,
.vl_tft = 1,
.vl_hpw = 0x04,
.vl_blw = 0x1c,
.vl_elw = 0x08,
.vl_vpw = 0x01,
.vl_bfw = 0x07,
.vl_efw = 0x08,
};
#endif /* CONFIG_ACX517AKN */
#ifdef CONFIG_ACX544AKN
# define LCD_BPP LCD_COLOR16
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x003008f9
# define REG_LCCR3 0x04700007 /* 16bpp */
vidinfo_t panel_info = {
.vl_col = 320,
.vl_row = 320,
.vl_width = 320,
.vl_height = 320,
.vl_clkp = CONFIG_SYS_LOW,
.vl_oep = CONFIG_SYS_LOW,
.vl_hsp = CONFIG_SYS_LOW,
.vl_vsp = CONFIG_SYS_LOW,
.vl_dp = CONFIG_SYS_LOW,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 0,
.vl_clor = 1,
.vl_tft = 1,
.vl_hpw = 0x05,
.vl_blw = 0x13,
.vl_elw = 0x08,
.vl_vpw = 0x02,
.vl_bfw = 0x07,
.vl_efw = 0x05,
};
#endif /* CONFIG_ACX544AKN */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_LQ038J7DH53
# define LCD_BPP LCD_COLOR8
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x003008f9
# define REG_LCCR3 0x03700004
vidinfo_t panel_info = {
.vl_col = 320,
.vl_row = 480,
.vl_width = 320,
.vl_height = 480,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_LOW,
.vl_hsp = CONFIG_SYS_LOW,
.vl_vsp = CONFIG_SYS_LOW,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 1,
.vl_clor = 1,
.vl_tft = 1,
.vl_hpw = 0x04,
.vl_blw = 0x20,
.vl_elw = 0x01,
.vl_vpw = 0x01,
.vl_bfw = 0x04,
.vl_efw = 0x01,
};
#endif /* CONFIG_ACX517AKN */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_LITTLETON_LCD
# define LCD_BPP LCD_COLOR8
/* you have to set lccr0 and lccr3 (including pcd) */
# define REG_LCCR0 0x003008f8
# define REG_LCCR3 0x0300FF04
vidinfo_t panel_info = {
.vl_col = 480,
.vl_row = 640,
.vl_width = 480,
.vl_height = 640,
.vl_clkp = CONFIG_SYS_HIGH,
.vl_oep = CONFIG_SYS_HIGH,
.vl_hsp = CONFIG_SYS_HIGH,
.vl_vsp = CONFIG_SYS_HIGH,
.vl_dp = CONFIG_SYS_HIGH,
.vl_bpix = LCD_BPP,
.vl_lbw = 0,
.vl_splt = 0,
.vl_clor = 0,
.vl_tft = 1,
.vl_hpw = 9,
.vl_blw = 8,
.vl_elw = 24,
.vl_vpw = 2,
.vl_bfw = 2,
.vl_efw = 4,
};
#endif /* CONFIG_LITTLETON_LCD */
/*----------------------------------------------------------------------*/
static int pxafb_init_mem (void *lcdbase, vidinfo_t *vid);
static void pxafb_setup_gpio (vidinfo_t *vid);
static void pxafb_enable_controller (vidinfo_t *vid);
static int pxafb_init (vidinfo_t *vid);
/************************************************************************/
/* --------------- PXA chipset specific functions ------------------- */
/************************************************************************/
ushort *configuration_get_cmap(void)
{
struct pxafb_info *fbi = &panel_info.pxa;
return (ushort *)fbi->palette;
}
void lcd_ctrl_init (void *lcdbase)
{
pxafb_init_mem(lcdbase, &panel_info);
pxafb_init(&panel_info);
pxafb_setup_gpio(&panel_info);
pxafb_enable_controller(&panel_info);
}
/*----------------------------------------------------------------------*/
#if LCD_BPP == LCD_COLOR8
void
lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue)
{
struct pxafb_info *fbi = &panel_info.pxa;
unsigned short *palette = (unsigned short *)fbi->palette;
u_int val;
if (regno < fbi->palette_size) {
val = ((red << 8) & 0xf800);
val |= ((green << 4) & 0x07e0);
val |= (blue & 0x001f);
#ifdef LCD_INVERT_COLORS
palette[regno] = ~val;
#else
palette[regno] = val;
#endif
}
debug ("setcolreg: reg %2d @ %p: R=%02X G=%02X B=%02X => %04X\n",
regno, &palette[regno],
red, green, blue,
palette[regno]);
}
#endif /* LCD_COLOR8 */
/*----------------------------------------------------------------------*/
__weak void lcd_enable(void)
{
}
/************************************************************************/
/* ** PXA255 specific routines */
/************************************************************************/
/*
* Calculate fb size for VIDEOLFB_ATAG. Size returned contains fb,
* descriptors and palette areas.
*/
ulong calc_fbsize (void)
{
ulong size;
int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8;
size = line_length * panel_info.vl_row;
size += PAGE_SIZE;
return size;
}
static int pxafb_init_mem (void *lcdbase, vidinfo_t *vid)
{
u_long palette_mem_size;
struct pxafb_info *fbi = &vid->pxa;
int fb_size = vid->vl_row * (vid->vl_col * NBITS (vid->vl_bpix)) / 8;
fbi->screen = (u_long)lcdbase;
fbi->palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16;
palette_mem_size = fbi->palette_size * sizeof(u16);
debug("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
/* locate palette and descs at end of page following fb */
fbi->palette = (u_long)lcdbase + fb_size + PAGE_SIZE - palette_mem_size;
return 0;
}
#ifdef CONFIG_CPU_MONAHANS
static inline void pxafb_setup_gpio (vidinfo_t *vid) {}
#else
static void pxafb_setup_gpio (vidinfo_t *vid)
{
u_long lccr0;
/*
* setup is based on type of panel supported
*/
lccr0 = vid->pxa.reg_lccr0;
/* 4 bit interface */
if ((lccr0 & LCCR0_CMS) && (lccr0 & LCCR0_SDS) && !(lccr0 & LCCR0_DPD))
{
debug("Setting GPIO for 4 bit data\n");
/* bits 58-61 */
writel(readl(GPDR1) | (0xf << 26), GPDR1);
writel((readl(GAFR1_U) & ~(0xff << 20)) | (0xaa << 20),
GAFR1_U);
/* bits 74-77 */
writel(readl(GPDR2) | (0xf << 10), GPDR2);
writel((readl(GAFR2_L) & ~(0xff << 20)) | (0xaa << 20),
GAFR2_L);
}
/* 8 bit interface */
else if (((lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_DPD))) ||
(!(lccr0 & LCCR0_CMS) && !(lccr0 & LCCR0_PAS) && !(lccr0 & LCCR0_SDS)))
{
debug("Setting GPIO for 8 bit data\n");
/* bits 58-65 */
writel(readl(GPDR1) | (0x3f << 26), GPDR1);
writel(readl(GPDR2) | (0x3), GPDR2);
writel((readl(GAFR1_U) & ~(0xfff << 20)) | (0xaaa << 20),
GAFR1_U);
writel((readl(GAFR2_L) & ~0xf) | (0xa), GAFR2_L);
/* bits 74-77 */
writel(readl(GPDR2) | (0xf << 10), GPDR2);
writel((readl(GAFR2_L) & ~(0xff << 20)) | (0xaa << 20),
GAFR2_L);
}
/* 16 bit interface */
else if (!(lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_PAS)))
{
debug("Setting GPIO for 16 bit data\n");
/* bits 58-77 */
writel(readl(GPDR1) | (0x3f << 26), GPDR1);
writel(readl(GPDR2) | 0x00003fff, GPDR2);
writel((readl(GAFR1_U) & ~(0xfff << 20)) | (0xaaa << 20),
GAFR1_U);
writel((readl(GAFR2_L) & 0xf0000000) | 0x0aaaaaaa, GAFR2_L);
}
else
{
printf("pxafb_setup_gpio: unable to determine bits per pixel\n");
}
}
#endif
static void pxafb_enable_controller (vidinfo_t *vid)
{
debug("Enabling LCD controller\n");
/* Sequence from 11.7.10 */
writel(vid->pxa.reg_lccr3, LCCR3);
writel(vid->pxa.reg_lccr2, LCCR2);
writel(vid->pxa.reg_lccr1, LCCR1);
writel(vid->pxa.reg_lccr0 & ~LCCR0_ENB, LCCR0);
writel(vid->pxa.fdadr0, FDADR0);
writel(vid->pxa.fdadr1, FDADR1);
writel(readl(LCCR0) | LCCR0_ENB, LCCR0);
#ifdef CONFIG_CPU_MONAHANS
writel(readl(CKENA) | CKENA_1_LCD, CKENA);
#else
writel(readl(CKEN) | CKEN16_LCD, CKEN);
#endif
debug("FDADR0 = 0x%08x\n", readl(FDADR0));
debug("FDADR1 = 0x%08x\n", readl(FDADR1));
debug("LCCR0 = 0x%08x\n", readl(LCCR0));
debug("LCCR1 = 0x%08x\n", readl(LCCR1));
debug("LCCR2 = 0x%08x\n", readl(LCCR2));
debug("LCCR3 = 0x%08x\n", readl(LCCR3));
}
static int pxafb_init (vidinfo_t *vid)
{
struct pxafb_info *fbi = &vid->pxa;
debug("Configuring PXA LCD\n");
fbi->reg_lccr0 = REG_LCCR0;
fbi->reg_lccr3 = REG_LCCR3;
debug("vid: vl_col=%d hslen=%d lm=%d rm=%d\n",
vid->vl_col, vid->vl_hpw,
vid->vl_blw, vid->vl_elw);
debug("vid: vl_row=%d vslen=%d um=%d bm=%d\n",
vid->vl_row, vid->vl_vpw,
vid->vl_bfw, vid->vl_efw);
fbi->reg_lccr1 =
LCCR1_DisWdth(vid->vl_col) +
LCCR1_HorSnchWdth(vid->vl_hpw) +
LCCR1_BegLnDel(vid->vl_blw) +
LCCR1_EndLnDel(vid->vl_elw);
fbi->reg_lccr2 =
LCCR2_DisHght(vid->vl_row) +
LCCR2_VrtSnchWdth(vid->vl_vpw) +
LCCR2_BegFrmDel(vid->vl_bfw) +
LCCR2_EndFrmDel(vid->vl_efw);
fbi->reg_lccr3 = REG_LCCR3 & ~(LCCR3_HSP | LCCR3_VSP);
fbi->reg_lccr3 |= (vid->vl_hsp ? LCCR3_HorSnchL : LCCR3_HorSnchH)
| (vid->vl_vsp ? LCCR3_VrtSnchL : LCCR3_VrtSnchH);
/* setup dma descriptors */
fbi->dmadesc_fblow = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette - 3*16);
fbi->dmadesc_fbhigh = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette - 2*16);
fbi->dmadesc_palette = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette - 1*16);
#define BYTES_PER_PANEL ((fbi->reg_lccr0 & LCCR0_SDS) ? \
(vid->vl_col * vid->vl_row * NBITS(vid->vl_bpix) / 8 / 2) : \
(vid->vl_col * vid->vl_row * NBITS(vid->vl_bpix) / 8))
/* populate descriptors */
fbi->dmadesc_fblow->fdadr = (u_long)fbi->dmadesc_fblow;
fbi->dmadesc_fblow->fsadr = fbi->screen + BYTES_PER_PANEL;
fbi->dmadesc_fblow->fidr = 0;
fbi->dmadesc_fblow->ldcmd = BYTES_PER_PANEL;
fbi->fdadr1 = (u_long)fbi->dmadesc_fblow; /* only used in dual-panel mode */
fbi->dmadesc_fbhigh->fsadr = fbi->screen;
fbi->dmadesc_fbhigh->fidr = 0;
fbi->dmadesc_fbhigh->ldcmd = BYTES_PER_PANEL;
fbi->dmadesc_palette->fsadr = fbi->palette;
fbi->dmadesc_palette->fidr = 0;
fbi->dmadesc_palette->ldcmd = (fbi->palette_size * 2) | LDCMD_PAL;
if( NBITS(vid->vl_bpix) < 12)
{
/* assume any mode with <12 bpp is palette driven */
fbi->dmadesc_palette->fdadr = (u_long)fbi->dmadesc_fbhigh;
fbi->dmadesc_fbhigh->fdadr = (u_long)fbi->dmadesc_palette;
/* flips back and forth between pal and fbhigh */
fbi->fdadr0 = (u_long)fbi->dmadesc_palette;
}
else
{
/* palette shouldn't be loaded in true-color mode */
fbi->dmadesc_fbhigh->fdadr = (u_long)fbi->dmadesc_fbhigh;
fbi->fdadr0 = (u_long)fbi->dmadesc_fbhigh; /* no pal just fbhigh */
}
debug("fbi->dmadesc_fblow = 0x%lx\n", (u_long)fbi->dmadesc_fblow);
debug("fbi->dmadesc_fbhigh = 0x%lx\n", (u_long)fbi->dmadesc_fbhigh);
debug("fbi->dmadesc_palette = 0x%lx\n", (u_long)fbi->dmadesc_palette);
debug("fbi->dmadesc_fblow->fdadr = 0x%lx\n", fbi->dmadesc_fblow->fdadr);
debug("fbi->dmadesc_fbhigh->fdadr = 0x%lx\n", fbi->dmadesc_fbhigh->fdadr);
debug("fbi->dmadesc_palette->fdadr = 0x%lx\n", fbi->dmadesc_palette->fdadr);
debug("fbi->dmadesc_fblow->fsadr = 0x%lx\n", fbi->dmadesc_fblow->fsadr);
debug("fbi->dmadesc_fbhigh->fsadr = 0x%lx\n", fbi->dmadesc_fbhigh->fsadr);
debug("fbi->dmadesc_palette->fsadr = 0x%lx\n", fbi->dmadesc_palette->fsadr);
debug("fbi->dmadesc_fblow->ldcmd = 0x%lx\n", fbi->dmadesc_fblow->ldcmd);
debug("fbi->dmadesc_fbhigh->ldcmd = 0x%lx\n", fbi->dmadesc_fbhigh->ldcmd);
debug("fbi->dmadesc_palette->ldcmd = 0x%lx\n", fbi->dmadesc_palette->ldcmd);
return 0;
}
/************************************************************************/
/************************************************************************/
#endif /* CONFIG_LCD */
@@ -0,0 +1,351 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 STMicroelectronics - All Rights Reserved
* Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
* Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
*
* This rm68200 panel driver is inspired from the Linux Kernel driver
* drivers/gpu/drm/panel/panel-raydium-rm68200.c.
*/
#include <common.h>
#include <backlight.h>
#include <dm.h>
#include <mipi_dsi.h>
#include <panel.h>
#include <asm/gpio.h>
#include <power/regulator.h>
/*** Manufacturer Command Set ***/
#define MCS_CMD_MODE_SW 0xFE /* CMD Mode Switch */
#define MCS_CMD1_UCS 0x00 /* User Command Set (UCS = CMD1) */
#define MCS_CMD2_P0 0x01 /* Manufacture Command Set Page0 (CMD2 P0) */
#define MCS_CMD2_P1 0x02 /* Manufacture Command Set Page1 (CMD2 P1) */
#define MCS_CMD2_P2 0x03 /* Manufacture Command Set Page2 (CMD2 P2) */
#define MCS_CMD2_P3 0x04 /* Manufacture Command Set Page3 (CMD2 P3) */
/* CMD2 P0 commands (Display Options and Power) */
#define MCS_STBCTR 0x12 /* TE1 Output Setting Zig-Zag Connection */
#define MCS_SGOPCTR 0x16 /* Source Bias Current */
#define MCS_SDCTR 0x1A /* Source Output Delay Time */
#define MCS_INVCTR 0x1B /* Inversion Type */
#define MCS_EXT_PWR_IC 0x24 /* External PWR IC Control */
#define MCS_SETAVDD 0x27 /* PFM Control for AVDD Output */
#define MCS_SETAVEE 0x29 /* PFM Control for AVEE Output */
#define MCS_BT2CTR 0x2B /* DDVDL Charge Pump Control */
#define MCS_BT3CTR 0x2F /* VGH Charge Pump Control */
#define MCS_BT4CTR 0x34 /* VGL Charge Pump Control */
#define MCS_VCMCTR 0x46 /* VCOM Output Level Control */
#define MCS_SETVGN 0x52 /* VG M/S N Control */
#define MCS_SETVGP 0x54 /* VG M/S P Control */
#define MCS_SW_CTRL 0x5F /* Interface Control for PFM and MIPI */
/* CMD2 P2 commands (GOA Timing Control) - no description in datasheet */
#define GOA_VSTV1 0x00
#define GOA_VSTV2 0x07
#define GOA_VCLK1 0x0E
#define GOA_VCLK2 0x17
#define GOA_VCLK_OPT1 0x20
#define GOA_BICLK1 0x2A
#define GOA_BICLK2 0x37
#define GOA_BICLK3 0x44
#define GOA_BICLK4 0x4F
#define GOA_BICLK_OPT1 0x5B
#define GOA_BICLK_OPT2 0x60
#define MCS_GOA_GPO1 0x6D
#define MCS_GOA_GPO2 0x71
#define MCS_GOA_EQ 0x74
#define MCS_GOA_CLK_GALLON 0x7C
#define MCS_GOA_FS_SEL0 0x7E
#define MCS_GOA_FS_SEL1 0x87
#define MCS_GOA_FS_SEL2 0x91
#define MCS_GOA_FS_SEL3 0x9B
#define MCS_GOA_BS_SEL0 0xAC
#define MCS_GOA_BS_SEL1 0xB5
#define MCS_GOA_BS_SEL2 0xBF
#define MCS_GOA_BS_SEL3 0xC9
#define MCS_GOA_BS_SEL4 0xD3
/* CMD2 P3 commands (Gamma) */
#define MCS_GAMMA_VP 0x60 /* Gamma VP1~VP16 */
#define MCS_GAMMA_VN 0x70 /* Gamma VN1~VN16 */
struct rm68200_panel_priv {
struct udevice *reg;
struct udevice *backlight;
struct gpio_desc reset;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
};
static const struct display_timing default_timing = {
.pixelclock.typ = 54000000,
.hactive.typ = 720,
.hfront_porch.typ = 48,
.hback_porch.typ = 48,
.hsync_len.typ = 9,
.vactive.typ = 1280,
.vfront_porch.typ = 12,
.vback_porch.typ = 12,
.vsync_len.typ = 5,
};
static void rm68200_dcs_write_buf(struct udevice *dev, const void *data,
size_t len)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
int err;
err = mipi_dsi_dcs_write_buffer(device, data, len);
if (err < 0)
dev_err(dev, "MIPI DSI DCS write buffer failed: %d\n", err);
}
static void rm68200_dcs_write_cmd(struct udevice *dev, u8 cmd, u8 value)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
int err;
err = mipi_dsi_dcs_write(device, cmd, &value, 1);
if (err < 0)
dev_err(dev, "MIPI DSI DCS write failed: %d\n", err);
}
#define dcs_write_seq(ctx, seq...) \
({ \
static const u8 d[] = { seq }; \
\
rm68200_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \
})
/*
* 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_cmd_seq(ctx, cmd, seq...) \
({ \
static const u8 d[] = { seq }; \
unsigned int i; \
\
for (i = 0; i < ARRAY_SIZE(d) ; i++) \
rm68200_dcs_write_cmd(ctx, cmd + i, d[i]); \
})
static void rm68200_init_sequence(struct udevice *dev)
{
/* Enter CMD2 with page 0 */
dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P0);
dcs_write_cmd_seq(dev, MCS_EXT_PWR_IC, 0xC0, 0x53, 0x00);
dcs_write_seq(dev, MCS_BT2CTR, 0xE5);
dcs_write_seq(dev, MCS_SETAVDD, 0x0A);
dcs_write_seq(dev, MCS_SETAVEE, 0x0A);
dcs_write_seq(dev, MCS_SGOPCTR, 0x52);
dcs_write_seq(dev, MCS_BT3CTR, 0x53);
dcs_write_seq(dev, MCS_BT4CTR, 0x5A);
dcs_write_seq(dev, MCS_INVCTR, 0x00);
dcs_write_seq(dev, MCS_STBCTR, 0x0A);
dcs_write_seq(dev, MCS_SDCTR, 0x06);
dcs_write_seq(dev, MCS_VCMCTR, 0x56);
dcs_write_seq(dev, MCS_SETVGN, 0xA0, 0x00);
dcs_write_seq(dev, MCS_SETVGP, 0xA0, 0x00);
dcs_write_seq(dev, MCS_SW_CTRL, 0x11); /* 2 data lanes, see doc */
dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P2);
dcs_write_seq(dev, GOA_VSTV1, 0x05);
dcs_write_seq(dev, 0x02, 0x0B);
dcs_write_seq(dev, 0x03, 0x0F);
dcs_write_seq(dev, 0x04, 0x7D, 0x00, 0x50);
dcs_write_cmd_seq(dev, GOA_VSTV2, 0x05, 0x16, 0x0D, 0x11, 0x7D, 0x00,
0x50);
dcs_write_cmd_seq(dev, GOA_VCLK1, 0x07, 0x08, 0x01, 0x02, 0x00, 0x7D,
0x00, 0x85, 0x08);
dcs_write_cmd_seq(dev, GOA_VCLK2, 0x03, 0x04, 0x05, 0x06, 0x00, 0x7D,
0x00, 0x85, 0x08);
dcs_write_seq(dev, GOA_VCLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00);
dcs_write_cmd_seq(dev, GOA_BICLK1, 0x07, 0x08);
dcs_write_seq(dev, 0x2D, 0x01);
dcs_write_seq(dev, 0x2F, 0x02, 0x00, 0x40, 0x05, 0x08, 0x54, 0x7D,
0x00);
dcs_write_cmd_seq(dev, GOA_BICLK2, 0x03, 0x04, 0x05, 0x06, 0x00);
dcs_write_seq(dev, 0x3D, 0x40);
dcs_write_seq(dev, 0x3F, 0x05, 0x08, 0x54, 0x7D, 0x00);
dcs_write_seq(dev, GOA_BICLK3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00);
dcs_write_seq(dev, GOA_BICLK4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00);
dcs_write_seq(dev, 0x58, 0x00, 0x00, 0x00);
dcs_write_seq(dev, GOA_BICLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00);
dcs_write_seq(dev, GOA_BICLK_OPT2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
dcs_write_seq(dev, MCS_GOA_GPO1, 0x00, 0x00, 0x00, 0x00);
dcs_write_seq(dev, MCS_GOA_GPO2, 0x00, 0x20, 0x00);
dcs_write_seq(dev, MCS_GOA_EQ, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00);
dcs_write_seq(dev, MCS_GOA_CLK_GALLON, 0x00, 0x00);
dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL0, 0xBF, 0x02, 0x06, 0x14, 0x10,
0x16, 0x12, 0x08, 0x3F);
dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0C,
0x0A, 0x0E, 0x3F, 0x3F, 0x00);
dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL2, 0x04, 0x3F, 0x3F, 0x3F, 0x3F,
0x05, 0x01, 0x3F, 0x3F, 0x0F);
dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL3, 0x0B, 0x0D, 0x3F, 0x3F, 0x3F,
0x3F);
dcs_write_cmd_seq(dev, 0xA2, 0x3F, 0x09, 0x13, 0x17, 0x11, 0x15);
dcs_write_cmd_seq(dev, 0xA9, 0x07, 0x03, 0x3F);
dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL0, 0x3F, 0x05, 0x01, 0x17, 0x13,
0x15, 0x11, 0x0F, 0x3F);
dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0B,
0x0D, 0x09, 0x3F, 0x3F, 0x07);
dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL2, 0x03, 0x3F, 0x3F, 0x3F, 0x3F,
0x02, 0x06, 0x3F, 0x3F, 0x08);
dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL3, 0x0C, 0x0A, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x0E, 0x10, 0x14);
dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL4, 0x12, 0x16, 0x00, 0x04, 0x3F);
dcs_write_seq(dev, 0xDC, 0x02);
dcs_write_seq(dev, 0xDE, 0x12);
dcs_write_seq(dev, MCS_CMD_MODE_SW, 0x0E); /* No documentation */
dcs_write_seq(dev, 0x01, 0x75);
dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P3);
dcs_write_cmd_seq(dev, MCS_GAMMA_VP, 0x00, 0x0C, 0x12, 0x0E, 0x06,
0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,
0x12, 0x0C, 0x00);
dcs_write_cmd_seq(dev, MCS_GAMMA_VN, 0x00, 0x0C, 0x12, 0x0E, 0x06,
0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,
0x12, 0x0C, 0x00);
/* Exit CMD2 */
dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD1_UCS);
}
static int rm68200_panel_enable_backlight(struct udevice *dev)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
struct rm68200_panel_priv *priv = dev_get_priv(dev);
int ret;
ret = mipi_dsi_attach(device);
if (ret < 0)
return ret;
rm68200_init_sequence(dev);
ret = mipi_dsi_dcs_exit_sleep_mode(device);
if (ret)
return ret;
mdelay(125);
ret = mipi_dsi_dcs_set_display_on(device);
if (ret)
return ret;
mdelay(20);
ret = backlight_enable(priv->backlight);
if (ret)
return ret;
return 0;
}
static int rm68200_panel_get_display_timing(struct udevice *dev,
struct display_timing *timings)
{
struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
struct mipi_dsi_device *device = plat->device;
struct rm68200_panel_priv *priv = dev_get_priv(dev);
memcpy(timings, &default_timing, sizeof(*timings));
/* fill characteristics of DSI data link */
device->lanes = priv->lanes;
device->format = priv->format;
device->mode_flags = priv->mode_flags;
return 0;
}
static int rm68200_panel_ofdata_to_platdata(struct udevice *dev)
{
struct rm68200_panel_priv *priv = dev_get_priv(dev);
int ret;
if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
ret = device_get_supply_regulator(dev, "power-supply",
&priv->reg);
if (ret && ret != -ENOENT) {
dev_err(dev, "Warning: cannot get power supply\n");
return ret;
}
}
ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
GPIOD_IS_OUT);
if (ret) {
dev_err(dev, "Warning: cannot get reset GPIO\n");
if (ret != -ENOENT)
return ret;
}
ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
"backlight", &priv->backlight);
if (ret) {
dev_err(dev, "Cannot get backlight: ret=%d\n", ret);
return ret;
}
return 0;
}
static int rm68200_panel_probe(struct udevice *dev)
{
struct rm68200_panel_priv *priv = dev_get_priv(dev);
int ret;
if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
ret = regulator_set_enable(priv->reg, true);
if (ret)
return ret;
}
/* reset panel */
dm_gpio_set_value(&priv->reset, true);
mdelay(1);
dm_gpio_set_value(&priv->reset, false);
mdelay(10);
priv->lanes = 2;
priv->format = MIPI_DSI_FMT_RGB888;
priv->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
return 0;
}
static const struct panel_ops rm68200_panel_ops = {
.enable_backlight = rm68200_panel_enable_backlight,
.get_display_timing = rm68200_panel_get_display_timing,
};
static const struct udevice_id rm68200_panel_ids[] = {
{ .compatible = "raydium,rm68200" },
{ }
};
U_BOOT_DRIVER(rm68200_panel) = {
.name = "rm68200_panel",
.id = UCLASS_PANEL,
.of_match = rm68200_panel_ids,
.ops = &rm68200_panel_ops,
.ofdata_to_platdata = rm68200_panel_ofdata_to_platdata,
.probe = rm68200_panel_probe,
.platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat),
.priv_auto_alloc_size = sizeof(struct rm68200_panel_priv),
};

Some files were not shown because too many files have changed in this diff Show More