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:
parent
9c5d377583
commit
e1e96ba6a2
@ -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
|
||||
|
@ -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
|
||||
|
27
drivers/video/nexell/Kconfig
Normal file
27
drivers/video/nexell/Kconfig
Normal 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
|
12
drivers/video/nexell/Makefile
Normal file
12
drivers/video/nexell/Makefile
Normal 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
|
341
drivers/video/nexell/s5pxx18_dp.c
Normal file
341
drivers/video/nexell/s5pxx18_dp.c
Normal 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;
|
||||
}
|
545
drivers/video/nexell/s5pxx18_dp_hdmi.c
Normal file
545
drivers/video/nexell/s5pxx18_dp_hdmi.c
Normal 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);
|
||||
}
|
274
drivers/video/nexell/s5pxx18_dp_lvds.c
Normal file
274
drivers/video/nexell/s5pxx18_dp_lvds.c
Normal 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);
|
||||
}
|
677
drivers/video/nexell/s5pxx18_dp_mipi.c
Normal file
677
drivers/video/nexell/s5pxx18_dp_mipi.c
Normal 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);
|
||||
}
|
69
drivers/video/nexell/s5pxx18_dp_rgb.c
Normal file
69
drivers/video/nexell/s5pxx18_dp_rgb.c
Normal 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);
|
||||
}
|
651
drivers/video/nexell_display.c
Normal file
651
drivers/video/nexell_display.c
Normal 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),
|
||||
};
|
Loading…
Reference in New Issue
Block a user