GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
|
||||
false, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
/* Disable synchronization */
|
||||
writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
|
||||
false, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
/* Disable pixel clock */
|
||||
writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
|
||||
false, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
/* Disable PWM */
|
||||
writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->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,
|
||||
®s->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,
|
||||
®s->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, ®s->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, ®s->lcdc_lcdcfg1);
|
||||
|
||||
value = LCDC_LCDCFG2_VBPW(panel_info.vl_upper_margin);
|
||||
value |= LCDC_LCDCFG2_VFPW(panel_info.vl_lower_margin - 1);
|
||||
writel(value, ®s->lcdc_lcdcfg2);
|
||||
|
||||
value = LCDC_LCDCFG3_HBPW(panel_info.vl_left_margin - 1);
|
||||
value |= LCDC_LCDCFG3_HFPW(panel_info.vl_right_margin - 1);
|
||||
writel(value, ®s->lcdc_lcdcfg3);
|
||||
|
||||
/* Display size */
|
||||
value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1);
|
||||
value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1);
|
||||
writel(value, ®s->lcdc_lcdcfg4);
|
||||
|
||||
writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
|
||||
®s->lcdc_basecfg0);
|
||||
|
||||
switch (NBITS(panel_info.vl_bpix)) {
|
||||
case 16:
|
||||
writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
|
||||
®s->lcdc_basecfg1);
|
||||
break;
|
||||
case 32:
|
||||
writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
|
||||
®s->lcdc_basecfg1);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
writel(LCDC_BASECFG2_XSTRIDE(0), ®s->lcdc_basecfg2);
|
||||
writel(0, ®s->lcdc_basecfg3);
|
||||
writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4);
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel(~0UL, ®s->lcdc_lcdidr);
|
||||
writel(~0UL, ®s->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, ®s->lcdc_baseaddr);
|
||||
writel(desc->control, ®s->lcdc_basectrl);
|
||||
writel(desc->next, ®s->lcdc_basenext);
|
||||
writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
|
||||
®s->lcdc_basecher);
|
||||
|
||||
/* Enable LCD */
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
|
||||
true, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
|
||||
true, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
|
||||
true, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
|
||||
false, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
/* Disable synchronization */
|
||||
writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
|
||||
false, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
/* Disable pixel clock */
|
||||
writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
|
||||
false, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
/* Disable PWM */
|
||||
writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis);
|
||||
ret = wait_for_bit_le32(®s->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,
|
||||
®s->lcdc_lcdcfg0);
|
||||
|
||||
} else {
|
||||
writel(LCDC_LCDCFG0_CLKDIV(value - 2)
|
||||
| LCDC_LCDCFG0_CGDISHCR
|
||||
| LCDC_LCDCFG0_CGDISHEO
|
||||
| LCDC_LCDCFG0_CGDISOVR1
|
||||
| LCDC_LCDCFG0_CGDISBASE
|
||||
| vl_clk_pol,
|
||||
®s->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, ®s->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, ®s->lcdc_lcdcfg1);
|
||||
|
||||
value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ);
|
||||
value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1);
|
||||
writel(value, ®s->lcdc_lcdcfg2);
|
||||
|
||||
value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1);
|
||||
value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1);
|
||||
writel(value, ®s->lcdc_lcdcfg3);
|
||||
|
||||
/* Display size */
|
||||
value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1);
|
||||
value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1);
|
||||
writel(value, ®s->lcdc_lcdcfg4);
|
||||
|
||||
writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
|
||||
®s->lcdc_basecfg0);
|
||||
|
||||
switch (VNBITS(priv->vl_bpix)) {
|
||||
case 16:
|
||||
writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
|
||||
®s->lcdc_basecfg1);
|
||||
break;
|
||||
case 32:
|
||||
writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
|
||||
®s->lcdc_basecfg1);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
writel(LCDC_BASECFG2_XSTRIDE(0), ®s->lcdc_basecfg2);
|
||||
writel(0, ®s->lcdc_basecfg3);
|
||||
writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4);
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel(~0UL, ®s->lcdc_lcdidr);
|
||||
writel(~0UL, ®s->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, ®s->lcdc_baseaddr);
|
||||
writel(desc->control, ®s->lcdc_basectrl);
|
||||
writel(desc->next, ®s->lcdc_basenext);
|
||||
writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
|
||||
®s->lcdc_basecher);
|
||||
|
||||
/* Enable LCD */
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
|
||||
true, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
|
||||
true, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
|
||||
true, 1000, false);
|
||||
if (ret)
|
||||
printf("%s: %d: Timeout!\n", __func__, __LINE__);
|
||||
value = readl(®s->lcdc_lcden);
|
||||
writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden);
|
||||
ret = wait_for_bit_le32(®s->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", ®);
|
||||
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, ®->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, ®->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)®->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)®->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)®->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)®->vidosd0b +
|
||||
EXYNOS_VIDOSD(win_id));
|
||||
|
||||
/* set window size for window0*/
|
||||
cfg = EXYNOS_VIDOSD_SIZE(priv->vl_col * priv->vl_row);
|
||||
writel(cfg, (unsigned int)®->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)®->vidw00add0b0 +
|
||||
EXYNOS_BUFFER_OFFSET(win_id));
|
||||
writel(end_addr, (unsigned int)®->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(®->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, ®->vidcon0);
|
||||
}
|
||||
|
||||
void exynos_set_trigger(struct exynos_fb_priv *priv)
|
||||
{
|
||||
struct exynos_fb *reg = priv->reg;
|
||||
unsigned int cfg = 0;
|
||||
|
||||
cfg = readl(®->trigcon);
|
||||
|
||||
cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
|
||||
|
||||
writel(cfg, ®->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(®->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(®->vidcon0);
|
||||
cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
|
||||
writel(cfg, ®->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)®->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
|
||||
writel(cfg, (unsigned int)®->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
|
||||
cfg = readl(®->winshmap);
|
||||
cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
|
||||
writel(cfg, ®->winshmap);
|
||||
}
|
||||
|
||||
void exynos_fimd_lcd_off(struct exynos_fb_priv *priv)
|
||||
{
|
||||
struct exynos_fb *reg = priv->reg;
|
||||
unsigned int cfg = 0;
|
||||
|
||||
cfg = readl(®->vidcon0);
|
||||
cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
|
||||
writel(cfg, ®->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)®->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
|
||||
writel(cfg, (unsigned int)®->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
|
||||
cfg = readl(®->winshmap);
|
||||
cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
|
||||
writel(cfg, ®->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, ®->vidcon0);
|
||||
|
||||
cfg = readl(®->vidcon2);
|
||||
cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
|
||||
EXYNOS_VIDCON2_TVFORMATSEL_MASK |
|
||||
EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
|
||||
cfg |= EXYNOS_VIDCON2_WB_DISABLE;
|
||||
writel(cfg, ®->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)®->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)®->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)®->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)®->vidtcon2 + offset);
|
||||
}
|
||||
|
||||
/* set display mode */
|
||||
cfg = readl(®->vidcon0);
|
||||
cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
|
||||
cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
|
||||
writel(cfg, ®->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)®->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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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(®s->ctrldescl[i][0], 0);
|
||||
dcu_write32(®s->ctrldescl[i][1], 0);
|
||||
dcu_write32(®s->ctrldescl[i][2], 0);
|
||||
dcu_write32(®s->ctrldescl[i][3], 0);
|
||||
dcu_write32(®s->ctrldescl[i][4], 0);
|
||||
dcu_write32(®s->ctrldescl[i][5], 0);
|
||||
dcu_write32(®s->ctrldescl[i][6], 0);
|
||||
dcu_write32(®s->ctrldescl[i][7], 0);
|
||||
dcu_write32(®s->ctrldescl[i][8], 0);
|
||||
dcu_write32(®s->ctrldescl[i][9], 0);
|
||||
dcu_write32(®s->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(®s->ctrldescl[index][0],
|
||||
DCU_CTRLDESCLN_1_HEIGHT(fbinfo.var.yres) |
|
||||
DCU_CTRLDESCLN_1_WIDTH(fbinfo.var.xres));
|
||||
|
||||
dcu_write32(®s->ctrldescl[index][1],
|
||||
DCU_CTRLDESCLN_2_POSY(0) |
|
||||
DCU_CTRLDESCLN_2_POSX(0));
|
||||
|
||||
dcu_write32(®s->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(®s->ctrldescl[index][3],
|
||||
DCU_CTRLDESCLN_4_EN |
|
||||
DCU_CTRLDESCLN_4_TRANS(0xff) |
|
||||
DCU_CTRLDESCLN_4_BPP(bpp) |
|
||||
DCU_CTRLDESCLN_4_AB(0));
|
||||
|
||||
dcu_write32(®s->ctrldescl[index][4],
|
||||
DCU_CTRLDESCLN_5_CKMAX_R(0xff) |
|
||||
DCU_CTRLDESCLN_5_CKMAX_G(0xff) |
|
||||
DCU_CTRLDESCLN_5_CKMAX_B(0xff));
|
||||
dcu_write32(®s->ctrldescl[index][5],
|
||||
DCU_CTRLDESCLN_6_CKMIN_R(0) |
|
||||
DCU_CTRLDESCLN_6_CKMIN_G(0) |
|
||||
DCU_CTRLDESCLN_6_CKMIN_B(0));
|
||||
|
||||
dcu_write32(®s->ctrldescl[index][6],
|
||||
DCU_CTRLDESCLN_7_TILE_VER(0) |
|
||||
DCU_CTRLDESCLN_7_TILE_HOR(0));
|
||||
|
||||
dcu_write32(®s->ctrldescl[index][7], DCU_CTRLDESCLN_8_FG_FCOLOR(0));
|
||||
dcu_write32(®s->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(®s->disp_size,
|
||||
DCU_DISP_SIZE_DELTA_Y(fbinfo->var.yres) |
|
||||
DCU_DISP_SIZE_DELTA_X(fbinfo->var.xres / 16));
|
||||
|
||||
dcu_write32(®s->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(®s->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(®s->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(®s->bgnd,
|
||||
DCU_BGND_R(0) | DCU_BGND_G(0) | DCU_BGND_B(0));
|
||||
|
||||
dcu_write32(®s->mode,
|
||||
DCU_MODE_BLEND_ITER(2) |
|
||||
DCU_MODE_RASTER_EN);
|
||||
|
||||
dcu_write32(®s->threshold,
|
||||
DCU_THRESHOLD_LS_BF_VS(0x3) |
|
||||
DCU_THRESHOLD_OUT_BUF_HIGH(0x78) |
|
||||
DCU_THRESHOLD_OUT_BUF_LOW(0));
|
||||
|
||||
mode = dcu_read32(®s->mode);
|
||||
dcu_write32(®s->mode, mode | DCU_MODE_NORMAL);
|
||||
|
||||
layer_ctrldesc_init(*fbinfo, 0, pixel_format);
|
||||
|
||||
div = dcu_set_pixel_clock(fbinfo->var.pixclock);
|
||||
dcu_write32(®s->div_ratio, (div - 1));
|
||||
|
||||
dcu_write32(®s->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(¶ms, 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, ¶ms);
|
||||
|
||||
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, ®16);
|
||||
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, ®16);
|
||||
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, ®8);
|
||||
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, ®32);
|
||||
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(¶ms, panel.gdfIndex,
|
||||
panel.plnSizeX, panel.plnSizeY, stride_bytes);
|
||||
ipu_ch_param_set_buffer(¶ms, 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 *)¶ms, 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(®s->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,
|
||||
®s->hw_lcdif_ctrl);
|
||||
|
||||
writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET,
|
||||
®s->hw_lcdif_ctrl1);
|
||||
|
||||
mxsfb_system_setup();
|
||||
|
||||
writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres,
|
||||
®s->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, ®s->hw_lcdif_vdctrl0);
|
||||
writel(mode->upper_margin + mode->lower_margin +
|
||||
mode->vsync_len + mode->yres,
|
||||
®s->hw_lcdif_vdctrl1);
|
||||
writel((mode->hsync_len << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) |
|
||||
(mode->left_margin + mode->right_margin +
|
||||
mode->hsync_len + mode->xres),
|
||||
®s->hw_lcdif_vdctrl2);
|
||||
writel(((mode->left_margin + mode->hsync_len) <<
|
||||
LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) |
|
||||
(mode->upper_margin + mode->vsync_len),
|
||||
®s->hw_lcdif_vdctrl3);
|
||||
writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres,
|
||||
®s->hw_lcdif_vdctrl4);
|
||||
|
||||
writel(fb_addr, ®s->hw_lcdif_cur_buf);
|
||||
writel(fb_addr, ®s->hw_lcdif_next_buf);
|
||||
|
||||
/* Flush FIFO first */
|
||||
writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_set);
|
||||
|
||||
#ifndef CONFIG_VIDEO_MXS_MODE_SYSTEM
|
||||
/* Sync signals ON */
|
||||
setbits_le32(®s->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON);
|
||||
#endif
|
||||
|
||||
/* FIFO cleared */
|
||||
writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_clr);
|
||||
|
||||
/* RUN! */
|
||||
writel(LCDIF_CTRL_RUN, ®s->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(®s->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, ®s->hw_lcdif_cur_buf_reg);
|
||||
writel(fb, ®s->hw_lcdif_next_buf_reg);
|
||||
writel(LCDIF_CTRL1_VSYNC_EDGE_IRQ, ®s->hw_lcdif_ctrl1_clr);
|
||||
while (--timeout) {
|
||||
if (readl(®s->hw_lcdif_ctrl1_reg) &
|
||||
LCDIF_CTRL1_VSYNC_EDGE_IRQ)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
mxs_reset_block((struct mxs_register_32 *)®s->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
Reference in New Issue
Block a user