Pit: graphics
This includes the new dp code, which is better, and the fimd code, which is changed and improved. We took the chance to remove un-needed files, and also to remove some foolish u-boot habits, but not all of them. That will take time. With these changes we get graphics. Since the only mainboards we have with 16 bit graphics are 5:6:5, adjust edid.c to just use that format. If at some future time we need 4:4:4, which seems unlikely, we'll need to add a function to adjust the lb_framebuffer. Note that you can't just divine this from the EDID, as the graphics pipe format need not match the actual final format used. The EDID reading works. We've been requested to support hard-coded EDIDs and that will come in the next revision. Currently the hard-coded EDID is ignored for testing. Change-Id: Ib4d06dc3388ab90c834f94808a51133e5b515a4d Signed-off-by: Ronald G. Minnich <rminnich@google.com> Reviewed-on: https://gerrit.chromium.org/gerrit/64240 Reviewed-by: Stefan Reinauer <reinauer@google.com> Tested-by: Ronald G. Minnich <rminnich@chromium.org> Commit-Queue: Gabe Black <gabeblack@chromium.org> Reviewed-on: http://review.coreboot.org/4432 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
This commit is contained in:
parent
052bf4ba21
commit
c0d5eb2a33
|
@ -48,8 +48,7 @@ ramstage-y += monotonic_timer.c
|
|||
ramstage-y += timer.c
|
||||
ramstage-y += gpio.c
|
||||
ramstage-y += i2c.c
|
||||
ramstage-y += dp-reg.c
|
||||
ramstage-y += fb.c
|
||||
ramstage-y += dp.c dp_lowlevel.c fimd.c
|
||||
ramstage-y += usb.c
|
||||
ramstage-y += cbmem.c
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
void system_clock_init(void)
|
||||
{
|
||||
struct exynos5420_clock *clk =
|
||||
(struct exynos5420_clock *)EXYNOS5_CLOCK_BASE;
|
||||
(struct exynos5420_clock *)EXYNOS5420_CLOCK_BASE;
|
||||
struct exynos5_mct_regs *mct_regs =
|
||||
(struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE;
|
||||
u32 val;
|
||||
|
@ -49,6 +49,7 @@ void system_clock_init(void)
|
|||
writel(IPLL_LOCK_VAL, &clk->ipll_lock);
|
||||
writel(SPLL_LOCK_VAL, &clk->spll_lock);
|
||||
writel(KPLL_LOCK_VAL, &clk->kpll_lock);
|
||||
writel(RPLL_LOCK_VAL, &clk->rpll_lock);
|
||||
|
||||
setbits_le32(&clk->clk_src_cpu, MUX_HPM_SEL_MASK);
|
||||
|
||||
|
@ -85,7 +86,7 @@ void system_clock_init(void)
|
|||
|
||||
/* Set DPLL */
|
||||
writel(DPLL_CON1_VAL, &clk->dpll_con1);
|
||||
val = set_pll(0xc8, 0x2, 0x2);
|
||||
val = set_pll(0x190, 0x4, 0x2);
|
||||
writel(val, &clk->dpll_con0);
|
||||
while ((readl(&clk->dpll_con0) & PLL_LOCKED) == 0)
|
||||
;
|
||||
|
@ -133,6 +134,22 @@ void system_clock_init(void)
|
|||
while ((readl(&clk->spll_con0) & PLL_LOCKED) == 0)
|
||||
;
|
||||
|
||||
/* We use RPLL as the source for FIMD video stream clock */
|
||||
writel(RPLL_CON1_VAL, &clk->rpll_con1);
|
||||
writel(RPLL_CON2_VAL, &clk->rpll_con2);
|
||||
/* computed by gabe from first principles; u-boot is probably
|
||||
* wrong again
|
||||
*/
|
||||
val = set_pll(0xa0, 0x3, 0x2);
|
||||
writel(val, &clk->rpll_con0);
|
||||
/* note: this is a meaningless exercise. The hardware lock
|
||||
* detection does not work. So this just spins for some
|
||||
* time and is done. NO indication of success should attach
|
||||
* to this or any other spin on a con0 value.
|
||||
*/
|
||||
while ((readl(&clk->rpll_con0) & PLL_LOCKED) == 0)
|
||||
;
|
||||
|
||||
writel(CLK_DIV_CDREX0_VAL, &clk->clk_div_cdrex0);
|
||||
writel(CLK_DIV_CDREX1_VAL, &clk->clk_div_cdrex1);
|
||||
|
||||
|
@ -194,8 +211,3 @@ void clock_gate(void)
|
|||
{
|
||||
/* Not implemented for now. */
|
||||
}
|
||||
|
||||
void clock_init_dp_clock(void)
|
||||
{
|
||||
/* Not implemented for now. */
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#include <device/device.h>
|
||||
#include <cbmem.h>
|
||||
#include <arch/cache.h>
|
||||
#include "dp.h"
|
||||
#include "fimd.h"
|
||||
#include "dp-core.h"
|
||||
#include "cpu.h"
|
||||
#include "clk.h"
|
||||
#include "usb.h"
|
||||
|
@ -138,9 +138,7 @@ static void exynos_displayport_init(device_t dev, u32 lcdbase,
|
|||
dcache_clean_invalidate_by_mva(lower, upper - lower);
|
||||
mmu_config_range(lower / MiB, (upper - lower) / MiB, DCACHE_OFF);
|
||||
|
||||
printk(BIOS_DEBUG, "Initializing Exynos LCD.\n");
|
||||
|
||||
lcd_ctrl_init(fb_size, &panel, (void *)lcdbase);
|
||||
mmio_resource(dev, 1, lcdbase/KiB, (fb_size + KiB - 1)/KiB);
|
||||
}
|
||||
|
||||
static void cpu_enable(device_t dev)
|
||||
|
|
|
@ -197,6 +197,7 @@
|
|||
#define samsung_get_base_clock() ((struct exynos5420_clock *)EXYNOS5_CLOCK_BASE)
|
||||
#define samsung_get_base_ace_sfr() ((struct exynos5_ace_sfr *)EXYNOS5_ACE_SFR_BASE)
|
||||
#define samsung_get_base_dsim() ((struct exynos5_dsim *)EXYNOS5_MIPI_DSI1_BASE)
|
||||
#define samsung_get_base_dp() ((struct exynos_dp *)EXYNOS5420_DP_BASE)
|
||||
#define samsung_get_base_disp_ctrl() ((struct exynos5_disp_ctrl *)EXYNOS5_DISP1_CTRL_BASE)
|
||||
#define samsung_get_base_fimd() ((struct exynos5_fimd *)EXYNOS5_FIMD_BASE)
|
||||
#define samsung_get_base_pro_id() ((struct exynos5_pro_id *)EXYNOS5_PRO_ID)
|
||||
|
|
|
@ -1,260 +0,0 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* Header file for Samsung DP (Display Port) interface driver. */
|
||||
|
||||
#ifndef CPU_SAMSUNG_EXYNOS5420_DP_CORE_H
|
||||
#define CPU_SAMSUNG_EXYNOS5420_DP_CORE_H
|
||||
|
||||
#define STREAM_ON_TIMEOUT 100
|
||||
#define PLL_LOCK_TIMEOUT 10
|
||||
#define DP_INIT_TRIES 10
|
||||
#define MAX_CR_LOOP 5
|
||||
#define MAX_EQ_LOOP 4
|
||||
|
||||
/* Link tare type */
|
||||
enum link_rate {
|
||||
LINK_RATE_1_62GBPS = 0x06,
|
||||
LINK_RATE_2_70GBPS = 0x0a
|
||||
};
|
||||
|
||||
/* Number of lanes supported */
|
||||
enum link_lane_count {
|
||||
LANE_COUNT1 = 1,
|
||||
LANE_COUNT2 = 2,
|
||||
LANE_COUNT4 = 4
|
||||
};
|
||||
|
||||
/* Pre emphasis level */
|
||||
enum pre_emphasis_level {
|
||||
PRE_EMPHASIS_LEVEL_0,
|
||||
PRE_EMPHASIS_LEVEL_1,
|
||||
PRE_EMPHASIS_LEVEL_2,
|
||||
PRE_EMPHASIS_LEVEL_3,
|
||||
};
|
||||
|
||||
/* Type of color space */
|
||||
enum color_space {
|
||||
COLOR_RGB,
|
||||
COLOR_YCBCR422,
|
||||
COLOR_YCBCR444
|
||||
};
|
||||
|
||||
/* Video input Bit Per Color */
|
||||
enum color_depth {
|
||||
COLOR_6,
|
||||
COLOR_8,
|
||||
COLOR_10,
|
||||
COLOR_12
|
||||
};
|
||||
|
||||
/* Type of YCbCr coefficient */
|
||||
enum color_coefficient {
|
||||
COLOR_YCBCR601,
|
||||
COLOR_YCBCR709
|
||||
};
|
||||
|
||||
/* Color range */
|
||||
enum dynamic_range {
|
||||
VESA,
|
||||
CEA
|
||||
};
|
||||
|
||||
/* Status of PLL clock */
|
||||
enum pll_status {
|
||||
PLL_UNLOCKED,
|
||||
PLL_LOCKED
|
||||
};
|
||||
|
||||
/* To choose type of m_value */
|
||||
enum clock_recovery_m_value_type {
|
||||
CALCULATED_M,
|
||||
REGISTER_M
|
||||
};
|
||||
|
||||
struct video_info {
|
||||
enum color_space color_space;
|
||||
enum dynamic_range dynamic_range;
|
||||
enum color_coefficient ycbcr_coeff;
|
||||
enum color_depth color_depth;
|
||||
|
||||
enum link_rate link_rate;
|
||||
enum link_lane_count lane_count;
|
||||
|
||||
char *name;
|
||||
|
||||
unsigned int h_sync_polarity:1;
|
||||
unsigned int v_sync_polarity:1;
|
||||
unsigned int interlaced:1;
|
||||
};
|
||||
|
||||
struct link_train {
|
||||
u8 link_rate;
|
||||
u8 lane_count;
|
||||
};
|
||||
|
||||
struct s5p_dp_device {
|
||||
unsigned int irq;
|
||||
struct exynos5_dp *base;
|
||||
struct video_info *video_info;
|
||||
struct link_train link_train;
|
||||
};
|
||||
|
||||
/* s5p_dp_reg.c */
|
||||
|
||||
/*
|
||||
* Reset DP module
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
void s5p_dp_reset(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Initialize DP to recieve video stream
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
void s5p_dp_init_video(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Check whether PLL is locked
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* return Lock status
|
||||
*/
|
||||
unsigned int s5p_dp_get_pll_lock_status(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Initialize analog functions of DP
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* return 0 on success
|
||||
*/
|
||||
int s5p_dp_init_analog_func(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Initialize DP for AUX transaction
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
void s5p_dp_init_aux(struct s5p_dp_device *dp);
|
||||
|
||||
/*
|
||||
* Start an AUX transaction.
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
int s5p_dp_start_aux_transaction(struct s5p_dp_device *dp);
|
||||
|
||||
/*
|
||||
* Write a byte to DPCD register
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param reg_addr DPCD register to be written
|
||||
* param data byte data to be written
|
||||
* return write status
|
||||
*/
|
||||
int s5p_dp_write_byte_to_dpcd(struct s5p_dp_device *dp,
|
||||
unsigned int reg_addr,
|
||||
unsigned char data);
|
||||
/*
|
||||
* Read a byte from DPCD register
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param reg_addr DPCD register to read
|
||||
* param data read byte data
|
||||
* return read status
|
||||
*/
|
||||
int s5p_dp_read_byte_from_dpcd(struct s5p_dp_device *dp,
|
||||
unsigned int reg_addr,
|
||||
unsigned char *data);
|
||||
/*
|
||||
* Initialize DP video functions
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
//void s5p_dp_init_video(struct s5p_dp_device *dp);
|
||||
|
||||
/*
|
||||
* Set color parameters for display
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param color_depth Video input Bit Per Color
|
||||
* param color_space Colorimetric format of input video
|
||||
* param dynamic_range VESA range or CEA range
|
||||
* param coeff YCbCr Coefficients of input video
|
||||
*/
|
||||
void s5p_dp_set_video_color_format(struct s5p_dp_device *dp,
|
||||
unsigned int color_depth,
|
||||
unsigned int color_space,
|
||||
unsigned int dynamic_range,
|
||||
unsigned int coeff);
|
||||
/*
|
||||
* Check whether video clock is on
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* return clock status
|
||||
*/
|
||||
int s5p_dp_is_slave_video_stream_clock_on(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Check whether video clock is on
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param type clock_recovery_m_value_type
|
||||
* param m_value to caluculate m_vid value
|
||||
* param n_value to caluculate n_vid value
|
||||
*/
|
||||
void s5p_dp_set_video_cr_mn(struct s5p_dp_device *dp,
|
||||
enum clock_recovery_m_value_type type,
|
||||
unsigned int m_value,
|
||||
unsigned int n_value);
|
||||
/*
|
||||
* Set DP to video slave mode thereby enabling video master
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
void s5p_dp_enable_video_master(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Check whether video stream is on
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* return video stream status
|
||||
*/
|
||||
int s5p_dp_is_video_stream_on(struct s5p_dp_device *dp);
|
||||
/*
|
||||
* Configure DP in slave mode
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param video_info pointer to main video_info structure.
|
||||
*/
|
||||
void s5p_dp_config_video_slave_mode(struct s5p_dp_device *dp,
|
||||
struct video_info *video_info);
|
||||
|
||||
/*
|
||||
* Wait unitl HW link training done
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
void s5p_dp_wait_hw_link_training_done(struct s5p_dp_device *dp);
|
||||
|
||||
/* startup and init */
|
||||
struct exynos5_fimd_panel;
|
||||
void fb_init(unsigned long int fb_size, void *lcdbase,
|
||||
struct exynos5_fimd_panel *pd);
|
||||
int dp_controller_init(struct s5p_dp_device *dp_device);
|
||||
int lcd_ctrl_init(unsigned long int fb_size,
|
||||
struct exynos5_fimd_panel *panel_data, void *lcdbase);
|
||||
#endif /* CPU_SAMSUNG_EXYNOS5420_DP_CORE_H */
|
|
@ -1,503 +0,0 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* Samsung DP (Display port) register interface driver. */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <arch/io.h>
|
||||
#include <delay.h>
|
||||
#include <timer.h>
|
||||
#include "clk.h"
|
||||
#include "cpu.h"
|
||||
#include "periph.h"
|
||||
#include "dp.h"
|
||||
#include "fimd.h"
|
||||
#include "dp-core.h"
|
||||
|
||||
void s5p_dp_reset(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
writel(RESET_DP_TX, &base->dp_tx_sw_reset);
|
||||
|
||||
/* Stop Video */
|
||||
clrbits_le32(&base->video_ctl_1, VIDEO_EN);
|
||||
clrbits_le32(&base->video_ctl_1, HDCP_VIDEO_MUTE);
|
||||
|
||||
reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
|
||||
AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
|
||||
HDCP_FUNC_EN_N | SW_FUNC_EN_N;
|
||||
writel(reg, &base->func_en_1);
|
||||
|
||||
reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
|
||||
SERDES_FIFO_FUNC_EN_N |
|
||||
LS_CLK_DOMAIN_FUNC_EN_N;
|
||||
writel(reg, &base->func_en_2);
|
||||
|
||||
udelay(20);
|
||||
|
||||
reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
|
||||
LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
|
||||
|
||||
writel(reg, &base->lane_map);
|
||||
|
||||
writel(0x0, &base->sys_ctl_1);
|
||||
writel(0x40, &base->sys_ctl_2);
|
||||
writel(0x0, &base->sys_ctl_3);
|
||||
writel(0x0, &base->sys_ctl_4);
|
||||
|
||||
writel(0x0, &base->pkt_send_ctl);
|
||||
writel(0x0, &base->dp_hdcp_ctl);
|
||||
|
||||
writel(0x5e, &base->dp_hpd_deglitch_l);
|
||||
writel(0x1a, &base->dp_hpd_deglitch_h);
|
||||
|
||||
writel(0x10, &base->dp_debug_ctl);
|
||||
|
||||
writel(0x0, &base->dp_phy_test);
|
||||
|
||||
writel(0x0, &base->dp_video_fifo_thrd);
|
||||
writel(0x20, &base->dp_audio_margin);
|
||||
|
||||
writel(0x4, &base->m_vid_gen_filter_th);
|
||||
writel(0x2, &base->m_aud_gen_filter_th);
|
||||
|
||||
writel(0x00000101, &base->soc_general_ctl);
|
||||
|
||||
/* Set Analog Parameters */
|
||||
writel(0x10, &base->analog_ctl_1);
|
||||
writel(0x0C, &base->analog_ctl_2);
|
||||
writel(0x85, &base->analog_ctl_3);
|
||||
writel(0x66, &base->pll_filter_ctl_1);
|
||||
writel(0x0, &base->tx_amp_tuning_ctl);
|
||||
|
||||
/* Set interrupt pin assertion polarity as high */
|
||||
writel(INT_POL0 | INT_POL1, &base->int_ctl);
|
||||
|
||||
/* Clear pending regisers */
|
||||
writel(0xff, &base->common_int_sta_1);
|
||||
writel(0x4f, &base->common_int_sta_2);
|
||||
writel(0xe0, &base->common_int_sta_3);
|
||||
writel(0xe7, &base->common_int_sta_4);
|
||||
writel(0x63, &base->dp_int_sta);
|
||||
|
||||
/* 0:mask,1: unmask */
|
||||
writel(0x00, &base->common_int_mask_1);
|
||||
writel(0x00, &base->common_int_mask_2);
|
||||
writel(0x00, &base->common_int_mask_3);
|
||||
writel(0x00, &base->common_int_mask_4);
|
||||
writel(0x00, &base->int_sta_mask);
|
||||
}
|
||||
|
||||
unsigned int s5p_dp_get_pll_lock_status(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(&dp->base->dp_debug_ctl);
|
||||
if (reg & PLL_LOCK)
|
||||
return PLL_LOCKED;
|
||||
else
|
||||
return PLL_UNLOCKED;
|
||||
}
|
||||
|
||||
int s5p_dp_init_analog_func(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct mono_time current, end;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
writel(0x00, &base->dp_phy_pd);
|
||||
|
||||
reg = PLL_LOCK_CHG;
|
||||
writel(reg, &base->common_int_sta_1);
|
||||
|
||||
clrbits_le32(&base->dp_debug_ctl, (F_PLL_LOCK | PLL_LOCK_CTRL));
|
||||
|
||||
/* Power up PLL */
|
||||
if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
|
||||
|
||||
clrbits_le32(&base->dp_pll_ctl, DP_PLL_PD);
|
||||
|
||||
timer_monotonic_get(¤t);
|
||||
end = current;
|
||||
mono_time_add_msecs(&end, PLL_LOCK_TIMEOUT);
|
||||
|
||||
while (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
|
||||
if (mono_time_after(¤t, &end)) {
|
||||
printk(BIOS_ERR, "%s: PLL is not locked\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
timer_monotonic_get(¤t);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable Serdes FIFO function and Link symbol clock domain module */
|
||||
clrbits_le32(&base->func_en_2, (SERDES_FIFO_FUNC_EN_N |
|
||||
LS_CLK_DOMAIN_FUNC_EN_N | AUX_FUNC_EN_N));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s5p_dp_init_aux(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
/* Clear inerrupts related to AUX channel */
|
||||
reg = RPLY_RECEIV | AUX_ERR;
|
||||
writel(reg, &base->dp_int_sta);
|
||||
|
||||
/* Disable AUX channel module */
|
||||
setbits_le32(&base->func_en_2, AUX_FUNC_EN_N);
|
||||
|
||||
/* Disable AUX transaction H/W retry */
|
||||
reg = (3 & AUX_BIT_PERIOD_MASK) << AUX_BIT_PERIOD_SHIFT;
|
||||
reg |= (0 & AUX_HW_RETRY_COUNT_MASK) << AUX_HW_RETRY_COUNT_SHIFT;
|
||||
reg |= (AUX_HW_RETRY_INTERVAL_600_US << AUX_HW_RETRY_INTERVAL_SHIFT);
|
||||
writel(reg, &base->aux_hw_retry_ctl) ;
|
||||
|
||||
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
|
||||
reg = DEFER_CTRL_EN;
|
||||
reg |= (1 & DEFER_COUNT_MASK) << DEFER_COUNT_SHIFT;
|
||||
writel(reg, &base->aux_ch_defer_dtl);
|
||||
|
||||
/* Enable AUX channel module */
|
||||
clrbits_le32(&base->func_en_2, AUX_FUNC_EN_N);
|
||||
}
|
||||
|
||||
int s5p_dp_start_aux_transaction(struct s5p_dp_device *dp)
|
||||
{
|
||||
int reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
/* Enable AUX CH operation */
|
||||
setbits_le32(&base->aux_ch_ctl_2, AUX_EN);
|
||||
|
||||
/* Is AUX CH command reply received? */
|
||||
reg = readl(&base->dp_int_sta);
|
||||
while (!(reg & RPLY_RECEIV))
|
||||
reg = readl(&base->dp_int_sta);
|
||||
|
||||
/* Clear interrupt source for AUX CH command reply */
|
||||
writel(RPLY_RECEIV, &base->dp_int_sta);
|
||||
|
||||
/* Clear interrupt source for AUX CH access error */
|
||||
reg = readl(&base->dp_int_sta);
|
||||
if (reg & AUX_ERR) {
|
||||
printk(BIOS_ERR, "%s: AUX_ERR encountered, dp_int_sta: "
|
||||
"0x%02x\n", __func__, reg);
|
||||
writel(AUX_ERR, &base->dp_int_sta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check AUX CH error access status */
|
||||
reg = readl(&base->dp_int_sta);
|
||||
if ((reg & AUX_STATUS_MASK) != 0) {
|
||||
printk(BIOS_ERR, "AUX CH error happens: %d\n\n",
|
||||
reg & AUX_STATUS_MASK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s5p_dp_write_byte_to_dpcd(struct s5p_dp_device *dp,
|
||||
unsigned int reg_addr,
|
||||
unsigned char data)
|
||||
{
|
||||
u32 reg;
|
||||
int i;
|
||||
int retval;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
for (i = 0; i < MAX_AUX_RETRY_COUNT; i++) {
|
||||
/* Clear AUX CH data buffer */
|
||||
writel(BUF_CLR, &base->buf_data_ctl);
|
||||
|
||||
/* Select DPCD device address */
|
||||
reg = reg_addr >> AUX_ADDR_7_0_SHIFT;
|
||||
reg &= AUX_ADDR_7_0_MASK;
|
||||
writel(reg, &base->aux_addr_7_0);
|
||||
reg = reg_addr >> AUX_ADDR_15_8_SHIFT;
|
||||
reg &= AUX_ADDR_15_8_MASK;
|
||||
writel(reg, &base->aux_addr_15_8);
|
||||
reg = reg_addr >> AUX_ADDR_19_16_SHIFT;
|
||||
reg &= AUX_ADDR_19_16_MASK;
|
||||
writel(reg, &base->aux_addr_19_16);
|
||||
|
||||
/* Write data buffer */
|
||||
reg = (unsigned int)data;
|
||||
writel(reg, &base->buf_data_0);
|
||||
|
||||
/*
|
||||
* Set DisplayPort transaction and write 1 byte
|
||||
* If bit 3 is 1, DisplayPort transaction.
|
||||
* If Bit 3 is 0, I2C transaction.
|
||||
*/
|
||||
reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
|
||||
writel(reg, &base->aux_ch_ctl_1);
|
||||
|
||||
/* Start AUX transaction */
|
||||
retval = s5p_dp_start_aux_transaction(dp);
|
||||
if (retval == 0)
|
||||
break;
|
||||
else
|
||||
printk(BIOS_DEBUG, "Aux Transaction fail!\n");
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int s5p_dp_read_byte_from_dpcd(struct s5p_dp_device *dp,
|
||||
unsigned int reg_addr,
|
||||
unsigned char *data)
|
||||
{
|
||||
u32 reg;
|
||||
int i;
|
||||
int retval;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
for (i = 0; i < MAX_AUX_RETRY_COUNT; i++) {
|
||||
/* Clear AUX CH data buffer */
|
||||
writel(BUF_CLR, &base->buf_data_ctl);
|
||||
|
||||
/* Select DPCD device address */
|
||||
reg = reg_addr >> AUX_ADDR_7_0_SHIFT;
|
||||
reg &= AUX_ADDR_7_0_MASK;
|
||||
writel(reg, &base->aux_addr_7_0);
|
||||
reg = reg_addr >> AUX_ADDR_15_8_SHIFT;
|
||||
reg &= AUX_ADDR_15_8_MASK;
|
||||
writel(reg, &base->aux_addr_15_8);
|
||||
reg = reg_addr >> AUX_ADDR_19_16_SHIFT;
|
||||
reg &= AUX_ADDR_19_16_MASK;
|
||||
writel(reg, &base->aux_addr_19_16);
|
||||
|
||||
/*
|
||||
* Set DisplayPort transaction and read 1 byte
|
||||
* If bit 3 is 1, DisplayPort transaction.
|
||||
* If Bit 3 is 0, I2C transaction.
|
||||
*/
|
||||
reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
|
||||
writel(reg, &base->aux_ch_ctl_1);
|
||||
|
||||
/* Start AUX transaction */
|
||||
retval = s5p_dp_start_aux_transaction(dp);
|
||||
if (retval == 0)
|
||||
break;
|
||||
else
|
||||
printk(BIOS_DEBUG, "Aux Transaction fail!\n");
|
||||
}
|
||||
|
||||
/* Read data buffer */
|
||||
if (!retval) {
|
||||
reg = readl(&base->buf_data_0);
|
||||
*data = (unsigned char)(reg & 0xff);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void s5p_dp_init_video(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
|
||||
writel(reg, &base->common_int_sta_1);
|
||||
|
||||
reg = 0x0;
|
||||
writel(reg, &base->sys_ctl_1);
|
||||
|
||||
reg = (4 & CHA_CRI_MASK) << CHA_CRI_SHIFT;
|
||||
reg |= CHA_CTRL;
|
||||
writel(reg, &base->sys_ctl_2);
|
||||
|
||||
reg = 0x0;
|
||||
writel(reg, &base->sys_ctl_3);
|
||||
}
|
||||
|
||||
void s5p_dp_set_video_color_format(struct s5p_dp_device *dp,
|
||||
unsigned int color_depth,
|
||||
unsigned int color_space,
|
||||
unsigned int dynamic_range,
|
||||
unsigned int coeff)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
/* Configure the input color depth, color space, dynamic range */
|
||||
reg = (dynamic_range << IN_D_RANGE_SHIFT) |
|
||||
(color_depth << IN_BPC_SHIFT) |
|
||||
(color_space << IN_COLOR_F_SHIFT);
|
||||
writel(reg, &base->video_ctl_2);
|
||||
|
||||
/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
|
||||
reg = readl(&base->video_ctl_3);
|
||||
reg &= ~IN_YC_COEFFI_MASK;
|
||||
if (coeff)
|
||||
reg |= IN_YC_COEFFI_ITU709;
|
||||
else
|
||||
reg |= IN_YC_COEFFI_ITU601;
|
||||
writel(reg, &base->video_ctl_3);
|
||||
}
|
||||
|
||||
int s5p_dp_is_slave_video_stream_clock_on(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
reg = readl(&base->sys_ctl_1);
|
||||
writel(reg, &base->sys_ctl_1);
|
||||
|
||||
reg = readl(&base->sys_ctl_1);
|
||||
|
||||
if (!(reg & DET_STA))
|
||||
return -1;
|
||||
|
||||
reg = readl(&base->sys_ctl_2);
|
||||
writel(reg, &base->sys_ctl_2);
|
||||
|
||||
reg = readl(&base->sys_ctl_2);
|
||||
|
||||
if (reg & CHA_STA) {
|
||||
printk(BIOS_DEBUG, "Input stream clk is changing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s5p_dp_set_video_cr_mn(struct s5p_dp_device *dp,
|
||||
enum clock_recovery_m_value_type type,
|
||||
unsigned int m_value,
|
||||
unsigned int n_value)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
if (type == REGISTER_M) {
|
||||
setbits_le32(&base->sys_ctl_4, FIX_M_VID);
|
||||
|
||||
reg = m_value >> M_VID_0_VALUE_SHIFT;
|
||||
writel(reg, &base->m_vid_0);
|
||||
|
||||
reg = (m_value >> M_VID_1_VALUE_SHIFT);
|
||||
writel(reg, &base->m_vid_1);
|
||||
|
||||
reg = (m_value >> M_VID_2_VALUE_SHIFT);
|
||||
writel(reg, &base->m_vid_2);
|
||||
|
||||
reg = n_value >> N_VID_0_VALUE_SHIFT;
|
||||
writel(reg, &base->n_vid_0);
|
||||
|
||||
reg = (n_value >> N_VID_1_VALUE_SHIFT);
|
||||
writel(reg, &base->n_vid_1);
|
||||
|
||||
reg = (n_value >> N_VID_2_VALUE_SHIFT);
|
||||
writel(reg, &base->n_vid_2);
|
||||
} else {
|
||||
clrbits_le32(&base->sys_ctl_4, FIX_M_VID);
|
||||
|
||||
writel(0x00, &base->n_vid_0);
|
||||
writel(0x80, &base->n_vid_1);
|
||||
writel(0x00, &base->n_vid_2);
|
||||
}
|
||||
}
|
||||
|
||||
void s5p_dp_enable_video_master(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
reg = readl(&base->soc_general_ctl);
|
||||
reg &= ~VIDEO_MODE_MASK;
|
||||
reg |= VIDEO_MODE_SLAVE_MODE;
|
||||
writel(reg, &base->soc_general_ctl);
|
||||
}
|
||||
|
||||
int s5p_dp_is_video_stream_on(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg, i = 0;
|
||||
struct mono_time current, end;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
/* Wait for 4 VSYNC_DET interrupts */
|
||||
timer_monotonic_get(¤t);
|
||||
end = current;
|
||||
mono_time_add_msecs(&end, STREAM_ON_TIMEOUT);
|
||||
|
||||
do {
|
||||
reg = readl(&base->common_int_sta_1);
|
||||
if (reg & VSYNC_DET) {
|
||||
i++;
|
||||
writel(reg | VSYNC_DET, &base->common_int_sta_1);
|
||||
}
|
||||
if (i == 4)
|
||||
break;
|
||||
timer_monotonic_get(¤t);
|
||||
} while (mono_time_before(¤t, &end));
|
||||
|
||||
if (i != 4) {
|
||||
printk(BIOS_DEBUG, "s5p_dp_is_video_stream_on timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s5p_dp_config_video_slave_mode(struct s5p_dp_device *dp,
|
||||
struct video_info *video_info)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
reg = readl(&base->func_en_1);
|
||||
reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
|
||||
reg |= MASTER_VID_FUNC_EN_N;
|
||||
writel(reg, &base->func_en_1);
|
||||
|
||||
reg = readl(&base->video_ctl_10);
|
||||
reg &= ~INTERACE_SCAN_CFG;
|
||||
reg |= (video_info->interlaced << 2);
|
||||
writel(reg, &base->video_ctl_10);
|
||||
|
||||
reg = readl(&base->video_ctl_10);
|
||||
reg &= ~VSYNC_POLARITY_CFG;
|
||||
reg |= (video_info->v_sync_polarity << 1);
|
||||
writel(reg, &base->video_ctl_10);
|
||||
|
||||
reg = readl(&base->video_ctl_10);
|
||||
reg &= ~HSYNC_POLARITY_CFG;
|
||||
reg |= (video_info->h_sync_polarity << 0);
|
||||
writel(reg, &base->video_ctl_10);
|
||||
|
||||
reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
|
||||
writel(reg, &base->soc_general_ctl);
|
||||
}
|
||||
|
||||
void s5p_dp_wait_hw_link_training_done(struct s5p_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
reg = readl(&base->dp_hw_link_training);
|
||||
while (reg & HW_TRAINING_EN)
|
||||
reg = readl(&base->dp_hw_link_training);
|
||||
}
|
|
@ -0,0 +1,906 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
* Author: Donghwa Lee <dh09.lee@samsung.com>
|
||||
*
|
||||
* 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; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <arch/io.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <timer.h>
|
||||
#include <delay.h>
|
||||
#include <console/console.h>
|
||||
#include <lib.h>
|
||||
#include "timer.h"
|
||||
#include "cpu.h"
|
||||
#include "power.h"
|
||||
#include "sysreg.h"
|
||||
#include <drivers/maxim/max77686/max77686.h>
|
||||
|
||||
#include "device/i2c.h"
|
||||
#include "i2c.h"
|
||||
#include "fimd.h"
|
||||
#include "dp.h"
|
||||
|
||||
/*
|
||||
* Here is the rough outline of how we bring up the display:
|
||||
* 1. Upon power-on Sink generates a hot plug detection pulse thru HPD
|
||||
* 2. Source determines video mode by reading DPCD receiver capability field
|
||||
* (DPCD 00000h to 0000Dh) including eDP CP capability register (DPCD
|
||||
* 0000Dh).
|
||||
* 3. Sink replies DPCD receiver capability field.
|
||||
* 4. Source starts EDID read thru I2C-over-AUX.
|
||||
* 5. Sink replies EDID thru I2C-over-AUX.
|
||||
* 6. Source determines link configuration, such as MAX_LINK_RATE and
|
||||
* MAX_LANE_COUNT. Source also determines which type of eDP Authentication
|
||||
* method to use and writes DPCD link configuration field (DPCD 00100h to
|
||||
* 0010Ah) including eDP configuration set (DPCD 0010Ah).
|
||||
* 7. Source starts link training. Sink does clock recovery and equalization.
|
||||
* 8. Source reads DPCD link status field (DPCD 00200h to 0020Bh).
|
||||
* 9. Sink replies DPCD link status field. If main link is not stable, Source
|
||||
* repeats Step 7.
|
||||
* 10. Source sends MSA (Main Stream Attribute) data. Sink extracts video
|
||||
* parameters and recovers stream clock.
|
||||
* 11. Source sends video data.
|
||||
*/
|
||||
|
||||
|
||||
static int exynos_dp_init_dp(void)
|
||||
{
|
||||
int ret;
|
||||
exynos_dp_reset();
|
||||
|
||||
/* SW defined function Normal operation */
|
||||
exynos_dp_enable_sw_func(DP_ENABLE);
|
||||
|
||||
ret = exynos_dp_init_analog_func();
|
||||
if (ret != EXYNOS_DP_SUCCESS)
|
||||
return ret;
|
||||
|
||||
exynos_dp_init_hpd();
|
||||
exynos_dp_init_aux();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
|
||||
{
|
||||
int i;
|
||||
unsigned char sum = 0;
|
||||
|
||||
for (i = 0; i < EDID_BLOCK_LENGTH; i++)
|
||||
sum = sum + edid_data[i];
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_read_edid(void)
|
||||
{
|
||||
unsigned char edid[EDID_BLOCK_LENGTH * 2];
|
||||
unsigned int extend_block = 0;
|
||||
unsigned char sum;
|
||||
unsigned char test_vector;
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
* EDID device address is 0x50.
|
||||
* However, if necessary, you must have set upper address
|
||||
* into E-EDID in I2C device, 0x30.
|
||||
*/
|
||||
|
||||
/* Read Extension Flag, Number of 128-byte EDID extension blocks */
|
||||
if (exynos_dp_read_byte_from_i2c
|
||||
(I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG, &extend_block))
|
||||
return -1;
|
||||
|
||||
if (extend_block > 0) {
|
||||
/* Read EDID data */
|
||||
retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR,
|
||||
EDID_HEADER_PATTERN,
|
||||
EDID_BLOCK_LENGTH,
|
||||
&edid[EDID_HEADER_PATTERN]);
|
||||
|
||||
if (retval != 0) {
|
||||
printk(BIOS_ERR, "DP EDID Read failed!\n");
|
||||
return -1;
|
||||
}
|
||||
sum = exynos_dp_calc_edid_check_sum(edid);
|
||||
if (sum != 0) {
|
||||
printk(BIOS_ERR, "DP EDID bad checksum!\n");
|
||||
return -1;
|
||||
}
|
||||
/* Read additional EDID data */
|
||||
retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR,
|
||||
EDID_BLOCK_LENGTH,
|
||||
EDID_BLOCK_LENGTH,
|
||||
&edid[EDID_BLOCK_LENGTH]);
|
||||
if (retval != 0) {
|
||||
printk(BIOS_ERR, "DP EDID Read failed!\n");
|
||||
return -1;
|
||||
}
|
||||
sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
|
||||
if (sum != 0) {
|
||||
printk(BIOS_ERR, "DP EDID bad checksum!\n");
|
||||
return -1;
|
||||
}
|
||||
exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST,
|
||||
&test_vector);
|
||||
if (test_vector & DPCD_TEST_EDID_READ) {
|
||||
exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM,
|
||||
edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
|
||||
exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE,
|
||||
DPCD_TEST_EDID_CHECKSUM_WRITE);
|
||||
}
|
||||
} else {
|
||||
/* Read EDID data */
|
||||
retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR,
|
||||
EDID_HEADER_PATTERN,
|
||||
EDID_BLOCK_LENGTH,
|
||||
&edid[EDID_HEADER_PATTERN]);
|
||||
|
||||
if (retval != 0) {
|
||||
printk(BIOS_ERR, "DP EDID Read failed!\n");
|
||||
return -1;
|
||||
}
|
||||
sum = exynos_dp_calc_edid_check_sum(edid);
|
||||
if (sum != 0) {
|
||||
printk(BIOS_ERR, "DP EDID bad checksum!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST,
|
||||
&test_vector);
|
||||
if (test_vector & DPCD_TEST_EDID_READ) {
|
||||
exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM,
|
||||
edid[EDID_CHECKSUM]);
|
||||
exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE,
|
||||
DPCD_TEST_EDID_CHECKSUM_WRITE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_handle_edid(struct edp_device_info *edp_info)
|
||||
{
|
||||
unsigned char buf[12];
|
||||
unsigned int ret;
|
||||
unsigned char temp;
|
||||
unsigned char retry_cnt;
|
||||
unsigned char dpcd_rev[16];
|
||||
unsigned char lane_bw[16];
|
||||
unsigned char lane_cnt[16];
|
||||
|
||||
memset(dpcd_rev, 0, sizeof(dpcd_rev));
|
||||
memset(lane_bw, 0, sizeof(lane_bw));
|
||||
memset(lane_cnt, 0, sizeof(lane_cnt));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
retry_cnt = 5;
|
||||
while (retry_cnt) {
|
||||
/* Read DPCD 0x0000-0x000b */
|
||||
ret = exynos_dp_read_bytes_from_dpcd(DPCD_DPCD_REV, 12,
|
||||
buf);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
if (retry_cnt == 0) {
|
||||
printk(BIOS_ERR, "DP read_byte_from_dpcd() failed\n");
|
||||
return ret;
|
||||
}
|
||||
retry_cnt--;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/* */
|
||||
temp = buf[DPCD_DPCD_REV];
|
||||
if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11)
|
||||
edp_info->dpcd_rev = temp;
|
||||
else {
|
||||
printk(BIOS_ERR, "DP Wrong DPCD Rev : %x\n", temp);
|
||||
return -1;
|
||||
}
|
||||
temp = buf[DPCD_MAX_LINK_RATE];
|
||||
if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70)
|
||||
edp_info->lane_bw = temp;
|
||||
else {
|
||||
printk(BIOS_ERR, "DP Wrong MAX LINK RATE : %x\n", temp);
|
||||
return -1;
|
||||
}
|
||||
/*Refer VESA Display Port Stnadard Ver1.1a Page 120 */
|
||||
if (edp_info->dpcd_rev == DP_DPCD_REV_11) {
|
||||
temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f;
|
||||
if (buf[DPCD_MAX_LANE_COUNT] & 0x80)
|
||||
edp_info->dpcd_efc = 1;
|
||||
else
|
||||
edp_info->dpcd_efc = 0;
|
||||
} else {
|
||||
temp = buf[DPCD_MAX_LANE_COUNT];
|
||||
edp_info->dpcd_efc = 0;
|
||||
}
|
||||
|
||||
if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 ||
|
||||
temp == DP_LANE_CNT_4) {
|
||||
edp_info->lane_cnt = temp;
|
||||
} else {
|
||||
printk(BIOS_ERR, "DP Wrong MAX LANE COUNT : %x\n", temp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = exynos_dp_read_edid();
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP exynos_dp_read_edid() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_dp_init_training(void)
|
||||
{
|
||||
/*
|
||||
* MACRO_RST must be applied after the PLL_LOCK to avoid
|
||||
* the DP inter pair skew issue for at least 10 us
|
||||
*/
|
||||
exynos_dp_reset_macro();
|
||||
|
||||
/* All DP analog module power up */
|
||||
exynos_dp_set_analog_power_down(POWER_ALL, 0);
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_link_start(struct edp_device_info *edp_info)
|
||||
{
|
||||
unsigned char buf[5];
|
||||
unsigned int ret;
|
||||
|
||||
edp_info->lt_info.lt_status = DP_LT_CR;
|
||||
edp_info->lt_info.ep_loop = 0;
|
||||
edp_info->lt_info.cr_loop[0] = 0;
|
||||
edp_info->lt_info.cr_loop[1] = 0;
|
||||
edp_info->lt_info.cr_loop[2] = 0;
|
||||
edp_info->lt_info.cr_loop[3] = 0;
|
||||
|
||||
/* Set sink to D0 (Sink Not Ready) mode. */
|
||||
ret = exynos_dp_write_byte_to_dpcd(DPCD_SINK_POWER_STATE,
|
||||
DPCD_SET_POWER_STATE_D0);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP write_dpcd_byte failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set link rate and count as you want to establish*/
|
||||
exynos_dp_set_link_bandwidth(edp_info->lane_bw);
|
||||
exynos_dp_set_lane_count(edp_info->lane_cnt);
|
||||
|
||||
/* Setup RX configuration */
|
||||
buf[0] = edp_info->lane_bw;
|
||||
buf[1] = edp_info->lane_cnt;
|
||||
|
||||
ret = exynos_dp_write_bytes_to_dpcd(DPCD_LINK_BW_SET, 2,
|
||||
buf);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP write_dpcd_byte failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_dp_set_lane_pre_emphasis(PRE_EMPHASIS_LEVEL_0,
|
||||
edp_info->lane_cnt);
|
||||
|
||||
/* Set training pattern 1 */
|
||||
exynos_dp_set_training_pattern(TRAINING_PTN1);
|
||||
|
||||
/* Set RX training pattern */
|
||||
buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1;
|
||||
|
||||
buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
|
||||
DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
|
||||
buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
|
||||
DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
|
||||
buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
|
||||
DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
|
||||
buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
|
||||
DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
|
||||
|
||||
ret = exynos_dp_write_bytes_to_dpcd(DPCD_TRAINING_PATTERN_SET,
|
||||
5, buf);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP write_dpcd_byte failed\n");
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_training_pattern_dis(void)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
exynos_dp_set_training_pattern(DP_NONE);
|
||||
|
||||
ret = exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET,
|
||||
DPCD_TRAINING_PATTERN_DISABLED);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP requst_link_traninig_req failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_enable_rx_to_enhanced_mode(unsigned char enable)
|
||||
{
|
||||
unsigned char data;
|
||||
unsigned int ret;
|
||||
|
||||
ret = exynos_dp_read_byte_from_dpcd(DPCD_LANE_COUNT_SET,
|
||||
&data);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read_from_dpcd failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data);
|
||||
else
|
||||
data = DPCD_LN_COUNT_SET(data);
|
||||
|
||||
ret = exynos_dp_write_byte_to_dpcd(DPCD_LANE_COUNT_SET,
|
||||
data);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP write_to_dpcd failed\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_set_enhanced_mode(unsigned char enhance_mode)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
ret = exynos_dp_enable_rx_to_enhanced_mode(enhance_mode);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP rx_enhance_mode failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
exynos_dp_enable_enhanced_mode(enhance_mode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_dp_read_dpcd_lane_stat(struct edp_device_info *edp_info,
|
||||
unsigned char *status)
|
||||
{
|
||||
unsigned int ret, i;
|
||||
unsigned char buf[2];
|
||||
unsigned char lane_stat[DP_LANE_CNT_4] = {0,};
|
||||
const unsigned char shift_val[] = {0, 4, 0, 4};
|
||||
|
||||
ret = exynos_dp_read_bytes_from_dpcd(DPCD_LANE0_1_STATUS, 2, buf);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read lane status failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < edp_info->lane_cnt; i++) {
|
||||
lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f;
|
||||
if (lane_stat[0] != lane_stat[i]) {
|
||||
printk(BIOS_ERR, "Wrong lane status\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*status = lane_stat[0];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_read_dpcd_adj_req(unsigned char lane_num,
|
||||
unsigned char *sw, unsigned char *em)
|
||||
{
|
||||
const unsigned char shift_val[] = {0, 4, 0, 4};
|
||||
unsigned int ret;
|
||||
unsigned char buf;
|
||||
unsigned int dpcd_addr;
|
||||
|
||||
/*lane_num value is used as arry index, so this range 0 ~ 3 */
|
||||
dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2);
|
||||
|
||||
ret = exynos_dp_read_byte_from_dpcd(dpcd_addr, &buf);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read adjust request failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sw = ((buf >> shift_val[lane_num]) & 0x03);
|
||||
*em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_dp_equalizer_err_link(struct edp_device_info *edp_info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = exynos_dp_training_pattern_dis();
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP training_patter_disable() failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
}
|
||||
|
||||
ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP set_enhanced_mode() failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_dp_reduce_link_rate(struct edp_device_info *edp_info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (edp_info->lane_bw == DP_LANE_BW_2_70) {
|
||||
edp_info->lane_bw = DP_LANE_BW_1_62;
|
||||
printk(BIOS_ERR, "DP Change lane bw to 1.62Gbps\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_START;
|
||||
ret = EXYNOS_DP_SUCCESS;
|
||||
} else {
|
||||
ret = exynos_dp_training_pattern_dis();
|
||||
if (ret != EXYNOS_DP_SUCCESS)
|
||||
printk(BIOS_ERR, "DP training_patter_disable() failed\n");
|
||||
|
||||
ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc);
|
||||
if (ret != EXYNOS_DP_SUCCESS)
|
||||
printk(BIOS_ERR, "DP set_enhanced_mode() failed\n");
|
||||
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_process_clock_recovery(struct edp_device_info
|
||||
*edp_info)
|
||||
{
|
||||
unsigned int ret;
|
||||
unsigned char lane_stat;
|
||||
unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, };
|
||||
unsigned int i;
|
||||
unsigned char adj_req_sw;
|
||||
unsigned char adj_req_em;
|
||||
unsigned char buf[5];
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read lane status failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (lane_stat & DP_LANE_STAT_CR_DONE) {
|
||||
printk(BIOS_DEBUG,"DP clock Recovery training succeed\n");
|
||||
exynos_dp_set_training_pattern(TRAINING_PTN2);
|
||||
|
||||
for (i = 0; i < edp_info->lane_cnt; i++) {
|
||||
ret = exynos_dp_read_dpcd_adj_req(i, &adj_req_sw,
|
||||
&adj_req_em);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
lt_ctl_val[i] = 0;
|
||||
lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
|
||||
|
||||
if ((adj_req_sw == VOLTAGE_LEVEL_3)
|
||||
|| (adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
|
||||
lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
|
||||
MAX_PRE_EMPHASIS_REACH_3;
|
||||
}
|
||||
exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i);
|
||||
}
|
||||
|
||||
buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2;
|
||||
buf[1] = lt_ctl_val[0];
|
||||
buf[2] = lt_ctl_val[1];
|
||||
buf[3] = lt_ctl_val[2];
|
||||
buf[4] = lt_ctl_val[3];
|
||||
|
||||
ret = exynos_dp_write_bytes_to_dpcd(
|
||||
DPCD_TRAINING_PATTERN_SET, 5, buf);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP write training pattern1 failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
return ret;
|
||||
} else
|
||||
edp_info->lt_info.lt_status = DP_LT_ET;
|
||||
} else {
|
||||
for (i = 0; i < edp_info->lane_cnt; i++) {
|
||||
lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(i);
|
||||
ret = exynos_dp_read_dpcd_adj_req(i,
|
||||
&adj_req_sw, &adj_req_em);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read adj req failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
|
||||
(adj_req_em == PRE_EMPHASIS_LEVEL_3))
|
||||
ret = exynos_dp_reduce_link_rate(edp_info);
|
||||
|
||||
if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) ==
|
||||
adj_req_sw) &&
|
||||
(PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) ==
|
||||
adj_req_em)) {
|
||||
edp_info->lt_info.cr_loop[i]++;
|
||||
if (edp_info->lt_info.cr_loop[i] == MAX_CR_LOOP)
|
||||
ret = exynos_dp_reduce_link_rate(
|
||||
edp_info);
|
||||
}
|
||||
|
||||
lt_ctl_val[i] = 0;
|
||||
lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
|
||||
|
||||
if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
|
||||
(adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
|
||||
lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
|
||||
MAX_PRE_EMPHASIS_REACH_3;
|
||||
}
|
||||
exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i);
|
||||
}
|
||||
|
||||
ret = exynos_dp_write_bytes_to_dpcd(
|
||||
DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP write training pattern2 failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_process_equalizer_training(struct edp_device_info
|
||||
*edp_info)
|
||||
{
|
||||
unsigned int ret;
|
||||
unsigned char lane_stat, adj_req_sw, adj_req_em, i;
|
||||
unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,};
|
||||
unsigned char interlane_aligned = 0;
|
||||
unsigned char f_bw;
|
||||
unsigned char f_lane_cnt;
|
||||
unsigned char sink_stat;
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read lane status failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
printk(BIOS_DEBUG,"DP lane stat : %x\n", lane_stat);
|
||||
|
||||
if (lane_stat & DP_LANE_STAT_CR_DONE) {
|
||||
printk(BIOS_DEBUG, "DP_LANE_STAT_CR_DONE ok\n");
|
||||
ret = exynos_dp_read_byte_from_dpcd(DPCD_LN_ALIGN_UPDATED,
|
||||
&sink_stat);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
printk(BIOS_ERR, "DP read DPCD_LN_ALIGN_UPDATED failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE);
|
||||
printk(BIOS_DEBUG, "interlane_aligned: %d\n", interlane_aligned);
|
||||
printk(BIOS_DEBUG, "Check %d lanes\n", edp_info->lane_cnt);
|
||||
|
||||
for (i = 0; i < edp_info->lane_cnt; i++) {
|
||||
ret = exynos_dp_read_dpcd_adj_req(i,
|
||||
&adj_req_sw, &adj_req_em);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP read adj req 1 failed\n");
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
lt_ctl_val[i] = 0;
|
||||
lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
|
||||
|
||||
if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
|
||||
(adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
|
||||
lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3;
|
||||
lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3;
|
||||
}
|
||||
}
|
||||
|
||||
if (((lane_stat&DP_LANE_STAT_CE_DONE) &&
|
||||
(lane_stat&DP_LANE_STAT_SYM_LOCK))
|
||||
&& (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) {
|
||||
printk(BIOS_DEBUG,"DP Equalizer training succeed\n");
|
||||
|
||||
f_bw = exynos_dp_get_link_bandwidth();
|
||||
f_lane_cnt = exynos_dp_get_lane_count();
|
||||
|
||||
printk(BIOS_DEBUG,"DP final BandWidth : %x\n", f_bw);
|
||||
printk(BIOS_DEBUG,"DP final Lane Count : %x\n", f_lane_cnt);
|
||||
|
||||
edp_info->lt_info.lt_status = DP_LT_FINISHED;
|
||||
|
||||
exynos_dp_equalizer_err_link(edp_info);
|
||||
|
||||
} else {
|
||||
edp_info->lt_info.ep_loop++;
|
||||
|
||||
if (edp_info->lt_info.ep_loop > MAX_EQ_LOOP) {
|
||||
if (edp_info->lane_bw == DP_LANE_BW_2_70) {
|
||||
ret = exynos_dp_reduce_link_rate(
|
||||
edp_info);
|
||||
} else {
|
||||
edp_info->lt_info.lt_status =
|
||||
DP_LT_FAIL;
|
||||
exynos_dp_equalizer_err_link(edp_info);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < edp_info->lane_cnt; i++)
|
||||
exynos_dp_set_lanex_pre_emphasis(
|
||||
lt_ctl_val[i], i);
|
||||
|
||||
ret = exynos_dp_write_bytes_to_dpcd(
|
||||
DPCD_TRAINING_LANE0_SET,
|
||||
4, lt_ctl_val);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP set lt pattern failed\n");
|
||||
edp_info->lt_info.lt_status =
|
||||
DP_LT_FAIL;
|
||||
exynos_dp_equalizer_err_link(edp_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (edp_info->lane_bw == DP_LANE_BW_2_70) {
|
||||
ret = exynos_dp_reduce_link_rate(edp_info);
|
||||
} else {
|
||||
edp_info->lt_info.lt_status = DP_LT_FAIL;
|
||||
exynos_dp_equalizer_err_link(edp_info);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_sw_link_training(struct edp_device_info *edp_info)
|
||||
{
|
||||
/* the C compiler is almost smart enough to know this gets set.
|
||||
* But not quite.
|
||||
*/
|
||||
unsigned int ret = 0;
|
||||
int training_finished;
|
||||
|
||||
/* Turn off unnecessary lane */
|
||||
if (edp_info->lane_cnt == 1)
|
||||
exynos_dp_set_analog_power_down(CH1_BLOCK, 1);
|
||||
|
||||
training_finished = 0;
|
||||
|
||||
edp_info->lt_info.lt_status = DP_LT_START;
|
||||
|
||||
/* Process here */
|
||||
while (!training_finished) {
|
||||
switch (edp_info->lt_info.lt_status) {
|
||||
case DP_LT_START:
|
||||
ret = exynos_dp_link_start(edp_info);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP LT:link start failed\n");
|
||||
training_finished = 1;
|
||||
}
|
||||
break;
|
||||
case DP_LT_CR:
|
||||
ret = exynos_dp_process_clock_recovery(edp_info);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP LT:clock recovery failed\n");
|
||||
training_finished = 1;
|
||||
}
|
||||
break;
|
||||
case DP_LT_ET:
|
||||
ret = exynos_dp_process_equalizer_training(edp_info);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP LT:equalizer training failed\n");
|
||||
training_finished = 1;
|
||||
}
|
||||
break;
|
||||
case DP_LT_FINISHED:
|
||||
training_finished = 1;
|
||||
break;
|
||||
case DP_LT_FAIL:
|
||||
printk(BIOS_ERR,"DP: %s: DP_LT_FAIL: failed\n", __func__);
|
||||
training_finished = 1;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_set_link_train(struct edp_device_info *edp_info)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
exynos_dp_init_training();
|
||||
|
||||
ret = exynos_dp_sw_link_training(edp_info);
|
||||
if (ret != EXYNOS_DP_SUCCESS)
|
||||
printk(BIOS_ERR, "DP dp_sw_link_traning() failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_dp_enable_scramble(unsigned int enable)
|
||||
{
|
||||
unsigned char data;
|
||||
|
||||
if (enable) {
|
||||
exynos_dp_enable_scrambling(DP_ENABLE);
|
||||
|
||||
exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET,
|
||||
&data);
|
||||
exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET,
|
||||
(u8)(data & ~DPCD_SCRAMBLING_DISABLED));
|
||||
} else {
|
||||
exynos_dp_enable_scrambling(DP_DISABLE);
|
||||
exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET,
|
||||
&data);
|
||||
exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET,
|
||||
(u8)(data | DPCD_SCRAMBLING_DISABLED));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
unsigned int retry_cnt;
|
||||
|
||||
mdelay(1);
|
||||
|
||||
if (edp_info->video_info.master_mode) {
|
||||
printk(BIOS_ERR,
|
||||
"DP does not support master mode: bailing out\n");
|
||||
return -1;
|
||||
} else {
|
||||
/* debug slave */
|
||||
exynos_dp_config_video_slave_mode(&edp_info->video_info);
|
||||
}
|
||||
|
||||
exynos_dp_set_video_color_format(&edp_info->video_info);
|
||||
|
||||
ret = exynos_dp_get_pll_lock_status();
|
||||
if (ret != PLL_LOCKED) {
|
||||
printk(BIOS_ERR, "DP PLL is not locked yet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (edp_info->video_info.master_mode == 0) {
|
||||
retry_cnt = 10;
|
||||
while (retry_cnt) {
|
||||
ret = exynos_dp_is_slave_video_stream_clock_on();
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
if (retry_cnt == 0) {
|
||||
printk(BIOS_ERR, "DP stream_clock_on failed\n");
|
||||
return ret;
|
||||
}
|
||||
retry_cnt--;
|
||||
mdelay(1);
|
||||
} else {
|
||||
printk(BIOS_DEBUG, "DP stream_clock succeeds\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set to use the register calculated M/N video */
|
||||
exynos_dp_set_video_cr_mn(CALCULATED_M, 0, 0);
|
||||
|
||||
/* For video bist, Video timing must be generated by register
|
||||
* not clear if we still need this. We could take it out and it
|
||||
* might appear to work, then fail strangely.
|
||||
*/
|
||||
exynos_dp_set_video_timing_mode(VIDEO_TIMING_FROM_CAPTURE);
|
||||
|
||||
/* we need to be sure this is off. */
|
||||
exynos_dp_disable_video_bist();
|
||||
|
||||
/* Disable video mute */
|
||||
exynos_dp_enable_video_mute(DP_DISABLE);
|
||||
|
||||
/* Configure video Master or Slave mode */
|
||||
exynos_dp_enable_video_master(edp_info->video_info.master_mode);
|
||||
|
||||
/* Enable video */
|
||||
exynos_dp_start_video();
|
||||
|
||||
if (edp_info->video_info.master_mode == 0) {
|
||||
retry_cnt = 100;
|
||||
while (retry_cnt) {
|
||||
ret = exynos_dp_is_video_stream_on();
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
retry_cnt--;
|
||||
if (retry_cnt == 0) {
|
||||
printk(BIOS_ERR, "DP Timeout of video stream\n");
|
||||
}
|
||||
} else {
|
||||
printk(BIOS_DEBUG, "DP video stream is on\n");
|
||||
break;
|
||||
}
|
||||
mdelay(5);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int exynos_init_dp(struct edp_device_info *edp_info)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
|
||||
dp_phy_control(1);
|
||||
|
||||
ret = exynos_dp_init_dp();
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP exynos_dp_init_dp() failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = exynos_dp_handle_edid(edp_info);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "EDP handle_edid fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = exynos_dp_set_link_train(edp_info);
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "DP link training fail\n");
|
||||
return ret;
|
||||
}
|
||||
printk(BIOS_DEBUG, "EDP link training ok\n");
|
||||
|
||||
exynos_dp_enable_scramble(DP_ENABLE);
|
||||
exynos_dp_enable_rx_to_enhanced_mode(DP_ENABLE);
|
||||
exynos_dp_enable_enhanced_mode(DP_ENABLE);
|
||||
|
||||
exynos_dp_set_link_bandwidth(edp_info->lane_bw);
|
||||
exynos_dp_set_lane_count(edp_info->lane_cnt);
|
||||
|
||||
exynos_dp_init_video();
|
||||
ret = exynos_dp_config_video(edp_info);
|
||||
|
||||
if (ret != EXYNOS_DP_SUCCESS) {
|
||||
printk(BIOS_ERR, "Exynos DP init failed\n");
|
||||
return ret;
|
||||
}
|
||||
printk(BIOS_DEBUG, "Exynos DP init done\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,587 +0,0 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* LCD driver for Exynos */
|
||||
|
||||
#include <arch/io.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <timer.h>
|
||||
#include <delay.h>
|
||||
#include <console/console.h>
|
||||
#include "cpu.h"
|
||||
#include "power.h"
|
||||
#include "sysreg.h"
|
||||
#include <drivers/maxim/max77686/max77686.h>
|
||||
|
||||
#include "device/i2c.h"
|
||||
#include "i2c.h"
|
||||
#include "fimd.h"
|
||||
#include "dp.h"
|
||||
#include "dp-core.h"
|
||||
|
||||
/*
|
||||
* Here is the rough outline of how we bring up the display:
|
||||
* 1. Upon power-on Sink generates a hot plug detection pulse thru HPD
|
||||
* 2. Source determines video mode by reading DPCD receiver capability field
|
||||
* (DPCD 00000h to 0000Dh) including eDP CP capability register (DPCD
|
||||
* 0000Dh).
|
||||
* 3. Sink replies DPCD receiver capability field.
|
||||
* 4. Source starts EDID read thru I2C-over-AUX.
|
||||
* 5. Sink replies EDID thru I2C-over-AUX.
|
||||
* 6. Source determines link configuration, such as MAX_LINK_RATE and
|
||||
* MAX_LANE_COUNT. Source also determines which type of eDP Authentication
|
||||
* method to use and writes DPCD link configuration field (DPCD 00100h to
|
||||
* 0010Ah) including eDP configuration set (DPCD 0010Ah).
|
||||
* 7. Source starts link training. Sink does clock recovery and equalization.
|
||||
* 8. Source reads DPCD link status field (DPCD 00200h to 0020Bh).
|
||||
* 9. Sink replies DPCD link status field. If main link is not stable, Source
|
||||
* repeats Step 7.
|
||||
* 10. Source sends MSA (Main Stream Attribute) data. Sink extracts video
|
||||
* parameters and recovers stream clock.
|
||||
* 11. Source sends video data.
|
||||
*/
|
||||
|
||||
/* To help debug any init errors here, define a list of possible errors */
|
||||
enum {
|
||||
ERR_PLL_NOT_UNLOCKED = 2,
|
||||
ERR_VIDEO_CLOCK_BAD,
|
||||
ERR_VIDEO_STREAM_BAD,
|
||||
ERR_DPCD_READ_ERROR1, /* 5 */
|
||||
|
||||
ERR_DPCD_WRITE_ERROR1,
|
||||
ERR_DPCD_READ_ERROR2,
|
||||
ERR_DPCD_WRITE_ERROR2,
|
||||
ERR_INVALID_LANE,
|
||||
ERR_PLL_NOT_LOCKED, /* 10 */
|
||||
|
||||
ERR_PRE_EMPHASIS_LEVELS,
|
||||
ERR_LINK_RATE_ABNORMAL,
|
||||
ERR_MAX_LANE_COUNT_ABNORMAL,
|
||||
ERR_LINK_TRAINING_FAILURE,
|
||||
ERR_MISSING_DP_BASE, /* 15 */
|
||||
|
||||
ERR_NO_FDT_NODE,
|
||||
};
|
||||
/* ok, this is stupid, but we're going to leave the variables in here until we
|
||||
* know it works. One cleanup task at a time.
|
||||
*/
|
||||
enum stage_t {
|
||||
STAGE_START = 0,
|
||||
STAGE_LCD_VDD,
|
||||
STAGE_BRIDGE_SETUP,
|
||||
STAGE_BRIDGE_INIT,
|
||||
STAGE_BRIDGE_RESET,
|
||||
STAGE_HOTPLUG,
|
||||
STAGE_DP_CONTROLLER,
|
||||
STAGE_BACKLIGHT_VDD,
|
||||
STAGE_BACKLIGHT_PWM,
|
||||
STAGE_BACKLIGHT_EN,
|
||||
STAGE_DONE,
|
||||
};
|
||||
|
||||
int lcd_line_length;
|
||||
int lcd_color_fg;
|
||||
int lcd_color_bg;
|
||||
|
||||
void *lcd_console_address; /* Start of console buffer */
|
||||
|
||||
short console_col;
|
||||
short console_row;
|
||||
|
||||
/* Bypass FIMD of DISP1_BLK */
|
||||
static void fimd_bypass(void)
|
||||
{
|
||||
struct exynos5_sysreg *sysreg = samsung_get_base_sysreg();
|
||||
|
||||
setbits_le32(&sysreg->disp1blk_cfg, FIMDBYPASS_DISP1);
|
||||
sysreg->disp1blk_cfg &= ~FIMDBYPASS_DISP1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize display controller.
|
||||
*
|
||||
* @param lcdbase pointer to the base address of framebuffer.
|
||||
* @pd pointer to the main panel_data structure
|
||||
*/
|
||||
void fb_init(unsigned long int fb_size, void *lcdbase,
|
||||
struct exynos5_fimd_panel *pd)
|
||||
{
|
||||
unsigned int val;
|
||||
struct exynos5_fimd *fimd = samsung_get_base_fimd();
|
||||
struct exynos5_disp_ctrl *disp_ctrl = samsung_get_base_disp_ctrl();
|
||||
|
||||
fb_size = ALIGN(fb_size, 4096);
|
||||
|
||||
writel(pd->ivclk | pd->fixvclk, &disp_ctrl->vidcon1);
|
||||
val = ENVID_ON | ENVID_F_ON | (pd->clkval_f << CLKVAL_F_OFFSET);
|
||||
writel(val, &fimd->vidcon0);
|
||||
|
||||
val = (pd->vsync << VSYNC_PULSE_WIDTH_OFFSET) |
|
||||
(pd->lower_margin << V_FRONT_PORCH_OFFSET) |
|
||||
(pd->upper_margin << V_BACK_PORCH_OFFSET);
|
||||
writel(val, &disp_ctrl->vidtcon0);
|
||||
|
||||
val = (pd->hsync << HSYNC_PULSE_WIDTH_OFFSET) |
|
||||
(pd->right_margin << H_FRONT_PORCH_OFFSET) |
|
||||
(pd->left_margin << H_BACK_PORCH_OFFSET);
|
||||
writel(val, &disp_ctrl->vidtcon1);
|
||||
|
||||
val = ((pd->xres - 1) << HOZVAL_OFFSET) |
|
||||
((pd->yres - 1) << LINEVAL_OFFSET);
|
||||
writel(val, &disp_ctrl->vidtcon2);
|
||||
|
||||
writel((unsigned int)lcdbase, &fimd->vidw00add0b0);
|
||||
writel((unsigned int)lcdbase + fb_size, &fimd->vidw00add1b0);
|
||||
|
||||
writel(pd->xres * 2, &fimd->vidw00add2);
|
||||
|
||||
val = ((pd->xres - 1) << OSD_RIGHTBOTX_F_OFFSET);
|
||||
val |= ((pd->yres - 1) << OSD_RIGHTBOTY_F_OFFSET);
|
||||
writel(val, &fimd->vidosd0b);
|
||||
writel(pd->xres * pd->yres, &fimd->vidosd0c);
|
||||
|
||||
setbits_le32(&fimd->shadowcon, CHANNEL0_EN);
|
||||
|
||||
val = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET;
|
||||
val |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN;
|
||||
writel(val, &fimd->wincon0);
|
||||
|
||||
/* DPCLKCON_ENABLE */
|
||||
writel(1 << 1, &fimd->dpclkcon);
|
||||
}
|
||||
|
||||
#ifdef UNUSED_CODE
|
||||
void exynos_fimd_disable(void)
|
||||
{
|
||||
struct exynos5_fimd *fimd = samsung_get_base_fimd();
|
||||
|
||||
writel(0, &fimd->wincon0);
|
||||
clrbits_le32(&fimd->shadowcon, CHANNEL0_EN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Configure DP in slave mode and wait for video stream.
|
||||
*
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param video_info pointer to main video_info structure.
|
||||
* return status
|
||||
*/
|
||||
static int s5p_dp_config_video(struct s5p_dp_device *dp,
|
||||
struct video_info *video_info)
|
||||
{
|
||||
int timeout = 0;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
struct mono_time start, current, end;
|
||||
s5p_dp_config_video_slave_mode(dp, video_info);
|
||||
|
||||
s5p_dp_set_video_color_format(dp, video_info->color_depth,
|
||||
video_info->color_space,
|
||||
video_info->dynamic_range,
|
||||
video_info->ycbcr_coeff);
|
||||
|
||||
if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
|
||||
printk(BIOS_DEBUG, "PLL is not locked yet.\n");
|
||||
return -ERR_PLL_NOT_UNLOCKED;
|
||||
}
|
||||
|
||||
timer_monotonic_get(&start);
|
||||
end = current = start;
|
||||
mono_time_add_usecs(&end, STREAM_ON_TIMEOUT * USECS_PER_MSEC);
|
||||
do {
|
||||
if (s5p_dp_is_slave_video_stream_clock_on(dp) == 0) {
|
||||
timeout++;
|
||||
break;
|
||||
}
|
||||
timer_monotonic_get(¤t);
|
||||
} while (mono_time_before(¤t, &end));
|
||||
|
||||
if (!timeout) {
|
||||
printk(BIOS_ERR, "Video Clock Not ok after %ldus.\n",
|
||||
mono_time_diff_microseconds(&start, &end));
|
||||
return -ERR_VIDEO_CLOCK_BAD;
|
||||
}
|
||||
|
||||
/* Set to use the register calculated M/N video */
|
||||
s5p_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
|
||||
|
||||
clrbits_le32(&base->video_ctl_10, FORMAT_SEL);
|
||||
|
||||
/* Disable video mute */
|
||||
clrbits_le32(&base->video_ctl_1, HDCP_VIDEO_MUTE);
|
||||
|
||||
/* Configure video slave mode */
|
||||
s5p_dp_enable_video_master(dp);
|
||||
|
||||
/* Enable video */
|
||||
setbits_le32(&base->video_ctl_1, VIDEO_EN);
|
||||
timeout = s5p_dp_is_video_stream_on(dp);
|
||||
|
||||
if (timeout) {
|
||||
printk(BIOS_DEBUG, "Video Stream Not on\n");
|
||||
return -ERR_VIDEO_STREAM_BAD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set DP to enhanced mode. We use this for EVT1
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* return status
|
||||
*/
|
||||
static int s5p_dp_enable_rx_to_enhanced_mode(struct s5p_dp_device *dp)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data)) {
|
||||
printk(BIOS_DEBUG, "DPCD read error\n");
|
||||
return -ERR_DPCD_READ_ERROR1;
|
||||
}
|
||||
if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
|
||||
DPCD_ENHANCED_FRAME_EN |
|
||||
(data & DPCD_LANE_COUNT_SET_MASK))) {
|
||||
printk(BIOS_DEBUG, "DPCD write error\n");
|
||||
return -ERR_DPCD_WRITE_ERROR1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable scrambles mode. We use this for EVT1
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* return status
|
||||
*/
|
||||
static int s5p_dp_enable_scramble(struct s5p_dp_device *dp)
|
||||
{
|
||||
u8 data;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
clrbits_le32(&base->dp_training_ptn_set, SCRAMBLING_DISABLE);
|
||||
|
||||
if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
&data)) {
|
||||
printk(BIOS_DEBUG, "DPCD read error\n");
|
||||
return -ERR_DPCD_READ_ERROR2;
|
||||
}
|
||||
|
||||
if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
(u8)(data & ~DPCD_SCRAMBLING_DISABLED))) {
|
||||
printk(BIOS_DEBUG, "DPCD write error\n");
|
||||
return -ERR_DPCD_WRITE_ERROR2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset DP and prepare DP for init training
|
||||
* param dp pointer to main s5p-dp structure
|
||||
*/
|
||||
static int s5p_dp_init_dp(struct s5p_dp_device *dp)
|
||||
{
|
||||
int ret, i;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
for (i = 0; i < DP_INIT_TRIES; i++) {
|
||||
s5p_dp_reset(dp);
|
||||
|
||||
/* SW defined function Normal operation */
|
||||
clrbits_le32(&base->func_en_1, SW_FUNC_EN_N);
|
||||
|
||||
ret = s5p_dp_init_analog_func(dp);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
udelay(5000);
|
||||
printk(BIOS_DEBUG, "LCD retry init, attempt=%d ret=%d\n", i, ret);
|
||||
}
|
||||
if (i == DP_INIT_TRIES) {
|
||||
printk(BIOS_DEBUG, "LCD initialization failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
s5p_dp_init_aux(dp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set pre-emphasis level
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param pre_emphasis pre-emphasis level
|
||||
* param lane lane number(0 - 3)
|
||||
* return status
|
||||
*/
|
||||
static int s5p_dp_set_lane_lane_pre_emphasis(struct s5p_dp_device *dp,
|
||||
int pre_emphasis, int lane)
|
||||
{
|
||||
u32 reg;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
reg = pre_emphasis << PRE_EMPHASIS_SET_SHIFT;
|
||||
switch (lane) {
|
||||
case 0:
|
||||
writel(reg, &base->ln0_link_trn_ctl);
|
||||
break;
|
||||
case 1:
|
||||
writel(reg, &base->ln1_link_trn_ctl);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
writel(reg, &base->ln2_link_trn_ctl);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
writel(reg, &base->ln3_link_trn_ctl);
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_DEBUG, "%s: Invalid lane %d\n", __func__, lane);
|
||||
return -ERR_INVALID_LANE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read supported bandwidth type
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param bandwidth pointer to variable holding bandwidth type
|
||||
*/
|
||||
static void s5p_dp_get_max_rx_bandwidth(struct s5p_dp_device *dp,
|
||||
u8 *bandwidth)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
/*
|
||||
* For DP rev.1.1, Maximum link rate of Main Link lanes
|
||||
* 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
|
||||
*/
|
||||
s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data);
|
||||
*bandwidth = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset DP and prepare DP for init training
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param lane_count pointer to variable holding no of lanes
|
||||
*/
|
||||
static void s5p_dp_get_max_rx_lane_count(struct s5p_dp_device *dp,
|
||||
u8 *lane_count)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
/*
|
||||
* For DP rev.1.1, Maximum number of Main Link lanes
|
||||
* 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
|
||||
*/
|
||||
s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
|
||||
*lane_count = data & DPCD_MAX_LANE_COUNT_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* DP H/w Link Training. Set DPCD link rate and bandwidth.
|
||||
* param dp pointer to main s5p-dp structure
|
||||
* param max_lane No of lanes
|
||||
* param max_rate bandwidth
|
||||
* return status
|
||||
*/
|
||||
static int s5p_dp_hw_link_training(struct s5p_dp_device *dp,
|
||||
unsigned int max_lane,
|
||||
unsigned int max_rate)
|
||||
{
|
||||
int pll_is_locked = 0;
|
||||
u32 data;
|
||||
int lane;
|
||||
struct mono_time current, end;
|
||||
struct exynos5_dp *base = dp->base;
|
||||
|
||||
/* Stop Video */
|
||||
clrbits_le32(&base->video_ctl_1, VIDEO_EN);
|
||||
|
||||
timer_monotonic_get(¤t);
|
||||
end = current;
|
||||
mono_time_add_msecs(&end, PLL_LOCK_TIMEOUT);
|
||||
|
||||
while ((pll_is_locked = s5p_dp_get_pll_lock_status(dp)) == PLL_UNLOCKED) {
|
||||
if (mono_time_after(¤t, &end)) {
|
||||
/* Ignore this error, and try to continue */
|
||||
printk(BIOS_ERR, "PLL is not locked yet.\n");
|
||||
break;
|
||||
}
|
||||
timer_monotonic_get(¤t);
|
||||
}
|
||||
printk(BIOS_SPEW, "PLL is %slocked\n",
|
||||
pll_is_locked == PLL_LOCKED ? "": "not ");
|
||||
/* Reset Macro */
|
||||
setbits_le32(&base->dp_phy_test, MACRO_RST);
|
||||
|
||||
/* 10 us is the minimum reset time. */
|
||||
udelay(10);
|
||||
|
||||
clrbits_le32(&base->dp_phy_test, MACRO_RST);
|
||||
|
||||
/* Set TX pre-emphasis to minimum */
|
||||
for (lane = 0; lane < max_lane; lane++)
|
||||
if (s5p_dp_set_lane_lane_pre_emphasis(dp,
|
||||
PRE_EMPHASIS_LEVEL_0, lane)) {
|
||||
printk(BIOS_DEBUG, "Unable to set pre emphasis level\n");
|
||||
return -ERR_PRE_EMPHASIS_LEVELS;
|
||||
}
|
||||
|
||||
/* All DP analog module power up */
|
||||
writel(0x00, &base->dp_phy_pd);
|
||||
|
||||
/* Initialize by reading RX's DPCD */
|
||||
s5p_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
|
||||
s5p_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
|
||||
|
||||
printk(BIOS_SPEW, "%s: rate 0x%x, lane_count %d\n", __func__,
|
||||
dp->link_train.link_rate, dp->link_train.lane_count);
|
||||
|
||||
if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
|
||||
(dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
|
||||
printk(BIOS_DEBUG, "Rx Max Link Rate is abnormal :%x !\n",
|
||||
dp->link_train.link_rate);
|
||||
/* Not Retrying */
|
||||
return -ERR_LINK_RATE_ABNORMAL;
|
||||
}
|
||||
|
||||
if (dp->link_train.lane_count == 0) {
|
||||
printk(BIOS_DEBUG, "Rx Max Lane count is abnormal :%x !\n",
|
||||
dp->link_train.lane_count);
|
||||
/* Not retrying */
|
||||
return -ERR_MAX_LANE_COUNT_ABNORMAL;
|
||||
}
|
||||
|
||||
/* Setup TX lane count & rate */
|
||||
if (dp->link_train.lane_count > max_lane)
|
||||
dp->link_train.lane_count = max_lane;
|
||||
if (dp->link_train.link_rate > max_rate)
|
||||
dp->link_train.link_rate = max_rate;
|
||||
|
||||
/* Set link rate and count as you want to establish*/
|
||||
writel(dp->link_train.lane_count, &base->lane_count_set);
|
||||
writel(dp->link_train.link_rate, &base->link_bw_set);
|
||||
|
||||
/* Set sink to D0 (Sink Not Ready) mode. */
|
||||
s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE,
|
||||
DPCD_SET_POWER_STATE_D0);
|
||||
|
||||
/* Start HW link training */
|
||||
writel(HW_TRAINING_EN, &base->dp_hw_link_training);
|
||||
|
||||
/* Wait until HW link training done */
|
||||
s5p_dp_wait_hw_link_training_done(dp);
|
||||
|
||||
/* Get hardware link training status */
|
||||
data = readl(&base->dp_hw_link_training);
|
||||
printk(BIOS_SPEW, "hardware link training status: 0x%08x\n", data);
|
||||
if (data != 0) {
|
||||
printk(BIOS_ERR, " H/W link training failure: 0x%x\n", data);
|
||||
return -ERR_LINK_TRAINING_FAILURE;
|
||||
}
|
||||
|
||||
/* Get Link Bandwidth */
|
||||
data = readl(&base->link_bw_set);
|
||||
|
||||
dp->link_train.link_rate = data;
|
||||
|
||||
data = readl(&base->lane_count_set);
|
||||
dp->link_train.lane_count = data;
|
||||
printk(BIOS_SPEW, "Done training: Link bandwidth: 0x%x, lane_count: %d\n",
|
||||
dp->link_train.link_rate, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize DP display
|
||||
*/
|
||||
int dp_controller_init(struct s5p_dp_device *dp_device)
|
||||
{
|
||||
int ret;
|
||||
struct s5p_dp_device *dp = dp_device;
|
||||
struct exynos5_dp *base;
|
||||
|
||||
clock_init_dp_clock();
|
||||
|
||||
power_enable_dp_phy();
|
||||
ret = s5p_dp_init_dp(dp);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "%s: Could not initialize dp\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = s5p_dp_hw_link_training(dp, dp->video_info->lane_count,
|
||||
dp->video_info->link_rate);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "unable to do link train\n");
|
||||
return ret;
|
||||
}
|
||||
/* Minimum delay after H/w Link training */
|
||||
udelay(1000);
|
||||
|
||||
ret = s5p_dp_enable_scramble(dp);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "unable to set scramble mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = s5p_dp_enable_rx_to_enhanced_mode(dp);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "unable to set enhanced mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
base = dp->base;
|
||||
/* Enable enhanced mode */
|
||||
setbits_le32(&base->sys_ctl_4, ENHANCED);
|
||||
|
||||
writel(dp->link_train.lane_count, &base->lane_count_set);
|
||||
writel(dp->link_train.link_rate, &base->link_bw_set);
|
||||
|
||||
s5p_dp_init_video(dp);
|
||||
ret = s5p_dp_config_video(dp, dp->video_info);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "unable to config video\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the LCD controller
|
||||
*
|
||||
* @param lcdbase Base address of LCD frame buffer
|
||||
* @return 0 if ok, -ve error code on error
|
||||
*/
|
||||
int lcd_ctrl_init(unsigned long int fb_size,
|
||||
struct exynos5_fimd_panel *panel_data, void *lcdbase)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
fimd_bypass();
|
||||
fb_init(fb_size, lcdbase, panel_data);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
* Author: InKi Dae <inki.dae@samsung.com>
|
||||
* Author: Donghwa Lee <dh09.lee@samsung.com>
|
||||
*
|
||||
* 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; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 <delay.h>
|
||||
#include "timer.h"
|
||||
#include "clk.h"
|
||||
#include "cpu.h"
|
||||
#include "periph.h"
|
||||
#include "dp.h"
|
||||
#include "fimd.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
/* left here because it's a counterexample of good style. u-boot
|
||||
* likes to call a function to get a base address. To get that they
|
||||
* walk the device tree several times. It's costly. So they turn a
|
||||
* known-at-build-time constant into a bunch of function calls. What
|
||||
* to do about this inefficiency? Fix the inefficiency? No, scarf
|
||||
* variables away in global memory of course, and remove all hope of
|
||||
* optimization. Bad idea.
|
||||
*
|
||||
* the u-boot sources that define fimd base are ... bizarre. I'm going
|
||||
* with a hardcoded hack until we get this working. FIXME.
|
||||
*
|
||||
* static struct exynos_fb *fimd_ctrl * =
|
||||
* (void *)0x14400000; //samsung_get_base_fimd();
|
||||
*/
|
||||
|
||||
/* fairly useful debugging stuff. */
|
||||
#if 0
|
||||
static inline void fwadl(unsigned long l,void *v) {
|
||||
writel(l, v);
|
||||
printk(BIOS_SPEW, "W %p %p\n", v, (void *)l);
|
||||
}
|
||||
#define lwritel(a,b) fwadl((unsigned long)(a), (void *)(b))
|
||||
|
||||
static inline unsigned long fradl(void *v) {
|
||||
unsigned long l = readl(v);
|
||||
printk(BIOS_SPEW, "R %p %p\n", v, (void *)l);
|
||||
return l;
|
||||
}
|
||||
|
||||
#define lreadl(a) fradl((void *)(a))
|
||||
|
||||
#else
|
||||
#define lwritel(a,b) writel((unsigned long)(a), (void *)(b))
|
||||
#define lreadl(a) readl((void *)(a))
|
||||
#endif
|
||||
|
||||
/* not sure where we want this so ... */
|
||||
static unsigned long get_lcd_clk(void)
|
||||
{
|
||||
struct exynos5420_clock *clk = samsung_get_base_clock();
|
||||
u32 pclk, sclk;
|
||||
unsigned int sel;
|
||||
unsigned int ratio;
|
||||
|
||||
/*
|
||||
* CLK_SRC_DISP10
|
||||
* CLKMUX_FIMD1 [4]
|
||||
* 0: SCLK_RPLL
|
||||
* 1: SCLK_SPLL
|
||||
*/
|
||||
sel = lreadl(&clk->clk_src_disp10);
|
||||
sel &= (1 << 4);
|
||||
|
||||
if (sel){
|
||||
sclk = get_pll_clk(SPLL);
|
||||
} else {
|
||||
sclk = get_pll_clk(RPLL);
|
||||
}
|
||||
|
||||
/*
|
||||
* CLK_DIV_DISP10
|
||||
* FIMD1_RATIO [3:0]
|
||||
*/
|
||||
ratio = lreadl(&clk->clk_div_disp10);
|
||||
ratio = ratio & 0xf;
|
||||
|
||||
pclk = sclk / (ratio + 1);
|
||||
|
||||
return pclk;
|
||||
}
|
||||
|
||||
static void exynos_fimd_set_dualrgb(vidinfo_t *vid, unsigned int enabled)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
printk(BIOS_SPEW, "%s %s\n", __func__, enabled ? "enabled" : "not enabled");
|
||||
if (enabled) {
|
||||
cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
|
||||
EXYNOS_DUALRGB_VDEN_EN_ENABLE;
|
||||
|
||||
/* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
|
||||
cfg |= EXYNOS_DUALRGB_SUB_CNT(vid->vl_col / 2) |
|
||||
EXYNOS_DUALRGB_MAIN_CNT(0);
|
||||
}
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->dualrgb);
|
||||
}
|
||||
|
||||
static void exynos_fimd_set_dp_clkcon(unsigned int enabled)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
|
||||
if (enabled){
|
||||
cfg = EXYNOS_DP_CLK_ENABLE;
|
||||
}
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->dp_mie_clkcon);
|
||||
}
|
||||
|
||||
static void exynos_fimd_set_par(vidinfo_t *vid, unsigned int win_id)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
|
||||
/* set window control */
|
||||
cfg = lreadl(&FIMD_CTRL->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
|
||||
cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
|
||||
EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
|
||||
EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
|
||||
EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
|
||||
|
||||
/* DATAPATH is DMA */
|
||||
cfg |= EXYNOS_WINCON_DATAPATH_DMA;
|
||||
|
||||
cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
|
||||
|
||||
/* dma burst is 16 */
|
||||
cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
|
||||
|
||||
cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
|
||||
/* set window position to x=0, y=0*/
|
||||
cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
|
||||
lwritel(cfg, &FIMD_CTRL->vidosd0a +
|
||||
EXYNOS_VIDOSD(win_id));
|
||||
|
||||
cfg = EXYNOS_VIDOSD_RIGHT_X(vid->vl_col - 1) |
|
||||
EXYNOS_VIDOSD_BOTTOM_Y(vid->vl_row - 1) |
|
||||
EXYNOS_VIDOSD_RIGHT_X_E(1) |
|
||||
EXYNOS_VIDOSD_BOTTOM_Y_E(0);
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->vidosd0b +
|
||||
EXYNOS_VIDOSD(win_id));
|
||||
/* set window size for window0*/
|
||||
cfg = EXYNOS_VIDOSD_SIZE(vid->vl_col * vid->vl_row);
|
||||
lwritel(cfg, &FIMD_CTRL->vidosd0c +
|
||||
EXYNOS_VIDOSD(win_id));
|
||||
}
|
||||
|
||||
static void exynos_fimd_set_buffer_address(vidinfo_t *vid,
|
||||
void *screen_base, int win_id)
|
||||
{
|
||||
u32 start_addr, end_addr;
|
||||
printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
|
||||
start_addr = (u32)screen_base;
|
||||
end_addr = start_addr + ((vid->vl_col * ((1<<vid->vl_bpix) / 8)) *
|
||||
vid->vl_row);
|
||||
|
||||
lwritel(start_addr, &FIMD_CTRL->vidw00add0b0 +
|
||||
EXYNOS_BUFFER_OFFSET(win_id));
|
||||
lwritel(end_addr, &FIMD_CTRL->vidw00add1b0 +
|
||||
EXYNOS_BUFFER_OFFSET(win_id));
|
||||
}
|
||||
|
||||
static void exynos_fimd_set_clock(vidinfo_t *vid)
|
||||
{
|
||||
unsigned int cfg = 0, div = 0, remainder = 0, remainder_div;
|
||||
unsigned long pixel_clock;
|
||||
unsigned long long src_clock;
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
if (vid->dual_lcd_enabled) {
|
||||
pixel_clock = vid->vl_freq *
|
||||
(vid->vl_hspw + vid->vl_hfpd +
|
||||
vid->vl_hbpd + vid->vl_col / 2) *
|
||||
(vid->vl_vspw + vid->vl_vfpd +
|
||||
vid->vl_vbpd + vid->vl_row);
|
||||
} else if (vid->interface_mode == FIMD_CPU_INTERFACE) {
|
||||
pixel_clock = vid->vl_freq *
|
||||
vid->vl_width * vid->vl_height *
|
||||
(vid->cs_setup + vid->wr_setup +
|
||||
vid->wr_act + vid->wr_hold + 1);
|
||||
} else {
|
||||
pixel_clock = vid->vl_freq *
|
||||
(vid->vl_hspw + vid->vl_hfpd +
|
||||
vid->vl_hbpd + vid->vl_col) *
|
||||
(vid->vl_vspw + vid->vl_vfpd +
|
||||
vid->vl_vbpd + vid->vl_row);
|
||||
}
|
||||
printk(BIOS_SPEW, "Pixel clock is %lx\n", pixel_clock);
|
||||
|
||||
cfg = lreadl(&FIMD_CTRL->vidcon0);
|
||||
cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
|
||||
EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
|
||||
EXYNOS_VIDCON0_CLKDIR_MASK);
|
||||
cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
|
||||
EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
|
||||
|
||||
src_clock = (unsigned long long) get_lcd_clk();
|
||||
|
||||
/* get quotient and remainder. */
|
||||
remainder = src_clock % pixel_clock;
|
||||
src_clock /= pixel_clock;
|
||||
|
||||
div = src_clock;
|
||||
|
||||
remainder *= 10;
|
||||
remainder_div = remainder / pixel_clock;
|
||||
|
||||
/* round about one places of decimals. */
|
||||
if (remainder_div >= 5)
|
||||
div++;
|
||||
|
||||
/* in case of dual lcd mode. */
|
||||
if (vid->dual_lcd_enabled)
|
||||
div--;
|
||||
|
||||
cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon0);
|
||||
}
|
||||
|
||||
void exynos_set_trigger(void)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
cfg = lreadl(&FIMD_CTRL->trigcon);
|
||||
|
||||
cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->trigcon);
|
||||
}
|
||||
|
||||
int exynos_is_i80_frame_done(void)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
int status;
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
cfg = lreadl(&FIMD_CTRL->trigcon);
|
||||
|
||||
/* frame done func is valid only when TRIMODE[0] is set to 1. */
|
||||
status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
|
||||
EXYNOS_I80STATUS_TRIG_DONE;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void exynos_fimd_lcd_on(void)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
/* display on */
|
||||
cfg = lreadl(&FIMD_CTRL->vidcon0);
|
||||
cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon0);
|
||||
}
|
||||
|
||||
static void exynos_fimd_window_on(unsigned int win_id)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
|
||||
/* enable window */
|
||||
cfg = lreadl(&FIMD_CTRL->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
|
||||
lwritel(cfg, &FIMD_CTRL->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
|
||||
cfg = lreadl(&FIMD_CTRL->winshmap);
|
||||
cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
|
||||
lwritel(cfg, &FIMD_CTRL->winshmap);
|
||||
cfg = lreadl(&FIMD_CTRL->winshmap);
|
||||
}
|
||||
|
||||
void exynos_fimd_lcd_off(void)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
|
||||
cfg = lreadl(&FIMD_CTRL->vidcon0);
|
||||
cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon0);
|
||||
}
|
||||
|
||||
void exynos_fimd_window_off(unsigned int win_id)
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
|
||||
|
||||
cfg = lreadl(&FIMD_CTRL->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
|
||||
lwritel(cfg, &FIMD_CTRL->wincon0 +
|
||||
EXYNOS_WINCON(win_id));
|
||||
|
||||
cfg = lreadl(&FIMD_CTRL->winshmap);
|
||||
cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
|
||||
lwritel(cfg, &FIMD_CTRL->winshmap);
|
||||
}
|
||||
|
||||
static void exynos5_set_system_display(void)
|
||||
{
|
||||
struct exynos5_sysreg *sysreg = samsung_get_base_sysreg();
|
||||
unsigned int cfg = 0;
|
||||
|
||||
/*
|
||||
* system register path set
|
||||
* 0: MIE/MDNIE
|
||||
* 1: FIMD Bypass
|
||||
*/
|
||||
cfg = lreadl(&sysreg->disp1blk_cfg);
|
||||
cfg |= (1 << 15);
|
||||
lwritel(cfg, &sysreg->disp1blk_cfg);
|
||||
}
|
||||
|
||||
void exynos_fimd_lcd_init(vidinfo_t *vid)
|
||||
{
|
||||
unsigned int cfg = 0, rgb_mode;
|
||||
unsigned int offset;
|
||||
|
||||
offset = exynos_fimd_get_base_offset();
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
exynos5_set_system_display();
|
||||
|
||||
rgb_mode = vid->rgb_mode;
|
||||
|
||||
if (vid->interface_mode == FIMD_RGB_INTERFACE) {
|
||||
printk(BIOS_SPEW, "%s FIMD_RGB_INTERFACE\n", __func__);
|
||||
|
||||
cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon0);
|
||||
|
||||
cfg = lreadl(&FIMD_CTRL->vidcon2);
|
||||
cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
|
||||
EXYNOS_VIDCON2_TVFORMATSEL_MASK |
|
||||
EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
|
||||
cfg |= EXYNOS_VIDCON2_WB_DISABLE;
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon2);
|
||||
|
||||
/* set polarity */
|
||||
cfg = 0;
|
||||
if (!vid->vl_clkp)
|
||||
cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
|
||||
if (!vid->vl_hsp)
|
||||
cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
|
||||
if (!vid->vl_vsp)
|
||||
cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
|
||||
if (!vid->vl_dp)
|
||||
cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon1 + offset);
|
||||
|
||||
/* set timing */
|
||||
cfg = EXYNOS_VIDTCON0_VFPD(vid->vl_vfpd - 1);
|
||||
cfg |= EXYNOS_VIDTCON0_VBPD(vid->vl_vbpd - 1);
|
||||
cfg |= EXYNOS_VIDTCON0_VSPW(vid->vl_vspw - 1);
|
||||
lwritel(cfg, &FIMD_CTRL->vidtcon0 + offset);
|
||||
|
||||
cfg = EXYNOS_VIDTCON1_HFPD(vid->vl_hfpd - 1);
|
||||
cfg |= EXYNOS_VIDTCON1_HBPD(vid->vl_hbpd - 1);
|
||||
cfg |= EXYNOS_VIDTCON1_HSPW(vid->vl_hspw - 1);
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->vidtcon1 + offset);
|
||||
|
||||
/* set lcd size */
|
||||
cfg = EXYNOS_VIDTCON2_HOZVAL(vid->vl_col - 1) |
|
||||
EXYNOS_VIDTCON2_LINEVAL(vid->vl_row - 1) |
|
||||
EXYNOS_VIDTCON2_HOZVAL_E(vid->vl_col - 1) |
|
||||
EXYNOS_VIDTCON2_LINEVAL_E(vid->vl_row - 1);
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->vidtcon2 + offset);
|
||||
}
|
||||
|
||||
/* set display mode */
|
||||
cfg = lreadl(&FIMD_CTRL->vidcon0);
|
||||
cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
|
||||
cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
|
||||
lwritel(cfg, &FIMD_CTRL->vidcon0);
|
||||
|
||||
/* set par */
|
||||
exynos_fimd_set_par(vid, vid->win_id);
|
||||
|
||||
/* set memory address */
|
||||
exynos_fimd_set_buffer_address(vid, vid->screen_base, vid->win_id);
|
||||
|
||||
/* set buffer size */
|
||||
cfg = EXYNOS_VIDADDR_PAGEWIDTH(vid->vl_col * (1<<vid->vl_bpix) / 8) |
|
||||
EXYNOS_VIDADDR_PAGEWIDTH_E(vid->vl_col * (1<<vid->vl_bpix) / 8) |
|
||||
EXYNOS_VIDADDR_OFFSIZE(0) |
|
||||
EXYNOS_VIDADDR_OFFSIZE_E(0);
|
||||
|
||||
lwritel(cfg, &FIMD_CTRL->vidw00add2 +
|
||||
EXYNOS_BUFFER_SIZE(vid->win_id));
|
||||
|
||||
/* set clock */
|
||||
exynos_fimd_set_clock(vid);
|
||||
|
||||
/* set rgb mode to dual lcd. */
|
||||
exynos_fimd_set_dualrgb(vid, vid->dual_lcd_enabled);
|
||||
|
||||
/* display on */
|
||||
exynos_fimd_lcd_on();
|
||||
|
||||
/* window on */
|
||||
exynos_fimd_window_on(vid->win_id);
|
||||
|
||||
exynos_fimd_set_dp_clkcon(vid->dp_enabled);
|
||||
exynos5_set_system_display();
|
||||
printk(BIOS_SPEW, "%s: done\n", __func__);
|
||||
}
|
||||
|
||||
unsigned long exynos_fimd_calc_fbsize(vidinfo_t *vid)
|
||||
{
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
return vid->vl_col * vid->vl_row * ((1<<vid->vl_bpix) / 8);
|
||||
}
|
||||
|
||||
void exynos_fimd_lcd_disable(void)
|
||||
{
|
||||
int i;
|
||||
printk(BIOS_SPEW, "%s\n", __func__);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
exynos_fimd_window_off(i);
|
||||
}
|
|
@ -25,39 +25,39 @@
|
|||
/* FIMD register map */
|
||||
struct exynos5_fimd {
|
||||
/* This is an incomplete list. Add registers as and when required */
|
||||
unsigned int vidcon0;
|
||||
unsigned char res1[0x1c];
|
||||
unsigned int wincon0;
|
||||
unsigned int wincon1;
|
||||
unsigned int wincon2;
|
||||
unsigned int wincon3;
|
||||
unsigned int wincon4;
|
||||
unsigned int shadowcon;
|
||||
unsigned char res2[0x8];
|
||||
unsigned int vidosd0a;
|
||||
unsigned int vidosd0b;
|
||||
unsigned int vidosd0c;
|
||||
unsigned char res3[0x54];
|
||||
unsigned int vidw00add0b0;
|
||||
unsigned char res4[0x2c];
|
||||
unsigned int vidw00add1b0;
|
||||
unsigned char res5[0x2c];
|
||||
unsigned int vidw00add2;
|
||||
unsigned char res6[0x3c];
|
||||
unsigned int w1keycon0;
|
||||
unsigned int w1keycon1;
|
||||
unsigned int w2keycon0;
|
||||
unsigned int w2keycon1;
|
||||
unsigned int w3keycon0;
|
||||
unsigned int w3keycon1;
|
||||
unsigned int w4keycon0;
|
||||
unsigned int w4keycon1;
|
||||
unsigned char res7[0x20];
|
||||
unsigned int win0map;
|
||||
unsigned char res8[0xdc];
|
||||
unsigned int blendcon;
|
||||
unsigned char res9[0x18];
|
||||
unsigned int dpclkcon;
|
||||
u32 vidcon0;
|
||||
u8 res1[0x1c];
|
||||
u32 wincon0;
|
||||
u32 wincon1;
|
||||
u32 wincon2;
|
||||
u32 wincon3;
|
||||
u32 wincon4;
|
||||
u32 shadowcon;
|
||||
u8 res2[0x8];
|
||||
u32 vidosd0a;
|
||||
u32 vidosd0b;
|
||||
u32 vidosd0c;
|
||||
u8 res3[0x54];
|
||||
u32 vidw00add0b0;
|
||||
u8 res4[0x2c];
|
||||
u32 vidw00add1b0;
|
||||
u8 res5[0x2c];
|
||||
u32 vidw00add2;
|
||||
u8 res6[0x3c];
|
||||
u32 w1keycon0;
|
||||
u32 w1keycon1;
|
||||
u32 w2keycon0;
|
||||
u32 w2keycon1;
|
||||
u32 w3keycon0;
|
||||
u32 w3keycon1;
|
||||
u32 w4keycon0;
|
||||
u32 w4keycon1;
|
||||
u8 res7[0x20];
|
||||
u32 win0map;
|
||||
u8 res8[0xdc];
|
||||
u32 blendcon;
|
||||
u8 res9[0x18];
|
||||
u32 dpclkcon;
|
||||
};
|
||||
|
||||
#define W0_SHADOW_PROTECT (0x1 << 10)
|
||||
|
@ -86,7 +86,7 @@ struct exynos5_fimd_panel {
|
|||
unsigned int upper_margin; /* Vertical Backporch */
|
||||
unsigned int lower_margin; /* Vertical frontporch */
|
||||
unsigned int vsync; /* Vertical Sync Pulse Width */
|
||||
unsigned int left_margin; /* Horizantal Backporch */
|
||||
unsigned int left_margin; /* Horizontal Backporch */
|
||||
unsigned int right_margin; /* Horizontal Frontporch */
|
||||
unsigned int hsync; /* Horizontal Sync Pulse Width */
|
||||
unsigned int xres; /* X Resolution */
|
||||
|
@ -95,15 +95,15 @@ struct exynos5_fimd_panel {
|
|||
|
||||
/* LCDIF Register Map */
|
||||
struct exynos5_disp_ctrl {
|
||||
unsigned int vidout_con;
|
||||
unsigned int vidcon1;
|
||||
unsigned char res1[0x8];
|
||||
unsigned int vidtcon0;
|
||||
unsigned int vidtcon1;
|
||||
unsigned int vidtcon2;
|
||||
unsigned int vidtcon3;
|
||||
unsigned char res2[0x184];
|
||||
unsigned int trigcon;
|
||||
u32 vidout_con;
|
||||
u32 vidcon1;
|
||||
u8 res1[0x8];
|
||||
u32 vidtcon0;
|
||||
u32 vidtcon1;
|
||||
u32 vidtcon2;
|
||||
u32 vidtcon3;
|
||||
u8 res2[0x184];
|
||||
u32 trigcon;
|
||||
};
|
||||
|
||||
#define VCLK_RISING_EDGE (1 << 7)
|
||||
|
@ -135,4 +135,79 @@ struct exynos5_disp_ctrl {
|
|||
|
||||
#define OSD_RIGHTBOTX_F_OFFSET 11
|
||||
#define OSD_RIGHTBOTY_F_OFFSET 0
|
||||
|
||||
#define FIMD_CTRL ((struct exynos_fb *)0x14400000)
|
||||
|
||||
/* from u-boot fb.h. It needs to be merged with these dp structs maybe. */
|
||||
enum {
|
||||
FIMD_RGB_INTERFACE = 1,
|
||||
FIMD_CPU_INTERFACE = 2,
|
||||
};
|
||||
|
||||
enum exynos_fb_rgb_mode_t {
|
||||
MODE_RGB_P = 0,
|
||||
MODE_BGR_P = 1,
|
||||
MODE_RGB_S = 2,
|
||||
MODE_BGR_S = 3,
|
||||
};
|
||||
|
||||
typedef struct vidinfo {
|
||||
u16 vl_col; /* Number of columns (i.e. 640) */
|
||||
u16 vl_row; /* Number of rows (i.e. 480) */
|
||||
u16 vl_width; /* Width of display area in millimeters */
|
||||
u16 vl_height; /* Height of display area in millimeters */
|
||||
|
||||
/* LCD configuration register */
|
||||
u8 vl_freq; /* Frequency */
|
||||
u8 vl_clkp; /* Clock polarity */
|
||||
u8 vl_oep; /* Output Enable polarity */
|
||||
u8 vl_hsp; /* Horizontal Sync polarity */
|
||||
u8 vl_vsp; /* Vertical Sync polarity */
|
||||
u8 vl_dp; /* Data polarity */
|
||||
u8 vl_bpix; /* Bits per pixel */
|
||||
|
||||
/* Horizontal control register. Timing from data sheet */
|
||||
u8 vl_hspw; /* Horz sync pulse width */
|
||||
u8 vl_hfpd; /* Wait before of line */
|
||||
u8 vl_hbpd; /* Wait end of line */
|
||||
|
||||
/* Vertical control register. */
|
||||
u8 vl_vspw; /* Vertical sync pulse width */
|
||||
u8 vl_vfpd; /* Wait before of frame */
|
||||
u8 vl_vbpd; /* Wait end of frame */
|
||||
u8 vl_cmd_allow_len; /* Wait end of frame */
|
||||
|
||||
unsigned int win_id;
|
||||
unsigned int init_delay;
|
||||
unsigned int power_on_delay;
|
||||
unsigned int reset_delay;
|
||||
unsigned int interface_mode;
|
||||
unsigned int mipi_enabled;
|
||||
unsigned int dp_enabled;
|
||||
unsigned int cs_setup;
|
||||
unsigned int wr_setup;
|
||||
unsigned int wr_act;
|
||||
unsigned int wr_hold;
|
||||
unsigned int rgb_mode;
|
||||
unsigned int resolution;
|
||||
|
||||
/* parent clock name(MPLL, EPLL or VPLL) */
|
||||
unsigned int pclk_name;
|
||||
/* ratio value for source clock from parent clock. */
|
||||
unsigned int sclk_div;
|
||||
|
||||
unsigned int dual_lcd_enabled;
|
||||
void *screen_base;
|
||||
void *cmap; /* Points at 8 to 16 bit conversion map. */
|
||||
} vidinfo_t;
|
||||
|
||||
/* fimd.c */
|
||||
void exynos_set_trigger(void);
|
||||
int exynos_is_i80_frame_done(void);
|
||||
void exynos_fimd_lcd_off(void);
|
||||
void exynos_fimd_window_off(unsigned int win_id);
|
||||
unsigned long exynos_fimd_calc_fbsize(vidinfo_t *vid);
|
||||
void exynos_fimd_lcd_disable(void);
|
||||
void exynos_fimd_lcd_init(vidinfo_t *vid);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -64,7 +64,7 @@ void power_enable_dp_phy(void)
|
|||
struct exynos5_power *power =
|
||||
samsung_get_base_power();
|
||||
|
||||
setbits_le32(&power->dptx_phy_control, DPTX_PHY_ENABLE);
|
||||
setbits_le32(&power->dptx_phy_control, EXYNOS_DP_PHY_ENABLE);
|
||||
}
|
||||
|
||||
void power_enable_usb_phy(void)
|
||||
|
|
|
@ -32,7 +32,7 @@ void power_enable_hw_thermal_trip(void);
|
|||
#define POWER_PS_HOLD_CONTROL_DATA_HIGH (1 << 8)
|
||||
#define POWER_ENABLE_HW_TRIP (1UL << 31)
|
||||
|
||||
#define DPTX_PHY_ENABLE (1 << 0)
|
||||
#define EXYNOS_DP_PHY_ENABLE (1 << 0)
|
||||
|
||||
/* PMU_DEBUG bits [12:8] = 0x1000 selects XXTI clock source */
|
||||
#define PMU_DEBUG_XXTI 0x1000
|
||||
|
|
|
@ -1448,12 +1448,12 @@ void set_vbe_mode_info_valid(struct edid *edid, uintptr_t fb_addr)
|
|||
break;
|
||||
case 16:
|
||||
/* packed into 2-byte words */
|
||||
edid_fb.red_mask_pos = 12;
|
||||
edid_fb.red_mask_size = 4;
|
||||
edid_fb.green_mask_pos = 8;
|
||||
edid_fb.green_mask_size = 4;
|
||||
edid_fb.red_mask_pos = 11;
|
||||
edid_fb.red_mask_size = 5;
|
||||
edid_fb.green_mask_pos = 5;
|
||||
edid_fb.green_mask_size = 6;
|
||||
edid_fb.blue_mask_pos = 0;
|
||||
edid_fb.blue_mask_size = 4;
|
||||
edid_fb.blue_mask_size = 5;
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_SPEW, "%s: unsupported BPP %d\n", __func__,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <console/console.h>
|
||||
#include <device/device.h>
|
||||
#include <device/i2c.h>
|
||||
|
@ -33,7 +34,8 @@
|
|||
#include <cpu/samsung/exynos5420/gpio.h>
|
||||
#include <cpu/samsung/exynos5420/power.h>
|
||||
#include <cpu/samsung/exynos5420/i2c.h>
|
||||
#include <cpu/samsung/exynos5420/dp-core.h>
|
||||
#include <cpu/samsung/exynos5420/dp.h>
|
||||
#include <cpu/samsung/exynos5420/fimd.h>
|
||||
#include <drivers/parade/ps8625/ps8625.h>
|
||||
#include <ec/google/chromeec/ec.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -52,6 +54,64 @@ static struct edid edid = {
|
|||
.bytes_per_line = 2 * 1366
|
||||
};
|
||||
|
||||
/* from the fdt */
|
||||
static struct vidinfo vidinfo = {
|
||||
.vl_freq = 60,
|
||||
.vl_col = 1366,
|
||||
.vl_row = 768,
|
||||
.vl_width = 1366,
|
||||
.vl_height = 768,
|
||||
.vl_clkp = 1,
|
||||
.vl_dp = 1,
|
||||
.vl_bpix = 4,
|
||||
.vl_hspw = 32,
|
||||
.vl_hbpd = 40,
|
||||
.vl_hfpd = 40,
|
||||
.vl_vspw = 6,
|
||||
.vl_vbpd = 10,
|
||||
.vl_vfpd = 12,
|
||||
.vl_cmd_allow_len = 0xf,
|
||||
.win_id = 3,
|
||||
.dp_enabled = 1,
|
||||
.dual_lcd_enabled = 0,
|
||||
.interface_mode = FIMD_RGB_INTERFACE,
|
||||
};
|
||||
|
||||
static unsigned char panel_edid[] = {
|
||||
0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
|
||||
0x06,0xaf,0x5c,0x31,0x00,0x00,0x00,0x00,
|
||||
0x00,0x16,0x01,0x03,0x80,0x1a,0x0e,0x78,
|
||||
0x0a,0x99,0x85,0x95,0x55,0x56,0x92,0x28,
|
||||
0x22,0x50,0x54,0x00,0x00,0x00,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0xa3,0x1b,
|
||||
0x56,0x7e,0x50,0x00,0x16,0x30,0x30,0x20,
|
||||
0x36,0x00,0x00,0x90,0x10,0x00,0x00,0x18,
|
||||
0x6d,0x12,0x56,0x7e,0x50,0x00,0x16,0x30,
|
||||
0x30,0x20,0x36,0x00,0x00,0x90,0x10,0x00,
|
||||
0x00,0x18,0x00,0x00,0x00,0xfe,0x00,0x41,
|
||||
0x55,0x4f,0x0a,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfe,
|
||||
0x00,0x42,0x31,0x31,0x36,0x58,0x57,0x30,
|
||||
0x33,0x20,0x56,0x31,0x20,0x0a,0x00,0x3d,
|
||||
0x00,0xc0,0x00,0x00,0x27,0xfd,0x00,0x20,
|
||||
0x02,0x59,0x07,0x00,0x64,0x3e,0x07,0x02,
|
||||
0x00,0x00,0xcd,0x12,0x59,0xff,0x10,0x03,
|
||||
0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,
|
||||
0x9c,0x3f,0x07,0x02,0x31,0xf9,0x00,0x20,
|
||||
0x59,0xff,0x10,0x03,0x00,0x00,0x00,0x00,
|
||||
0xbc,0x3e,0x07,0x02,0xc0,0x9b,0x01,0x20,
|
||||
0x00,0x00,0x00,0x00,0xdb,0xf8,0x00,0x20,
|
||||
0x98,0x3e,0x07,0x02,0x8b,0xaf,0x00,0x20,
|
||||
0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0xe5,0xcd,0x16,0x00,0xe9,0xcd,0x16,0x00,
|
||||
0xe8,0x03,0x00,0x00,0x6c,0x55,0x01,0x20,
|
||||
0x2c,0x01,0x00,0x00,0x85,0xbb,0x00,0x20,
|
||||
0xe8,0x03,0x00,0x00,0xe9,0xcd,0x16,0x00,
|
||||
};
|
||||
|
||||
static const struct parade_write parade_writes[] = {
|
||||
{ 0x02, 0xa1, 0x01 }, /* HPD low */
|
||||
/*
|
||||
|
@ -176,6 +236,8 @@ static enum exynos5_gpio_pin bl_en = GPIO_X22; /* active high */
|
|||
|
||||
static void parade_dp_bridge_setup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
gpio_set_value(dp_pd_l, 1);
|
||||
gpio_cfg_pin(dp_pd_l, GPIO_OUTPUT);
|
||||
gpio_set_pull(dp_pd_l, GPIO_PULL_NONE);
|
||||
|
@ -186,6 +248,8 @@ static void parade_dp_bridge_setup(void)
|
|||
udelay(10);
|
||||
gpio_set_value(dp_rst_l, 1);
|
||||
|
||||
|
||||
gpio_set_pull(dp_hpd, GPIO_PULL_NONE);
|
||||
gpio_cfg_pin(dp_hpd, GPIO_INPUT);
|
||||
|
||||
/* De-assert PD (and possibly RST) to power up the bridge. */
|
||||
|
@ -199,9 +263,19 @@ static void parade_dp_bridge_setup(void)
|
|||
exynos_pinmux_i2c7();
|
||||
i2c_init(7, 100000, 0x00);
|
||||
|
||||
parade_ps8625_bridge_setup(7, 0x48,
|
||||
parade_writes,
|
||||
parade_ps8625_bridge_setup(7, 0x48, parade_writes,
|
||||
ARRAY_SIZE(parade_writes));
|
||||
/* Spin until the display is ready.
|
||||
* It's quite important to try really hard to get the display up,
|
||||
* so be generous. It will typically be ready in only 5 ms. and
|
||||
* we're out of here.
|
||||
* If it's not ready after a second, then we're in big trouble.
|
||||
*/
|
||||
for(i = 0; i < 1000; i++){
|
||||
if (gpio_get_value(dp_hpd))
|
||||
break;
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -230,22 +304,17 @@ static void backlight_en(void)
|
|||
gpio_direction_output(bl_en, 1);
|
||||
}
|
||||
|
||||
//static struct video_info smdk5420_dp_config = {
|
||||
static struct video_info dp_video_info = {
|
||||
/* FIXME: fix video_info struct to use const for name */
|
||||
.name = (char *)"eDP-LVDS NXP PTN3460",
|
||||
|
||||
|
||||
static struct edp_video_info dp_video_info = {
|
||||
.master_mode = 0,
|
||||
.h_sync_polarity = 0,
|
||||
.v_sync_polarity = 0,
|
||||
.interlaced = 0,
|
||||
|
||||
.color_space = COLOR_RGB,
|
||||
.dynamic_range = VESA,
|
||||
.ycbcr_coeff = COLOR_YCBCR601,
|
||||
.color_depth = COLOR_8,
|
||||
|
||||
.link_rate = LINK_RATE_2_70GBPS,
|
||||
.lane_count = LANE_COUNT2,
|
||||
};
|
||||
|
||||
/* FIXME: move some place more appropriate */
|
||||
|
@ -305,14 +374,13 @@ static void backlight_vdd(void)
|
|||
/* this happens after cpu_init where exynos resources are set */
|
||||
static void mainboard_init(device_t dev)
|
||||
{
|
||||
struct s5p_dp_device dp_device = {
|
||||
.base = (struct exynos5_dp *)EXYNOS5420_DP1_BASE,
|
||||
.video_info = &dp_video_info,
|
||||
};
|
||||
/* we'll stick with the crummy u-boot struct for now.*/
|
||||
/* doing this as an auto since the struct has to be writeable */
|
||||
struct edp_device_info device_info;
|
||||
|
||||
void *fb_addr = (void *)(get_fb_base_kb() * KiB);
|
||||
|
||||
gpio_init();
|
||||
|
||||
tmu_init(&exynos5420_tmu_info);
|
||||
|
||||
/* Clock Gating all the unused IP's to save power */
|
||||
|
@ -334,17 +402,30 @@ static void mainboard_init(device_t dev)
|
|||
|
||||
lcd_vdd();
|
||||
|
||||
/* Start the fimd running before you do the phy and lcd setup.
|
||||
* why do fimd before training etc?
|
||||
* because we need a data stream from
|
||||
* the fimd or the clock recovery step fails.
|
||||
*/
|
||||
vidinfo.screen_base = fb_addr;
|
||||
exynos_fimd_lcd_init(&vidinfo);
|
||||
|
||||
parade_dp_bridge_setup();
|
||||
dp_controller_init(&dp_device);
|
||||
|
||||
/* this might get more dynamic in future ... */
|
||||
memset(&device_info, 0, sizeof(device_info));
|
||||
device_info.disp_info.name = (char *)"Pit display";
|
||||
device_info.disp_info.h_total = 1366;
|
||||
device_info.disp_info.v_total = 768;
|
||||
device_info.video_info = dp_video_info;
|
||||
device_info.raw_edid = panel_edid;
|
||||
exynos_init_dp(&device_info);
|
||||
|
||||
udelay(LCD_T3_DELAY_MS * 1000);
|
||||
|
||||
backlight_vdd();
|
||||
backlight_pwm();
|
||||
backlight_en();
|
||||
|
||||
// Uncomment to get excessive GPIO output:
|
||||
// gpio_info();
|
||||
}
|
||||
|
||||
#if !CONFIG_DYNAMIC_CBMEM
|
||||
|
|
Loading…
Reference in New Issue