video: add nexell video driver (display/video driver)

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- nexell_display.c: Changed to DM, CONFIG_FB_ADDR can not be used
  anymore because framebuffer is allocated by video_reserve() in
  video-uclass.c. Therefore code changed appropriately.
- '#ifdef CONFIG...' changed to 'if (IS_ENABLED(CONFIG...))' where
  possible (and similar).
- livetree API (dev_read_...) is used instead of fdt one (fdt...).

Signed-off-by: Stefan Bosch <stefan_b@posteo.net>
This commit is contained in:
Stefan Bosch 2020-07-10 19:07:36 +02:00 committed by Tom Rini
parent 9c5d377583
commit e1e96ba6a2
10 changed files with 2607 additions and 0 deletions

View File

@ -644,6 +644,16 @@ source "drivers/video/bridge/Kconfig"
source "drivers/video/imx/Kconfig"
config VIDEO_NX
bool "Enable video support on Nexell SoC"
depends on ARCH_S5P6818 || ARCH_S5P4418
help
Nexell SoC supports many video output options including eDP and
HDMI. This option enables this support which can be used on devices
which have an eDP display connected.
source "drivers/video/nexell/Kconfig"
config VIDEO
bool "Enable legacy video support"
depends on !DM_VIDEO

View File

@ -62,6 +62,7 @@ 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_NX) += nexell_display.o videomodes.o nexell/
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

View File

@ -0,0 +1,27 @@
if VIDEO_NX
menu "LCD select"
config VIDEO_NX_RGB
bool "RGB LCD"
help
Support for RGB lcd output.
config VIDEO_NX_LVDS
bool "LVDS LCD"
help
Support for LVDS lcd output.
config VIDEO_NX_MIPI
bool "MiPi"
help
Support for MiPi lcd output.
config VIDEO_NX_HDMI
bool "HDMI"
help
Support for hdmi output.
endmenu
endif

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2016 Nexell
# Junghyun, kim<jhkim@nexell.co.kr>
obj-$(CONFIG_VIDEO_NX) += s5pxx18_dp.o
obj-$(CONFIG_VIDEO_NX) += soc/
obj-$(CONFIG_VIDEO_NX_RGB) += s5pxx18_dp_rgb.o
obj-$(CONFIG_VIDEO_NX_LVDS) += s5pxx18_dp_lvds.o
obj-$(CONFIG_VIDEO_NX_MIPI) += s5pxx18_dp_mipi.o
obj-$(CONFIG_VIDEO_NX_HDMI) += s5pxx18_dp_hdmi.o

View File

@ -0,0 +1,341 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Nexell Co., Ltd.
*
* Author: junghyun, kim <jhkim@nexell.co.kr>
*/
#include <config.h>
#include <common.h>
#include <errno.h>
#include <log.h>
#include <asm/arch/reset.h>
#include <asm/arch/nexell.h>
#include <asm/arch/display.h>
#include "soc/s5pxx18_soc_disptop.h"
#include "soc/s5pxx18_soc_dpc.h"
#include "soc/s5pxx18_soc_mlc.h"
#define MLC_LAYER_RGB_0 0 /* number of RGB layer 0 */
#define MLC_LAYER_RGB_1 1 /* number of RGB layer 1 */
#define MLC_LAYER_VIDEO 3 /* number of Video layer: 3 = VIDEO */
#define __io_address(a) (void *)(uintptr_t)(a)
void dp_control_init(int module)
{
void *base;
/* top */
base = __io_address(nx_disp_top_get_physical_address());
nx_disp_top_set_base_address(base);
/* control */
base = __io_address(nx_dpc_get_physical_address(module));
nx_dpc_set_base_address(module, base);
/* top controller */
nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_NEGATE);
/* display controller */
nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_NEGATE);
nx_dpc_set_clock_pclk_mode(module, nx_pclkmode_always);
}
int dp_control_setup(int module,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl)
{
unsigned int out_format;
unsigned int delay_mask;
int rgb_pvd = 0, hsync_cp1 = 7, vsync_fram = 7, de_cp2 = 7;
int v_vso = 1, v_veo = 1, e_vso = 1, e_veo = 1;
int interlace = 0;
int invert_field;
int swap_rb;
unsigned int yc_order;
int vck_select;
int vclk_invert;
int emb_sync;
enum nx_dpc_dither r_dither, g_dither, b_dither;
int rgb_mode = 0;
if (NULL == sync || NULL == ctrl) {
debug("error, dp.%d not set sync or pad clock info !!!\n",
module);
return -EINVAL;
}
out_format = ctrl->out_format;
delay_mask = ctrl->delay_mask;
interlace = sync->interlace;
invert_field = ctrl->invert_field;
swap_rb = ctrl->swap_RB;
yc_order = ctrl->yc_order;
vck_select = ctrl->vck_select;
vclk_invert = ctrl->clk_inv_lv0 | ctrl->clk_inv_lv1;
emb_sync = (out_format == DPC_FORMAT_CCIR656 ? 1 : 0);
/* set delay mask */
if (delay_mask & DP_SYNC_DELAY_RGB_PVD)
rgb_pvd = ctrl->d_rgb_pvd;
if (delay_mask & DP_SYNC_DELAY_HSYNC_CP1)
hsync_cp1 = ctrl->d_hsync_cp1;
if (delay_mask & DP_SYNC_DELAY_VSYNC_FRAM)
vsync_fram = ctrl->d_vsync_fram;
if (delay_mask & DP_SYNC_DELAY_DE_CP)
de_cp2 = ctrl->d_de_cp2;
if (ctrl->vs_start_offset != 0 ||
ctrl->vs_end_offset != 0 ||
ctrl->ev_start_offset != 0 || ctrl->ev_end_offset != 0) {
v_vso = ctrl->vs_start_offset;
v_veo = ctrl->vs_end_offset;
e_vso = ctrl->ev_start_offset;
e_veo = ctrl->ev_end_offset;
}
if (nx_dpc_format_rgb555 == out_format ||
nx_dpc_format_mrgb555a == out_format ||
nx_dpc_format_mrgb555b == out_format) {
r_dither = nx_dpc_dither_5bit;
g_dither = nx_dpc_dither_5bit;
b_dither = nx_dpc_dither_5bit;
rgb_mode = 1;
} else if (nx_dpc_format_rgb565 == out_format ||
nx_dpc_format_mrgb565 == out_format) {
r_dither = nx_dpc_dither_5bit;
b_dither = nx_dpc_dither_5bit;
g_dither = nx_dpc_dither_6bit, rgb_mode = 1;
} else if ((nx_dpc_format_rgb666 == out_format) ||
(nx_dpc_format_mrgb666 == out_format)) {
r_dither = nx_dpc_dither_6bit;
g_dither = nx_dpc_dither_6bit;
b_dither = nx_dpc_dither_6bit;
rgb_mode = 1;
} else {
r_dither = nx_dpc_dither_bypass;
g_dither = nx_dpc_dither_bypass;
b_dither = nx_dpc_dither_bypass;
rgb_mode = 1;
}
/* CLKGEN0/1 */
nx_dpc_set_clock_source(module, 0, ctrl->clk_src_lv0 == 3 ?
6 : ctrl->clk_src_lv0);
nx_dpc_set_clock_divisor(module, 0, ctrl->clk_div_lv0);
nx_dpc_set_clock_source(module, 1, ctrl->clk_src_lv1);
nx_dpc_set_clock_divisor(module, 1, ctrl->clk_div_lv1);
nx_dpc_set_clock_out_delay(module, 0, ctrl->clk_delay_lv0);
nx_dpc_set_clock_out_delay(module, 1, ctrl->clk_delay_lv1);
/* LCD out */
nx_dpc_set_mode(module, out_format, interlace, invert_field,
rgb_mode, swap_rb, yc_order, emb_sync, emb_sync,
vck_select, vclk_invert, 0);
nx_dpc_set_hsync(module, sync->h_active_len, sync->h_sync_width,
sync->h_front_porch, sync->h_back_porch,
sync->h_sync_invert);
nx_dpc_set_vsync(module, sync->v_active_len, sync->v_sync_width,
sync->v_front_porch, sync->v_back_porch,
sync->v_sync_invert, sync->v_active_len,
sync->v_sync_width, sync->v_front_porch,
sync->v_back_porch);
nx_dpc_set_vsync_offset(module, v_vso, v_veo, e_vso, e_veo);
nx_dpc_set_delay(module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2);
nx_dpc_set_dither(module, r_dither, g_dither, b_dither);
if (IS_ENABLED(CONFIG_MACH_S5P6818)) {
/* Set TFT_CLKCTRL (offset : 1030h)
* Field name : DPC0_CLKCTRL, DPC1_CLKCRL
* Default value : clk_inv_lv0/1 = 0 : PADCLK_InvCLK
* Invert case : clk_inv_lv0/1 = 1 : PADCLK_CLK
*/
if (module == 0 && ctrl->clk_inv_lv0)
nx_disp_top_set_padclock(padmux_primary_mlc,
padclk_clk);
if (module == 1 && ctrl->clk_inv_lv1)
nx_disp_top_set_padclock(padmux_secondary_mlc,
padclk_clk);
}
debug("%s: dp.%d x:%4d, hf:%3d, hb:%3d, hs:%3d, hi=%d\n",
__func__, module, sync->h_active_len, sync->h_front_porch,
sync->h_back_porch, sync->h_sync_width, sync->h_sync_invert);
debug("%s: dp.%d y:%4d, vf:%3d, vb:%3d, vs:%3d, vi=%d\n",
__func__, module, sync->v_active_len, sync->v_front_porch,
sync->v_back_porch, sync->v_sync_width, sync->h_sync_invert);
debug("%s: dp.%d ck.0:%d:%d:%d, ck.1:%d:%d:%d\n",
__func__, module,
ctrl->clk_src_lv0, ctrl->clk_div_lv0, ctrl->clk_inv_lv0,
ctrl->clk_src_lv1, ctrl->clk_div_lv1, ctrl->clk_inv_lv1);
debug("%s: dp.%d vs:%d, ve:%d, es:%d, ee:%d\n",
__func__, module, v_vso, v_veo, e_vso, e_veo);
debug("%s: dp.%d delay RGB:%d, hs:%d, vs:%d, de:%d, fmt:0x%x\n",
__func__, module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2,
out_format);
return 0;
}
void dp_control_enable(int module, int on)
{
debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF");
nx_dpc_set_dpc_enable(module, on);
nx_dpc_set_clock_divisor_enable(module, on);
}
void dp_plane_init(int module)
{
void *base = __io_address(nx_mlc_get_physical_address(module));
nx_mlc_set_base_address(module, base);
nx_mlc_set_clock_pclk_mode(module, nx_pclkmode_always);
nx_mlc_set_clock_bclk_mode(module, nx_bclkmode_always);
}
int dp_plane_screen_setup(int module, struct dp_plane_top *top)
{
int width = top->screen_width;
int height = top->screen_height;
int interlace = top->interlace;
int video_prior = top->video_prior;
unsigned int bg_color = top->back_color;
/* MLC TOP layer */
nx_mlc_set_screen_size(module, width, height);
nx_mlc_set_layer_priority(module, video_prior);
nx_mlc_set_background(module, bg_color);
nx_mlc_set_field_enable(module, interlace);
nx_mlc_set_rgblayer_gama_table_power_mode(module, 0, 0, 0);
nx_mlc_set_rgblayer_gama_table_sleep_mode(module, 1, 1, 1);
nx_mlc_set_rgblayer_gamma_enable(module, 0);
nx_mlc_set_dither_enable_when_using_gamma(module, 0);
nx_mlc_set_gamma_priority(module, 0);
nx_mlc_set_top_power_mode(module, 1);
nx_mlc_set_top_sleep_mode(module, 0);
debug("%s: dp.%d screen %dx%d, %s, priority:%d, bg:0x%x\n",
__func__, module, width, height,
interlace ? "Interlace" : "Progressive",
video_prior, bg_color);
return 0;
}
void dp_plane_screen_enable(int module, int on)
{
/* enable top screen */
nx_mlc_set_mlc_enable(module, on);
nx_mlc_set_top_dirty_flag(module);
debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF");
}
int dp_plane_layer_setup(int module, struct dp_plane_info *plane)
{
int sx = plane->left;
int sy = plane->top;
int ex = sx + plane->width - 1;
int ey = sy + plane->height - 1;
int pixel_byte = plane->pixel_byte;
int mem_lock_size = 16; /* fix mem lock size */
int layer = plane->layer;
unsigned int format = plane->format;
if (!plane->enable)
return -EINVAL;
/* MLC layer */
nx_mlc_set_lock_size(module, layer, mem_lock_size);
nx_mlc_set_alpha_blending(module, layer, 0, 15);
nx_mlc_set_transparency(module, layer, 0, 0);
nx_mlc_set_color_inversion(module, layer, 0, 0);
nx_mlc_set_rgblayer_invalid_position(module, layer, 0, 0, 0, 0, 0, 0);
nx_mlc_set_rgblayer_invalid_position(module, layer, 1, 0, 0, 0, 0, 0);
nx_mlc_set_format_rgb(module, layer, format);
nx_mlc_set_position(module, layer, sx, sy, ex, ey);
nx_mlc_set_rgblayer_stride(module, layer, pixel_byte,
plane->width * pixel_byte);
nx_mlc_set_rgblayer_address(module, layer, plane->fb_base);
debug("%s: dp.%d.%d %d * %d, %dbpp, fmt:0x%x\n",
__func__, module, layer, plane->width, plane->height,
pixel_byte * 8, format);
debug("%s: b:0x%x, l:%d, t:%d, r:%d, b:%d, hs:%d, vs:%d\n",
__func__, plane->fb_base, sx, sy, ex, ey,
plane->width * pixel_byte, pixel_byte);
return 0;
}
int dp_plane_set_enable(int module, int layer, int on)
{
int hl, hc;
int vl, vc;
debug("%s: dp.%d.%d %s:%s\n",
__func__, module, layer,
layer == MLC_LAYER_VIDEO ? "Video" : "RGB",
on ? "ON" : "OFF");
if (layer != MLC_LAYER_VIDEO) {
nx_mlc_set_layer_enable(module, layer, on);
nx_mlc_set_dirty_flag(module, layer);
return 0;
}
/* video layer */
if (on) {
nx_mlc_set_video_layer_line_buffer_power_mode(module, 1);
nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 0);
nx_mlc_set_layer_enable(module, layer, 1);
nx_mlc_set_dirty_flag(module, layer);
} else {
nx_mlc_set_layer_enable(module, layer, 0);
nx_mlc_set_dirty_flag(module, layer);
nx_mlc_get_video_layer_scale_filter(module,
&hl, &hc, &vl, &vc);
if (hl || hc || vl || vc)
nx_mlc_set_video_layer_scale_filter(module, 0, 0, 0, 0);
nx_mlc_set_video_layer_line_buffer_power_mode(module, 0);
nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 1);
nx_mlc_set_dirty_flag(module, layer);
}
return 0;
}
void dp_plane_layer_enable(int module,
struct dp_plane_info *plane, int on)
{
dp_plane_set_enable(module, plane->layer, on);
}
int dp_plane_set_address(int module, int layer, unsigned int address)
{
nx_mlc_set_rgblayer_address(module, layer, address);
nx_mlc_set_dirty_flag(module, layer);
return 0;
}
int dp_plane_wait_vsync(int module, int layer, int fps)
{
int cnt = 0;
if (fps == 0)
return (int)nx_mlc_get_dirty_flag(module, layer);
while (fps > cnt++) {
while (nx_mlc_get_dirty_flag(module, layer))
;
nx_mlc_set_dirty_flag(module, layer);
}
return 0;
}

View File

@ -0,0 +1,545 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Nexell Co., Ltd.
*
* Author: junghyun, kim <jhkim@nexell.co.kr>
*/
#include <config.h>
#include <common.h>
#include <errno.h>
#include <log.h>
#include <asm/arch/nexell.h>
#include <asm/arch/tieoff.h>
#include <asm/arch/reset.h>
#include <asm/arch/display.h>
#include <linux/delay.h>
#include "soc/s5pxx18_soc_dpc.h"
#include "soc/s5pxx18_soc_hdmi.h"
#include "soc/s5pxx18_soc_disptop.h"
#include "soc/s5pxx18_soc_disptop_clk.h"
#define __io_address(a) (void *)(uintptr_t)(a)
static const u8 hdmiphy_preset74_25[32] = {
0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0xc8, 0x81,
0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a,
0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54,
0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x10, 0x80,
};
static const u8 hdmiphy_preset148_5[32] = {
0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0xc8, 0x81,
0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a,
0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54,
0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
};
#define HDMIPHY_PRESET_TABLE_SIZE (32)
enum NXP_HDMI_PRESET {
NXP_HDMI_PRESET_720P = 0, /* 1280 x 720 */
NXP_HDMI_PRESET_1080P, /* 1920 x 1080 */
NXP_HDMI_PRESET_MAX
};
static void hdmi_reset(void)
{
nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_NEGATE);
nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_NEGATE);
nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_NEGATE);
}
static int hdmi_phy_enable(int preset, int enable)
{
const u8 *table = NULL;
int size = 0;
u32 addr, i = 0;
if (!enable)
return 0;
switch (preset) {
case NXP_HDMI_PRESET_720P:
table = hdmiphy_preset74_25;
size = 32;
break;
case NXP_HDMI_PRESET_1080P:
table = hdmiphy_preset148_5;
size = 31;
break;
default:
printf("hdmi: phy not support preset %d\n", preset);
return -EINVAL;
}
nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7));
nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7));
nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4));
nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4));
nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7));
nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7));
for (i = 0, addr = HDMI_PHY_REG04; size > i; i++, addr += 4) {
nx_hdmi_set_reg(0, addr, table[i]);
nx_hdmi_set_reg(0, addr, table[i]);
}
nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80);
nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80);
nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7));
nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7));
debug("%s: preset = %d\n", __func__, preset);
return 0;
}
static inline int hdmi_wait_phy_ready(void)
{
int count = 500;
do {
u32 val = nx_hdmi_get_reg(0, HDMI_LINK_PHY_STATUS_0);
if (val & 0x01) {
printf("HDMI: phy ready...\n");
return 1;
}
mdelay(10);
} while (count--);
return 0;
}
static inline int hdmi_get_vsync(int preset,
struct dp_sync_info *sync,
struct dp_ctrl_info *ctrl)
{
switch (preset) {
case NXP_HDMI_PRESET_720P: /* 720p: 1280x720 */
sync->h_active_len = 1280;
sync->h_sync_width = 40;
sync->h_back_porch = 220;
sync->h_front_porch = 110;
sync->h_sync_invert = 0;
sync->v_active_len = 720;
sync->v_sync_width = 5;
sync->v_back_porch = 20;
sync->v_front_porch = 5;
sync->v_sync_invert = 0;
break;
case NXP_HDMI_PRESET_1080P: /* 1080p: 1920x1080 */
sync->h_active_len = 1920;
sync->h_sync_width = 44;
sync->h_back_porch = 148;
sync->h_front_porch = 88;
sync->h_sync_invert = 0;
sync->v_active_len = 1080;
sync->v_sync_width = 5;
sync->v_back_porch = 36;
sync->v_front_porch = 4;
sync->v_sync_invert = 0;
break;
default:
printf("HDMI: not support preset sync %d\n", preset);
return -EINVAL;
}
ctrl->clk_src_lv0 = 4;
ctrl->clk_div_lv0 = 1;
ctrl->clk_src_lv1 = 7;
ctrl->clk_div_lv1 = 1;
ctrl->out_format = outputformat_rgb888;
ctrl->delay_mask = (DP_SYNC_DELAY_RGB_PVD | DP_SYNC_DELAY_HSYNC_CP1 |
DP_SYNC_DELAY_VSYNC_FRAM | DP_SYNC_DELAY_DE_CP);
ctrl->d_rgb_pvd = 0;
ctrl->d_hsync_cp1 = 0;
ctrl->d_vsync_fram = 0;
ctrl->d_de_cp2 = 7;
/* HFP + HSW + HBP + AVWidth-VSCLRPIXEL- 1; */
ctrl->vs_start_offset = (sync->h_front_porch + sync->h_sync_width +
sync->h_back_porch + sync->h_active_len - 1);
ctrl->vs_end_offset = 0;
/* HFP + HSW + HBP + AVWidth-EVENVSCLRPIXEL- 1 */
ctrl->ev_start_offset = (sync->h_front_porch + sync->h_sync_width +
sync->h_back_porch + sync->h_active_len - 1);
ctrl->ev_end_offset = 0;
debug("%s: preset: %d\n", __func__, preset);
return 0;
}
static void hdmi_clock(void)
{
void *base =
__io_address(nx_disp_top_clkgen_get_physical_address
(to_mipi_clkgen));
nx_disp_top_clkgen_set_base_address(to_mipi_clkgen, base);
nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 0);
nx_disp_top_clkgen_set_clock_pclk_mode(to_mipi_clkgen,
nx_pclkmode_always);
nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, HDMI_SPDIF_CLKOUT,
2);
nx_disp_top_clkgen_set_clock_divisor(to_mipi_clkgen, HDMI_SPDIF_CLKOUT,
2);
nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, 1, 7);
nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 1);
/* must initialize this !!! */
nx_disp_top_hdmi_set_vsync_hsstart_end(0, 0);
nx_disp_top_hdmi_set_vsync_start(0);
nx_disp_top_hdmi_set_hactive_start(0);
nx_disp_top_hdmi_set_hactive_end(0);
}
static void hdmi_vsync(struct dp_sync_info *sync)
{
int width = sync->h_active_len;
int hsw = sync->h_sync_width;
int hbp = sync->h_back_porch;
int height = sync->v_active_len;
int vsw = sync->v_sync_width;
int vbp = sync->v_back_porch;
int v_sync_s = vsw + vbp + height - 1;
int h_active_s = hsw + hbp;
int h_active_e = width + hsw + hbp;
int v_sync_hs_se0 = hsw + hbp + 1;
int v_sync_hs_se1 = hsw + hbp + 2;
nx_disp_top_hdmi_set_vsync_start(v_sync_s);
nx_disp_top_hdmi_set_hactive_start(h_active_s);
nx_disp_top_hdmi_set_hactive_end(h_active_e);
nx_disp_top_hdmi_set_vsync_hsstart_end(v_sync_hs_se0, v_sync_hs_se1);
}
static int hdmi_prepare(struct dp_sync_info *sync)
{
int width = sync->h_active_len;
int hsw = sync->h_sync_width;
int hfp = sync->h_front_porch;
int hbp = sync->h_back_porch;
int height = sync->v_active_len;
int vsw = sync->v_sync_width;
int vfp = sync->v_front_porch;
int vbp = sync->v_back_porch;
u32 h_blank, h_line, h_sync_start, h_sync_end;
u32 v_blank, v2_blank, v_line;
u32 v_sync_line_bef_1, v_sync_line_bef_2;
u32 fixed_ffff = 0xffff;
/* calculate sync variables */
h_blank = hfp + hsw + hbp;
v_blank = vfp + vsw + vbp;
v2_blank = height + vfp + vsw + vbp;
v_line = height + vfp + vsw + vbp; /* total v */
h_line = width + hfp + hsw + hbp; /* total h */
h_sync_start = hfp;
h_sync_end = hfp + hsw;
v_sync_line_bef_1 = vfp;
v_sync_line_bef_2 = vfp + vsw;
/* no blue screen mode, encoding order as it is */
nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0, (0 << 5) | (1 << 4));
/* set HDMI_LINK_BLUE_SCREEN_* to 0x0 */
nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_0, 0x5555);
nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_1, 0x5555);
nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_0, 0x5555);
nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_1, 0x5555);
nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_0, 0x5555);
nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_1, 0x5555);
/* set HDMI_CON_1 to 0x0 */
nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_1, 0x0);
nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_2, 0x0);
/* set interrupt : enable hpd_plug, hpd_unplug */
nx_hdmi_set_reg(0, HDMI_LINK_INTC_CON_0,
(1 << 6) | (1 << 3) | (1 << 2));
/* set STATUS_EN to 0x17 */
nx_hdmi_set_reg(0, HDMI_LINK_STATUS_EN, 0x17);
/* TODO set HDP to 0x0 : later check hpd */
nx_hdmi_set_reg(0, HDMI_LINK_HPD, 0x0);
/* set MODE_SEL to 0x02 */
nx_hdmi_set_reg(0, HDMI_LINK_MODE_SEL, 0x2);
/* set H_BLANK_*, V1_BLANK_*, V2_BLANK_*, V_LINE_*,
* H_LINE_*, H_SYNC_START_*, H_SYNC_END_ *
* V_SYNC_LINE_BEF_1_*, V_SYNC_LINE_BEF_2_*
*/
nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_0, h_blank % 256);
nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_1, h_blank >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_0, v_blank % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_1, v_blank >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_0, v2_blank % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_1, v2_blank >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_0, v_line % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_1, v_line >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_0, h_line % 256);
nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_1, h_line >> 8);
if (width == 1280) {
nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x1);
nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x1);
} else {
nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x0);
nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x0);
}
nx_hdmi_set_reg(0, HDMI_LINK_INT_PRO_MODE, 0x0);
nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_0, (h_sync_start % 256) - 2);
nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_1, h_sync_start >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_0, (h_sync_end % 256) - 2);
nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_1, h_sync_end >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_0,
v_sync_line_bef_1 % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_1,
v_sync_line_bef_1 >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_0,
v_sync_line_bef_2 % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_1,
v_sync_line_bef_2 >> 8);
/* Set V_SYNC_LINE_AFT*, V_SYNC_LINE_AFT_PXL*, VACT_SPACE* */
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_0, fixed_ffff % 256);
nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_1, fixed_ffff >> 8);
nx_hdmi_set_reg(0, HDMI_LINK_CSC_MUX, 0x0);
nx_hdmi_set_reg(0, HDMI_LINK_SYNC_GEN_MUX, 0x0);
nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_0, 0xfd);
nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_1, 0x01);
nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_0, 0x0d);
nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_1, 0x3a);
nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_2, 0x08);
/* Set DC_CONTROL to 0x00 */
nx_hdmi_set_reg(0, HDMI_LINK_DC_CONTROL, 0x0);
if (IS_ENABLED(CONFIG_HDMI_PATTERN))
nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x1);
else
nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x0);
nx_hdmi_set_reg(0, HDMI_LINK_GCP_CON, 0x0a);
return 0;
}
static void hdmi_init(void)
{
void *base;
/**
* [SEQ 2] set the HDMI CLKGEN's PCLKMODE to always enabled
*/
base =
__io_address(nx_disp_top_clkgen_get_physical_address(hdmi_clkgen));
nx_disp_top_clkgen_set_base_address(hdmi_clkgen, base);
nx_disp_top_clkgen_set_clock_pclk_mode(hdmi_clkgen, nx_pclkmode_always);
base = __io_address(nx_hdmi_get_physical_address(0));
nx_hdmi_set_base_address(0, base);
/**
* [SEQ 3] set the 0xC001100C[0] to 1
*/
nx_tieoff_set(NX_TIEOFF_DISPLAYTOP0_i_HDMI_PHY_REFCLK_SEL, 1);
/**
* [SEQ 4] release the resets of HDMI.i_PHY_nRST and HDMI.i_nRST
*/
nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_NEGATE);
nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_NEGATE);
}
void hdmi_enable(int input, int preset, struct dp_sync_info *sync, int enable)
{
if (enable) {
nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0,
(nx_hdmi_get_reg(0, HDMI_LINK_HDMI_CON_0) |
0x1));
hdmi_vsync(sync);
} else {
hdmi_phy_enable(preset, 0);
}
}
static int hdmi_setup(int input, int preset,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl)
{
u32 HDMI_SEL = 0;
int ret;
switch (input) {
case DP_DEVICE_DP0:
HDMI_SEL = primary_mlc;
break;
case DP_DEVICE_DP1:
HDMI_SEL = secondary_mlc;
break;
case DP_DEVICE_RESCONV:
HDMI_SEL = resolution_conv;
break;
default:
printf("HDMI: not support source device %d\n", input);
return -EINVAL;
}
/**
* [SEQ 5] set up the HDMI PHY to specific video clock.
*/
ret = hdmi_phy_enable(preset, 1);
if (ret < 0)
return ret;
/**
* [SEQ 6] I2S (or SPDIFTX) configuration for the source audio data
* this is done in another user app - ex> Android Audio HAL
*/
/**
* [SEQ 7] Wait for ECID ready
*/
/**
* [SEQ 8] release the resets of HDMI.i_VIDEO_nRST and HDMI.i_SPDIF_nRST
* and HDMI.i_TMDS_nRST
*/
hdmi_reset();
/**
* [SEQ 9] Wait for HDMI PHY ready (wait until 0xC0200020.[0], 1)
*/
if (hdmi_wait_phy_ready() == 0) {
printf("%s: failed to wait for hdmiphy ready\n", __func__);
hdmi_phy_enable(preset, 0);
return -EIO;
}
/* set mux */
nx_disp_top_set_hdmimux(1, HDMI_SEL);
/**
* [SEC 10] Set the DPC CLKGEN's Source Clock to HDMI_CLK &
* Set Sync Parameter
*/
hdmi_clock();
/* set hdmi link clk to clkgen vs default is hdmi phy clk */
/**
* [SEQ 11] Set up the HDMI Converter parameters
*/
hdmi_get_vsync(preset, sync, ctrl);
hdmi_prepare(sync);
return 0;
}
void nx_hdmi_display(int module,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_plane_top *top, struct dp_plane_info *planes,
struct dp_hdmi_dev *dev)
{
struct dp_plane_info *plane = planes;
int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
int count = top->plane_num;
int preset = dev->preset;
int i = 0;
debug("HDMI: display.%d\n", module);
switch (preset) {
case 0:
top->screen_width = 1280;
top->screen_height = 720;
sync->h_active_len = 1280;
sync->v_active_len = 720;
break;
case 1:
top->screen_width = 1920;
top->screen_height = 1080;
sync->h_active_len = 1920;
sync->v_active_len = 1080;
break;
default:
printf("hdmi not support preset %d\n", preset);
return;
}
printf("HDMI: display.%d, preset %d (%4d * %4d)\n",
module, preset, top->screen_width, top->screen_height);
dp_control_init(module);
dp_plane_init(module);
hdmi_init();
hdmi_setup(input, preset, sync, ctrl);
dp_plane_screen_setup(module, top);
for (i = 0; count > i; i++, plane++) {
if (!plane->enable)
continue;
dp_plane_layer_setup(module, plane);
dp_plane_layer_enable(module, plane, 1);
}
dp_plane_screen_enable(module, 1);
dp_control_setup(module, sync, ctrl);
dp_control_enable(module, 1);
hdmi_enable(input, preset, sync, 1);
}

View File

@ -0,0 +1,274 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Nexell Co., Ltd.
*
* Author: junghyun, kim <jhkim@nexell.co.kr>
*/
#include <config.h>
#include <common.h>
#include <errno.h>
#include <asm/arch/nexell.h>
#include <asm/arch/reset.h>
#include <asm/arch/display.h>
#include "soc/s5pxx18_soc_lvds.h"
#include "soc/s5pxx18_soc_disptop.h"
#include "soc/s5pxx18_soc_disptop_clk.h"
#define __io_address(a) (void *)(uintptr_t)(a)
static void lvds_phy_reset(void)
{
nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_NEGATE);
}
static void lvds_init(void)
{
int clkid = DP_CLOCK_LVDS;
int index = 0;
void *base;
base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid));
nx_disp_top_clkgen_set_base_address(clkid, base);
nx_lvds_initialize();
for (index = 0; nx_lvds_get_number_of_module() > index; index++)
nx_lvds_set_base_address(index,
(void *)__io_address(nx_lvds_get_physical_address(index)));
nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always);
}
static void lvds_enable(int enable)
{
int clkid = DP_CLOCK_LVDS;
int on = (enable ? 1 : 0);
nx_disp_top_clkgen_set_clock_divisor_enable(clkid, on);
}
static int lvds_setup(int module, int input,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_lvds_dev *dev)
{
unsigned int val;
int clkid = DP_CLOCK_LVDS;
enum dp_lvds_format format = DP_LVDS_FORMAT_JEIDA;
u32 voltage = DEF_VOLTAGE_LEVEL;
if (dev) {
format = dev->lvds_format;
voltage = dev->voltage_level;
}
printf("LVDS: ");
printf("%s, ", format == DP_LVDS_FORMAT_VESA ? "VESA" :
format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC");
printf("voltage LV:0x%x\n", voltage);
/*
*-------- predefined type.
* only change iTA to iTE in VESA mode
* wire [34:0] loc_VideoIn =
* {4'hf, 4'h0, i_VDEN, i_VSYNC, i_HSYNC, i_VD[23:0] };
*/
u32 VSYNC = 25;
u32 HSYNC = 24;
u32 VDEN = 26; /* bit position */
u32 ONE = 34;
u32 ZERO = 27;
/*====================================================
* current not use location mode
*====================================================
*/
u32 LOC_A[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE};
u32 LOC_B[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE};
u32 LOC_C[7] = {VDEN, VSYNC, HSYNC, ONE, HSYNC, VSYNC, VDEN};
u32 LOC_D[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO};
u32 LOC_E[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO};
switch (input) {
case DP_DEVICE_DP0:
input = 0;
break;
case DP_DEVICE_DP1:
input = 1;
break;
case DP_DEVICE_RESCONV:
input = 2;
break;
default:
return -EINVAL;
}
/*
* select TOP MUX
*/
nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 0);
nx_disp_top_clkgen_set_clock_source(clkid, 0, ctrl->clk_src_lv0);
nx_disp_top_clkgen_set_clock_divisor(clkid, 0, ctrl->clk_div_lv0);
nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv1);
nx_disp_top_clkgen_set_clock_divisor(clkid, 1, ctrl->clk_div_lv1);
/*
* LVDS Control Pin Setting
*/
val = (0 << 30) | /* CPU_I_VBLK_FLAG_SEL */
(0 << 29) | /* CPU_I_BVLK_FLAG */
(1 << 28) | /* SKINI_BST */
(1 << 27) | /* DLYS_BST */
(0 << 26) | /* I_AUTO_SEL */
(format << 19) | /* JEiDA data packing */
(0x1B << 13) | /* I_LOCK_PPM_SET, PPM setting for PLL lock */
(0x638 << 1); /* I_DESKEW_CNT_SEL, period of de-skew region */
nx_lvds_set_lvdsctrl0(0, val);
val = (0 << 28) | /* I_ATE_MODE, function mode */
(0 << 27) | /* I_TEST_CON_MODE, DA (test ctrl mode) */
(0 << 24) | /* I_TX4010X_DUMMY */
(0 << 15) | /* SKCCK 0 */
(0 << 12) | /* SKC4 (TX output skew control pin at ODD ch4) */
(0 << 9) | /* SKC3 (TX output skew control pin at ODD ch3) */
(0 << 6) | /* SKC2 (TX output skew control pin at ODD ch2) */
(0 << 3) | /* SKC1 (TX output skew control pin at ODD ch1) */
(0 << 0); /* SKC0 (TX output skew control pin at ODD ch0) */
nx_lvds_set_lvdsctrl1(0, val);
val = (0 << 15) | /* CK_POL_SEL, Input clock, bypass */
(0 << 14) | /* VSEL, VCO Freq. range. 0: Low(40MHz~90MHz),
* 1: High(90MHz~160MHz) */
(0x1 << 12) | /* S (Post-scaler) */
(0xA << 6) | /* M (Main divider) */
(0xA << 0); /* P (Pre-divider) */
nx_lvds_set_lvdsctrl2(0, val);
val = (0x03 << 6) | /* SK_BIAS, Bias current ctrl pin */
(0 << 5) | /* SKEWINI, skew selection pin, 0: bypass,
* 1: skew enable */
(0 << 4) | /* SKEW_EN_H, skew block power down, 0: power down,
* 1: operating */
(1 << 3) | /* CNTB_TDLY, delay control pin */
(0 << 2) | /* SEL_DATABF, input clock 1/2 division cont. pin */
(0x3 << 0); /* SKEW_REG_CUR, regulator bias current selection
* in SKEW block */
nx_lvds_set_lvdsctrl3(0, val);
val = (0 << 28) | /* FLT_CNT, filter control pin for PLL */
(0 << 27) | /* VOD_ONLY_CNT, the pre-emphasis's pre-diriver
* control pin (VOD only) */
(0 << 26) | /* CNNCT_MODE_SEL, connectivity mode selection,
* 0:TX operating, 1:con check */
(0 << 24) | /* CNNCT_CNT, connectivity ctrl pin,
* 0: tx operating, 1: con check */
(0 << 23) | /* VOD_HIGH_S, VOD control pin, 1: Vod only */
(0 << 22) | /* SRC_TRH, source termination resistor sel. pin */
(voltage << 14) |
(0x01 << 6) | /* CNT_PEN_H, TX driver pre-emphasis level cont. */
(0x4 << 3) | /* FC_CODE, vos control pin */
(0 << 2) | /* OUTCON, TX Driver state selectioin pin, 0:Hi-z,
* 1:Low */
(0 << 1) | /* LOCK_CNT, Lock signal selection pin, enable */
(0 << 0); /* AUTO_DSK_SEL, auto deskew sel. pin, normal */
nx_lvds_set_lvdsctrl4(0, val);
val = (0 << 24) | /* I_BIST_RESETB */
(0 << 23) | /* I_BIST_EN */
(0 << 21) | /* I_BIST_PAT_SEL */
(0 << 14) | /* I_BIST_USER_PATTERN */
(0 << 13) | /* I_BIST_FORCE_ERROR */
(0 << 7) | /* I_BIST_SKEW_CTRL */
(0 << 5) | /* I_BIST_CLK_INV */
(0 << 3) | /* I_BIST_DATA_INV */
(0 << 0); /* I_BIST_CH_SEL */
nx_lvds_set_lvdstmode0(0, val);
/* user do not need to modify this codes. */
val = (LOC_A[4] << 24) | (LOC_A[3] << 18) | (LOC_A[2] << 12) |
(LOC_A[1] << 6) | (LOC_A[0] << 0);
nx_lvds_set_lvdsloc0(0, val);
val = (LOC_B[2] << 24) | (LOC_B[1] << 18) | (LOC_B[0] << 12) |
(LOC_A[6] << 6) | (LOC_A[5] << 0);
nx_lvds_set_lvdsloc1(0, val);
val = (LOC_C[0] << 24) | (LOC_B[6] << 18) | (LOC_B[5] << 12) |
(LOC_B[4] << 6) | (LOC_B[3] << 0);
nx_lvds_set_lvdsloc2(0, val);
val = (LOC_C[5] << 24) | (LOC_C[4] << 18) | (LOC_C[3] << 12) |
(LOC_C[2] << 6) | (LOC_C[1] << 0);
nx_lvds_set_lvdsloc3(0, val);
val = (LOC_D[3] << 24) | (LOC_D[2] << 18) | (LOC_D[1] << 12) |
(LOC_D[0] << 6) | (LOC_C[6] << 0);
nx_lvds_set_lvdsloc4(0, val);
val = (LOC_E[1] << 24) | (LOC_E[0] << 18) | (LOC_D[6] << 12) |
(LOC_D[5] << 6) | (LOC_D[4] << 0);
nx_lvds_set_lvdsloc5(0, val);
val = (LOC_E[6] << 24) | (LOC_E[5] << 18) | (LOC_E[4] << 12) |
(LOC_E[3] << 6) | (LOC_E[2] << 0);
nx_lvds_set_lvdsloc6(0, val);
nx_lvds_set_lvdslocmask0(0, 0xffffffff);
nx_lvds_set_lvdslocmask1(0, 0xffffffff);
nx_lvds_set_lvdslocpol0(0, (0 << 19) | (0 << 18));
/*
* select TOP MUX
*/
nx_disp_top_set_lvdsmux(1, input);
/*
* LVDS PHY Reset, make sure last.
*/
lvds_phy_reset();
return 0;
}
void nx_lvds_display(int module,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_plane_top *top, struct dp_plane_info *planes,
struct dp_lvds_dev *dev)
{
struct dp_plane_info *plane = planes;
int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
int count = top->plane_num;
int i = 0;
printf("LVDS: dp.%d\n", module);
dp_control_init(module);
dp_plane_init(module);
lvds_init();
/* set plane */
dp_plane_screen_setup(module, top);
for (i = 0; count > i; i++, plane++) {
if (!plane->enable)
continue;
dp_plane_layer_setup(module, plane);
dp_plane_layer_enable(module, plane, 1);
}
dp_plane_screen_enable(module, 1);
/* set lvds */
lvds_setup(module, input, sync, ctrl, dev);
lvds_enable(1);
/* set dp control */
dp_control_setup(module, sync, ctrl);
dp_control_enable(module, 1);
}

View File

@ -0,0 +1,677 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Nexell Co., Ltd.
*
* Author: junghyun, kim <jhkim@nexell.co.kr>
*/
#include <config.h>
#include <common.h>
#include <errno.h>
#include <asm/arch/nexell.h>
#include <asm/arch/tieoff.h>
#include <asm/arch/reset.h>
#include <asm/arch/display.h>
#include "soc/s5pxx18_soc_mipi.h"
#include "soc/s5pxx18_soc_disptop.h"
#include "soc/s5pxx18_soc_disptop_clk.h"
#define PLLPMS_1000MHZ 0x33E8
#define BANDCTL_1000MHZ 0xF
#define PLLPMS_960MHZ 0x2280
#define BANDCTL_960MHZ 0xF
#define PLLPMS_900MHZ 0x2258
#define BANDCTL_900MHZ 0xE
#define PLLPMS_840MHZ 0x2230
#define BANDCTL_840MHZ 0xD
#define PLLPMS_750MHZ 0x43E8
#define BANDCTL_750MHZ 0xC
#define PLLPMS_660MHZ 0x21B8
#define BANDCTL_660MHZ 0xB
#define PLLPMS_600MHZ 0x2190
#define BANDCTL_600MHZ 0xA
#define PLLPMS_540MHZ 0x2168
#define BANDCTL_540MHZ 0x9
#define PLLPMS_512MHZ 0x03200
#define BANDCTL_512MHZ 0x9
#define PLLPMS_480MHZ 0x2281
#define BANDCTL_480MHZ 0x8
#define PLLPMS_420MHZ 0x2231
#define BANDCTL_420MHZ 0x7
#define PLLPMS_402MHZ 0x2219
#define BANDCTL_402MHZ 0x7
#define PLLPMS_330MHZ 0x21B9
#define BANDCTL_330MHZ 0x6
#define PLLPMS_300MHZ 0x2191
#define BANDCTL_300MHZ 0x5
#define PLLPMS_210MHZ 0x2232
#define BANDCTL_210MHZ 0x4
#define PLLPMS_180MHZ 0x21E2
#define BANDCTL_180MHZ 0x3
#define PLLPMS_150MHZ 0x2192
#define BANDCTL_150MHZ 0x2
#define PLLPMS_100MHZ 0x3323
#define BANDCTL_100MHZ 0x1
#define PLLPMS_80MHZ 0x3283
#define BANDCTL_80MHZ 0x0
#define MIPI_INDEX 0
#define MIPI_EXC_PRE_VALUE 1
#define MIPI_DSI_IRQ_MASK 29
#define __io_address(a) (void *)(uintptr_t)(a)
struct mipi_xfer_msg {
u8 id, data[2];
u16 flags;
const u8 *tx_buf;
u16 tx_len;
u8 *rx_buf;
u16 rx_len;
};
static void mipi_reset(void)
{
/* tieoff */
nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAA, 3);
nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAB, 3);
/* reset */
nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_MIPI_CSI, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_ASSERT);
nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_NEGATE);
nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_NEGATE);
nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_NEGATE);
nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_NEGATE);
}
static void mipi_init(void)
{
int clkid = DP_CLOCK_MIPI;
void *base;
/*
* neet to reset before open
*/
mipi_reset();
base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid));
nx_disp_top_clkgen_set_base_address(clkid, base);
nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always);
base = __io_address(nx_mipi_get_physical_address(0));
nx_mipi_set_base_address(0, base);
}
static int mipi_get_phy_pll(int bitrate, unsigned int *pllpms,
unsigned int *bandctl)
{
unsigned int pms, ctl;
switch (bitrate) {
case 1000:
pms = PLLPMS_1000MHZ;
ctl = BANDCTL_1000MHZ;
break;
case 960:
pms = PLLPMS_960MHZ;
ctl = BANDCTL_960MHZ;
break;
case 900:
pms = PLLPMS_900MHZ;
ctl = BANDCTL_900MHZ;
break;
case 840:
pms = PLLPMS_840MHZ;
ctl = BANDCTL_840MHZ;
break;
case 750:
pms = PLLPMS_750MHZ;
ctl = BANDCTL_750MHZ;
break;
case 660:
pms = PLLPMS_660MHZ;
ctl = BANDCTL_660MHZ;
break;
case 600:
pms = PLLPMS_600MHZ;
ctl = BANDCTL_600MHZ;
break;
case 540:
pms = PLLPMS_540MHZ;
ctl = BANDCTL_540MHZ;
break;
case 512:
pms = PLLPMS_512MHZ;
ctl = BANDCTL_512MHZ;
break;
case 480:
pms = PLLPMS_480MHZ;
ctl = BANDCTL_480MHZ;
break;
case 420:
pms = PLLPMS_420MHZ;
ctl = BANDCTL_420MHZ;
break;
case 402:
pms = PLLPMS_402MHZ;
ctl = BANDCTL_402MHZ;
break;
case 330:
pms = PLLPMS_330MHZ;
ctl = BANDCTL_330MHZ;
break;
case 300:
pms = PLLPMS_300MHZ;
ctl = BANDCTL_300MHZ;
break;
case 210:
pms = PLLPMS_210MHZ;
ctl = BANDCTL_210MHZ;
break;
case 180:
pms = PLLPMS_180MHZ;
ctl = BANDCTL_180MHZ;
break;
case 150:
pms = PLLPMS_150MHZ;
ctl = BANDCTL_150MHZ;
break;
case 100:
pms = PLLPMS_100MHZ;
ctl = BANDCTL_100MHZ;
break;
case 80:
pms = PLLPMS_80MHZ;
ctl = BANDCTL_80MHZ;
break;
default:
return -EINVAL;
}
*pllpms = pms;
*bandctl = ctl;
return 0;
}
static int mipi_prepare(int module, int input,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_mipi_dev *mipi)
{
int index = MIPI_INDEX;
u32 esc_pre_value = MIPI_EXC_PRE_VALUE;
int lpm = mipi->lpm_trans;
int ret = 0;
ret = mipi_get_phy_pll(mipi->hs_bitrate,
&mipi->hs_pllpms, &mipi->hs_bandctl);
if (ret < 0)
return ret;
ret = mipi_get_phy_pll(mipi->lp_bitrate,
&mipi->lp_pllpms, &mipi->lp_bandctl);
if (ret < 0)
return ret;
debug("%s: mipi lp:%dmhz:0x%x:0x%x, hs:%dmhz:0x%x:0x%x, %s trans\n",
__func__, mipi->lp_bitrate, mipi->lp_pllpms, mipi->lp_bandctl,
mipi->hs_bitrate, mipi->hs_pllpms, mipi->hs_bandctl,
lpm ? "low" : "high");
if (lpm)
nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
mipi->lp_pllpms, mipi->lp_bandctl, 0, 0);
else
nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
mipi->hs_pllpms, mipi->hs_bandctl, 0, 0);
#ifdef CONFIG_ARCH_S5P4418
/*
* disable the escape clock generating prescaler
* before soft reset.
*/
nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 0, 10);
mdelay(1);
#endif
nx_mipi_dsi_software_reset(index);
nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 1, esc_pre_value);
nx_mipi_dsi_set_phy(index, 0, 1, 1, 0, 0, 0, 0, 0);
if (lpm)
nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_lp,
nx_mipi_dsi_lpmode_lp);
else
nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_hs,
nx_mipi_dsi_lpmode_hs);
mdelay(20);
return 0;
}
static int mipi_enable(int module, int input,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_mipi_dev *mipi)
{
struct mipi_dsi_device *dsi = &mipi->dsi;
int clkid = DP_CLOCK_MIPI;
int index = MIPI_INDEX;
int width = sync->h_active_len;
int height = sync->v_active_len;
int HFP = sync->h_front_porch;
int HBP = sync->h_back_porch;
int HS = sync->h_sync_width;
int VFP = sync->v_front_porch;
int VBP = sync->v_back_porch;
int VS = sync->v_sync_width;
int en_prescaler = 1;
u32 esc_pre_value = MIPI_EXC_PRE_VALUE;
int txhsclock = 1;
int lpm = mipi->lpm_trans;
bool command_mode = mipi->command_mode;
enum nx_mipi_dsi_format dsi_format;
int data_len = dsi->lanes - 1;
bool burst = dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST ? true : false;
bool eot_enable = dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET ?
false : true;
/*
* disable the escape clock generating prescaler
* before soft reset.
*/
#ifdef CONFIG_ARCH_S5P4418
en_prescaler = 0;
#endif
debug("%s: mode:%s, lanes.%d\n", __func__,
command_mode ? "command" : "video", data_len + 1);
if (lpm)
nx_mipi_dsi_set_escape_lp(index,
nx_mipi_dsi_lpmode_hs,
nx_mipi_dsi_lpmode_hs);
nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF,
mipi->hs_pllpms, mipi->hs_bandctl, 0, 0);
mdelay(1);
nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, en_prescaler, 10);
mdelay(1);
nx_mipi_dsi_software_reset(index);
nx_mipi_dsi_set_clock(index, txhsclock, 0, 1,
1, 1, 0, 0, 0, 1, esc_pre_value);
switch (data_len) {
case 0: /* 1 lane */
nx_mipi_dsi_set_phy(index, data_len, 1, 1, 0, 0, 0, 0, 0);
break;
case 1: /* 2 lane */
nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 0, 0, 0, 0);
break;
case 2: /* 3 lane */
nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 0, 0, 0);
break;
case 3: /* 3 lane */
nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 1, 0, 0);
break;
default:
printf("%s: not support data lanes %d\n",
__func__, data_len + 1);
return -EINVAL;
}
switch (dsi->format) {
case MIPI_DSI_FMT_RGB565:
dsi_format = nx_mipi_dsi_format_rgb565;
break;
case MIPI_DSI_FMT_RGB666:
dsi_format = nx_mipi_dsi_format_rgb666;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
dsi_format = nx_mipi_dsi_format_rgb666_packed;
break;
case MIPI_DSI_FMT_RGB888:
dsi_format = nx_mipi_dsi_format_rgb888;
break;
default:
printf("%s: not support format %d\n", __func__, dsi->format);
return -EINVAL;
}
nx_mipi_dsi_set_config_video_mode(index, 1, 0, burst,
nx_mipi_dsi_syncmode_event,
eot_enable, 1, 1, 1, 1, 0, dsi_format,
HFP, HBP, HS, VFP, VBP, VS, 0);
nx_mipi_dsi_set_size(index, width, height);
/* set mux */
nx_disp_top_set_mipimux(1, module);
/* 0 is spdif, 1 is mipi vclk */
nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv0);
nx_disp_top_clkgen_set_clock_divisor(clkid, 1,
ctrl->clk_div_lv1 *
ctrl->clk_div_lv0);
/* SPDIF and MIPI */
nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 1);
/* START: CLKGEN, MIPI is started in setup function */
nx_disp_top_clkgen_set_clock_divisor_enable(clkid, true);
nx_mipi_dsi_set_enable(index, true);
return 0;
}
static int nx_mipi_transfer_tx(struct mipi_dsi_device *dsi,
struct mipi_xfer_msg *xfer)
{
const u8 *txb;
int size, index = 0;
u32 data;
if (xfer->tx_len > DSI_TX_FIFO_SIZE)
printf("warn: tx %d size over fifo %d\n",
(int)xfer->tx_len, DSI_TX_FIFO_SIZE);
/* write payload */
size = xfer->tx_len;
txb = xfer->tx_buf;
while (size >= 4) {
data = (txb[3] << 24) | (txb[2] << 16) |
(txb[1] << 8) | (txb[0]);
nx_mipi_dsi_write_payload(index, data);
txb += 4, size -= 4;
data = 0;
}
switch (size) {
case 3:
data |= txb[2] << 16;
case 2:
data |= txb[1] << 8;
case 1:
data |= txb[0];
nx_mipi_dsi_write_payload(index, data);
break;
case 0:
break; /* no payload */
}
/* write packet hdr */
data = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->id;
nx_mipi_dsi_write_pkheader(index, data);
return 0;
}
static int nx_mipi_transfer_done(struct mipi_dsi_device *dsi)
{
int index = 0, count = 100;
u32 value;
do {
mdelay(1);
value = nx_mipi_dsi_read_fifo_status(index);
if (((1 << 22) & value))
break;
} while (count-- > 0);
if (count < 0)
return -EINVAL;
return 0;
}
static int nx_mipi_transfer_rx(struct mipi_dsi_device *dsi,
struct mipi_xfer_msg *xfer)
{
u8 *rxb = xfer->rx_buf;
int index = 0, rx_len = 0;
u32 data, count = 0;
u16 size;
int err = -EINVAL;
nx_mipi_dsi_clear_interrupt_pending(index, 18);
while (1) {
/* Completes receiving data. */
if (nx_mipi_dsi_get_interrupt_pending(index, 18))
break;
mdelay(1);
if (count > 500) {
printf("%s: error recevice data\n", __func__);
err = -EINVAL;
goto clear_fifo;
} else {
count++;
}
}
data = nx_mipi_dsi_read_fifo(index);
switch (data & 0x3f) {
case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
if (xfer->rx_len >= 2) {
rxb[1] = data >> 16;
rx_len++;
}
/* Fall through */
case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
rxb[0] = data >> 8;
rx_len++;
xfer->rx_len = rx_len;
err = rx_len;
goto clear_fifo;
case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
printf("DSI Error Report: 0x%04x\n", (data >> 8) & 0xffff);
err = rx_len;
goto clear_fifo;
}
size = (data >> 8) & 0xffff;
if (size > xfer->rx_len)
size = xfer->rx_len;
else if (size < xfer->rx_len)
xfer->rx_len = size;
size = xfer->rx_len - rx_len;
rx_len += size;
/* Receive payload */
while (size >= 4) {
data = nx_mipi_dsi_read_fifo(index);
rxb[0] = (data >> 0) & 0xff;
rxb[1] = (data >> 8) & 0xff;
rxb[2] = (data >> 16) & 0xff;
rxb[3] = (data >> 24) & 0xff;
rxb += 4, size -= 4;
}
if (size) {
data = nx_mipi_dsi_read_fifo(index);
switch (size) {
case 3:
rxb[2] = (data >> 16) & 0xff;
case 2:
rxb[1] = (data >> 8) & 0xff;
case 1:
rxb[0] = data & 0xff;
}
}
if (rx_len == xfer->rx_len)
err = rx_len;
clear_fifo:
size = DSI_RX_FIFO_SIZE / 4;
do {
data = nx_mipi_dsi_read_fifo(index);
if (data == DSI_RX_FIFO_EMPTY)
break;
} while (--size);
return err;
}
#define IS_SHORT(t) (9 > ((t) & 0x0f))
static int nx_mipi_transfer(struct mipi_dsi_device *dsi,
const struct mipi_dsi_msg *msg)
{
struct mipi_xfer_msg xfer;
int err;
if (!msg->tx_len)
return -EINVAL;
/* set id */
xfer.id = msg->type | (msg->channel << 6);
/* short type msg */
if (IS_SHORT(msg->type)) {
const char *txb = msg->tx_buf;
if (msg->tx_len > 2)
return -EINVAL;
xfer.tx_len = 0; /* no payload */
xfer.data[0] = txb[0];
xfer.data[1] = (msg->tx_len == 2) ? txb[1] : 0;
xfer.tx_buf = NULL;
} else {
xfer.tx_len = msg->tx_len;
xfer.data[0] = msg->tx_len & 0xff;
xfer.data[1] = msg->tx_len >> 8;
xfer.tx_buf = msg->tx_buf;
}
xfer.rx_len = msg->rx_len;
xfer.rx_buf = msg->rx_buf;
xfer.flags = msg->flags;
err = nx_mipi_transfer_tx(dsi, &xfer);
if (xfer.rx_len)
err = nx_mipi_transfer_rx(dsi, &xfer);
nx_mipi_transfer_done(dsi);
return err;
}
static ssize_t nx_mipi_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;
}
if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
msg.flags |= MIPI_DSI_MSG_USE_LPM;
return nx_mipi_transfer(dsi, &msg);
}
__weak int nx_mipi_dsi_lcd_bind(struct mipi_dsi_device *dsi)
{
return 0;
}
/*
* disply
* MIPI DSI Setting
* (1) Initiallize MIPI(DSIM,DPHY,PLL)
* (2) Initiallize LCD
* (3) ReInitiallize MIPI(DSIM only)
* (4) Turn on display(MLC,DPC,...)
*/
void nx_mipi_display(int module,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_plane_top *top, struct dp_plane_info *planes,
struct dp_mipi_dev *dev)
{
struct dp_plane_info *plane = planes;
struct mipi_dsi_device *dsi = &dev->dsi;
int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
int count = top->plane_num;
int i = 0, ret;
printf("MIPI: dp.%d\n", module);
/* map mipi-dsi write callback func */
dsi->write_buffer = nx_mipi_write_buffer;
ret = nx_mipi_dsi_lcd_bind(dsi);
if (ret) {
printf("Error: bind mipi-dsi lcd driver !\n");
return;
}
dp_control_init(module);
dp_plane_init(module);
mipi_init();
/* set plane */
dp_plane_screen_setup(module, top);
for (i = 0; count > i; i++, plane++) {
if (!plane->enable)
continue;
dp_plane_layer_setup(module, plane);
dp_plane_layer_enable(module, plane, 1);
}
dp_plane_screen_enable(module, 1);
/* set mipi */
mipi_prepare(module, input, sync, ctrl, dev);
if (dsi->ops && dsi->ops->prepare)
dsi->ops->prepare(dsi);
if (dsi->ops && dsi->ops->enable)
dsi->ops->enable(dsi);
mipi_enable(module, input, sync, ctrl, dev);
/* set dp control */
dp_control_setup(module, sync, ctrl);
dp_control_enable(module, 1);
}

View File

@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Nexell Co., Ltd.
*
* Author: junghyun, kim <jhkim@nexell.co.kr>
*/
#include <config.h>
#include <common.h>
#include <errno.h>
#include <asm/arch/display.h>
#include "soc/s5pxx18_soc_disptop.h"
static int rgb_switch(int module, int input, struct dp_sync_info *sync,
struct dp_rgb_dev *dev)
{
int mpu = dev->lcd_mpu_type;
int rsc = 0, sel = 0;
switch (module) {
case 0:
sel = mpu ? 1 : 0;
break;
case 1:
sel = rsc ? 3 : 2;
break;
default:
printf("Fail, %s nuknown module %d\n", __func__, module);
return -1;
}
nx_disp_top_set_primary_mux(sel);
return 0;
}
void nx_rgb_display(int module,
struct dp_sync_info *sync, struct dp_ctrl_info *ctrl,
struct dp_plane_top *top, struct dp_plane_info *planes,
struct dp_rgb_dev *dev)
{
struct dp_plane_info *plane = planes;
int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1;
int count = top->plane_num;
int i = 0;
printf("RGB: dp.%d\n", module);
dp_control_init(module);
dp_plane_init(module);
/* set plane */
dp_plane_screen_setup(module, top);
for (i = 0; count > i; i++, plane++) {
if (!plane->enable)
continue;
dp_plane_layer_setup(module, plane);
dp_plane_layer_enable(module, plane, 1);
}
dp_plane_screen_enable(module, 1);
rgb_switch(module, input, sync, dev);
dp_control_setup(module, sync, ctrl);
dp_control_enable(module, 1);
}

View File

@ -0,0 +1,651 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Nexell Co., Ltd.
*
* Author: junghyun, kim <jhkim@nexell.co.kr>
*
* Copyright (C) 2020 Stefan Bosch <stefan_b@posteo.net>
*/
#include <config.h>
#include <common.h>
#include <command.h>
#include <dm.h>
#include <mapmem.h>
#include <malloc.h>
#include <linux/compat.h>
#include <linux/err.h>
#include <video.h> /* For struct video_uc_platdata */
#include <video_fb.h>
#include <lcd.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <asm/arch/display.h>
#include <asm/arch/display_dev.h>
#include "videomodes.h"
DECLARE_GLOBAL_DATA_PTR;
#if !defined(CONFIG_DM) && !defined(CONFIG_OF_CONTROL)
static struct nx_display_dev *dp_dev;
#endif
static char *const dp_dev_str[] = {
[DP_DEVICE_RESCONV] = "RESCONV",
[DP_DEVICE_RGBLCD] = "LCD",
[DP_DEVICE_HDMI] = "HDMI",
[DP_DEVICE_MIPI] = "MiPi",
[DP_DEVICE_LVDS] = "LVDS",
[DP_DEVICE_CVBS] = "TVOUT",
[DP_DEVICE_DP0] = "DP0",
[DP_DEVICE_DP1] = "DP1",
};
#if CONFIG_IS_ENABLED(OF_CONTROL)
static void nx_display_parse_dp_sync(ofnode node, struct dp_sync_info *sync)
{
sync->h_active_len = ofnode_read_s32_default(node, "h_active_len", 0);
sync->h_sync_width = ofnode_read_s32_default(node, "h_sync_width", 0);
sync->h_back_porch = ofnode_read_s32_default(node, "h_back_porch", 0);
sync->h_front_porch = ofnode_read_s32_default(node, "h_front_porch", 0);
sync->h_sync_invert = ofnode_read_s32_default(node, "h_sync_invert", 0);
sync->v_active_len = ofnode_read_s32_default(node, "v_active_len", 0);
sync->v_sync_width = ofnode_read_s32_default(node, "v_sync_width", 0);
sync->v_back_porch = ofnode_read_s32_default(node, "v_back_porch", 0);
sync->v_front_porch = ofnode_read_s32_default(node, "v_front_porch", 0);
sync->v_sync_invert = ofnode_read_s32_default(node, "v_sync_invert", 0);
sync->pixel_clock_hz = ofnode_read_s32_default(node, "pixel_clock_hz", 0);
debug("DP: sync ->\n");
debug("ha:%d, hs:%d, hb:%d, hf:%d, hi:%d\n",
sync->h_active_len, sync->h_sync_width,
sync->h_back_porch, sync->h_front_porch, sync->h_sync_invert);
debug("va:%d, vs:%d, vb:%d, vf:%d, vi:%d\n",
sync->v_active_len, sync->v_sync_width,
sync->v_back_porch, sync->v_front_porch, sync->v_sync_invert);
}
static void nx_display_parse_dp_ctrl(ofnode node, struct dp_ctrl_info *ctrl)
{
/* clock gen */
ctrl->clk_src_lv0 = ofnode_read_s32_default(node, "clk_src_lv0", 0);
ctrl->clk_div_lv0 = ofnode_read_s32_default(node, "clk_div_lv0", 0);
ctrl->clk_src_lv1 = ofnode_read_s32_default(node, "clk_src_lv1", 0);
ctrl->clk_div_lv1 = ofnode_read_s32_default(node, "clk_div_lv1", 0);
/* scan format */
ctrl->interlace = ofnode_read_s32_default(node, "interlace", 0);
/* syncgen format */
ctrl->out_format = ofnode_read_s32_default(node, "out_format", 0);
ctrl->invert_field = ofnode_read_s32_default(node, "invert_field", 0);
ctrl->swap_RB = ofnode_read_s32_default(node, "swap_RB", 0);
ctrl->yc_order = ofnode_read_s32_default(node, "yc_order", 0);
/* extern sync delay */
ctrl->delay_mask = ofnode_read_s32_default(node, "delay_mask", 0);
ctrl->d_rgb_pvd = ofnode_read_s32_default(node, "d_rgb_pvd", 0);
ctrl->d_hsync_cp1 = ofnode_read_s32_default(node, "d_hsync_cp1", 0);
ctrl->d_vsync_fram = ofnode_read_s32_default(node, "d_vsync_fram", 0);
ctrl->d_de_cp2 = ofnode_read_s32_default(node, "d_de_cp2", 0);
/* extern sync delay */
ctrl->vs_start_offset =
ofnode_read_s32_default(node, "vs_start_offset", 0);
ctrl->vs_end_offset = ofnode_read_s32_default(node, "vs_end_offset", 0);
ctrl->ev_start_offset =
ofnode_read_s32_default(node, "ev_start_offset", 0);
ctrl->ev_end_offset = ofnode_read_s32_default(node, "ev_end_offset", 0);
/* pad clock seletor */
ctrl->vck_select = ofnode_read_s32_default(node, "vck_select", 0);
ctrl->clk_inv_lv0 = ofnode_read_s32_default(node, "clk_inv_lv0", 0);
ctrl->clk_delay_lv0 = ofnode_read_s32_default(node, "clk_delay_lv0", 0);
ctrl->clk_inv_lv1 = ofnode_read_s32_default(node, "clk_inv_lv1", 0);
ctrl->clk_delay_lv1 = ofnode_read_s32_default(node, "clk_delay_lv1", 0);
ctrl->clk_sel_div1 = ofnode_read_s32_default(node, "clk_sel_div1", 0);
debug("DP: ctrl [%s] ->\n",
ctrl->interlace ? "Interlace" : " Progressive");
debug("cs0:%d, cd0:%d, cs1:%d, cd1:%d\n",
ctrl->clk_src_lv0, ctrl->clk_div_lv0,
ctrl->clk_src_lv1, ctrl->clk_div_lv1);
debug("fmt:0x%x, inv:%d, swap:%d, yb:0x%x\n",
ctrl->out_format, ctrl->invert_field,
ctrl->swap_RB, ctrl->yc_order);
debug("dm:0x%x, drp:%d, dhs:%d, dvs:%d, dde:0x%x\n",
ctrl->delay_mask, ctrl->d_rgb_pvd,
ctrl->d_hsync_cp1, ctrl->d_vsync_fram, ctrl->d_de_cp2);
debug("vss:%d, vse:%d, evs:%d, eve:%d\n",
ctrl->vs_start_offset, ctrl->vs_end_offset,
ctrl->ev_start_offset, ctrl->ev_end_offset);
debug("sel:%d, i0:%d, d0:%d, i1:%d, d1:%d, s1:%d\n",
ctrl->vck_select, ctrl->clk_inv_lv0, ctrl->clk_delay_lv0,
ctrl->clk_inv_lv1, ctrl->clk_delay_lv1, ctrl->clk_sel_div1);
}
static void nx_display_parse_dp_top_layer(ofnode node, struct dp_plane_top *top)
{
top->screen_width = ofnode_read_s32_default(node, "screen_width", 0);
top->screen_height = ofnode_read_s32_default(node, "screen_height", 0);
top->video_prior = ofnode_read_s32_default(node, "video_prior", 0);
top->interlace = ofnode_read_s32_default(node, "interlace", 0);
top->back_color = ofnode_read_s32_default(node, "back_color", 0);
top->plane_num = DP_PLANS_NUM;
debug("DP: top [%s] ->\n",
top->interlace ? "Interlace" : " Progressive");
debug("w:%d, h:%d, prior:%d, bg:0x%x\n",
top->screen_width, top->screen_height,
top->video_prior, top->back_color);
}
static void nx_display_parse_dp_layer(ofnode node, struct dp_plane_info *plane)
{
plane->left = ofnode_read_s32_default(node, "left", 0);
plane->width = ofnode_read_s32_default(node, "width", 0);
plane->top = ofnode_read_s32_default(node, "top", 0);
plane->height = ofnode_read_s32_default(node, "height", 0);
plane->pixel_byte = ofnode_read_s32_default(node, "pixel_byte", 0);
plane->format = ofnode_read_s32_default(node, "format", 0);
plane->alpha_on = ofnode_read_s32_default(node, "alpha_on", 0);
plane->alpha_depth = ofnode_read_s32_default(node, "alpha", 0);
plane->tp_on = ofnode_read_s32_default(node, "tp_on", 0);
plane->tp_color = ofnode_read_s32_default(node, "tp_color", 0);
/* enable layer */
if (plane->fb_base)
plane->enable = 1;
else
plane->enable = 0;
if (plane->fb_base == 0) {
printf("fail : dp plane.%d invalid fb base [0x%x] ->\n",
plane->layer, plane->fb_base);
return;
}
debug("DP: plane.%d [0x%x] ->\n", plane->layer, plane->fb_base);
debug("f:0x%x, l:%d, t:%d, %d * %d, bpp:%d, a:%d(%d), t:%d(0x%x)\n",
plane->format, plane->left, plane->top, plane->width,
plane->height, plane->pixel_byte, plane->alpha_on,
plane->alpha_depth, plane->tp_on, plane->tp_color);
}
static void nx_display_parse_dp_planes(ofnode node,
struct nx_display_dev *dp,
struct video_uc_platdata *plat)
{
const char *name;
ofnode subnode;
ofnode_for_each_subnode(subnode, node) {
name = ofnode_get_name(subnode);
if (strcmp(name, "layer_top") == 0)
nx_display_parse_dp_top_layer(subnode, &dp->top);
/*
* TODO: Is it sure that only one layer is used? Otherwise
* fb_base must be different?
*/
if (strcmp(name, "layer_0") == 0) {
dp->planes[0].fb_base =
(uint)map_sysmem(plat->base, plat->size);
debug("%s(): dp->planes[0].fb_base == 0x%x\n", __func__,
(uint)dp->planes[0].fb_base);
nx_display_parse_dp_layer(subnode, &dp->planes[0]);
}
if (strcmp(name, "layer_1") == 0) {
dp->planes[1].fb_base =
(uint)map_sysmem(plat->base, plat->size);
debug("%s(): dp->planes[1].fb_base == 0x%x\n", __func__,
(uint)dp->planes[1].fb_base);
nx_display_parse_dp_layer(subnode, &dp->planes[1]);
}
if (strcmp(name, "layer_2") == 0) {
dp->planes[2].fb_base =
(uint)map_sysmem(plat->base, plat->size);
debug("%s(): dp->planes[2].fb_base == 0x%x\n", __func__,
(uint)dp->planes[2].fb_base);
nx_display_parse_dp_layer(subnode, &dp->planes[2]);
}
}
}
static int nx_display_parse_dp_lvds(ofnode node, struct nx_display_dev *dp)
{
struct dp_lvds_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
printf("failed to allocate display LVDS object.\n");
return -ENOMEM;
}
dp->device = dev;
dev->lvds_format = ofnode_read_s32_default(node, "format", 0);
dev->pol_inv_hs = ofnode_read_s32_default(node, "pol_inv_hs", 0);
dev->pol_inv_vs = ofnode_read_s32_default(node, "pol_inv_vs", 0);
dev->pol_inv_de = ofnode_read_s32_default(node, "pol_inv_de", 0);
dev->pol_inv_ck = ofnode_read_s32_default(node, "pol_inv_ck", 0);
dev->voltage_level = ofnode_read_s32_default(node, "voltage_level", 0);
if (!dev->voltage_level)
dev->voltage_level = DEF_VOLTAGE_LEVEL;
debug("DP: LVDS -> %s, voltage LV:0x%x\n",
dev->lvds_format == DP_LVDS_FORMAT_VESA ? "VESA" :
dev->lvds_format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC",
dev->voltage_level);
debug("pol inv hs:%d, vs:%d, de:%d, ck:%d\n",
dev->pol_inv_hs, dev->pol_inv_vs,
dev->pol_inv_de, dev->pol_inv_ck);
return 0;
}
static int nx_display_parse_dp_rgb(ofnode node, struct nx_display_dev *dp)
{
struct dp_rgb_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
printf("failed to allocate display RGB LCD object.\n");
return -ENOMEM;
}
dp->device = dev;
dev->lcd_mpu_type = ofnode_read_s32_default(node, "lcd_mpu_type", 0);
debug("DP: RGB -> MPU[%s]\n", dev->lcd_mpu_type ? "O" : "X");
return 0;
}
static int nx_display_parse_dp_mipi(ofnode node, struct nx_display_dev *dp)
{
struct dp_mipi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
printf("failed to allocate display MiPi object.\n");
return -ENOMEM;
}
dp->device = dev;
dev->lp_bitrate = ofnode_read_s32_default(node, "lp_bitrate", 0);
dev->hs_bitrate = ofnode_read_s32_default(node, "hs_bitrate", 0);
dev->lpm_trans = 1;
dev->command_mode = 0;
debug("DP: MIPI ->\n");
debug("lp:%dmhz, hs:%dmhz\n", dev->lp_bitrate, dev->hs_bitrate);
return 0;
}
static int nx_display_parse_dp_hdmi(ofnode node, struct nx_display_dev *dp)
{
struct dp_hdmi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
printf("failed to allocate display HDMI object.\n");
return -ENOMEM;
}
dp->device = dev;
dev->preset = ofnode_read_s32_default(node, "preset", 0);
debug("DP: HDMI -> %d\n", dev->preset);
return 0;
}
static int nx_display_parse_dp_lcds(ofnode node, const char *type,
struct nx_display_dev *dp)
{
if (strcmp(type, "lvds") == 0) {
dp->dev_type = DP_DEVICE_LVDS;
return nx_display_parse_dp_lvds(node, dp);
} else if (strcmp(type, "rgb") == 0) {
dp->dev_type = DP_DEVICE_RGBLCD;
return nx_display_parse_dp_rgb(node, dp);
} else if (strcmp(type, "mipi") == 0) {
dp->dev_type = DP_DEVICE_MIPI;
return nx_display_parse_dp_mipi(node, dp);
} else if (strcmp(type, "hdmi") == 0) {
dp->dev_type = DP_DEVICE_HDMI;
return nx_display_parse_dp_hdmi(node, dp);
}
printf("%s: node %s unknown display type\n", __func__,
ofnode_get_name(node));
return -EINVAL;
return 0;
}
#define DT_SYNC (1 << 0)
#define DT_CTRL (1 << 1)
#define DT_PLANES (1 << 2)
#define DT_DEVICE (1 << 3)
static int nx_display_parse_dt(struct udevice *dev,
struct nx_display_dev *dp,
struct video_uc_platdata *plat)
{
const char *name, *dtype;
int ret = 0;
unsigned int dt_status = 0;
ofnode subnode;
if (!dev)
return -ENODEV;
dp->module = dev_read_s32_default(dev, "module", -1);
if (dp->module == -1)
dp->module = dev_read_s32_default(dev, "index", 0);
dtype = dev_read_string(dev, "lcd-type");
ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
name = ofnode_get_name(subnode);
if (strcmp("dp-sync", name) == 0) {
dt_status |= DT_SYNC;
nx_display_parse_dp_sync(subnode, &dp->sync);
}
if (strcmp("dp-ctrl", name) == 0) {
dt_status |= DT_CTRL;
nx_display_parse_dp_ctrl(subnode, &dp->ctrl);
}
if (strcmp("dp-planes", name) == 0) {
dt_status |= DT_PLANES;
nx_display_parse_dp_planes(subnode, dp, plat);
}
if (strcmp("dp-device", name) == 0) {
dt_status |= DT_DEVICE;
ret = nx_display_parse_dp_lcds(subnode, dtype, dp);
}
}
if (dt_status != (DT_SYNC | DT_CTRL | DT_PLANES | DT_DEVICE)) {
printf("Not enough DT config for display [0x%x]\n", dt_status);
return -ENODEV;
}
return ret;
}
#endif
__weak int nx_display_fixup_dp(struct nx_display_dev *dp)
{
return 0;
}
static struct nx_display_dev *nx_display_setup(void)
{
struct nx_display_dev *dp;
int i, ret;
int node = 0;
struct video_uc_platdata *plat = NULL;
struct udevice *dev;
/* call driver probe */
debug("DT: uclass device call...\n");
ret = uclass_get_device(UCLASS_VIDEO, 0, &dev);
if (ret) {
debug("%s(): uclass_get_device(UCLASS_VIDEO, 0, &dev) != 0 --> return NULL\n",
__func__);
return NULL;
}
plat = dev_get_uclass_platdata(dev);
if (!dev) {
debug("%s(): dev_get_uclass_platdata(dev) == NULL --> return NULL\n",
__func__);
return NULL;
}
dp = dev_get_priv(dev);
if (!dp) {
debug("%s(): dev_get_priv(dev) == NULL --> return NULL\n",
__func__);
return NULL;
}
node = dev->node.of_offset;
if (CONFIG_IS_ENABLED(OF_CONTROL)) {
ret = nx_display_parse_dt(dev, dp, plat);
if (ret)
goto err_setup;
}
nx_display_fixup_dp(dp);
for (i = 0; dp->top.plane_num > i; i++) {
dp->planes[i].layer = i;
if (dp->planes[i].enable && !dp->fb_plane) {
dp->fb_plane = &dp->planes[i];
dp->fb_addr = dp->fb_plane->fb_base;
dp->depth = dp->fb_plane->pixel_byte;
}
}
switch (dp->dev_type) {
#ifdef CONFIG_VIDEO_NX_RGB
case DP_DEVICE_RGBLCD:
nx_rgb_display(dp->module,
&dp->sync, &dp->ctrl, &dp->top,
dp->planes, (struct dp_rgb_dev *)dp->device);
break;
#endif
#ifdef CONFIG_VIDEO_NX_LVDS
case DP_DEVICE_LVDS:
nx_lvds_display(dp->module,
&dp->sync, &dp->ctrl, &dp->top,
dp->planes, (struct dp_lvds_dev *)dp->device);
break;
#endif
#ifdef CONFIG_VIDEO_NX_MIPI
case DP_DEVICE_MIPI:
nx_mipi_display(dp->module,
&dp->sync, &dp->ctrl, &dp->top,
dp->planes, (struct dp_mipi_dev *)dp->device);
break;
#endif
#ifdef CONFIG_VIDEO_NX_HDMI
case DP_DEVICE_HDMI:
nx_hdmi_display(dp->module,
&dp->sync, &dp->ctrl, &dp->top,
dp->planes, (struct dp_hdmi_dev *)dp->device);
break;
#endif
default:
printf("fail : not support lcd type %d !!!\n", dp->dev_type);
goto err_setup;
};
printf("LCD: [%s] dp.%d.%d %dx%d %dbpp FB:0x%08x\n",
dp_dev_str[dp->dev_type], dp->module, dp->fb_plane->layer,
dp->fb_plane->width, dp->fb_plane->height, dp->depth * 8,
dp->fb_addr);
return dp;
err_setup:
kfree(dp);
return NULL;
}
#if defined CONFIG_LCD
/* default lcd */
struct vidinfo panel_info = {
.vl_col = 320, .vl_row = 240, .vl_bpix = 32,
};
void lcd_ctrl_init(void *lcdbase)
{
vidinfo_t *pi = &panel_info;
struct nx_display_dev *dp;
int bpix;
dp = nx_display_setup();
if (!dp)
return NULL;
switch (dp->depth) {
case 2:
bpix = LCD_COLOR16;
break;
case 3:
case 4:
bpix = LCD_COLOR32;
break;
default:
printf("fail : not support LCD bit per pixel %d\n",
dp->depth * 8);
return NULL;
}
dp->panel_info = pi;
/* set resolution with config */
pi->vl_bpix = bpix;
pi->vl_col = dp->fb_plane->width;
pi->vl_row = dp->fb_plane->height;
pi->priv = dp;
gd->fb_base = dp->fb_addr;
}
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
{
}
__weak void lcd_enable(void)
{
}
#endif
static int nx_display_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 nx_display_platdata *plat = dev_get_platdata(dev);
static GraphicDevice *graphic_device;
char addr[64];
debug("%s()\n", __func__);
if (!dev)
return -EINVAL;
if (!uc_plat) {
debug("%s(): video_uc_platdata *plat == NULL --> return -EINVAL\n",
__func__);
return -EINVAL;
}
if (!uc_priv) {
debug("%s(): video_priv *uc_priv == NULL --> return -EINVAL\n",
__func__);
return -EINVAL;
}
if (!plat) {
debug("%s(): nx_display_platdata *plat == NULL --> return -EINVAL\n",
__func__);
return -EINVAL;
}
struct nx_display_dev *dp;
unsigned int pp_index = 0;
dp = nx_display_setup();
if (!dp) {
debug("%s(): nx_display_setup() == 0 --> return -EINVAL\n",
__func__);
return -EINVAL;
}
switch (dp->depth) {
case 2:
pp_index = GDF_16BIT_565RGB;
uc_priv->bpix = VIDEO_BPP16;
break;
case 3:
/* There is no VIDEO_BPP24 because these values are of
* type video_log2_bpp
*/
case 4:
pp_index = GDF_32BIT_X888RGB;
uc_priv->bpix = VIDEO_BPP32;
break;
default:
printf("fail : not support LCD bit per pixel %d\n",
dp->depth * 8);
return -EINVAL;
}
uc_priv->xsize = dp->fb_plane->width;
uc_priv->ysize = dp->fb_plane->height;
uc_priv->rot = 0;
graphic_device = &dp->graphic_device;
graphic_device->frameAdrs = dp->fb_addr;
graphic_device->gdfIndex = pp_index;
graphic_device->gdfBytesPP = dp->depth;
graphic_device->winSizeX = dp->fb_plane->width;
graphic_device->winSizeY = dp->fb_plane->height;
graphic_device->plnSizeX =
graphic_device->winSizeX * graphic_device->gdfBytesPP;
/*
* set environment variable "fb_addr" (frame buffer address), required
* for splash image. Because drv_video_init() in common/stdio.c is only
* called when CONFIG_VIDEO is set (and not if CONFIG_DM_VIDEO is set).
*/
sprintf(addr, "0x%x", dp->fb_addr);
debug("%s(): env_set(\"fb_addr\", %s) ...\n", __func__, addr);
env_set("fb_addr", addr);
return 0;
}
static int nx_display_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
debug("%s()\n", __func__);
/* Datasheet S5p4418:
* Resolution up to 2048 x 1280, up to 12 Bit per color (HDMI)
* Actual (max.) size is 0x1000000 because in U-Boot nanopi2-2016.01
* "#define CONFIG_FB_ADDR 0x77000000" and next address is
* "#define BMP_LOAD_ADDR 0x78000000"
*/
plat->size = 0x1000000;
return 0;
}
static const struct udevice_id nx_display_ids[] = {
{.compatible = "nexell,nexell-display", },
{}
};
U_BOOT_DRIVER(nexell_display) = {
.name = "nexell-display",
.id = UCLASS_VIDEO,
.of_match = nx_display_ids,
.platdata_auto_alloc_size =
sizeof(struct nx_display_platdata),
.bind = nx_display_bind,
.probe = nx_display_probe,
.priv_auto_alloc_size = sizeof(struct nx_display_dev),
};