t124: Clean up display init functions

The existing display init functions were translated from a script. The new
code will play the same functions but are cleaner and readable and easier to
be ported to new panel.

BUG=none
TEST=build nyan and boot up kernel.

Signed-off-by: Jimmy Zhang <jimmzhang@nvidia.com>

Change-Id: Ic9983e57684a03e206efe3731968ec62905f4ee8
Reviewed-on: https://chromium-review.googlesource.com/189518
Commit-Queue: Jimmy Zhang <jimmzhang@nvidia.com>
Tested-by: Jimmy Zhang <jimmzhang@nvidia.com>
Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
Jimmy Zhang 2014-03-10 12:42:05 -07:00 committed by chrome-internal-fetch
commit 5998f991ea
13 changed files with 1396 additions and 1648 deletions

View file

@ -28,9 +28,14 @@ chip soc/nvidia/tegra124
register "display_controller" = "TEGRA_ARM_DISPLAYA"
register "xres" = "1366"
register "yres" = "768"
# this setting is what nvidia does; it makes no sense
# and does not agree with hardware. Why'd they do it?
register "framebuffer_bits_per_pixel" = "18"
# bits per pixel and color depth
register "framebuffer_bits_per_pixel" = "16"
register "color_depth" = "6"
# "6" is defined as COLOR_DEPTH_B5G6R5 in dc_reg.h
register "panel_bits_per_pixel" = "18"
register "cache_policy" = "DCACHE_WRITETHROUGH"
# With some help from the mainbaord designer
@ -40,12 +45,12 @@ chip soc/nvidia/tegra124
register "panel_vdd_gpio" = "0"
register "pwm" = "1"
# taken from u-boot; these look wrong however.
register "vdd_delay" = "400"
register "vdd_data_delay" = "4"
register "data_backlight_delay" = "203"
register "backlight_pwm_delay" = "17"
register "pwm_backlight_en_delay" = "15"
# various panel delay time
register "vdd_delay_ms" = "200"
register "vdd_to_hpd_delay_ms" = "200"
register "hpd_unplug_min_us" = "2000"
register "hpd_plug_min_us" = "250"
register "hpd_irq_min_us" = "250"
# How to compute these: xrandr --verbose will give you this:
#Detailed mode: Clock 285.250 MHz, 272 mm x 181 mm
@ -64,17 +69,33 @@ chip soc/nvidia/tegra124
# 1366x768 (0x45) 76.4MHz -HSync -VSync *current +preferred
# h: width 1366 start 1502 end 1532 total 1592
# v: height 768 start 776 end 788 total 800
# These numbers were provided by Nvidia.
register "href_to_sync" = "1"
register "hfront_porch" = "44"
register "hsync_width" = "46"
register "hback_porch" = "44"
register "href_to_sync" = "68"
register "hfront_porch" = "136"
register "hsync_width" = "30"
register "hback_porch" = "60"
register "vref_to_sync" = "1"
register "vfront_porch" = "6"
register "vsync_width" = "8"
register "vback_porch" = "6"
register "vref_to_sync" = "4"
register "vfront_porch" = "8"
register "vsync_width" = "12"
register "vback_porch" = "12"
# we *know* the pixel clock for this system.
register "pixel_clock" = "71"
# 1592 x 800 x 60Hz = 76416000
register "pixel_clock" = "76416000"
register "pll_div" = "2"
# use plld_out0 (ie, plld/2) as clock source
# plld -> plld_out0 -> pclk
# plld = plld_out0 * 2 = (pclk * pll_div) * 2
# = 305664000Hz
# link configurations
register "lane_count" = "1"
register "enhanced_framing" = "1"
register "link_bw" = "10"
# "10" is defined as SOR_LINK_SPEED_G2_7 in sor.h
register "drive_current" = "0x13131313"
register "preemphasis" = "0x00000000"
register "postcursor" = "0"
end

View file

@ -85,7 +85,7 @@ struct dc_cmd_reg {
/* Address 0x040 ~ 0x043 */
u32 state_access; /* _CMD_STATE_ACCESS_0 */
u32 state_ctrl; /* _CMD_STATE_CONTROL_0 */
u32 state_ctrl; /* _CMD_STATE_CONTROL_0 */
u32 disp_win_header; /* _CMD_DISPLAY_WINDOW_HEADER_0 */
u32 reg_act_ctrl; /* _CMD_REG_ACT_CONTROL_0 */
};
@ -195,14 +195,13 @@ enum dc_disp_pp_select {
struct dc_disp_reg {
/* Address 0x400 ~ 0x40a */
u32 disp_signal_opt0; /* _DISP_DISP_SIGNAL_OPTIONS0_0 */
u32 disp_signal_opt1; /* _DISP_DISP_SIGNAL_OPTIONS1_0 */
u32 rsvd_401;
u32 disp_win_opt; /* _DISP_DISP_WIN_OPTIONS_0 */
u32 mem_high_pri; /* _DISP_MEM_HIGH_PRIORITY_0 */
u32 mem_high_pri_timer; /* _DISP_MEM_HIGH_PRIORITY_TIMER_0 */
u32 rsvd_403[2]; /* 403 - 404 */
u32 disp_timing_opt; /* _DISP_DISP_TIMING_OPTIONS_0 */
u32 ref_to_sync; /* _DISP_REF_TO_SYNC_0 */
u32 sync_width; /* _DISP_SYNC_WIDTH_0 */
u32 back_porch; /* _DISP_BACK_PORCH_0 */
u32 sync_width; /* _DISP_SYNC_WIDTH_0 */
u32 back_porch; /* _DISP_BACK_PORCH_0 */
u32 disp_active; /* _DISP_DISP_ACTIVE_0 */
u32 front_porch; /* _DISP_FRONT_PORCH_0 */
@ -217,64 +216,49 @@ struct dc_disp_reg {
struct _disp_v_pulse2 v_pulse3; /* _DISP_V_PULSE2_ */
struct _disp_v_pulse2 v_pulse4; /* _DISP_V_PULSE3_ */
/* Address 0x426 ~ 0x429 */
u32 m0_ctrl; /* _DISP_M0_CONTROL_0 */
u32 m1_ctrl; /* _DISP_M1_CONTROL_0 */
u32 di_ctrl; /* _DISP_DI_CONTROL_0 */
u32 pp_ctrl; /* _DISP_PP_CONTROL_0 */
u32 rsvd_426[8]; /* 426 - 42d */
/* Address 0x42a ~ 0x42d: _DISP_PP_SELECT_A/B/C/D_0 */
u32 pp_select[PP_SELECT_COUNT];
/* Address 0x42e ~ 0x435 */
/* Address 0x42e ~ 0x430 */
u32 disp_clk_ctrl; /* _DISP_DISP_CLOCK_CONTROL_0 */
u32 disp_interface_ctrl; /* _DISP_DISP_INTERFACE_CONTROL_0 */
u32 disp_color_ctrl; /* _DISP_DISP_COLOR_CONTROL_0 */
u32 shift_clk_opt; /* _DISP_SHIFT_CLOCK_OPTIONS_0 */
u32 data_enable_opt; /* _DISP_DATA_ENABLE_OPTIONS_0 */
u32 serial_interface_opt; /* _DISP_SERIAL_INTERFACE_OPTIONS_0 */
u32 lcd_spi_opt; /* _DISP_LCD_SPI_OPTIONS_0 */
u32 border_color; /* _DISP_BORDER_COLOR_0 */
/* Address 0x436 ~ 0x439 */
u32 color_key0_lower; /* _DISP_COLOR_KEY0_LOWER_0 */
u32 rsvd_431[6]; /* 431 - 436 */
/* Address 0x437 ~ 0x439 */
u32 color_key0_upper; /* _DISP_COLOR_KEY0_UPPER_0 */
u32 color_key1_lower; /* _DISP_COLOR_KEY1_LOWER_0 */
u32 color_key1_upper; /* _DISP_COLOR_KEY1_UPPER_0 */
u32 reserved0[2]; /* reserved_0[2] */
u32 reserved0[2]; /* 43a - 43b */
/* Address 0x43c ~ 0x442 */
/* Address 0x43c ~ 0x441 */
u32 cursor_foreground; /* _DISP_CURSOR_FOREGROUND_0 */
u32 cursor_background; /* _DISP_CURSOR_BACKGROUND_0 */
u32 cursor_start_addr; /* _DISP_CURSOR_START_ADDR_0 */
u32 cursor_start_addr_ns; /* _DISP_CURSOR_START_ADDR_NS_0 */
u32 cursor_pos; /* _DISP_CURSOR_POSITION_0 */
u32 cursor_pos_ns; /* _DISP_CURSOR_POSITION_NS_0 */
u32 seq_ctrl; /* _DISP_INIT_SEQ_CONTROL_0 */
/* Address 0x442 ~ 0x446 */
u32 spi_init_seq_data_a; /* _DISP_SPI_INIT_SEQ_DATA_A_0 */
u32 spi_init_seq_data_b; /* _DISP_SPI_INIT_SEQ_DATA_B_0 */
u32 spi_init_seq_data_c; /* _DISP_SPI_INIT_SEQ_DATA_C_0 */
u32 spi_init_seq_data_d; /* _DISP_SPI_INIT_SEQ_DATA_D_0 */
u32 rsvd_442[62]; /* 442 - 47f */
u32 reserved1[0x39]; /* reserved1[0x39], */
/* Address 0x480 ~ 0x484 */
/* Address 0x480 ~ 0x483 */
u32 dc_mccif_fifoctrl; /* _DISP_DC_MCCIF_FIFOCTRL_0 */
u32 mccif_disp0a_hyst; /* _DISP_MCCIF_DISPLAY0A_HYST_0 */
u32 mccif_disp0b_hyst; /* _DISP_MCCIF_DISPLAY0B_HYST_0 */
u32 mccif_disp0c_hyst; /* _DISP_MCCIF_DISPLAY0C_HYST_0 */
u32 mccif_disp1b_hyst; /* _DISP_MCCIF_DISPLAY1B_HYST_0 */
u32 reserved2[0x3b]; /* reserved2[0x3b] */
u32 rsvd_484[61]; /* 484 - 4c0 */
/* Address 0x4c0 ~ 0x4c1 */
u32 dac_crt_ctrl; /* _DISP_DAC_CRT_CTRL_0 */
/* Address 0x4c1 */
u32 disp_misc_ctrl; /* _DISP_DISP_MISC_CONTROL_0 */
u32 rsvd_4c2[34]; /* 4c2 - 4e3 */
/* Address 0x4e4 */
u32 blend_background_color; /* _DISP_BLEND_BACKGROUND_COLOR_0 */
};
check_member(dc_disp_reg, disp_misc_ctrl, (0x4c1 - 0x400) * 4);
check_member(dc_disp_reg, blend_background_color, (0x4e4 - 0x400) * 4);
enum dc_winc_filter_p {
WINC_FILTER_COUNT = 0x10,
@ -310,9 +294,9 @@ struct dc_winc_reg {
};
check_member(dc_winc_reg, v_filter_p, (0x619 - 0x500) * 4);
/* WIN A/B/C Register 0x700 ~ 0x714*/
/* WIN A/B/C Register 0x700 ~ 0x719*/
struct dc_win_reg {
/* Address 0x700 ~ 0x714 */
/* Address 0x700 ~ 0x719 */
u32 win_opt; /* _WIN_WIN_OPTIONS_0 */
u32 byte_swap; /* _WIN_BYTE_SWAP_0 */
u32 buffer_ctrl; /* _WIN_BUFFER_CONTROL_0 */
@ -324,22 +308,27 @@ struct dc_win_reg {
u32 v_initial_dda; /* _WIN_V_INITIAL_DDA_0 */
u32 dda_increment; /* _WIN_DDA_INCREMENT_0 */
u32 line_stride; /* _WIN_LINE_STRIDE_0 */
u32 buf_stride; /* _WIN_BUF_STRIDE_0 */
u32 buf_stride; /* _WIN_BUF_STRIDE_0 */
u32 uv_buf_stride; /* _WIN_UV_BUF_STRIDE_0 */
u32 buffer_addr_mode; /* _WIN_BUFFER_ADDR_MODE_0 */
u32 dv_ctrl; /* _WIN_DV_CONTROL_0 */
u32 blend_nokey; /* _WIN_BLEND_NOKEY_0 */
u32 blend_1win; /* _WIN_BLEND_1WIN_0 */
u32 blend_1win; /* _WIN_BLEND_1WIN_0 */
u32 blend_2win_x; /* _WIN_BLEND_2WIN_X_0 */
u32 blend_2win_y; /* _WIN_BLEND_2WIN_Y_0 */
u32 blend_3win_xy; /* _WIN_BLEND_3WIN_XY_0 */
u32 hp_fetch_ctrl; /* _WIN_HP_FETCH_CONTROL_0 */
u32 global_alpha; /* _WIN_GLOBAL_ALPHA */
u32 blend_layer_ctrl; /* _WINBUF_BLEND_LAYER_CONTROL_0 */
u32 blend_match_select; /* _WINBUF_BLEND_MATCH_SELECT_0 */
u32 blend_nomatch_select; /* _WINBUF_BLEND_NOMATCH_SELECT_0 */
u32 blend_alpha_1bit; /* _WINBUF_BLEND_ALPHA_1BIT_0 */
};
check_member(dc_win_reg, hp_fetch_ctrl, (0x714 - 0x700) * 4);
check_member(dc_win_reg, blend_alpha_1bit, (0x719 - 0x700) * 4);
/* WINBUF A/B/C Register 0x800 ~ 0x80a */
/* WINBUF A/B/C Register 0x800 ~ 0x80d */
struct dc_winbuf_reg {
/* Address 0x800 ~ 0x80a */
/* Address 0x800 ~ 0x80d */
u32 start_addr; /* _WINBUF_START_ADDR_0 */
u32 start_addr_ns; /* _WINBUF_START_ADDR_NS_0 */
u32 start_addr_u; /* _WINBUF_START_ADDR_U_0 */
@ -351,8 +340,11 @@ struct dc_winbuf_reg {
u32 addr_v_offset; /* _WINBUF_ADDR_V_OFFSET_0 */
u32 addr_v_offset_ns; /* _WINBUF_ADDR_V_OFFSET_NS_0 */
u32 uflow_status; /* _WINBUF_UFLOW_STATUS_0 */
u32 buffer_surface_kind; /* DC_WIN_BUFFER_SURFACE_KIND */
u32 rsvd_80c;
u32 start_addr_hi; /* DC_WINBUF_START_ADDR_HI_0 */
};
check_member(dc_winbuf_reg, uflow_status, (0x80a - 0x800) * 4);
check_member(dc_winbuf_reg, start_addr_hi, (0x80d - 0x800) * 4);
/* Display Controller (DC_) regs */
struct display_controller {
@ -362,55 +354,28 @@ struct display_controller {
struct dc_com_reg com; /* COM register 0x300 ~ 0x329 */
u32 reserved1[0xd6];
struct dc_disp_reg disp; /* DISP register 0x400 ~ 0x4c1 */
u32 reserved2[0x3e];
struct dc_disp_reg disp; /* DISP register 0x400 ~ 0x4e4 */
u32 reserved2[0x1b];
struct dc_winc_reg winc; /* Window A/B/C 0x500 ~ 0x628 */
u32 reserved3[0xd7];
struct dc_win_reg win; /* WIN A/B/C 0x700 ~ 0x714*/
u32 reserved4[0xeb];
struct dc_win_reg win; /* WIN A/B/C 0x700 ~ 0x719*/
u32 reserved4[0xe6];
struct dc_winbuf_reg winbuf; /* WINBUF A/B/C 0x800 ~ 0x80a */
struct dc_winbuf_reg winbuf; /* WINBUF A/B/C 0x800 ~ 0x80d */
};
check_member(display_controller, winbuf, 0x800 * 4);
#define BIT(pos) (1U << pos)
#define BIT(pos) (1U << pos)
/* DC_CMD_DISPLAY_COMMAND 0x032 */
#define CTRL_MODE_SHIFT 5
#define CTRL_MODE_MASK (0x3 << CTRL_MODE_SHIFT)
enum {
CTRL_MODE_STOP,
CTRL_MODE_C_DISPLAY,
CTRL_MODE_NC_DISPLAY,
};
/* _WIN_COLOR_DEPTH_0 */
enum win_color_depth_id {
COLOR_DEPTH_P1,
COLOR_DEPTH_P2,
COLOR_DEPTH_P4,
COLOR_DEPTH_P8,
COLOR_DEPTH_B4G4R4A4,
COLOR_DEPTH_B5G5R5A,
COLOR_DEPTH_B5G6R5,
COLOR_DEPTH_AB5G5R5,
COLOR_DEPTH_B8G8R8A8 = 12,
COLOR_DEPTH_R8G8B8A8,
COLOR_DEPTH_B6x2G6x2R6x2A8,
COLOR_DEPTH_R6x2G6x2B6x2A8,
COLOR_DEPTH_YCbCr422,
COLOR_DEPTH_YUV422,
COLOR_DEPTH_YCbCr420P,
COLOR_DEPTH_YUV420P,
COLOR_DEPTH_YCbCr422P,
COLOR_DEPTH_YUV422P,
COLOR_DEPTH_YCbCr422R,
COLOR_DEPTH_YUV422R,
COLOR_DEPTH_YCbCr422RA,
COLOR_DEPTH_YUV422RA,
};
#define DISP_COMMAND_RAISE (1 << 0)
#define DISP_CTRL_MODE_STOP (0 << 5)
#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
#define DISP_COMMAND_RAISE_VECTOR(x) (((x) & 0x1f) << 22)
#define DISP_COMMAND_RAISE_CHANNEL_ID(x) (((x) & 0xf) << 27)
/* DC_CMD_DISPLAY_POWER_CONTROL 0x036 */
#define PW0_ENABLE BIT(0)
@ -423,20 +388,45 @@ enum win_color_depth_id {
#define SPI_ENABLE BIT(24)
#define HSPI_ENABLE BIT(25)
/* DC_CMD_STATE_ACCESS 0x040 */
#define READ_MUX_ASSEMBLY (0 << 0)
#define READ_MUX_ACTIVE (1 << 0)
#define WRITE_MUX_ASSEMBLY (0 << 2)
#define WRITE_MUX_ACTIVE (1 << 2)
/* DC_CMD_STATE_CONTROL 0x041 */
#define GENERAL_ACT_REQ BIT(0)
#define WIN_A_ACT_REQ BIT(1)
#define WIN_B_ACT_REQ BIT(2)
#define WIN_C_ACT_REQ BIT(3)
#define WIN_D_ACT_REQ BIT(4)
#define WIN_H_ACT_REQ BIT(5)
#define CURSOR_ACT_REQ BIT(7)
#define GENERAL_UPDATE BIT(8)
#define WIN_A_UPDATE BIT(9)
#define WIN_B_UPDATE BIT(10)
#define WIN_C_UPDATE BIT(11)
#define WIN_D_UPDATE BIT(12)
#define WIN_H_UPDATE BIT(13)
#define CURSOR_UPDATE BIT(15)
#define NC_HOST_TRIG BIT(24)
/* DC_CMD_DISPLAY_WINDOW_HEADER 0x042 */
#define WINDOW_A_SELECT BIT(4)
#define WINDOW_B_SELECT BIT(5)
#define WINDOW_C_SELECT BIT(6)
#define WINDOW_D_SELECT BIT(7)
#define WINDOW_H_SELECT BIT(8)
/* DC_DISP_DISP_WIN_OPTIONS 0x402 */
#define CURSOR_ENABLE BIT(16)
#define SOR_ENABLE BIT(25)
#define TVO_ENABLE BIT(28)
#define DSI_ENABLE BIT(29)
#define HDMI_ENABLE BIT(30)
/* DC_DISP_DISP_TIMING_OPTIONS 0x405 */
#define VSYNC_H_POSITION(x) ((x) & 0xfff)
/* DC_DISP_DISP_CLOCK_CONTROL 0x42e */
#define SHIFT_CLK_DIVIDER_SHIFT 0
@ -458,118 +448,61 @@ enum {
PIXEL_CLK_DIVIDER_PCD24,
PIXEL_CLK_DIVIDER_PCD13,
};
/* DC_DISP_DISP_INTERFACE_CONTROL 0x42f */
#define DATA_FORMAT_SHIFT 0
#define DATA_FORMAT_MASK (0xf << DATA_FORMAT_SHIFT)
enum {
DATA_FORMAT_DF1P1C,
DATA_FORMAT_DF1P2C24B,
DATA_FORMAT_DF1P2C18B,
DATA_FORMAT_DF1P2C16B,
DATA_FORMAT_DF2S,
DATA_FORMAT_DF3S,
DATA_FORMAT_DFSPI,
DATA_FORMAT_DF1P3C24B,
DATA_FORMAT_DF1P3C18B,
};
#define DATA_ALIGNMENT_SHIFT 8
enum {
DATA_ALIGNMENT_MSB,
DATA_ALIGNMENT_LSB,
};
#define DATA_ORDER_SHIFT 9
enum {
DATA_ORDER_RED_BLUE,
DATA_ORDER_BLUE_RED,
};
/* DC_DISP_DATA_ENABLE_OPTIONS 0x432 */
#define DE_SELECT_SHIFT 0
#define DE_SELECT_MASK (0x3 << DE_SELECT_SHIFT)
#define DE_SELECT_ACTIVE_BLANK 0x0
#define DE_SELECT_ACTIVE 0x1
#define DE_SELECT_ACTIVE_IS 0x2
#define DE_CONTROL_SHIFT 2
#define DE_CONTROL_MASK (0x7 << DE_CONTROL_SHIFT)
enum {
DE_CONTROL_ONECLK,
DE_CONTROL_NORMAL,
DE_CONTROL_EARLY_EXT,
DE_CONTROL_EARLY,
DE_CONTROL_ACTIVE_BLANK,
};
#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff)
/* DC_WIN_WIN_OPTIONS 0x700 */
#define H_DIRECTION BIT(0)
#define H_DIRECTION_DECREMENT(x) ((x) << 0)
#define V_DIRECTION_DECREMENT(x) ((x) << 2)
#define WIN_SCAN_COLUMN BIT(4)
#define COLOR_EXPAND BIT(6)
#define H_FILTER_ENABLE(x) ((x) << 8)
#define V_FILTER_ENABLE(x) ((x) << 10)
#define CP_ENABLE BIT(16)
#define CSC_ENABLE BIT(18)
#define DV_ENABLE BIT(20)
#define INTERLACE_ENABLE BIT(23)
#define INTERLACE_DISABLE (0 << 23)
#define WIN_ENABLE BIT(30)
/* _WIN_COLOR_DEPTH_0 0x703 */
enum {
H_DIRECTION_INCREMENT,
H_DIRECTION_DECREMENT,
COLOR_DEPTH_P8 = 3,
COLOR_DEPTH_B4G4R4A4,
COLOR_DEPTH_B5G5R5A,
COLOR_DEPTH_B5G6R5,
COLOR_DEPTH_AB5G5R5,
COLOR_DEPTH_B8G8R8A8 = 12,
COLOR_DEPTH_R8G8B8A8,
COLOR_DEPTH_YCbCr422 = 16,
COLOR_DEPTH_YUV422,
COLOR_DEPTH_YCbCr420P,
COLOR_DEPTH_YUV420P,
COLOR_DEPTH_YCbCr422P,
COLOR_DEPTH_YUV422P,
COLOR_DEPTH_N422R,
COLOR_DEPTH_YCbCr422R = COLOR_DEPTH_N422R,
COLOR_DEPTH_N422R_TRUE,
COLOR_DEPTH_YUV422R = COLOR_DEPTH_N422R_TRUE,
COLOR_DEPTH_CrYCbY422,
COLOR_DEPTH_VYUY422,
};
#define V_DIRECTION BIT(2)
enum {
V_DIRECTION_INCREMENT,
V_DIRECTION_DECREMENT,
};
#define COLOR_EXPAND BIT(6)
#define CP_ENABLE BIT(16)
#define DV_ENABLE BIT(20)
#define WIN_ENABLE BIT(30)
/* DC_WIN_BYTE_SWAP 0x701 */
#define BYTE_SWAP_SHIFT 0
#define BYTE_SWAP_MASK (3 << BYTE_SWAP_SHIFT)
enum {
BYTE_SWAP_NOSWAP,
BYTE_SWAP_SWAP2,
BYTE_SWAP_SWAP4,
BYTE_SWAP_SWAP4HW
};
/* DC_WIN_POSITION 0x704 */
#define H_POSITION_SHIFT 0
#define H_POSITION_MASK (0x1FFF << H_POSITION_SHIFT)
#define V_POSITION_SHIFT 16
#define V_POSITION_MASK (0x1FFF << V_POSITION_SHIFT)
/* DC_WIN_SIZE 0x705 */
#define H_SIZE_SHIFT 0
#define H_SIZE_MASK (0x1FFF << H_SIZE_SHIFT)
#define V_SIZE_SHIFT 16
#define V_SIZE_MASK (0x1FFF << V_SIZE_SHIFT)
/* DC_WIN_PRESCALED_SIZE 0x706 */
#define H_PRESCALED_SIZE_SHIFT 0
#define H_PRESCALED_SIZE_MASK (0x7FFF << H_PRESCALED_SIZE)
#define V_PRESCALED_SIZE_SHIFT 16
#define V_PRESCALED_SIZE_MASK (0x1FFF << V_PRESCALED_SIZE)
/* DC_WIN_DDA_INCREMENT 0x709 */
#define H_DDA_INC_SHIFT 0
#define H_DDA_INC_MASK (0xFFFF << H_DDA_INC_SHIFT)
#define V_DDA_INC_SHIFT 16
#define V_DDA_INC_MASK (0xFFFF << V_DDA_INC_SHIFT)
#define H_DDA_INC(x) (((x) & 0xffff) << 0)
#define V_DDA_INC(x) (((x) & 0xffff) << 16)
/* This holds information about a window which can be displayed */
/* TODO: do we really need this for basic setup? Not sure yet. */
struct disp_ctl_win {
enum win_color_depth_id fmt; /* Color depth/format */
u32 bpp; /* Bits per pixel */
u32 phys_addr; /* Physical address in memory */
u32 x; /* Horizontal address offset (bytes) */
u32 y; /* Veritical address offset (bytes) */
u32 w; /* Width of source window */
u32 h; /* Height of source window */
u32 stride; /* Number of bytes per line */
u32 out_x; /* Left edge of output window (col) */
u32 out_y; /* Top edge of output window (row) */
u32 out_w; /* Width of output window in pixels */
u32 out_h; /* Height of output window in pixels */
struct tegra_dc {
void *config;
void *out;
void *base;
};
void display_startup(device_t dev);
void dp_bringup(u32 winb_addr);
unsigned long READL(void * p);
void WRITEL(unsigned long value, void * p);
void display_startup(device_t dev);
void dp_init(void * _config);
void dp_enable(void * _dp);
unsigned int fb_base_mb(void);
#endif /* __SOC_NVIDIA_TEGRA_DC_H */

View file

@ -1,7 +1,7 @@
/*
* drivers/video/tegra/dc/dpaux_regs.h
*
* Copyright (c) 2011, NVIDIA Corporation.
* Copyright (c) 2014, NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -121,6 +121,8 @@
#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP (0)
#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERDOWN (1)
#define DPAUX_HPD_CONFIG_UNPLUG_MIN_TIME_SHIFT (16)
/* TODO: figure out which of the NV_ constants are the same as all the other
* display port standard constants.
*/
@ -133,7 +135,6 @@
#define DP_AUX_MAX_BYTES 16
#define DP_LCDVCC_TO_HPD_DELAY_MS 200
#define DP_AUX_TIMEOUT_MS 40
#define DP_DPCP_RETRY_SLEEP_NS 400
@ -171,9 +172,12 @@ enum {
#define EDP_PWR_OFF_TO_ON_TIME_MS (500+10)
struct tegra_dc_dp_data {
struct tegra_dc *dc;
struct tegra_dc_sor_data sor;
void *aux_base;
struct tegra_dc_dp_link_config link_cfg;
struct tegra_dc_dp_link_config link_cfg;
u8 revision;
int enabled;
};
@ -304,11 +308,4 @@ struct tegra_dc_dp_data {
#define NV_DPCD_HDCP_BINFO_OFFSET (0x0006802A)
#define NV_DPCD_HDCP_KSV_FIFO_OFFSET (0x0006802C)
#define NV_DPCD_HDCP_AINFO_OFFSET (0x0006803B)
int tegra_dc_dpaux_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr,
u8 *data, u32 *size, u32 *aux_stat);
int dpaux_write(u32 addr, u32 size, u32 data);
int dpaux_read(u32 addr, u32 size, u8 *data);
void debug_dpaux_print(u32 addr, u32 size);
void dp_link_training(u32 lanes, u32 speed);
#endif /* __SOC_NVIDIA_TEGRA_DISPLAYPORT_H__ */

View file

@ -42,7 +42,7 @@ endif
ramstage-y += cbfs.c
ramstage-y += cbmem.c
ramstage-y += clock.c
ramstage-y += display.c displayhack.c
ramstage-y += display.c
ramstage-y += dma.c
ramstage-y += i2c.c
ramstage-y += maincpu.S

View file

@ -23,14 +23,19 @@
#include <soc/addressmap.h>
#include "gpio.h"
#define EFAULT 1
#define EINVAL 2
/* this is a misuse of the device tree. We're going to let it go for now but
* we should at minimum have a struct for the display controller, since
* the chip supports two.
*/
struct soc_nvidia_tegra124_config {
int xres;
int yres;
int framebuffer_bits_per_pixel;
u32 xres;
u32 yres;
u32 framebuffer_bits_per_pixel;
u32 color_depth;
u32 panel_bits_per_pixel;
int cache_policy;
/* there are two. It's not unimaginable that we might someday
* have two of these structs in a single mainboard.
@ -60,32 +65,41 @@ struct soc_nvidia_tegra124_config {
* This is stated to be four timings in the
* u-boot docs. In any event, in coreboot, we generally
* only delay long enough to let the panel wake up and then
* do the control operations -- meaming, for *coreboot*
* do the control operations -- meaning, for *coreboot*
* we probably only need the vdd_delay, but payloads may
* need the other info.
*/
/* Delay before from power on asserting vdd */
int vdd_delay;
/* delay between panel_vdd-rise and data-rise*/
int vdd_data_delay;
/* delay between data-rise and backlight_vdd-rise */
int data_backlight_delay;
/* delay between backlight_vdd and pwm-rise */
int backlight_pwm_delay;
/* delay between pwm-rise and backlight_en-rise */
int pwm_backlight_en_delay;
/* display timing.
* we have not found a dts in which these are set */
int href_to_sync; /* u-boot code says 'set to 1' */
int vdd_delay_ms;
/* Delay before HPD high */
int vdd_to_hpd_delay_ms;
int hpd_unplug_min_us;
int hpd_plug_min_us;
int hpd_irq_min_us;
int href_to_sync;
int hsync_width;
int hback_porch;
int hfront_porch;
int vref_to_sync; /* u-boot code says 'set to 1' */
int vref_to_sync;
int vsync_width;
int vback_porch;
int vfront_porch;
int pixel_clock;
int pll_div;;
/* The minimum link configuraton settings */
u32 lane_count;
u32 enhanced_framing;
u32 link_bw;
u32 drive_current;
u32 preemphasis;
u32 postcursor;
void *dc_data;
};
#endif /* __SOC_NVIDIA_TEGRA124_CHIP_H__ */

View file

@ -88,10 +88,9 @@ struct {
int khz;
struct pllcx_dividers pllx; /* target: CONFIG_PLLX_KHZ */
struct pllcx_dividers pllc; /* target: 600 MHz */
struct pllpad_dividers plld; /* target: 925 MHz */
struct pllpad_dividers plld; /* target: 306 MHz */
struct pllu_dividers pllu; /* target; 960 MHz */
struct pllcx_dividers plldp; /* target; 270 MHz */
struct pllcx_dividers plld2; /* target; 570 MHz */
/* Based on T124 TRM (to be updatd), PLLP is set to 408MHz in HW.
* Unless configuring PLLP to a frequency other than 408MHz,
* software configuration on PLLP is unneeded. */
@ -100,46 +99,41 @@ struct {
.khz = 12000,
.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0},
.pllc = {.n = 50, .m = 1, .p = 0},
.plld = {.n = 283, .m = 12, .p = 0, .cpcon = 8}, /* 283 MHz */
.plld = {.n = 306, .m = 12, .p = 0, .cpcon = 8},
.pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2},
.plldp = {.n = 90, .m = 1, .p = 3},
.plld2 = {.n = 95, .m = 1, .p = 1},
},
[OSC_FREQ_OSC13]{
.khz = 13000,
.pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m = 1, .p = 0},
.pllc = {.n = 231, .m = 5, .p = 0}, /* 600.6 MHz */
.plld = {.n = 283, .m = 13, .p = 0, .cpcon = 8}, /* 283 MHz*/
.plld = {.n = 306, .m = 13, .p = 0, .cpcon = 8},
.pllu = {.n = 960, .m = 13, .p = 0, .cpcon = 12, .lfcon = 2},
.plldp = {.n = 83, .m = 1, .p = 3}, /* 269.75 MHz */
.plld2 = {.n = 88, .m = 1, .p = 1}, /* 572 MHz */
},
[OSC_FREQ_OSC16P8]{
.khz = 16800,
.pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m = 1, .p = 0},
.pllc = {.n = 250, .m = 7, .p = 0},
.plld = {.n = 286, .m = 17, .p = 0, .cpcon = 8}, /* 282.6 MHz*/
.plld = {.n = 309, .m = 17, .p = 0, .cpcon = 8}, /* 305.4 MHz*/
.pllu = {.n = 400, .m = 7, .p = 0, .cpcon = 5, .lfcon = 2},
.plldp = {.n = 64, .m = 1, .p = 3}, /* 268.8 MHz */
.plld2 = {.n = 68, .m = 1, .p = 1}, /* 571.2 MHz */
},
[OSC_FREQ_OSC19P2]{
.khz = 19200,
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
.pllc = {.n = 125, .m = 4, .p = 0},
.plld = {.n = 251, .m = 17, .p = 0, .cpcon = 8}, /* 283.5 MHz */
.plld = {.n = 271, .m = 17, .p = 0, .cpcon = 8}, /* 306.1 MHz */
.pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2},
.plldp = {.n = 56, .m = 1, .p = 3}, /* 270.75 MHz */
.plld2 = {.n = 59, .m = 1, .p = 1}, /* 566.4 MHz */
},
[OSC_FREQ_OSC26]{
.khz = 26000,
.pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m = 1, .p = 0},
.pllc = {.n = 23, .m = 1, .p = 0}, /* 598 MHz */
.plld = {.n = 283, .m = 26, .p = 0, .cpcon = 8}, /* 283 MHz */
.plld = {.n = 306, .m = 26, .p = 0, .cpcon = 8},
.pllu = {.n = 960, .m = 26, .p = 0, .cpcon = 12, .lfcon = 2},
.plldp = {.n = 83, .m = 2, .p = 3}, /* 266.50 MHz */
.plld2 = {.n = 88, .m = 2, .p = 1}, /* 572 MHz */
},
[OSC_FREQ_OSC38P4]{
.khz = 38400,
@ -149,10 +143,9 @@ struct {
*/
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
.pllc = {.n = 125, .m = 4, .p = 0},
.plld = {.n = 125, .m = 17, .p = 0, .cpcon = 8}, /* 282.4 MHz */
.plld = {.n = 271, .m = 17, .p = 0, .cpcon = 8}, /* 306.1 MHz */
.pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2},
.plldp = {.n = 56, .m = 2, .p = 3}, /* 268 MHz */
.plld2 = {.n = 59, .m = 2, .p = 1}, /* 566 MHz */
},
[OSC_FREQ_OSC48]{
.khz = 48000,
@ -162,10 +155,9 @@ struct {
*/
.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0},
.pllc = {.n = 50, .m = 1, .p = 0},
.plld = {.n = 71, .m = 12, .p = 0, .cpcon = 8}, /* 284 MHz */
.plld = {.n = 306, .m = 12, .p = 0, .cpcon = 8},
.pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2},
.plldp = {.n = 90, .m = 4, .p = 3}, /* 264 MHz */
.plld2 = {.n = 95, .m = 4, .p = 1},
},
};

View file

@ -38,18 +38,26 @@
#include "chip.h"
#include <soc/display.h>
struct tegra_dc dc_data;
int dump = 0;
unsigned long READL(void * p);
void WRITEL(unsigned long value, void * p);
unsigned long READL(void * p)
{
unsigned long value = readl(p);
unsigned long value;
/*
* In case of hard hung on readl(p), we can set dump > 1 to print out
* the address accessed.
*/
if (dump > 1)
printk(BIOS_SPEW, "readl %p\n", p);
value = readl(p);
if (dump)
printk(BIOS_SPEW, "readl %p %08lx\n", p, value);
return value;
}
void WRITEL(unsigned long value, void * p)
{
if (dump)
@ -57,174 +65,133 @@ void WRITEL(unsigned long value, void * p)
writel(value, p);
}
static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};
/* return in 1000ths of a Hertz */
static int tegra_dc_calc_refresh(const struct soc_nvidia_tegra124_config *config)
{
int h_total, v_total, refresh;
int pclk = config->pixel_clock;
static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
0x00000000,
0x01000000,
0x00000000,
0x00000000,
};
h_total = config->xres + config->hfront_porch + config->hback_porch +
config->hsync_width;
v_total = config->yres + config->vfront_porch + config->vback_porch +
config->vsync_width;
if (!pclk || !h_total || !v_total)
return 0;
refresh = pclk / h_total;
refresh *= 1000;
refresh /= v_total;
return refresh;
}
static const u32 rgb_data_tab[PIN_REG_COUNT] = {
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};
static void print_mode(const struct soc_nvidia_tegra124_config *config)
{
if (config) {
int refresh = tegra_dc_calc_refresh(config);
printk(BIOS_ERR,
"MODE:%dx%d@%d.%03uHz pclk=%d\n",
config->xres, config->yres,
refresh / 1000, refresh % 1000,
config->pixel_clock);
}
}
static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00210222,
0x00002200,
0x00020000,
};
static int update_display_mode(struct dc_disp_reg *disp,
static int update_display_mode(struct display_controller *disp_ctrl,
struct soc_nvidia_tegra124_config *config)
{
u32 val;
u32 rate;
u32 div;
unsigned long div = config->pll_div;
WRITEL(0x0, &disp->disp_timing_opt);
print_mode(config);
WRITEL(0x1, &disp_ctrl->disp.disp_timing_opt);
WRITEL(config->vref_to_sync << 16 | config->href_to_sync,
&disp->ref_to_sync);
WRITEL(config->vsync_width << 16 | config->hsync_width, &disp->sync_width);
WRITEL(config->vback_porch << 16 | config->hback_porch, &disp->back_porch);
WRITEL(config->vfront_porch << 16 | config->hfront_porch,
&disp->front_porch);
&disp_ctrl->disp.ref_to_sync);
WRITEL(config->xres | (config->yres << 16), &disp->disp_active);
WRITEL(config->vsync_width << 16 | config->hsync_width,
&disp_ctrl->disp.sync_width);
val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
WRITEL(val, &disp->data_enable_opt);
WRITEL(((config->vback_porch - config->vref_to_sync) << 16) | config->hback_porch,
&disp_ctrl->disp.back_porch);
val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
WRITEL(val, &disp->disp_interface_ctrl);
WRITEL(((config->vfront_porch + config->vref_to_sync) << 16) | config->hfront_porch,
&disp_ctrl->disp.front_porch);
/*
* The pixel clock divider is in 7.1 format (where the bottom bit
* represents 0.5). Here we calculate the divider needed to get from
* the display clock (typically 600MHz) to the pixel clock. We round
* up or down as requried.
* We use pllp for now.
*/
rate = 600 * 1000000;
div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2;
printk(BIOS_SPEW, "Display clock %d, divider %d\n", rate, div);
WRITEL(0x00010001, &disp->shift_clk_opt);
val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
val |= div << SHIFT_CLK_DIVIDER_SHIFT;
WRITEL(val, &disp->disp_clk_ctrl);
WRITEL(config->xres | (config->yres << 16),
&disp_ctrl->disp.disp_active);
WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
SHIFT_CLK_DIVIDER(div),
&disp_ctrl->disp.disp_clk_ctrl);
return 0;
}
static int setup_window(struct disp_ctl_win *win,
struct soc_nvidia_tegra124_config *config)
static void update_window(struct display_controller *disp_ctrl,
struct soc_nvidia_tegra124_config *config)
{
int log2_bpp = log2(config->framebuffer_bits_per_pixel);
win->x = 0;
win->y = 0;
win->w = config->xres;
win->h = config->yres;
win->out_x = 0;
win->out_y = 0;
win->out_w = config->xres;
win->out_h = config->yres;
win->phys_addr = config->framebuffer_base;
win->stride = config->xres * (1 << log2_bpp) / 8;
printk(BIOS_SPEW, "%s: depth = %d\n", __func__, log2_bpp);
switch (log2_bpp) {
case 5:
case 24:
win->fmt = COLOR_DEPTH_R8G8B8A8;
win->bpp = 32;
break;
case 4:
win->fmt = COLOR_DEPTH_B5G6R5;
win->bpp = 16;
break;
default:
printk(BIOS_SPEW, "Unsupported LCD bit depth");
return -1;
}
return 0;
}
static void update_window(struct display_controller *dc,
struct disp_ctl_win *win,
struct soc_nvidia_tegra124_config *config)
{
u32 h_dda, v_dda;
u32 val;
val = READL(&dc->cmd.disp_win_header);
val |= WINDOW_A_SELECT;
WRITEL(val, &dc->cmd.disp_win_header);
WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
WRITEL(win->fmt, &dc->win.color_depth);
WRITEL(((config->yres << 16) | config->xres), &disp_ctrl->win.size);
WRITEL(((config->yres << 16) |
(config->xres * config->framebuffer_bits_per_pixel / 8)),
&disp_ctrl->win.prescaled_size);
WRITEL(((config->xres * config->framebuffer_bits_per_pixel / 8 + 31) /
32 * 32), &disp_ctrl->win.line_stride);
clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK,
BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT);
WRITEL(config->color_depth, &disp_ctrl->win.color_depth);
val = win->out_x << H_POSITION_SHIFT;
val |= win->out_y << V_POSITION_SHIFT;
WRITEL(val, &dc->win.pos);
WRITEL(config->framebuffer_base, &disp_ctrl->winbuf.start_addr);
WRITEL((V_DDA_INC(0x1000) | H_DDA_INC(0x1000)), &disp_ctrl->win.dda_increment);
val = win->out_w << H_SIZE_SHIFT;
val |= win->out_h << V_SIZE_SHIFT;
WRITEL(val, &dc->win.size);
WRITEL(COLOR_WHITE, &disp_ctrl->disp.blend_background_color);
WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd);
val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT;
val |= win->h << V_PRESCALED_SIZE_SHIFT;
WRITEL(val, &dc->win.prescaled_size);
WRITEL(0, &dc->win.h_initial_dda);
WRITEL(0, &dc->win.v_initial_dda);
h_dda = (win->w * 0x1000) / MAX(win->out_w - 1, 1);
v_dda = (win->h * 0x1000) / MAX(win->out_h - 1, 1);
val = h_dda << H_DDA_INC_SHIFT;
val |= v_dda << V_DDA_INC_SHIFT;
WRITEL(val, &dc->win.dda_increment);
WRITEL(win->stride, &dc->win.line_stride);
WRITEL(0, &dc->win.buf_stride);
val = WIN_ENABLE;
if (win->bpp < 24)
val |= COLOR_EXPAND;
WRITEL(val, &dc->win.win_opt);
WRITEL((u32) win->phys_addr, &dc->winbuf.start_addr);
WRITEL(win->x, &dc->winbuf.addr_h_offset);
WRITEL(win->y, &dc->winbuf.addr_v_offset);
WRITEL(0xff00, &dc->win.blend_nokey);
WRITEL(0xff00, &dc->win.blend_1win);
WRITEL(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
val |= GENERAL_UPDATE | WIN_A_UPDATE;
WRITEL(val, &dc->cmd.state_ctrl);
WRITEL(val, &disp_ctrl->cmd.state_ctrl);
// Enable win_a
val = READL(&disp_ctrl->win.win_opt);
WRITEL(val | WIN_ENABLE, &disp_ctrl->win.win_opt);
}
static int tegra_dc_init(struct display_controller *disp_ctrl)
{
/* do not accept interrupts during initialization */
WRITEL(0x00000000, &disp_ctrl->cmd.int_mask);
WRITEL(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
&disp_ctrl->cmd.state_access);
WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
WRITEL(0x00000000, &disp_ctrl->win.win_opt);
WRITEL(0x00000000, &disp_ctrl->win.byte_swap);
WRITEL(0x00000000, &disp_ctrl->win.buffer_ctrl);
WRITEL(0x00000000, &disp_ctrl->win.pos);
WRITEL(0x00000000, &disp_ctrl->win.h_initial_dda);
WRITEL(0x00000000, &disp_ctrl->win.v_initial_dda);
WRITEL(0x00000000, &disp_ctrl->win.dda_increment);
WRITEL(0x00000000, &disp_ctrl->win.dv_ctrl);
WRITEL(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
WRITEL(0x00000000, &disp_ctrl->win.blend_match_select);
WRITEL(0x00000000, &disp_ctrl->win.blend_nomatch_select);
WRITEL(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
WRITEL(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
WRITEL(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
WRITEL(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
WRITEL(0x00000000, &disp_ctrl->com.crc_checksum);
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
WRITEL(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
return 0;
}
uint32_t fb_base_mb(void)
@ -237,16 +204,22 @@ uint32_t fb_base_mb(void)
*/
void display_startup(device_t dev)
{
u32 val;
int i;
struct soc_nvidia_tegra124_config *config = dev->chip_info;
struct display_controller *dc = (void *)config->display_controller;
struct pwm_controller *pwm = (void *)TEGRA_PWM_BASE;
struct disp_ctl_win window;
struct display_controller *disp_ctrl = (void *)config->display_controller;
struct pwm_controller *pwm = (void *)TEGRA_PWM_BASE;
struct tegra_dc *dc = &dc_data;
/* should probably just make it all MiB ... in future */
u32 framebuffer_size_mb = config->framebuffer_size / MiB;
u32 framebuffer_base_mb= config->framebuffer_base / MiB;
/* init dc */
dc->base = (void *)TEGRA_ARM_DISPLAYA;
dc->config = config;
config->dc_data = dc;
dp_init(config);
/* light it all up */
/* This one may have been done in romstage but that's ok for now. */
if (config->panel_vdd_gpio){
@ -254,13 +227,12 @@ void display_startup(device_t dev)
printk(BIOS_SPEW,"%s: panel_vdd setting gpio %08x to %d\n",
__func__, config->panel_vdd_gpio, 1);
}
delay(1);
udelay(config->vdd_delay_ms * 1000);
if (config->backlight_vdd_gpio){
gpio_output(config->backlight_vdd_gpio, 1);
printk(BIOS_SPEW,"%s: backlight vdd setting gpio %08x to %d\n",
__func__, config->backlight_vdd_gpio, 1);
}
delay(1);
if (config->lvds_shutdown_gpio){
gpio_output(config->lvds_shutdown_gpio, 0);
printk(BIOS_SPEW,"%s: lvds shutdown setting gpio %08x to %d\n",
@ -281,10 +253,6 @@ void display_startup(device_t dev)
0x02e), /* frequency divider */
&pwm->pwm[config->pwm].csr);
printk(BIOS_SPEW,
"%s: xres %d yres %d framebuffer_bits_per_pixel %d\n",
__func__,
config->xres, config->yres, config->framebuffer_bits_per_pixel);
if (framebuffer_size_mb == 0){
framebuffer_size_mb = ALIGN_UP(config->xres * config->yres *
(config->framebuffer_bits_per_pixel / 8), MiB)/MiB;
@ -293,13 +261,12 @@ void display_startup(device_t dev)
if (! framebuffer_base_mb)
framebuffer_base_mb = fb_base_mb();
config->framebuffer_size = framebuffer_size_mb * MiB;
config->framebuffer_base = framebuffer_base_mb * MiB;
mmu_config_range(framebuffer_base_mb, framebuffer_size_mb,
config->cache_policy);
/* Enable flushing after LCD writes if requested */
/* I don't understand this part yet.
lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH);
*/
printk(BIOS_SPEW, "LCD frame buffer at %dMiB to %dMiB\n", framebuffer_base_mb,
framebuffer_base_mb + framebuffer_size_mb);
@ -311,63 +278,34 @@ void display_startup(device_t dev)
* light things up here once we're sure it's all working.
*/
/* init dc_a */
init_dca_regs();
/* Init dc */
if (tegra_dc_init(disp_ctrl)) {
printk(BIOS_ERR, "dc: init failed\n");
return;
}
/* power up perip */
dp_io_powerup();
/* Configure dc mode */
update_display_mode(disp_ctrl, config);
/* bringup dp */
dp_bringup(framebuffer_base_mb*MiB);
/* Enable dp */
dp_enable(dc->out);
/* init frame buffer */
/* Init frame buffer */
memset((void *)(framebuffer_base_mb*MiB), 0x00,
framebuffer_size_mb*MiB);
update_window(disp_ctrl, config);
printk(BIOS_INFO, "%s: display init done.\n", __func__);
/* tell depthcharge ...
*/
struct edid edid;
edid.x_resolution = 1376;
edid.y_resolution = 768;
edid.bytes_per_line = 1376 * 2;
edid.framebuffer_bits_per_pixel = 16;
edid.bytes_per_line = ((config->xres * config->framebuffer_bits_per_pixel / 8 + 31) /
32 * 32);
edid.x_resolution = edid.bytes_per_line / (config->framebuffer_bits_per_pixel / 8);
edid.y_resolution = config->yres;
edid.framebuffer_bits_per_pixel = config->framebuffer_bits_per_pixel;
set_vbe_mode_info_valid(&edid, (uintptr_t)(framebuffer_base_mb*MiB));
if (0){
/* do we still need these? */
WRITEL(0x00000100, &dc->cmd.gen_incr_syncpt_ctrl);
WRITEL(0x0000011a, &dc->cmd.cont_syncpt_vsync);
WRITEL(0x00000000, &dc->cmd.int_type);
WRITEL(0x00000000, &dc->cmd.int_polarity);
WRITEL(0x00000000, &dc->cmd.int_mask);
WRITEL(0x00000000, &dc->cmd.int_enb);
val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE;
val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE;
val |= PM1_ENABLE;
WRITEL(val, &dc->cmd.disp_pow_ctrl);
val = READL(&dc->cmd.disp_cmd);
val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
WRITEL(val, &dc->cmd.disp_cmd);
WRITEL(0x00000020, &dc->disp.mem_high_pri);
WRITEL(0x00000001, &dc->disp.mem_high_pri_timer);
for (i = 0; i < PIN_REG_COUNT; i++) {
WRITEL(rgb_enb_tab[i], &dc->com.pin_output_enb[i]);
WRITEL(rgb_polarity_tab[i], &dc->com.pin_output_polarity[i]);
WRITEL(rgb_data_tab[i], &dc->com.pin_output_data[i]);
}
for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
WRITEL(rgb_sel_tab[i], &dc->com.pin_output_sel[i]);
if (config->pixel_clock)
update_display_mode(&dc->disp, config);
if (!setup_window(&window, config))
update_window(dc, &window, config);
}
}

View file

@ -1,738 +0,0 @@
/* this is too ugly to be allowed to live. But it's what works for now. */
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <arch/io.h>
#include <stdint.h>
#include <lib.h>
#include <stdlib.h>
#include <delay.h>
#include <soc/addressmap.h>
#include <device/device.h>
#include <stdlib.h>
#include <string.h>
#include <cpu/cpu.h>
#include <boot/tables.h>
#include <cbmem.h>
#include <soc/nvidia/tegra/dc.h>
#include "clk_rst.h"
#include <soc/clock.h>
#include "chip.h"
#include "sor.h"
#include <soc/display.h>
//#include <soc/nvidia/tegra/displayport.h>
extern int dump;
unsigned long READL(void *p);
void WRITEL(unsigned long value, void *p);
void debug_dpaux_print(u32 addr, u32 size);
int dpaux_write(u32 addr, u32 size, u32 data);
int dpaux_read(u32 addr, u32 size, u8 * data);
#define DCA_WRITE(reg, val) \
{ \
WRITEL(val, (void *)(TEGRA_ARM_DISPLAYA + (reg<<2))); \
}
#define DCA_READ_M_WRITE(reg, mask, val) \
{ \
u32 _reg_val; \
_reg_val = READL( (void *)(TEGRA_ARM_DISPLAYA + (reg<<2))); \
_reg_val &= ~mask; \
_reg_val |= val; \
WRITEL(_reg_val, (void *)(TEGRA_ARM_DISPLAYA + (reg<<2))); \
}
#define SOR_WRITE(reg, val) \
{ \
WRITEL(val, (void *)(TEGRA_ARM_SOR + (reg<<2))); \
}
#define SOR_READ(reg) READL((void *)(TEGRA_ARM_SOR + (reg<<2)))
#define SOR_READ_M_WRITE(reg, mask, val) \
{ \
u32 _reg_val; \
_reg_val = READL((void *)(TEGRA_ARM_SOR + (reg<<2))); \
_reg_val &= ~mask; \
_reg_val |= val; \
WRITEL(_reg_val, (void *)(TEGRA_ARM_SOR + (reg<<2))); \
}
#define DPAUX_WRITE(reg, val) \
{ \
WRITEL(val, (void *)(TEGRA_ARM_DPAUX + (reg<<2))); \
}
#define DPAUX_READ(reg) READL((void *)(TEGRA_ARM_DPAUX + (reg<<2)))
void init_dca_regs(void)
{
DCA_WRITE (DC_CMD_DISPLAY_WINDOW_HEADER_0, 0x000000F0);
DCA_WRITE (DC_WIN_A_WIN_OPTIONS_0, 0x00000000);
DCA_WRITE (DC_WIN_A_BYTE_SWAP_0, 0x00000000);
DCA_WRITE (DC_WIN_A_BUFFER_CONTROL_0, 0x00000000);
DCA_WRITE (DC_WIN_A_COLOR_DEPTH_0, 0x0000000C);
DCA_WRITE (DC_WIN_A_POSITION_0, 0x00000000);
DCA_WRITE (DC_WIN_A_SIZE_0, 0x00000000);
DCA_WRITE (DC_WIN_A_PRESCALED_SIZE_0, 0x00000000);
DCA_WRITE (DC_WIN_A_H_INITIAL_DDA_0, 0x00000000);
DCA_WRITE (DC_WIN_A_V_INITIAL_DDA_0, 0x00000000);
DCA_WRITE (DC_WIN_A_DDA_INCREMENT_0, 0x00000000);
DCA_WRITE (DC_WIN_A_LINE_STRIDE_0, 0x00000000);
DCA_WRITE (DC_WIN_A_DV_CONTROL_0, 0x00000000);
DCA_WRITE (DC_WIN_A_BLEND_LAYER_CONTROL_0, 0x01000000);
DCA_WRITE (DC_WIN_A_BLEND_MATCH_SELECT_0, 0x00000000);
DCA_WRITE (DC_WIN_A_BLEND_NOMATCH_SELECT_0, 0x00000000);
DCA_WRITE (DC_WIN_A_BLEND_ALPHA_1BIT_0, 0x00000000);
DCA_WRITE (DC_WINBUF_A_START_ADDR_HI_0, 0x00000000);
DCA_WRITE (DC_WINBUF_A_ADDR_H_OFFSET_0, 0x00000000);
DCA_WRITE (DC_WINBUF_A_ADDR_V_OFFSET_0, 0x00000000);
DCA_WRITE (DC_CMD_DISPLAY_WINDOW_HEADER_0, 0x00000000);
DCA_WRITE (DC_COM_CRC_CONTROL_0, 0x00000000);
DCA_WRITE (DC_COM_CRC_CHECKSUM_0, 0x00000000);
DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE0_0, 0x00000000);
DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE1_0, 0x00000000);
DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE2_0, 0x00510104);
DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE3_0, 0x00000555);
}
static int dp_poll_register(void *addr, u32 exp_val, u32 mask, u32 timeout_ms)
{
u32 reg_val = 0;
do {
udelay(1000);
reg_val = READL(addr);
} while (((reg_val & mask) != exp_val) && (--timeout_ms > 0));
if ((reg_val & mask) == exp_val)
return 0; /* success */
printk(BIOS_WARNING, "poll_register %p: timeout\n", addr);
return timeout_ms;
}
static void dp_io_set_dpd(u32 power_down)
{
/*
* power_down:
* 0: out of Deep power down
* 1: into deep power down
*/
u32 val_reg;
val_reg = READL((void *)(0x7000e400 + 0x1c4)); /* APBDEV_PMC_IO_DPD2_STATUS_0 */
if ((((val_reg & DP_LVDS) >> DP_LVDS_SHIFT) & 1) == power_down) {
printk(BIOS_DEBUG, "PAD already POWER=%d\n", 1 - power_down);
return;
}
/* APBDEV_PMC_IO_DPD2_REQ_0: E_DPD = power on */
WRITEL((DP_LVDS | ((1 + power_down) << 30)), (void *)(0x7000e400 + 0x1c0));
dp_poll_register((void *)(0x7000e400 + 0x1C4), 0, DP_LVDS, 1000);
/* APBDEV_PMC_IO_DPD2_STATUS_0 */
}
void dp_io_powerup(void)
{
SOR_WRITE(SOR_NV_PDISP_SOR_CLK_CNTRL_0, (6 << 2) | 2);//select PLLDP, lowest speed(6x)
SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, 0x00800000); //set PDCAL
SOR_WRITE(SOR_NV_PDISP_SOR_PLL0_0, 0x050003D5); //set PWR,VCOPD
SOR_WRITE(SOR_NV_PDISP_SOR_PLL1_0, 0x00001100); //default
SOR_WRITE(SOR_NV_PDISP_SOR_PLL2_0, 0x01C20000); //set AUX1,6,7,8; clr AUX2
SOR_WRITE(SOR_NV_PDISP_SOR_PLL3_0, 0x38002220);
/* Deassert E_DPD to enable core logic circuits, and wait for > 5us */
dp_io_set_dpd(0);
udelay(10);
/* Deassert PDBG to enable bandgap, and wait for > 20us. */
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0,
SOR_NV_PDISP_SOR_PLL2_0_AUX6_FIELD,
(0 << SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT));
udelay(25);
/*
* Enable the PLL/charge-pump/VCO, and wait for >200us for the PLL to
* lock. Input Clock must be running and stable before PDPLL
* de-assertion.
*/
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL0_0,
SOR_NV_PDISP_SOR_PLL0_0_PWR_FIELD,
(0 << SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL0_0,
SOR_NV_PDISP_SOR_PLL0_0_VCOPD_FIELD,
(0 << SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0,
SOR_NV_PDISP_SOR_PLL2_0_AUX8_FIELD,
(0 << SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT));
udelay(210);
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0,
SOR_NV_PDISP_SOR_PLL2_0_AUX7_FIELD,
(0 << SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0,
SOR_NV_PDISP_SOR_PLL2_0_AUX9_FIELD,
(1 << SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT));
udelay(100);
}
static int dpaux_check(u32 bytes, u32 data, u32 mask)
{
u32 status = 0;
u8 buf[16];
u32 temp;
DPAUX_WRITE(DPAUX_DP_AUXDATA_READ_W0, 0);
status = dpaux_read(0x202, bytes, buf);
if (status != 0)
printk(BIOS_ERR, "******AuxRead Error:%04x: status %08x\n",
0x202, status);
else {
temp = DPAUX_READ(DPAUX_DP_AUXDATA_READ_W0);
if ((temp & mask) != (data & mask)) {
printk(BIOS_ERR, "AuxCheck ERROR:(r_data) %08x &"
" (mask) %08x != (data) %08x & (mask) %08x\n",
temp, mask, data, mask);
return -1;
} else {
printk(BIOS_DEBUG, "AuxCheck PASS:(bytes=%d, "
"data=%08x, mask=%08x):0x%08x\n",
bytes, data, mask, temp);
return 0;
}
}
return -1;
}
/* Modify the drive parameters for DP. There are up to four DP
* lanes. In principle, each lane can have different current,
* pre-emphasis, and postcur values. Nobody individualizes them; every
* single driver I've seen drives all the lanes to the same value
* (across x86 and ARM code). Actualy adjusting them individually and
* getting it all to work is probably a PhD thesis anyway. So, rather
* than the very complex code we see many places, the people who wrote
* this code realize: we can represent the 'volume' as a number in the
* range 0..3, with '0' as the base and '3' as being 'not to exceed'.
*
* So they abstract the values away, take care of the proper values,
* and set it all in one blow. Very nice. By far the easiest one of
* these functions we've seen. Sure, they could have constants, but
* nobody knows what PRE_EMPHASIS_3_5 and the other values actually
* *mean* anyway. Well, the hardware guys might.
*/
static void pattern_level(u32 current, u32 preemph, u32 postcur)
{
//calibrating required
if (current == 0)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x20202020);
if (current == 1)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x24242424);
if (current == 2)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x30303030);
if (current == 3)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x40404040);
if (preemph == 0)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x00000000);
if (preemph == 1)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x08080808);
if (preemph == 2)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x10101010);
if (preemph == 3)
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x18181818);
if (postcur == 0)
SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x00000000);
if (postcur == 1)
SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x04040404);
if (postcur == 2)
SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x08080808);
if (postcur == 3)
SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x10101010);
}
static int dp_training(u32 level, u32 check, u32 speed)
{
/* The levels are one of four choices. This code
* packs them into the three lowest nibl's. We may change this.
*/
u32 dc_lv = level & 0x0f;
u32 pe_lv = (level >> 4) & 0x0f;
u32 pc_lv = (level >> 8) & 0x0f;
u32 cnt = 0;
u32 cfg, cfg_d = 0;
u32 wcfg;
u8 buf[16];
while (cnt <= 5) {
pattern_level(dc_lv, pe_lv, pc_lv);
wcfg = (pe_lv << 3) | dc_lv;
if (dc_lv == 3)
wcfg = wcfg | 0x04;
if (pe_lv == 3)
wcfg = wcfg | 0x20;
wcfg = wcfg | (wcfg << 8) | (wcfg << 16) | (wcfg << 24);
dpaux_write(0x103, 4, wcfg);
udelay(100);
DPAUX_WRITE(DPAUX_DP_AUXDATA_READ_W0, 0);
if (!dpaux_check(2, check, check))
cnt = 100;
else {
dpaux_read(0x206, 1, buf);
cfg = DPAUX_READ(DPAUX_DP_AUXDATA_READ_W0);
cfg &= 0x00ff;
if (cfg == cfg_d) {
++cnt;
if (cnt > 5)
printk(BIOS_ERR, "Error: link training FAILED\n");
} else {
cnt = 0;
cfg_d = cfg;
dc_lv = cfg & 0x3;
pe_lv = (cfg >> 2) & 0x3;
if (speed == 20) {
dpaux_read(0x20C, 1, buf);
cfg = DPAUX_READ(DPAUX_DP_AUXDATA_READ_W0);
pc_lv = cfg & 0x3;
} else {
pc_lv = 0;
}
}
}
debug_dpaux_print(0x200, 16);
}
return ((pc_lv << 8) | (pe_lv << 4) | (dc_lv));
}
void dp_link_training(u32 lanes, u32 speed);
void dp_link_training(u32 lanes, u32 speed)
{
u32 lane_on;
u32 mask, level;
u32 reg_val;
printk(BIOS_DEBUG, "\nLink training start\n");
switch (lanes) {
case 1:
lane_on = 0x04;
break;
case 2:
lane_on = 0x06;
break;
case 4:
lane_on = 0x0f;
break;
default:
printk(BIOS_DEBUG, "dp: invalid lane count: %d\n",
lanes);
return;
}
SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, (0x008000000 | lane_on));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0,
SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_FIELD,
(6 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0,
SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_FIELD,
(1 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT));
SOR_WRITE(SOR_NV_PDISP_SOR_LVDS_0, 0);
SOR_WRITE(SOR_NV_PDISP_SOR_CLK_CNTRL_0, ((speed << 2) | 2));
udelay(100 * 1000);
sor_clock_start();
SOR_WRITE(SOR_NV_PDISP_SOR_DP_LINKCTL0_0,
(((0xF >> (4 - lanes)) << 16) | 1));
SOR_WRITE(SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0, 0x80100000);
printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0.DONE\n");
dp_poll_register((void *)0x54540084, 0x00000000, 0x80000000, 1000);
debug_dpaux_print(0x202, 4);
printk(BIOS_DEBUG, "set link rate and lane number: %dMHz, %d lanes\n",
(speed * 27), lanes);
dpaux_write(0x100, 2, ((lanes << 8) | speed));
printk(BIOS_DEBUG, "precharge lane 10us\n");
reg_val = SOR_READ(SOR_NV_PDISP_SOR_DP_PADCTL0_0);
SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, (0x000000f0 | reg_val));
udelay(100 * 1000);
SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, reg_val);
printk(BIOS_DEBUG, "link training cr start\n");
SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x41414141);
dpaux_write(0x102, 1, 0x21);
mask = 0x0000ffff >> ((4 - lanes) * 4);
level = 0;
level = dp_training(level, 0x1111 & mask, speed);
printk(BIOS_DEBUG, "level:%x\n", level);
debug_dpaux_print(0x210, 16);
printk(BIOS_DEBUG, "link training eq start\n");
if (speed == 20) {
SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x43434343);
dpaux_write(0x102, 1, 0x23);
} else {
SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x42424242);
dpaux_write(0x102, 1, 0x22);
}
level = dp_training(level, (0x7777 & mask) | 0x10000, speed);
printk(BIOS_DEBUG, "level:%x\n", level);
debug_dpaux_print(0x210, 16);
SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x50505050);
dpaux_write(0x102, 1, 0);
dpaux_write(0x600, 1, 1);
debug_dpaux_print(0x200, 16);
debug_dpaux_print(0x210, 16);
printk(BIOS_DEBUG, "Link training done\n\n");
}
static u32 div_f(u32 a, u32 b, u32 one)
{
u32 d = (((a - (a / b * b)) * one) + (b / 2)) / b;
return (d);
}
u32 dp_setup_timing(u32 width, u32 height)
{
u32 pclk_freq = 0;
u32 PLL_FREQ = (12 / 12 * 283) / 1 / 2; /* 141.5 */
u32 PLL_DIV = 2;
u32 SYNC_WIDTH = (8 << 16) | 46;
u32 BACK_PORCH = (6 << 16) | 44;
u32 FRONT_PORCH = (6 << 16) | 44;
u32 HSYNC_NEG = 1;
u32 VSYNC_NEG = 1;
u32 SHIFT_CLK_DIVIDER = PLL_DIV * 2 - 2;
u32 DISP_ACTIVE = (height << 16) | width;
u32 DISP_TOTAL = DISP_ACTIVE + SYNC_WIDTH + BACK_PORCH + FRONT_PORCH;
u32 SYNC_END = SYNC_WIDTH - 0x10001;
u32 BLANK_END = SYNC_END + BACK_PORCH;
u32 BLANK_START = BLANK_END + DISP_ACTIVE;
u32 TOTAL_PIXELS = (DISP_TOTAL & 0xffff) * (DISP_TOTAL >> 16);
u32 PLL_FREQ_I, PLL_FREQ_F;
u32 PCLK_FREQ_I, PCLK_FREQ_F;
u32 FRATE_I, FRATE_F;
PLL_FREQ = PLL_FREQ * 1000000;
pclk_freq = PLL_FREQ / PLL_DIV;
PLL_FREQ_I = PLL_FREQ / 1000000;
PLL_FREQ_F = div_f(PLL_FREQ, 1000000, 100);
PCLK_FREQ_I = PLL_FREQ / (PLL_DIV * 1000000);
PCLK_FREQ_F = div_f(PLL_FREQ, PLL_DIV * 1000000, 100);
FRATE_I = PLL_FREQ / (PLL_DIV * TOTAL_PIXELS);
FRATE_F = div_f(PLL_FREQ, (PLL_DIV * TOTAL_PIXELS), 100);
/* nv_bug 1021453 */
BACK_PORCH = BACK_PORCH - 0x10000;
FRONT_PORCH = FRONT_PORCH + 0x10000;
printk(BIOS_DEBUG, "ACTIVE: %dx%d\n", (DISP_ACTIVE & 0xFFFF),
(DISP_ACTIVE >> 16));
printk(BIOS_DEBUG, "TOTAL: %dx%d\n", (DISP_TOTAL & 0xffff),
(DISP_TOTAL >> 16));
printk(BIOS_DEBUG, "PLL Freq: %d.%d MHz\n", PLL_FREQ_I, PLL_FREQ_F);
printk(BIOS_DEBUG, "Pclk Freq: %d.%d MHz\n", PCLK_FREQ_I,
PCLK_FREQ_F);
printk(BIOS_DEBUG, "Frame Rate: %d.%d Hz\n", FRATE_I, FRATE_F);
printk(BIOS_DEBUG, "\n");
DCA_WRITE(DC_CMD_STATE_ACCESS_0, 0x00000004);
DCA_WRITE(DC_DISP_DISP_CLOCK_CONTROL_0, SHIFT_CLK_DIVIDER);
//Raster Timing
DCA_WRITE(DC_DISP_DISP_TIMING_OPTIONS_0, 0x00000001);
DCA_WRITE(DC_DISP_REF_TO_SYNC_0, 0x00010001);
DCA_WRITE(DC_DISP_SYNC_WIDTH_0, SYNC_WIDTH);
DCA_WRITE(DC_DISP_BACK_PORCH_0, BACK_PORCH);
DCA_WRITE(DC_DISP_DISP_ACTIVE_0, DISP_ACTIVE);
DCA_WRITE(DC_DISP_FRONT_PORCH_0, FRONT_PORCH);
//REG(DC_DISP_DISP_WIN_OPTIONS_0, SOR_ENABLE , 1)
DCA_READ_M_WRITE(DC_DISP_DISP_WIN_OPTIONS_0,
DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_FIELD,
(1 << DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT));
SOR_WRITE(SOR_NV_PDISP_HEAD_STATE1_0, DISP_TOTAL);
SOR_WRITE(SOR_NV_PDISP_HEAD_STATE2_0, SYNC_END);
SOR_WRITE(SOR_NV_PDISP_HEAD_STATE3_0, BLANK_END);
SOR_WRITE(SOR_NV_PDISP_HEAD_STATE4_0, BLANK_START);
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_FIELD,
(HSYNC_NEG <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_FIELD,
(VSYNC_NEG <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_FIELD,
(SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_A <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_FIELD,
(SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_COMPLETE_RASTER <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_FIELD,
(SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_NONE <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_FIELD,
(SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD0 <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT));
return pclk_freq;
}
static u32 calc_config(u32 ts, u32 a, u32 b, u32 bpp)
{
u32 act_cnt = (ts * a) / b;
u32 diff = (ts * a) - (act_cnt * b);
u32 act_pol;
u32 act_frac;
u32 err;
u32 water_mark;
printk(BIOS_DEBUG, "calc_config ts %d a %d b %d bpp %d\n",
ts, a, b, bpp);
if (diff != 0) {
if (diff > (b / 2)) {
diff = b - diff;
act_pol = 1;
act_frac = (b + diff - 1) / diff;
err = diff * act_frac - b;
} else {
act_pol = 0;
act_frac = b / diff;
err = b - (diff * act_frac);
}
if (act_frac > 15) {
act_pol = 1 - act_pol;
act_frac = 1;
err = diff;
}
} else {
act_pol = 1;
act_frac = 1;
err = 0;
}
if (bpp) {
water_mark = (a * (b - a) * ts / (b * b)) + (2 * bpp / 8);
if (water_mark > 30)
water_mark = 30;
SOR_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0, 0x84000000);
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0,
SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_FIELD,
(act_pol <<
SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0,
SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_FIELD,
(act_frac <<
SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0,
SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_FIELD,
(act_cnt <<
SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT));
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0,
SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_FIELD,
(water_mark <<
SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT));
printk(BIOS_DEBUG,
"SOR_DP_CONFIG0:TU,CNT,POL,FRAC,WMK,ERR=%d,%d,%d,%d,%d,%d/%d\n",
ts, act_cnt, act_pol, act_frac, water_mark, err, b);
}
return (err);
}
static u32 dp_buf_config(u32 pclkfreq, u32 linkfreq, u32 lanes, u32 bpp)
{
//to avoid 32bit overflow
u32 tusize = 0;
u32 pf = pclkfreq;
u32 lf = linkfreq;
u32 i;
u32 a, b;
u32 min_err = 1000000000;
u32 ts = 64;
u32 c_err;
printk(BIOS_DEBUG, "dp buf config pclkfreq %d linkfreq %d lanes %d bpp %d\n",
pclkfreq, linkfreq, lanes, bpp);
for (i = 2; i <= 7; ++i) {
while (((pf / i * i) == pf) && ((lf / i * i) == lf)) {
pf = pf / i;
lf = lf / i;
}
}
a = pf * bpp / 8;
b = lf * lanes;
printk(BIOS_DEBUG, "ratio:%d/%d\n", a, b);
if (a > (b * 98 / 100))
printk(BIOS_ERR, "Error:link speed not enough\n");
//search best tusize
//min_err = 1000000000;
//ts = 64;
while (ts >= 32) {
c_err = calc_config(ts, a, b, 0);
if (c_err < min_err) {
if (c_err == 0) {
tusize = ts;
ts = 1;
} else {
min_err = c_err;
tusize = ts;
}
}
--ts;
}
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_LINKCTL0_0,
SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_FIELD,
(tusize << SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT));
calc_config(tusize, a, b, bpp);
return (tusize);
}
/*
void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr,
u32 lane_count, u32 enhanced_framing, u32 panel_edp,
u32 pclkfreq, u32 linkfreq);
*/
void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr,
u32 lane_count, u32 enhanced_framing, u32 panel_edp,
u32 pclkfreq, u32 linkfreq)
{
u32 tusize;
u32 linkctl;
printk(BIOS_DEBUG, "%s: winb: 0x%08x, panel_bpp %d ",
__func__, winb_addr, panel_bpp);
if (panel_bpp == 18) {
//0x54540010
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD,
(SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_18_444 <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT));
}
if (panel_bpp == 24) {
SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0,
SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD,
(SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_444 <<
SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT));
}
DCA_WRITE(DC_CMD_DISPLAY_WINDOW_HEADER_0, 0x00000010);
DCA_WRITE(DC_WIN_A_SIZE_0, ((height << 16) | width));
DCA_WRITE(DC_WIN_A_PRESCALED_SIZE_0,
((height << 16) | (width * SRC_BPP / 8)));
DCA_WRITE(DC_WIN_A_LINE_STRIDE_0,
((width * SRC_BPP / 8 + 31) / 32 * 32));
DCA_WRITE(DC_WIN_A_COLOR_DEPTH_0, COLORDEPTH);
DCA_WRITE(DC_WINBUF_A_START_ADDR_LO_0, winb_addr);
DCA_WRITE(DC_WIN_A_DDA_INCREMENT_0, 0x10001000);
SOR_WRITE(SOR_NV_PDISP_SOR_CRC_CNTRL_0, 0x00000001);
DCA_WRITE(DC_COM_CRC_CONTROL_0, 0x00000009); //CRC_ALWAYS+CRC_ENABLE
DCA_WRITE(DC_COM_PIN_OUTPUT_ENABLE2_0, 0x00000000);
DCA_WRITE(DC_COM_PIN_OUTPUT_ENABLE3_0, 0x00000000);
DCA_WRITE(DC_DISP_DISP_SIGNAL_OPTIONS0_0, 0x00000000);
DCA_WRITE(DC_DISP_BLEND_BACKGROUND_COLOR_0, COLOR_WHITE);
DCA_WRITE(DC_CMD_DISPLAY_COMMAND_0, 0x00000020);
SOR_WRITE(SOR_NV_PDISP_SOR_DP_AUDIO_VBLANK_SYMBOLS_0, 0x00000e48);
dpaux_write(0x101, 1, (enhanced_framing << 7) | lane_count);
if (panel_edp)
dpaux_write(0x10A, 1, 1);
tusize =
dp_buf_config(pclkfreq, (linkfreq * 1000000), lane_count, panel_bpp);
linkctl =
((0xF >> (4 - lane_count)) << 16) | (enhanced_framing << 14) | (tusize
<< 2) |
1;
SOR_WRITE(SOR_NV_PDISP_SOR_DP_LINKCTL0_0, linkctl);
SOR_WRITE(SOR_NV_PDISP_SOR_DP_SPARE0_0, ((panel_edp << 1) | 0x05));
SOR_WRITE(SOR_NV_PDISP_SOR_PWR_0, 0x80000001);
printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_PWR_0.DONE\n");
dp_poll_register((void *)0x54540054, 0x00000000, 0x80000000, 1000);
//SOR_NV_PDISP_SOR_PWR_0
//sor_update
SOR_WRITE(SOR_NV_PDISP_SOR_STATE0_0, 0x00000000);
SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE1_0, 0x00000006);
//sor_super_update
SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE0_0, 0x00000000);
SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE1_0, 0x0000000e);
//sor_super_update
SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE0_0, 0x00000000);
printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_TEST_0.ATTACHED\n");
dp_poll_register((void *)0x54540058, 0x00000400, 0x00000400, 1000);
//SOR_NV_PDISP_SOR_TEST_0
DCA_WRITE(DC_CMD_STATE_CONTROL_0, 0x00009f00);
DCA_WRITE(DC_CMD_STATE_CONTROL_0, 0x0000009f);
DCA_WRITE(DC_CMD_DISPLAY_POWER_CONTROL_0, 0x00050155);
printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_TEST_0.AWAKE\n");
dp_poll_register((void *)0x54540058, 0x00000200, 0x00000300, 1000);
//SOR_NV_PDISP_SOR_TEST_0
// DCA_WRITE (DC_CMD_STATE_ACCESS_0 ,0);
DCA_WRITE(DC_CMD_STATE_ACCESS_0, 4);
DCA_WRITE(DC_CMD_STATE_CONTROL_0, 0x0000ffff);
DCA_READ_M_WRITE(DC_WIN_A_WIN_OPTIONS_0,
DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_FIELD,
(DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_ENABLE <<
DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT));
}

View file

@ -24,12 +24,11 @@
#include <soc/addressmap.h>
#include <soc/nvidia/tegra/i2c.h>
#include <soc/nvidia/tegra/dc.h>
#include "chip.h"
#include "sor.h"
#include <soc/nvidia/tegra/displayport.h>
extern int dump;
unsigned long READL(void *p);
void WRITEL(unsigned long value, void *p);
struct tegra_dc_dp_data dp_data;
static inline u32 tegra_dpaux_readl(struct tegra_dc_dp_data *dp, u32 reg)
{
@ -182,35 +181,6 @@ static int tegra_dc_dpaux_write_chunk(struct tegra_dc_dp_data *dp, u32 cmd,
return -1;
}
static int tegra_dc_dpaux_write(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr,
u8 *data, u32 *size, u32 *aux_stat)
{
u32 cur_size = 0;
u32 finished = 0;
u32 cur_left;
int ret = 0;
do {
cur_size = *size - finished;
if (cur_size > DP_AUX_MAX_BYTES)
cur_size = DP_AUX_MAX_BYTES;
cur_left = cur_size;
ret = tegra_dc_dpaux_write_chunk(dp, cmd, addr,
data, &cur_left, aux_stat);
cur_size -= cur_left;
finished += cur_size;
addr += cur_size;
data += cur_size;
if (ret)
break;
} while (*size > finished);
*size = finished;
return ret;
}
static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd,
u32 addr, u8 *data, u32 *size,
u32 *aux_stat)
@ -235,12 +205,10 @@ static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd,
return -1;
}
if (0) {
*aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
printk(BIOS_SPEW, "dp: HPD is not detected\n");
//return EFAULT;
}
*aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
printk(BIOS_SPEW, "dp: HPD is not detected\n");
return -1;
}
tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
@ -262,8 +230,6 @@ static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd,
printk(BIOS_INFO, "dp: aux read transaction timeout\n");
*aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
printk(BIOS_DEBUG, "dp: %s: aux stat: 0x%08x\n", __func__,
*aux_stat);
if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
(*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
@ -309,8 +275,6 @@ static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd,
DPAUX_DP_AUXDATA_READ_W(i));
*size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
printk(BIOS_INFO, "dp: aux read data %d bytes\n",
*size);
memcpy(data, temp_data, *size);
return 0;
@ -325,230 +289,407 @@ static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd,
return -1;
}
int tegra_dc_dpaux_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr,
u8 * data, u32 * size, u32 * aux_stat)
{
u32 finished = 0;
u32 cur_size;
int ret = 0;
do {
cur_size = *size - finished;
if (cur_size > DP_AUX_MAX_BYTES)
cur_size = DP_AUX_MAX_BYTES;
ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr,
data, &cur_size, aux_stat);
/* cur_size should be the real size returned */
addr += cur_size;
data += cur_size;
finished += cur_size;
if (ret)
break;
} while (*size > finished);
*size = finished;
return ret;
}
static int tegra_dc_dp_dpcd_read(struct tegra_dc_dp_data *dp, u32 cmd,
u8 * data_ptr)
u8 * data_ptr)
{
u32 size = 1;
u32 status = 0;
int ret;
ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
cmd, data_ptr, &size, &status);
cmd, data_ptr, &size, &status);
if (ret)
printk(BIOS_ERR,
"dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n", cmd,
status);
"dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n",
cmd, status);
return ret;
}
static int tegra_dc_dp_init_max_link_cfg(struct tegra_dc_dp_data *dp,
struct tegra_dc_dp_link_config *cfg)
static int tegra_dc_dp_dpcd_write(struct tegra_dc_dp_data *dp, u32 cmd,
u8 data)
{
u32 size = 1;
u32 status = 0;
int ret;
ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR,
cmd, &data, &size, &status);
if (ret)
printk(BIOS_ERR,
"dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n",
cmd, status);
return ret;
}
static void tegra_dc_dpaux_enable(struct tegra_dc_dp_data *dp)
{
/* clear interrupt */
tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff);
/* do not enable interrupt for now. Enable them when Isr in place */
tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0);
tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL,
DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 |
DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 |
0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT |
DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE);
tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE,
DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP);
}
static void tegra_dc_dp_dump_link_cfg(struct tegra_dc_dp_data *dp,
const struct tegra_dc_dp_link_config *link_cfg)
{
printk(BIOS_INFO, "DP config: cfg_name "
"cfg_value\n");
printk(BIOS_INFO, " Lane Count %d\n",
link_cfg->max_lane_count);
printk(BIOS_INFO, " SupportEnhancedFraming %s\n",
link_cfg->support_enhanced_framing ? "Y" : "N");
printk(BIOS_INFO, " Bandwidth %d\n",
link_cfg->max_link_bw);
printk(BIOS_INFO, " bpp %d\n",
link_cfg->bits_per_pixel);
printk(BIOS_INFO, " EnhancedFraming %s\n",
link_cfg->enhanced_framing ? "Y" : "N");
printk(BIOS_INFO, " Scramble_enabled %s\n",
link_cfg->scramble_ena ? "Y" : "N");
printk(BIOS_INFO, " LinkBW %d\n",
link_cfg->link_bw);
printk(BIOS_INFO, " lane_count %d\n",
link_cfg->lane_count);
printk(BIOS_INFO, " activespolarity %d\n",
link_cfg->activepolarity);
printk(BIOS_INFO, " active_count %d\n",
link_cfg->active_count);
printk(BIOS_INFO, " tu_size %d\n",
link_cfg->tu_size);
printk(BIOS_INFO, " active_frac %d\n",
link_cfg->active_frac);
printk(BIOS_INFO, " watermark %d\n",
link_cfg->watermark);
printk(BIOS_INFO, " hblank_sym %d\n",
link_cfg->hblank_sym);
printk(BIOS_INFO, " vblank_sym %d\n",
link_cfg->vblank_sym);
};
/* Calcuate if given cfg can meet the mode request. */
/* Return true if mode is possible, false otherwise. */
static int tegra_dc_dp_calc_config(struct tegra_dc_dp_data *dp,
const struct soc_nvidia_tegra124_config *config,
struct tegra_dc_dp_link_config *link_cfg)
{
const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000;
const u64 f = 100000; /* precision factor */
u32 num_linkclk_line; /* Number of link clocks per line */
u64 ratio_f; /* Ratio of incoming to outgoing data rate */
u64 frac_f;
u64 activesym_f; /* Activesym per TU */
u64 activecount_f;
u32 activecount;
u32 activepolarity;
u64 approx_value_f;
u32 activefrac = 0;
u64 accumulated_error_f = 0;
u32 lowest_neg_activecount = 0;
u32 lowest_neg_activepolarity = 0;
u32 lowest_neg_tusize = 64;
u32 num_symbols_per_line;
u64 lowest_neg_activefrac = 0;
u64 lowest_neg_error_f = 64 * f;
u64 watermark_f;
int i;
int neg;
if (!link_rate || !link_cfg->lane_count || !config->pixel_clock ||
!link_cfg->bits_per_pixel)
return -1;
if ((u64)config->pixel_clock * link_cfg->bits_per_pixel >=
(u64)link_rate * 8 * link_cfg->lane_count)
return -1;
num_linkclk_line = (u32)((u64)link_rate * (u64)config->xres / config->pixel_clock);
ratio_f = (u64)config->pixel_clock * link_cfg->bits_per_pixel * f;
ratio_f /= 8;
ratio_f = (u64)(ratio_f / (link_rate * link_cfg->lane_count));
for (i = 64; i >= 32; --i) {
activesym_f = ratio_f * i;
activecount_f = (u64)(activesym_f / (u32)f) * f;
frac_f = activesym_f - activecount_f;
activecount = (u32)((u64)(activecount_f / (u32)f));
if (frac_f < (f / 2)) /* fraction < 0.5 */
activepolarity = 0;
else {
activepolarity = 1;
frac_f = f - frac_f;
}
if (frac_f != 0) {
frac_f = (u64)((f * f) / frac_f); /* 1/fraction */
if (frac_f > (15 * f))
activefrac = activepolarity ? 1 : 15;
else
activefrac = activepolarity ?
(u32)((u64)(frac_f / (u32)f)) + 1 :
(u32)((u64)(frac_f / (u32)f));
}
if (activefrac == 1)
activepolarity = 0;
if (activepolarity == 1)
approx_value_f = activefrac ? (u64)(
(activecount_f + (activefrac * f - f) * f) /
(activefrac * f)) :
activecount_f + f;
else
approx_value_f = activefrac ?
activecount_f + (u64)(f / activefrac) :
activecount_f;
if (activesym_f < approx_value_f) {
accumulated_error_f = num_linkclk_line *
(u64)((approx_value_f - activesym_f) / i);
neg = 1;
} else {
accumulated_error_f = num_linkclk_line *
(u64)((activesym_f - approx_value_f) / i);
neg = 0;
}
if ((neg && (lowest_neg_error_f > accumulated_error_f)) ||
(accumulated_error_f == 0)) {
lowest_neg_error_f = accumulated_error_f;
lowest_neg_tusize = i;
lowest_neg_activecount = activecount;
lowest_neg_activepolarity = activepolarity;
lowest_neg_activefrac = activefrac;
if (accumulated_error_f == 0)
break;
}
}
if (lowest_neg_activefrac == 0) {
link_cfg->activepolarity = 0;
link_cfg->active_count = lowest_neg_activepolarity ?
lowest_neg_activecount : lowest_neg_activecount - 1;
link_cfg->tu_size = lowest_neg_tusize;
link_cfg->active_frac = 1;
} else {
link_cfg->activepolarity = lowest_neg_activepolarity;
link_cfg->active_count = (u32)lowest_neg_activecount;
link_cfg->tu_size = lowest_neg_tusize;
link_cfg->active_frac = (u32)lowest_neg_activefrac;
}
watermark_f = (u64)((ratio_f * link_cfg->tu_size * (f - ratio_f)) / f);
link_cfg->watermark = (u32)((u64)((watermark_f + lowest_neg_error_f) /
f)) + link_cfg->bits_per_pixel / 4 - 1;
num_symbols_per_line = (config->xres * link_cfg->bits_per_pixel) /
(8 * link_cfg->lane_count);
if (link_cfg->watermark > 30) {
printk(BIOS_INFO,
"dp: sor setting: unable to get a good tusize, "
"force watermark to 30.\n");
link_cfg->watermark = 30;
return -1;
} else if (link_cfg->watermark > num_symbols_per_line) {
printk(BIOS_INFO,
"dp: sor setting: force watermark to the number "
"of symbols in the line.\n");
link_cfg->watermark = num_symbols_per_line;
return -1;
}
/* Refer to dev_disp.ref for more information. */
/* # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - */
/* SetRasterBlankStart.X - 7) * link_clk / pclk) */
/* - 3 * enhanced_framing - Y */
/* where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12 */
link_cfg->hblank_sym = (int)((u64)(((u64)(config->hback_porch +
config->hfront_porch + config->hsync_width - 7) *
link_rate) / config->pixel_clock)) -
3 * link_cfg->enhanced_framing -
(12 / link_cfg->lane_count);
if (link_cfg->hblank_sym < 0)
link_cfg->hblank_sym = 0;
/* Refer to dev_disp.ref for more information. */
/* # symbols/vblank = ((SetRasterBlankStart.X - */
/* SetRasterBlankEen.X - 25) * link_clk / pclk) */
/* - Y - 1; */
/* where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39 */
link_cfg->vblank_sym = (int)((u64)((u64)(config->xres - 25)
* link_rate / config->pixel_clock)) - (36 /
link_cfg->lane_count) - 4;
if (link_cfg->vblank_sym < 0)
link_cfg->vblank_sym = 0;
link_cfg->is_valid = 1;
tegra_dc_dp_dump_link_cfg(dp, link_cfg);
return 0;
}
static int tegra_dc_dp_init_link_cfg(
struct soc_nvidia_tegra124_config *config,
struct tegra_dc_dp_data *dp,
struct tegra_dc_dp_link_config *link_cfg)
{
u8 dpcd_data;
int ret;
ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_LANE_COUNT, &dpcd_data);
if (ret)
return ret;
link_cfg->max_lane_count = config->lane_count;
link_cfg->support_enhanced_framing = config->enhanced_framing;
link_cfg->max_link_bw = config->link_bw;
link_cfg->drive_current = config->drive_current;
link_cfg->preemphasis = config->preemphasis;
link_cfg->postcursor = config->postcursor;
link_cfg->bits_per_pixel = config->panel_bits_per_pixel;
cfg->max_lane_count = dpcd_data & NV_DPCD_MAX_LANE_COUNT_MASK;
printk(BIOS_INFO, "%s: max_lane_count: %d\n", __func__,
cfg->max_lane_count);
CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_EDP_CONFIG_CAP,
&dpcd_data));
link_cfg->alt_scramber_reset_cap =
(dpcd_data & NV_DPCD_EDP_CONFIG_CAP_ASC_RESET_YES) ?
1 : 0;
link_cfg->only_enhanced_framing =
(dpcd_data & NV_DPCD_EDP_CONFIG_CAP_FRAMING_CHANGE_YES) ?
1 : 0;
cfg->support_enhanced_framing =
(dpcd_data & NV_DPCD_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ? 1 : 0;
printk(BIOS_INFO, "%s: enh-framing: %d\n", __func__,
cfg->support_enhanced_framing);
link_cfg->lane_count = link_cfg->max_lane_count;
link_cfg->link_bw = link_cfg->max_link_bw;
link_cfg->enhanced_framing = link_cfg->support_enhanced_framing;
ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_DOWNSPREAD, &dpcd_data);
if (ret)
return ret;
cfg->downspread = (dpcd_data & NV_DPCD_MAX_DOWNSPREAD_VAL_0_5_PCT) ? 1 : 0;
printk(BIOS_INFO, "%s: downspread: %d\n", __func__, cfg->downspread);
ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_LINK_BANDWIDTH,
&cfg->max_link_bw);
if (ret)
return ret;
printk(BIOS_INFO, "%s: max_link_bw: %d\n", __func__, cfg->max_link_bw);
// cfg->bits_per_pixel = dp->dc->pdata->default_out->depth;
cfg->bits_per_pixel = 18;
/* TODO: need to come from the board file */
/* Venice2 settings */
cfg->drive_current = 0x20202020;
cfg->preemphasis = 0;
cfg->postcursor = 0;
ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_EDP_CONFIG_CAP, &dpcd_data);
if (ret)
return ret;
cfg->alt_scramber_reset_cap =
(dpcd_data & NV_DPCD_EDP_CONFIG_CAP_ASC_RESET_YES) ? 1 : 0;
cfg->only_enhanced_framing =
(dpcd_data & NV_DPCD_EDP_CONFIG_CAP_FRAMING_CHANGE_YES) ? 1 : 0;
printk(BIOS_DEBUG, "%s: alt_reset_cap: %d, only_enh_framing: %d\n",
__func__, cfg->alt_scramber_reset_cap, cfg->only_enhanced_framing);
cfg->lane_count = cfg->max_lane_count;
cfg->link_bw = NV_SOR_LINK_SPEED_G1_62;
cfg->enhanced_framing = cfg->support_enhanced_framing;
tegra_dc_dp_calc_config(dp, config, link_cfg);
return 0;
}
struct tegra_dc_dp_data dp_data;
static int tegra_dc_dpcd_read_rev(struct tegra_dc_dp_data *dp, u8 * rev)
{
u32 size;
int ret;
u32 status = 0;
size = 3;
ret = tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
NV_DPCD_REV, rev, &size, &status);
if (ret) {
printk(BIOS_WARNING, "dp: Failed to read NV_DPCD_REV\n");
return ret;
}
return 0;
}
u32 dp_setup_timing(u32 width, u32 height);
void dp_bringup(u32 winb_addr)
void dp_init(void * _config)
{
struct soc_nvidia_tegra124_config *config = (void *)_config;
struct tegra_dc *dc = config->dc_data;
struct tegra_dc_dp_data *dp = &dp_data;
u32 dpcd_rev;
u32 pclk_freq;
u32 xres = 1366; /* norrin display */
u32 yres = 768;
// set up links among config, dc, dp and sor
dp->dc = dc;
dc->out = dp;
dp->sor.dc = dc;
dp->sor.power_is_up = 0;
dp->sor.base = (void *)TEGRA_ARM_SOR;
dp->sor.pmc_base = (void *)TEGRA_PMC_BASE;
dp->sor.portnum = 0;
dp->sor.link_cfg = &dp->link_cfg;
dp->aux_base = (void *)TEGRA_ARM_DPAUX;
dp->link_cfg.is_valid = 0;
dp->enabled = 0;
}
/* read panel info */
if (!tegra_dc_dpcd_read_rev(dp, (u8 *)&dpcd_rev)) {
printk(BIOS_INFO, "PANEL info:\n");
printk(BIOS_INFO, "--DPCP version(%#x): %d.%d\n",
dpcd_rev, (dpcd_rev >> 4) & 0x0f,
(dpcd_rev & 0x0f));
static void tegra_dp_hpd_config(struct tegra_dc_dp_data *dp,
struct soc_nvidia_tegra124_config *config)
{
u32 val;
val = config->hpd_plug_min_us |
(config->hpd_unplug_min_us <<
DPAUX_HPD_CONFIG_UNPLUG_MIN_TIME_SHIFT);
tegra_dpaux_writel(dp, DPAUX_HPD_CONFIG, val);
tegra_dpaux_writel(dp, DPAUX_HPD_IRQ_CONFIG, config->hpd_irq_min_us);
}
static int tegra_dp_hpd_plug(struct tegra_dc_dp_data *dp, int timeout_ms)
{
u32 val;
u32 timeout = timeout_ms * 1000;
do {
val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)
return 0;
udelay(100);
timeout -= 100;
} while (timeout > 0);
return -1;
}
void dp_enable(void * _dp)
{
struct tegra_dc_dp_data *dp = _dp;
struct tegra_dc *dc = dp->dc;
struct soc_nvidia_tegra124_config *config = dc->config;
u8 data;
u32 retry;
int ret;
tegra_dc_dpaux_enable(dp);
tegra_dp_hpd_config(dp, config);
if (tegra_dp_hpd_plug(dp, config->vdd_to_hpd_delay_ms) < 0) {
printk(BIOS_ERR, "dp: hpd plug failed\n");
goto error_enable;
}
if (tegra_dc_dp_init_max_link_cfg(dp, &dp->link_cfg))
if (tegra_dc_dp_init_link_cfg(config, dp, &dp->link_cfg)) {
printk(BIOS_ERR, "dp: failed to init link configuration\n");
goto error_enable;
}
dp_link_training((u32) (dp->link_cfg.lane_count),
(u32) (dp->link_cfg.link_bw));
tegra_dc_sor_enable_dp(&dp->sor);
pclk_freq = dp_setup_timing(xres, yres);
printk(BIOS_DEBUG, "%s: pclk_freq: %d\n", __func__, pclk_freq);
tegra_dc_sor_set_panel_power(&dp->sor, 1);
/* Write power on to DPCD */
data = NV_DPCD_SET_POWER_VAL_D0_NORMAL;
retry = 0;
do {
ret = tegra_dc_dp_dpcd_write(dp,
NV_DPCD_SET_POWER, data);
} while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret);
void dp_misc_setting(u32 panel_bpp, u32 width, u32 height,
u32 winb_addr, u32 lane_count,
u32 enhanced_framing, u32 panel_edp,
u32 pclkfreq, u32 linkfreq);
dp_misc_setting(dp->link_cfg.bits_per_pixel,
xres, yres, winb_addr,
(u32) dp->link_cfg.lane_count,
(u32) dp->link_cfg.enhanced_framing,
(u32) dp->link_cfg.alt_scramber_reset_cap,
pclk_freq, dp->link_cfg.link_bw * 27);
}
void debug_dpaux_print(u32 addr, u32 size)
{
struct tegra_dc_dp_data *dp = &dp_data;
u32 status = 0;
u8 buf[16];
int i;
if ((size == 0) || (size > 16)) {
printk(BIOS_ERR, "dp: %s: invalid size %d\n", __func__, size);
return;
if (ret || retry >= DP_POWER_ON_MAX_TRIES) {
printk(BIOS_ERR,
"dp: failed to power on panel (0x%x)\n", ret);
goto error_enable;
}
if (tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
addr, buf, &size, &status)) {
printk(BIOS_ERR, "******AuxRead Error: 0x%04x: status 0x%08x\n",
addr, status);
return;
}
printk(BIOS_DEBUG, "%s: addr: 0x%04x, size: %d\n", __func__,
addr, size);
for (i = 0; i < size; ++i)
printk(BIOS_DEBUG, " %02x", buf[i]);
printk(BIOS_DEBUG, "\n");
}
int dpaux_read(u32 addr, u32 size, u8 * data)
{
struct tegra_dc_dp_data *dp = &dp_data;
u32 status = 0;
if ((size == 0) || (size > 16)) {
printk(BIOS_ERR, "dp: %s: invalid size %d\n", __func__, size);
return -1;
/* Confirm DP is plugging status */
if (!(tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT) &
DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
printk(BIOS_ERR, "dp: could not detect HPD\n");
goto error_enable;
}
if (tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
addr, data, &size, &status)) {
printk(BIOS_ERR, "dp: Failed to read reg %#x, status: %#x\n",
addr, status);
return -1;
}
/* Check DP version */
if (tegra_dc_dp_dpcd_read(dp, NV_DPCD_REV, &dp->revision))
printk(BIOS_ERR,
"dp: failed to read the revision number from sink\n");
return 0;
}
int dpaux_write(u32 addr, u32 size, u32 data)
{
struct tegra_dc_dp_data *dp = &dp_data;
u32 status = 0;
int ret;
ret = tegra_dc_dpaux_write(dp, DPAUX_DP_AUXCTL_CMD_AUXWR,
addr, (u8 *) & data, &size, &status);
if (ret)
printk(BIOS_ERR, "dp: Failed to write to reg %#x, status: 0x%x\n",
addr, status);
return ret;
tegra_dc_sor_set_power_state(&dp->sor, 1);
tegra_dc_sor_attach(&dp->sor);
/*
* Power down the unused lanes to save power
* (about hundreds milli-watts, varies from boards).
*/
tegra_dc_sor_power_down_unused_lanes(&dp->sor);
dp->enabled = 1;
error_enable:
return;
}

View file

@ -17,185 +17,10 @@
#ifndef __SOC_NVIDIA_TEGRA124_INCLUDE_SOC_DISPLAY_H__
#define __SOC_NVIDIA_TEGRA124_INCLUDE_SOC_DISPLAY_H__
/* ardisplay.h */
#define DC_CMD_DISPLAY_WINDOW_HEADER_0 0x42
#define DC_COM_CRC_CONTROL_0 0x300
#define DC_COM_CRC_CHECKSUM_0 0x301
#define DC_COM_PIN_OUTPUT_ENABLE0_0 0x302
#define DC_COM_PIN_OUTPUT_ENABLE1_0 0x303
#define DC_COM_PIN_OUTPUT_ENABLE2_0 0x304
#define DC_COM_PIN_OUTPUT_ENABLE3_0 0x305
#define DC_CMD_STATE_ACCESS_0 0x40
#define DC_DISP_DISP_CLOCK_CONTROL_0 0x42e
#define DC_DISP_DISP_TIMING_OPTIONS_0 0x405
#define DC_DISP_REF_TO_SYNC_0 0x406
#define DC_DISP_SYNC_WIDTH_0 0x407
#define DC_DISP_BACK_PORCH_0 0x408
#define DC_DISP_DISP_ACTIVE_0 0x409
#define DC_DISP_FRONT_PORCH_0 0x40a
#define DC_DISP_DISP_WIN_OPTIONS_0 0x402
#define DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT 25
#define DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_FIELD (0x1 << DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT)
#define DC_DISP_DISP_SIGNAL_OPTIONS0_0 0x400
#define DC_DISP_BLEND_BACKGROUND_COLOR_0 0x4e4
#define DC_CMD_DISPLAY_COMMAND_0 0x32
#define DC_CMD_STATE_CONTROL_0 0x41
#define DC_CMD_DISPLAY_POWER_CONTROL_0 0x36
/* ardisplay_a.h */
#define DC_WIN_A_WIN_OPTIONS_0 0x700
#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT 30
#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_FIELD (0x1 << DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT)
#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_ENABLE (1)
#define DC_WIN_A_BYTE_SWAP_0 0x701
#define DC_WIN_A_BUFFER_CONTROL_0 0x702
#define DC_WIN_A_COLOR_DEPTH_0 0x703
#define DC_WIN_A_POSITION_0 0x704
#define DC_WIN_A_SIZE_0 0x705
#define DC_WIN_A_PRESCALED_SIZE_0 0x706
#define DC_WIN_A_H_INITIAL_DDA_0 0x707
#define DC_WIN_A_V_INITIAL_DDA_0 0x708
#define DC_WIN_A_DDA_INCREMENT_0 0x709
#define DC_WIN_A_LINE_STRIDE_0 0x70a
#define DC_WIN_A_DV_CONTROL_0 0x70e
#define DC_WIN_A_BLEND_LAYER_CONTROL_0 0x716
#define DC_WIN_A_BLEND_MATCH_SELECT_0 0x717
#define DC_WIN_A_BLEND_NOMATCH_SELECT_0 0x718
#define DC_WIN_A_BLEND_ALPHA_1BIT_0 0x719
#define DC_WINBUF_A_START_ADDR_LO_0 0x800
#define DC_WINBUF_A_START_ADDR_HI_0 0x80d
#define DC_WINBUF_A_ADDR_H_OFFSET_0 0x806
#define DC_WINBUF_A_ADDR_V_OFFSET_0 0x808
/* ardisplay_bd.h */
#define DC_B_WIN_BD_SIZE_0 0xd85
#define DC_B_WIN_BD_PRESCALED_SIZE_0 0xd86
#define DC_B_WIN_BD_LINE_STRIDE_0 0xd8a
#define DC_B_WIN_BD_COLOR_DEPTH_0 0xd83
#define DC_B_WINBUF_BD_START_ADDR_0 0xdc0
#define DC_B_WIN_BD_DDA_INCREMENT_0 0xd89
#define DC_B_WIN_BD_WIN_OPTIONS_0 0xd80
#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_SHIFT 30
#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_FIELD (0x1 << DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_SHIFT)
#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_ENABLE (1)
/* arsor.h */
#define SOR_NV_PDISP_SOR_CLK_CNTRL_0 0x13
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0 0x5c
#define SOR_NV_PDISP_SOR_PLL0_0 0x17
#define SOR_NV_PDISP_SOR_PLL1_0 0x18
#define SOR_NV_PDISP_SOR_PLL2_0 0x19
#define SOR_NV_PDISP_SOR_PLL3_0 0x1a
#define SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT 22
#define SOR_NV_PDISP_SOR_PLL2_0_AUX6_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT)
#define SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT 0
#define SOR_NV_PDISP_SOR_PLL0_0_PWR_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT)
#define SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT 2
#define SOR_NV_PDISP_SOR_PLL0_0_VCOPD_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT)
#define SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT 24
#define SOR_NV_PDISP_SOR_PLL2_0_AUX8_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT)
#define SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT 23
#define SOR_NV_PDISP_SOR_PLL2_0_AUX7_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT)
#define SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT 25
#define SOR_NV_PDISP_SOR_PLL2_0_AUX9_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT)
#define SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0 0x4e
#define SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0 0x52
#define SOR_NV_PDISP_SOR_POSTCURSOR0_0 0x56
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0 0x5c
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT 8
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_FIELD (0xff << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT)
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT 22
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_FIELD (0x1 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT)
#define SOR_NV_PDISP_SOR_LVDS_0 0x1c
#define SOR_NV_PDISP_SOR_CLK_CNTRL_0 0x13
#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0 0x4c
#define SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0 0x21
#define SOR_NV_PDISP_SOR_DP_TPG_0 0x6d
#define SOR_NV_PDISP_HEAD_STATE1_0 0x7
#define SOR_NV_PDISP_HEAD_STATE2_0 0x9
#define SOR_NV_PDISP_HEAD_STATE3_0 0xb
#define SOR_NV_PDISP_HEAD_STATE4_0 0xd
#define SOR_NV_PDISP_SOR_STATE1_0 0x4
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT 12
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_FIELD (0x1 << SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT 13
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_FIELD (0x1 << SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT 8
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_LVDS_CUSTOM (0)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_A (8)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_B (9)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_CUSTOM (15)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_ACTIVE_RASTER (0)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_COMPLETE_RASTER (1)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_NON_ACTIVE_RASTER (2)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT 6
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_FIELD (0x3 << SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT 4
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_FIELD (0x3 << SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_NONE (0)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SUBHEAD0 (1)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SUBHEAD1 (2)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_BOTH (3)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT 0
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_NONE (0)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD0 (1)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD1 (2)
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0 0x58
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT 24
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_FIELD (0x1 << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT)
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT 16
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_FIELD (0xf << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT)
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT 8
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_FIELD (0x7f << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT)
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT 0
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_FIELD (0x3f << SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT)
#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT 2
#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_FIELD (0x7f << SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT 17
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_DEFAULTVAL (0)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_16_422 (1)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_18_444 (2)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_20_422 (3)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_422 (4)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_444 (5)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_30_444 (6)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_32_422 (7)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_36_444 (8)
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_48_444 (9)
#define SOR_NV_PDISP_SOR_CRC_CNTRL_0 0x11
#define SOR_NV_PDISP_SOR_DP_AUDIO_VBLANK_SYMBOLS_0 0x64
#define SOR_NV_PDISP_SOR_DP_SPARE0_0 0x60
#define SOR_NV_PDISP_SOR_PWR_0 0x15
#define SOR_NV_PDISP_SOR_STATE0_0 0x3
#define SOR_NV_PDISP_SOR_SUPER_STATE1_0 0x2
#define SOR_NV_PDISP_SOR_SUPER_STATE0_0 0x1
/* ardpaux.h */
#define DPAUX_DP_AUXDATA_READ_W0 0x19
#define DP_LVDS_SHIFT 25
#define DP_LVDS (1 << DP_LVDS_SHIFT)
#define SRC_BPP 16
#define COLORDEPTH 0x6
#define COLOR_WHITE 0xFFFFFF
struct soc_nvidia_tegra124_config; /* forward declaration */
void setup_display(struct soc_nvidia_tegra124_config *config);
void init_dca_regs(void);
void dp_io_powerup(void);
u32 dp_setup_timing(u32 width, u32 height);
void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr,
u32 lane_count, u32 enhanced_framing, u32 panel_edp,
u32 pclkfreq, u32 linkfreq);
#define FB_SIZE_MB (32)

View file

@ -21,8 +21,10 @@
#include <console/console.h>
#include <device/device.h>
#include <arch/io.h>
#include <soc/nvidia/tegra/dc.h>
#include <soc/nvidia/tegra124/sdram.h>
#include "chip.h"
#include <soc/display.h>
/* this sucks, but for now, fb size/location are hardcoded.

View file

@ -54,17 +54,18 @@
#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25)
#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25)
static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
{
u32 reg_val = readl((sor->base + reg * 4));
void *addr = sor->base + (u32) (reg << 2);
u32 reg_val = READL(addr);
return reg_val;
}
static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor,
u32 reg, u32 val)
{
writel(val, (sor->base + reg * 4));
void *addr = sor->base + (u32) (reg << 2);
WRITEL(val, addr);
}
static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
@ -76,8 +77,61 @@ static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
tegra_sor_writel(sor, reg, reg_val);
}
static u32 tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor,
u32 reg, u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us)
{
u32 temp = timeout_us;
u32 reg_val = 0;
do {
udelay(poll_interval_us);
reg_val = tegra_sor_readl(sor, reg);
if (timeout_us > poll_interval_us)
timeout_us -= poll_interval_us;
else
break;
} while ((reg_val & mask) != exp_val);
if ((reg_val & mask) == exp_val)
return 0; /* success */
printk(BIOS_ERR,
"sor_poll_register 0x%x: timeout, "
"(reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
reg, reg_val, mask, exp_val);
return temp;
}
int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
{
u32 reg_val;
u32 orig_val;
orig_val = tegra_sor_readl(sor, NV_SOR_PWR);
reg_val = pu_pd ? NV_SOR_PWR_NORMAL_STATE_PU :
NV_SOR_PWR_NORMAL_STATE_PD; /* normal state only */
if (reg_val == orig_val)
return 0; /* No update needed */
reg_val |= NV_SOR_PWR_SETTING_NEW_TRIGGER;
tegra_sor_writel(sor, NV_SOR_PWR, reg_val);
/* Poll to confirm it is done */
if (tegra_dc_sor_poll_register(sor, NV_SOR_PWR,
NV_SOR_PWR_SETTING_NEW_DEFAULT_MASK,
NV_SOR_PWR_SETTING_NEW_DONE,
100, TEGRA_SOR_TIMEOUT_MS * 1000)) {
printk(BIOS_ERR,
"dc timeout waiting for SOR_PWR = NEW_DONE\n");
return -EFAULT;
}
return 0;
}
void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
u8 training_pattern, const struct tegra_dc_dp_link_config *cfg)
u8 training_pattern, const struct tegra_dc_dp_link_config *link_cfg)
{
u32 reg_val;
@ -89,20 +143,20 @@ void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
reg_val &= NV_SOR_DP_LINKCTL_ENABLE_NO;
reg_val &= ~NV_SOR_DP_LINKCTL_TUSIZE_MASK;
reg_val |= (cfg->tu_size << NV_SOR_DP_LINKCTL_TUSIZE_SHIFT);
reg_val |= (link_cfg->tu_size << NV_SOR_DP_LINKCTL_TUSIZE_SHIFT);
if (cfg->enhanced_framing)
if (link_cfg->enhanced_framing)
reg_val |= NV_SOR_DP_LINKCTL_ENHANCEDFRAME_ENABLE;
tegra_sor_writel(sor, NV_SOR_DP_LINKCTL(sor->portnum), reg_val);
switch (training_pattern) {
case trainingPattern_1:
case training_pattern_1:
tegra_sor_writel(sor, NV_SOR_DP_TPG, 0x41414141);
break;
case trainingPattern_2:
case trainingPattern_3:
reg_val = (cfg->link_bw == NV_SOR_LINK_SPEED_G5_4) ?
case training_pattern_2:
case training_pattern_3:
reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ?
0x43434343 : 0x42424242;
tegra_sor_writel(sor, NV_SOR_DP_TPG, reg_val);
break;
@ -112,58 +166,612 @@ void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
}
}
void tegra_dc_sor_set_dp_lanedata(struct tegra_dc_sor_data *sor,
u32 lane, u32 pre_emphasis, u32 drive_current, u32 tx_pu)
static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
int pu, int is_lvds)
{
u32 d_cur;
u32 p_emp;
u32 reg_val;
/* SOR lane sequencer */
if (pu)
reg_val = NV_SOR_LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
NV_SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_PU;
else
reg_val = NV_SOR_LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
NV_SOR_LANE_SEQ_CTL_SEQUENCE_UP |
NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_PD;
d_cur = tegra_sor_readl(sor, NV_SOR_DC(sor->portnum));
p_emp = tegra_sor_readl(sor, NV_SOR_PR(sor->portnum));
if (is_lvds)
reg_val |= 15 << NV_SOR_LANE_SEQ_CTL_DELAY_SHIFT;
else
reg_val |= 1 << NV_SOR_LANE_SEQ_CTL_DELAY_SHIFT;
switch (lane) {
case 0:
p_emp &= ~NV_SOR_PR_LANE2_DP_LANE0_MASK;
p_emp |= (pre_emphasis <<
NV_SOR_PR_LANE2_DP_LANE0_SHIFT);
d_cur &= ~NV_SOR_DC_LANE2_DP_LANE0_MASK;
d_cur |= (drive_current <<
NV_SOR_DC_LANE2_DP_LANE0_SHIFT);
break;
case 1:
p_emp &= ~NV_SOR_PR_LANE1_DP_LANE1_MASK;
p_emp |= (pre_emphasis <<
NV_SOR_PR_LANE1_DP_LANE1_SHIFT);
d_cur &= ~NV_SOR_DC_LANE1_DP_LANE1_MASK;
d_cur |= (drive_current <<
NV_SOR_DC_LANE1_DP_LANE1_SHIFT);
break;
case 2:
p_emp &= ~NV_SOR_PR_LANE0_DP_LANE2_MASK;
p_emp |= (pre_emphasis <<
NV_SOR_PR_LANE0_DP_LANE2_SHIFT);
d_cur &= ~NV_SOR_DC_LANE0_DP_LANE2_MASK;
d_cur |= (drive_current <<
NV_SOR_DC_LANE0_DP_LANE2_SHIFT);
break;
case 3:
p_emp &= ~NV_SOR_PR_LANE3_DP_LANE3_MASK;
p_emp |= (pre_emphasis <<
NV_SOR_PR_LANE3_DP_LANE3_SHIFT);
d_cur &= ~NV_SOR_DC_LANE3_DP_LANE3_MASK;
d_cur |= (drive_current <<
NV_SOR_DC_LANE3_DP_LANE3_SHIFT);
break;
default:
printk(BIOS_SPEW, "dp: sor lane count %d is invalid\n", lane);
tegra_sor_writel(sor, NV_SOR_LANE_SEQ_CTL, reg_val);
if (tegra_dc_sor_poll_register(sor, NV_SOR_LANE_SEQ_CTL,
NV_SOR_LANE_SEQ_CTL_SETTING_MASK,
NV_SOR_LANE_SEQ_CTL_SETTING_NEW_DONE,
100, TEGRA_SOR_TIMEOUT_MS*1000)) {
printk(BIOS_ERR,
"dp: timeout while waiting for SOR lane sequencer "
"to power down langes\n");
return -1;
}
tegra_sor_write_field(sor, NV_SOR_DP_LINKCTL(sor->portnum),
NV_SOR_DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK,
tx_pu << NV_SOR_DP_PADCTL_TX_PU_VALUE_SHIFT);
tegra_sor_writel(sor, NV_SOR_DC(sor->portnum), d_cur);
tegra_sor_writel(sor, NV_SOR_PR(sor->portnum), p_emp);
return 0;
}
static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
u32 lane_count, int pu)
{
u32 reg_val;
reg_val = tegra_sor_readl(sor, NV_SOR_DP_PADCTL(sor->portnum));
if (pu) {
switch (lane_count) {
case 4:
reg_val |= (NV_SOR_DP_PADCTL_PD_TXD_3_NO |
NV_SOR_DP_PADCTL_PD_TXD_2_NO);
/* fall through */
case 2:
reg_val |= NV_SOR_DP_PADCTL_PD_TXD_1_NO;
case 1:
reg_val |= NV_SOR_DP_PADCTL_PD_TXD_0_NO;
break;
default:
printk(BIOS_ERR,
"dp: invalid lane number %d\n", lane_count);
return -1;
}
tegra_sor_writel(sor, NV_SOR_DP_PADCTL(sor->portnum), reg_val);
tegra_dc_sor_set_lane_count(sor, lane_count);
}
return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
}
void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
int power_up)
{
u32 reg_val;
/* !!TODO: need to enable panel power through GPIO operations */
/* Check bug 790854 for HW progress */
reg_val = tegra_sor_readl(sor, NV_SOR_DP_PADCTL(sor->portnum));
if (power_up)
reg_val |= NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERUP;
else
reg_val &= ~NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERUP;
tegra_sor_writel(sor, NV_SOR_DP_PADCTL(sor->portnum), reg_val);
}
static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor,
const struct tegra_dc_dp_link_config *link_cfg)
{
u32 reg_val;
tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
reg_val = tegra_sor_readl(sor, NV_SOR_DP_CONFIG(sor->portnum));
reg_val &= ~NV_SOR_DP_CONFIG_WATERMARK_MASK;
reg_val |= link_cfg->watermark;
reg_val &= ~NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_MASK;
reg_val |= (link_cfg->active_count <<
NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_SHIFT);
reg_val &= ~NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_MASK;
reg_val |= (link_cfg->active_frac <<
NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_SHIFT);
if (link_cfg->activepolarity)
reg_val |= NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
else
reg_val &= ~NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
reg_val |= (NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_ENABLE |
NV_SOR_DP_CONFIG_RD_RESET_VAL_NEGATIVE);
tegra_sor_writel(sor, NV_SOR_DP_CONFIG(sor->portnum), reg_val);
/* enable CRC */
reg_val = NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_EN <<
NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_SHIFT;
tegra_sor_writel(sor, NV_SOR_CRC_CNTRL, reg_val);
/* program h/vblank sym */
tegra_sor_write_field(sor, NV_SOR_DP_AUDIO_HBLANK_SYMBOLS,
NV_SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK, link_cfg->hblank_sym);
tegra_sor_write_field(sor, NV_SOR_DP_AUDIO_VBLANK_SYMBOLS,
NV_SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK, link_cfg->vblank_sym);
}
static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor)
{
tegra_sor_writel(sor, NV_SOR_SUPER_STATE0, 0);
tegra_sor_writel(sor, NV_SOR_SUPER_STATE0, 1);
tegra_sor_writel(sor, NV_SOR_SUPER_STATE0, 0);
}
static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor)
{
tegra_sor_writel(sor, NV_SOR_STATE0, 0);
tegra_sor_writel(sor, NV_SOR_STATE0, 1);
tegra_sor_writel(sor, NV_SOR_STATE0, 0);
}
static void tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
{
u32 reg_val;
void *pmc_base = sor->pmc_base;
if (up) {
WRITEL(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE,
pmc_base + APBDEV_PMC_DPD_SAMPLE);
WRITEL(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM);
}
reg_val = READL(pmc_base + APBDEV_PMC_IO_DPD2_REQ);
reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON ||
APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK);
reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON |
APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF :
APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF |
APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON;
WRITEL(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ);
/* Polling */
u32 temp = 10*1000;
do {
udelay(20);
reg_val = READL(pmc_base + APBDEV_PMC_IO_DPD2_STATUS);
if (temp > 20)
temp -= 20;
else
break;
} while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0);
if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0)
printk(BIOS_ERR,
"PMC_IO_DPD2 polling failed (0x%x)\n", reg_val);
if (up)
WRITEL(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE,
pmc_base + APBDEV_PMC_DPD_SAMPLE);
}
void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
{
u32 reg_val;
reg_val = tegra_sor_readl(sor, NV_SOR_DP_SPARE(sor->portnum));
if (is_int)
reg_val |= NV_SOR_DP_SPARE_PANEL_INTERNAL;
else
reg_val &= ~NV_SOR_DP_SPARE_PANEL_INTERNAL;
reg_val |= NV_SOR_DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK |
NV_SOR_DP_SPARE_SEQ_ENABLE_YES;
tegra_sor_writel(sor, NV_SOR_DP_SPARE(sor->portnum), reg_val);
}
void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
u8 *lane_count)
{
u32 reg_val;
reg_val = tegra_sor_readl(sor, NV_SOR_CLK_CNTRL);
*link_bw = (reg_val & NV_SOR_CLK_CNTRL_DP_LINK_SPEED_MASK)
>> NV_SOR_CLK_CNTRL_DP_LINK_SPEED_SHIFT;
reg_val = tegra_sor_readl(sor,
NV_SOR_DP_LINKCTL(sor->portnum));
switch (reg_val & NV_SOR_DP_LINKCTL_LANECOUNT_MASK) {
case NV_SOR_DP_LINKCTL_LANECOUNT_ZERO:
*lane_count = 0;
break;
case NV_SOR_DP_LINKCTL_LANECOUNT_ONE:
*lane_count = 1;
break;
case NV_SOR_DP_LINKCTL_LANECOUNT_TWO:
*lane_count = 2;
break;
case NV_SOR_DP_LINKCTL_LANECOUNT_FOUR:
*lane_count = 4;
break;
default:
printk(BIOS_ERR, "Unknown lane count\n");
}
}
void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw)
{
tegra_sor_write_field(sor, NV_SOR_CLK_CNTRL,
NV_SOR_CLK_CNTRL_DP_LINK_SPEED_MASK,
link_bw << NV_SOR_CLK_CNTRL_DP_LINK_SPEED_SHIFT);
}
void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
{
u32 reg_val;
reg_val = tegra_sor_readl(sor, NV_SOR_DP_LINKCTL(sor->portnum));
reg_val &= ~NV_SOR_DP_LINKCTL_LANECOUNT_MASK;
switch (lane_count) {
case 0:
break;
case 1:
reg_val |= NV_SOR_DP_LINKCTL_LANECOUNT_ONE;
break;
case 2:
reg_val |= NV_SOR_DP_LINKCTL_LANECOUNT_TWO;
break;
case 4:
reg_val |= NV_SOR_DP_LINKCTL_LANECOUNT_FOUR;
break;
default:
/* 0 should be handled earlier. */
printk(BIOS_ERR, "dp: Invalid lane count %d\n",
lane_count);
return;
}
tegra_sor_writel(sor, NV_SOR_DP_LINKCTL(sor->portnum), reg_val);
}
static void tegra_sor_enable_edp_clock(struct tegra_dc_sor_data *sor)
{
sor_clock_start();
}
/* The SOR power sequencer does not work for t124 so SW has to
go through the power sequence manually */
/* Power up steps from spec: */
/* STEP PDPORT PDPLL PDBG PLLVCOD PLLCAPD E_DPD PDCAL */
/* 1 1 1 1 1 1 1 1 */
/* 2 1 1 1 1 1 0 1 */
/* 3 1 1 0 1 1 0 1 */
/* 4 1 0 0 0 0 0 1 */
/* 5 0 0 0 0 0 0 1 */
static void tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor,
int is_lvds)
{
if (sor->power_is_up)
return;
/* Set link bw */
tegra_dc_sor_set_link_bandwidth(sor,
is_lvds ? NV_SOR_CLK_CNTRL_DP_LINK_SPEED_LVDS :
NV_SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62);
/* step 1 */
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */
NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */
NV_SOR_PLL2_AUX7_PORT_POWERDOWN_ENABLE |
NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE |
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE);
tegra_sor_write_field(sor, NV_SOR_PLL0,
NV_SOR_PLL0_PWR_MASK | /* PDPLL */
NV_SOR_PLL0_VCOPD_MASK, /* PLLVCOPD */
NV_SOR_PLL0_PWR_OFF |
NV_SOR_PLL0_VCOPD_ASSERT);
tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum),
NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */
NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN);
/* step 2 */
tegra_dc_sor_io_set_dpd(sor, 1);
udelay(15);
/* step 3 */
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
udelay(25);
/* step 4 */
tegra_sor_write_field(sor, NV_SOR_PLL0,
NV_SOR_PLL0_PWR_MASK | /* PDPLL */
NV_SOR_PLL0_VCOPD_MASK, /* PLLVCOPD */
NV_SOR_PLL0_PWR_ON | NV_SOR_PLL0_VCOPD_RESCIND);
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
udelay(225);
/* step 5 */
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK, /* PDPORT */
NV_SOR_PLL2_AUX7_PORT_POWERDOWN_DISABLE);
sor->power_is_up = 1;
}
static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor,
int is_lvds)
{
const struct tegra_dc *dc = sor->dc;
const struct tegra_dc_dp_data *dp = dc->out;
const struct tegra_dc_dp_link_config *link_cfg = &dp->link_cfg;
const struct soc_nvidia_tegra124_config *config = dc->config;
const int head_num = 0; // based on kernel dc driver
u32 reg_val = NV_SOR_STATE1_ASY_OWNER_HEAD0 << head_num;
u32 vtotal, htotal;
u32 vsync_end, hsync_end;
u32 vblank_end, hblank_end;
u32 vblank_start, hblank_start;
reg_val |= is_lvds ? NV_SOR_STATE1_ASY_PROTOCOL_LVDS_CUSTOM :
NV_SOR_STATE1_ASY_PROTOCOL_DP_A;
reg_val |= NV_SOR_STATE1_ASY_SUBOWNER_NONE |
NV_SOR_STATE1_ASY_CRCMODE_COMPLETE_RASTER;
reg_val |= NV_SOR_STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE;
reg_val |= NV_SOR_STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE;
reg_val |= (link_cfg->bits_per_pixel > 18) ?
NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_24_444 :
NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_18_444;
tegra_sor_writel(sor, NV_SOR_STATE1, reg_val);
/* Skipping programming NV_HEAD_STATE0, assuming:
interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB */
vtotal = config->vsync_width + config->vback_porch +
config->yres + config->vfront_porch;
htotal = config->hsync_width + config->hback_porch +
config->xres + config->hfront_porch;
tegra_sor_writel(sor, NV_HEAD_STATE1(head_num),
vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT |
htotal << NV_HEAD_STATE1_HTOTAL_SHIFT);
vsync_end = config->vsync_width - 1;
hsync_end = config->hsync_width - 1;
tegra_sor_writel(sor, NV_HEAD_STATE2(head_num),
vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT |
hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT);
vblank_end = vsync_end + config->vback_porch;
hblank_end = hsync_end + config->hback_porch;
tegra_sor_writel(sor, NV_HEAD_STATE3(head_num),
vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT |
hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT);
vblank_start = vblank_end + config->yres;
hblank_start = hblank_end + config->xres;
tegra_sor_writel(sor, NV_HEAD_STATE4(head_num),
vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT |
hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT);
/* TODO: adding interlace mode support */
tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1);
tegra_sor_write_field(sor, NV_SOR_CSTM,
NV_SOR_CSTM_ROTCLK_DEFAULT_MASK |
NV_SOR_CSTM_LVDS_EN_ENABLE,
2 << NV_SOR_CSTM_ROTCLK_SHIFT |
is_lvds ? NV_SOR_CSTM_LVDS_EN_ENABLE :
NV_SOR_CSTM_LVDS_EN_DISABLE);
}
static void tegra_dc_sor_enable_dc(struct tegra_dc_sor_data *sor)
{
struct tegra_dc *dc = sor->dc;
struct display_controller *disp_ctrl = (void *)dc->base;
u32 reg_val = READL(&disp_ctrl->cmd.state_access);
WRITEL(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
WRITEL(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt);
/* Enable DC */
WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd);
WRITEL(reg_val, &disp_ctrl->cmd.state_access);
}
void tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor)
{
const struct tegra_dc_dp_link_config *link_cfg = sor->link_cfg;
tegra_sor_write_field(sor, NV_SOR_CLK_CNTRL,
NV_SOR_CLK_CNTRL_DP_CLK_SEL_MASK,
NV_SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK);
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
udelay(25);
tegra_sor_write_field(sor, NV_SOR_PLL3,
NV_SOR_PLL3_PLLVDD_MODE_MASK,
NV_SOR_PLL3_PLLVDD_MODE_V3_3);
tegra_sor_writel(sor, NV_SOR_PLL0,
0xf << NV_SOR_PLL0_ICHPMP_SHFIT |
0x3 << NV_SOR_PLL0_VCOCAP_SHIFT |
NV_SOR_PLL0_PLLREG_LEVEL_V45 |
NV_SOR_PLL0_RESISTORSEL_EXT |
NV_SOR_PLL0_PWR_ON | NV_SOR_PLL0_VCOPD_RESCIND);
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX1_SEQ_MASK | NV_SOR_PLL2_AUX9_LVDSEN_OVERRIDE |
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
NV_SOR_PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE |
NV_SOR_PLL2_AUX9_LVDSEN_OVERRIDE |
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
tegra_sor_writel(sor, NV_SOR_PLL1,
NV_SOR_PLL1_TERM_COMPOUT_HIGH | NV_SOR_PLL1_TMDS_TERM_ENABLE);
if (tegra_dc_sor_poll_register(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE,
100, TEGRA_SOR_TIMEOUT_MS * 1000)) {
printk(BIOS_ERR, "DP failed to lock PLL\n");
return;
}
tegra_sor_write_field(sor, NV_SOR_PLL2,
NV_SOR_PLL2_AUX2_MASK | NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK,
NV_SOR_PLL2_AUX2_OVERRIDE_POWERDOWN |
NV_SOR_PLL2_AUX7_PORT_POWERDOWN_DISABLE);
tegra_dc_sor_power_up(sor, 0);
/* re-enable SOR clock */
tegra_sor_enable_edp_clock(sor); // select pll_dp as clock source
/* Power up lanes */
tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1);
tegra_dc_sor_set_dp_mode(sor, link_cfg);
}
void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor)
{
u32 reg_val;
struct display_controller *disp_ctrl = (void *)sor->dc->base;
tegra_dc_sor_enable_dc(sor);
tegra_dc_sor_config_panel(sor, 0);
WRITEL(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
reg_val = tegra_sor_readl(sor, NV_SOR_TEST);
if (reg_val & NV_SOR_TEST_ATTACHED_TRUE) {
printk(BIOS_INFO, "sor: Attached\n");
return;
}
/* Attach head */
tegra_dc_sor_update(sor);
reg_val = NV_SOR_SUPER_STATE1_ASY_HEAD_OP_AWAKE |
NV_SOR_SUPER_STATE1_ASY_ORMODE_NORMAL |
NV_SOR_SUPER_STATE1_ATTACHED_NO;
tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, reg_val);
tegra_dc_sor_super_update(sor);
reg_val |= NV_SOR_SUPER_STATE1_ATTACHED_YES;
tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, reg_val);
tegra_dc_sor_super_update(sor);
if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST,
NV_SOR_TEST_ATTACHED_DEFAULT_MASK,
NV_SOR_TEST_ATTACHED_TRUE,
100, TEGRA_SOR_ATTACH_TIMEOUT_MS * 1000)) {
printk(BIOS_ERR,
"dc timeout waiting for ATTACHED = TRUE\n");
}
/* Enable dc after attaching head */
WRITEL(0x9f00, &disp_ctrl->cmd.state_ctrl);
WRITEL(0x9f, &disp_ctrl->cmd.state_ctrl);
WRITEL(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE |
PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
&disp_ctrl->cmd.disp_pow_ctrl);
if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST,
NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
NV_SOR_TEST_ACT_HEAD_OPMODE_AWAKE,
100, TEGRA_SOR_ATTACH_TIMEOUT_MS * 1000)) {
printk(BIOS_ERR,
"dc timeout waiting for OPMOD = AWAKE\n");
}
}
void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
const struct tegra_dc_dp_link_config *link_cfg)
{
tegra_sor_writel(sor, NV_SOR_LANE_DRIVE_CURRENT(sor->portnum),
link_cfg->drive_current);
tegra_sor_writel(sor, NV_SOR_PR(sor->portnum),
link_cfg->preemphasis);
tegra_sor_writel(sor, NV_SOR_POSTCURSOR(sor->portnum),
link_cfg->postcursor);
tegra_sor_writel(sor, NV_SOR_LVDS, 0);
tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum),
NV_SOR_DP_PADCTL_TX_PU_ENABLE |
NV_SOR_DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK,
NV_SOR_DP_PADCTL_TX_PU_ENABLE |
2 << NV_SOR_DP_PADCTL_TX_PU_VALUE_SHIFT);
/* Precharge */
tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum),
0xf0, 0xf0);
udelay(20);
tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum),
0xf0, 0x0);
}
void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor)
{
u32 pad_ctrl = 0;
u32 drive_current = 0;
u32 pre_emphasis = 0;
int err = 0;
switch (sor->link_cfg->lane_count) {
case 4:
pad_ctrl = (NV_SOR_DP_PADCTL_PD_TXD_0_NO |
NV_SOR_DP_PADCTL_PD_TXD_1_NO |
NV_SOR_DP_PADCTL_PD_TXD_2_NO |
NV_SOR_DP_PADCTL_PD_TXD_3_NO);
break;
case 2:
pad_ctrl = (NV_SOR_DP_PADCTL_PD_TXD_0_NO |
NV_SOR_DP_PADCTL_PD_TXD_1_NO |
NV_SOR_DP_PADCTL_PD_TXD_2_YES |
NV_SOR_DP_PADCTL_PD_TXD_3_YES);
break;
case 1:
pad_ctrl = (NV_SOR_DP_PADCTL_PD_TXD_0_NO |
NV_SOR_DP_PADCTL_PD_TXD_1_YES |
NV_SOR_DP_PADCTL_PD_TXD_2_YES |
NV_SOR_DP_PADCTL_PD_TXD_3_YES);
break;
default:
printk(BIOS_ERR, "Invalid sor lane count: %u\n",
sor->link_cfg->lane_count);
return;
}
pad_ctrl |= NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN;
tegra_sor_writel(sor, NV_SOR_DP_PADCTL(sor->portnum), pad_ctrl);
err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0);
if (err) {
printk(BIOS_ERR,
"Wait for lane power down failed: %d\n", err);
return;
}
/* Set to a known-good pre-calibrated setting */
switch (sor->link_cfg->link_bw) {
case SOR_LINK_SPEED_G1_62:
case SOR_LINK_SPEED_G2_7:
drive_current = 0x13131313;
pre_emphasis = 0;
break;
case SOR_LINK_SPEED_G5_4:
drive_current = 0x19191919;
pre_emphasis = 0x09090909;
default:
printk(BIOS_ERR, "Invalid sor link bandwidth: %d\n",
sor->link_cfg->link_bw);
return;
}
tegra_sor_writel(sor, NV_SOR_LANE_DRIVE_CURRENT(sor->portnum),
drive_current);
tegra_sor_writel(sor, NV_SOR_PR(sor->portnum), pre_emphasis);
}

View file

@ -17,7 +17,6 @@
#ifndef __TEGRA124_SOR_H__
#define __TEGRA124_SOR_H__
#define NV_SOR_SUPER_STATE0 (0x1)
#define NV_SOR_SUPER_STATE0_UPDATE_SHIFT (0)
#define NV_SOR_SUPER_STATE0_UPDATE_DEFAULT_MASK (0x1)
@ -830,11 +829,11 @@
#define NV_SOR_DP_TPG_LANE0_PATTERN_HBR2_COMPLIANCE (8)
enum {
trainingPattern_Disabled = 0,
trainingPattern_1 = 1,
trainingPattern_2 = 2,
trainingPattern_3 = 3,
trainingPattern_None = 0xff
training_pattern_disabled = 0,
training_pattern_1 = 1,
training_pattern_2 = 2,
training_pattern_3 = 3,
training_pattern_none = 0xff
};
enum tegra_dc_sor_protocol {
@ -842,10 +841,10 @@ enum tegra_dc_sor_protocol {
SOR_LVDS,
};
#define NV_SOR_LINK_SPEED_G1_62 6
#define NV_SOR_LINK_SPEED_G2_7 10
#define NV_SOR_LINK_SPEED_G5_4 20
#define NV_SOR_LINK_SPEED_LVDS 7
#define SOR_LINK_SPEED_G1_62 6
#define SOR_LINK_SPEED_G2_7 10
#define SOR_LINK_SPEED_G5_4 20
#define SOR_LINK_SPEED_LVDS 7
/* todo: combine this and the intel_dp struct into one struct. */
struct tegra_dc_dp_link_config {
@ -885,21 +884,37 @@ struct tegra_dc_dp_link_config {
* having two channels.
*/
struct tegra_dc_sor_data {
void *base;
u8 portnum; /* 0 or 1 */
struct tegra_dc *dc;
void *base;
void *pmc_base;
u8 portnum; /* 0 or 1 */
struct tegra_dc_dp_link_config *link_cfg;
int power_is_up;
};
#define TEGRA_SOR_TIMEOUT_MS 1000
#define TEGRA_SOR_ATTACH_TIMEOUT_MS 100000
void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor,
int ena,
u8 training_pattern,
const struct tegra_dc_dp_link_config *cfg);
void tegra_dc_sor_set_dp_lanedata(struct tegra_dc_sor_data *sor,
u32 lane, u32 pre_emphasis,
u32 drive_current, u32 tx_pu);
#define CHECK_RET(x) \
do { \
ret = (x); \
if (ret != 0) \
return ret; \
} while (0)
void tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor);
int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd);
void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
u8 training_pattern, const struct tegra_dc_dp_link_config *link_cfg);
void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw);
void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count);
void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
int power_up);
void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int);
void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
u8 *lane_count);
void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor);
void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
const struct tegra_dc_dp_link_config *link_cfg);
void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor);
#endif /*__TEGRA124_SOR_H__ */