diff options
author | Ronald G. Minnich <rminnich@gmail.com> | 2013-03-01 10:18:14 -0800 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2013-03-06 23:41:42 +0100 |
commit | 6bde149d9c56a824eff5db7bb06d7c386fb2be30 (patch) | |
tree | 2e7e5254a29d53e2a24f42d723bbac9e66a03af5 /src/cpu/samsung/exynos5-common | |
parent | a4b802ce866a1f3397f0e93e530bf77e253f60ee (diff) |
samsung/exynos5: add display port and framebuffer defines and initialization
These are essential functions for setting up the display port and
framebuffer, and also enable such things as aux channel
communications. We do some very simple initialization in romstage,
mainly set a GPIO so that the graphics is powering up, but the complex
parts are done in the ramstage. This mirrors the way in which graphics
is done in the x86 size.
I've added a first pass at a real device, and put it in the mainboard
Kconfig, hoping for corrections. Because startup is so complex,
depending on device type, I've created a 'displayport' device that
removes some of the complexity and makes the flow *much* clearer. You
can actually follow the flow by looking at the code, which is not true
on other implementations. Since display port is perhaps the main port
used on these chips, that's a reasonable compromise. All parameters of
importance are now in the device tree.
Change-Id: I56400ec9016ecb8716ec5a5dae41fdfbfff4817a
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-on: http://review.coreboot.org/2570
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/cpu/samsung/exynos5-common')
-rw-r--r-- | src/cpu/samsung/exynos5-common/Makefile.inc | 4 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/displayport/Kconfig | 2 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/displayport/Makefile.inc | 2 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/displayport/chip.h | 40 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/displayport/displayport.c | 107 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/exynos-fb.c | 594 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/s5p-dp-core.h | 256 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/s5p-dp-reg.c | 481 |
8 files changed, 1486 insertions, 0 deletions
diff --git a/src/cpu/samsung/exynos5-common/Makefile.inc b/src/cpu/samsung/exynos5-common/Makefile.inc index 47abe0f058..7abd75c082 100644 --- a/src/cpu/samsung/exynos5-common/Makefile.inc +++ b/src/cpu/samsung/exynos5-common/Makefile.inc @@ -19,3 +19,7 @@ ramstage-y += pwm.c # needed by timer.c ramstage-y += timer.c ramstage-y += gpio.c ramstage-y += i2c.c +ramstage-y += s5p-dp-reg.c +ramstage-y += exynos-fb.c + +subdirs-y += displayport diff --git a/src/cpu/samsung/exynos5-common/displayport/Kconfig b/src/cpu/samsung/exynos5-common/displayport/Kconfig new file mode 100644 index 0000000000..26d1422a8c --- /dev/null +++ b/src/cpu/samsung/exynos5-common/displayport/Kconfig @@ -0,0 +1,2 @@ +config EXYNOS_DISPLAYPORT + bool diff --git a/src/cpu/samsung/exynos5-common/displayport/Makefile.inc b/src/cpu/samsung/exynos5-common/displayport/Makefile.inc new file mode 100644 index 0000000000..7c52eaf6f7 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/displayport/Makefile.inc @@ -0,0 +1,2 @@ +ramstage-$(CONFIG_EXYNOS_DISPLAYPORT) += displayport.c + diff --git a/src/cpu/samsung/exynos5-common/displayport/chip.h b/src/cpu/samsung/exynos5-common/displayport/chip.h new file mode 100644 index 0000000000..53b7836268 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/displayport/chip.h @@ -0,0 +1,40 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5_COMMON_DISPLAYPORT_H +#define CPU_SAMSUNG_EXYNOS5_COMMON_DISPLAYPORT_H + +struct cpu_samsung_exynos5_common_displayport_config { + /* special magic numbers! */ + int clkval_f; + int upper_margin; + int lower_margin; + int vsync; + int left_margin; + int right_margin; + int hsync; + + int xres; + int yres; + int bpp; + + u32 lcdbase; +}; + +#endif /* CPU_SAMSUNG_EXYNOS5-COMMON_DISPLAYPORT_H */ diff --git a/src/cpu/samsung/exynos5-common/displayport/displayport.c b/src/cpu/samsung/exynos5-common/displayport/displayport.c new file mode 100644 index 0000000000..1c08bc710e --- /dev/null +++ b/src/cpu/samsung/exynos5-common/displayport/displayport.c @@ -0,0 +1,107 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include <delay.h> +#include <arch/io.h> +#include <device/device.h> + +/* we distinguish a display port device from a raw graphics device because there are + * dramatic differences in startup depending on graphics usage. To make startup fast + * and easier to understand and debug we explicitly name this common case. The alternate + * approach, involving lots of machine and callbacks, is hard to debug and verify. + */ +static void exynos_displayport_init(void) +{ + struct cpu_samsung_exynos5_common_displayport_config *conf = dev->chip_info; + /* put these on the stack. If, at some point, we want to move this code to a + * pre-ram stage, it will be much easier. + */ + vidinfo_t vi; + struct exynos5_fimd_panel panel; + void *lcdbase; + + memset(vi, 0, sizeof(vi)); + memset(panel, 0, sizeof(panel)); + + panel.is_dp = 1; /* Display I/F is eDP */ + /* while it is true that we did a memset to zero, + * we leave some 'set to zero' entries here to make + * it clear what's going on. Graphics is confusing. + */ + panel.is_mipi = 0; + panel.fixvclk = 0; + panel.ivclk = 0; + panel.clkval_f = conf->clkval_f; + panel.upper_margin = conf->upper_margin; + panel.lower_margin = conf->lower_margin; + panel.vsync = conf->vsync; + panel.left_margin = conf->left_margin; + panel.right_margin = conf->right_margin; + panel.hsync = conf->hsync; + + vi->vl_col = conf->xres; + vi->fl_row = conf->yres; + vi->vl_bpix = conf->bpp; + vi->cmap = cbmem_reserve(64*1024); /* The size is a magic number from hardware. */ + + lcdbase = conf->lcdbase; + printk(BIOS_DEBUG, "Initializing exynos VGA\n"); + ret = lcd_ctrl_init(&vi, &panel, lcdbase); +#if 0 + ret = board_dp_lcd_vdd(blob, &wait_ms); + ret = board_dp_bridge_setup(blob, &wait_ms); + while (tries < 5) { + ret = board_dp_bridge_init(blob, &wait_ms); + ret = board_dp_hotplug(blob, &wait_ms); + if (ret) { + ret = board_dp_bridge_reset(blob, &wait_ms); + continue; + } + ret = dp_controller_init(blob, &wait_ms); + ret = board_dp_backlight_vdd(blob, &wait_ms); + ret = board_dp_backlight_pwm(blob, &wait_ms); + ret = board_dp_backlight_en(blob, &wait_ms); + } +#endif +} + +static void exynos_displayport_noop(device_t dummy) +{ +} + +static struct device_operations exynos_displayport_operations = { + .read_resources = exynos_displayport_noop, + .set_resources = exynos_displayport_noop, + .enable_resources = exynos_displayport_noop, + .init = exynos_displayport_init, + .scan_bus = exynos_displayport_noop, +}; + +static void exynos_displayport_enable(struct device *dev) +{ + if (dev->link_list != NULL) + dev->ops = &exynos_displayport_operations; +} + +struct chip_operations drivers_i2c_exynos_displayport_ops = { + CHIP_NAME("exynos displayport") + .enable_dev = exynos_displayport_enable; +}; diff --git a/src/cpu/samsung/exynos5-common/exynos-fb.c b/src/cpu/samsung/exynos5-common/exynos-fb.c new file mode 100644 index 0000000000..30d0767923 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/exynos-fb.c @@ -0,0 +1,594 @@ +/* + * LCD driver for Exynos + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; 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 <common.h> +#include <arch/io.h> +#include <stdlib.h> +#include <string.h> +#include <console/console.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/power.h> +#include <cpu/samsung/exynos5250/sysreg.h> +#include <drivers/maxim/max77686/max77686.h> + +#include "device/i2c.h" +#include "cpu/samsung/exynos5-common/i2c.h" +#include "cpu/samsung/exynos5250/dsim.h" +#include "cpu/samsung/exynos5250/fimd.h" + +#include "cpu/samsung/exynos5250/s5p-dp.h" +#include "s5p-dp-core.h" + +/* 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_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +short console_col; +short console_row; + + +#ifdef CONFIG_EXYNOS_DISPLAYPORT +static struct s5p_dp_device dp_device; + +#endif + +/* 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; +} + +/* Calculate the size of Framebuffer from the resolution */ +static u32 calc_fbsize(vidinfo_t *panel_info) +{ + /* They had PAGE_SIZE here instead of 4096. + * but that's a totally arbitrary number -- everything nowadays + * has lots of page sizes. + * So keep it obvious. + */ + return ALIGN((panel_info->vl_col * panel_info->vl_row * + ((1<<panel_info->vl_bpix) / 8)), 4096); +} + +/* + * Initialize display controller. + * + * @param lcdbase pointer to the base address of framebuffer. + * @pd pointer to the main panel_data structure + */ +void fb_init(vidinfo_t *panel_info, void *lcdbase, struct exynos5_fimd_panel *pd) +{ + unsigned int val; + u32 fbsize; + struct exynos5_fimd *fimd = + samsung_get_base_fimd(); + struct exynos5_disp_ctrl *disp_ctrl = + samsung_get_base_disp_ctrl(); + + 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)lcd_base, &fimd->vidw00add0b0); + + fbsize = calc_fbsize(panel_info); + writel((unsigned int)lcd_base + fbsize, &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); +} + +void exynos_fimd_disable(void); +void exynos_fimd_disable(void) +{ + struct exynos5_fimd *fimd = samsung_get_base_fimd(); + + writel(0, &fimd->wincon0); + clrbits_le32(&fimd->shadowcon, CHANNEL0_EN); +} + +/* + * 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; + u32 start; + struct exynos5_dp *base = dp->base; + + 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) { + debug("PLL is not locked yet.\n"); + return -ERR_PLL_NOT_UNLOCKED; + } + + start = get_timer(0); + do { + if (s5p_dp_is_slave_video_stream_clock_on(dp) == 0) { + timeout++; + break; + } + } while (get_timer(start) <= STREAM_ON_TIMEOUT); + + if (!timeout) { + debug("Video Clock Not ok\n"); + 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) { + 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)) { + 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))) { + 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)) { + 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))) { + 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); + debug("LCD retry init, attempt=%d ret=%d\n", i, ret); + } + if (i == DP_INIT_TRIES) { + 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: + 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) +{ + u32 data; + u32 start; + int lane; + struct exynos5_dp *base = dp->base; + + /* Stop Video */ + clrbits_le32(&base->video_ctl_1, VIDEO_EN); + + start = get_timer(0); + while (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + if (get_timer(start) > PLL_LOCK_TIMEOUT) { + /* Ignore this error, and try to continue */ + printk(BIOS_ERR, "PLL is not locked yet.\n"); + break; + } + } + + /* 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)) { + 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); + + if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) && + (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) { + 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) { + 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 unitl HW link training done */ + s5p_dp_wait_hw_link_training_done(dp); + + /* Get hardware link training status */ + data = readl(&base->dp_hw_link_training); + if (data != 0) { + debug(" 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; + + return 0; +} + +/* + * Initialize DP display + */ +int dp_controller_init(struct s5p_dp_device *dp_device, unsigned *wait_ms) +{ + int ret; + struct s5p_dp_device *dp = dp_device; + struct exynos5_dp *base; + + //dp->base = (struct exynos5_dp *)addr; + /* yes. we're a snow. Yet somehow our config is from a development kit? + * This Must Change */ + //dp->video_info = &smdk5250_dp_config; + + clock_init_dp_clock(); + + power_enable_dp_phy(); + ret = s5p_dp_init_dp(dp); + if (ret) { + debug("%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) { + debug("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) { + debug("unable to set scramble mode\n"); + return ret; + } + + ret = s5p_dp_enable_rx_to_enhanced_mode(dp); + if (ret) { + debug("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) { + debug("unable to config video\n"); + return ret; + } + + /* + * This delay is T3 in the LCD timing spec (defined as >200ms). We set + * this down to 60ms since that's the approximate maximum amount of time + * it'll take a bridge to start outputting LVDS data. The delay of + * >200ms is just a conservative value to avoid turning on the backlight + * when there's random LCD data on the screen. Shaving 140ms off the + * boot is an acceptable trade-off. + */ + *wait_ms = 60; + 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(vidinfo_t *panel_info, struct exynos5_fimd_panel *panel_data, void *lcdbase) +{ + int ret = 0; + + //vi->res = panel_info->vl_col; + //vi->yres = panel_info->vl_row; + + fimd_bypass(); + fb_init(panel_info, lcdbase, panel_data); + + /* Enable flushing after LCD writes if requested */ + // forget it. lcd_set_flush_dcache(1); + return ret; +} diff --git a/src/cpu/samsung/exynos5-common/s5p-dp-core.h b/src/cpu/samsung/exynos5-common/s5p-dp-core.h new file mode 100644 index 0000000000..67c1990cfd --- /dev/null +++ b/src/cpu/samsung/exynos5-common/s5p-dp-core.h @@ -0,0 +1,256 @@ +/* + * Header file for Samsung DP (Display Port) interface driver. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@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. + */ + +#ifndef _S5P_DP_CORE_H +#define _S5P_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; +}; + +/* this struct is used by mainboards to pass mode info to the driver */ +typedef struct vidinfo { + u16 vl_col; + u16 vl_row; + u8 vl_bpix; + u16 *cmap; +} vidinfo_t; + +/* 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 */ +void fb_init(vidinfo_t *panel_info, void *lcdbase, struct exynos5_fimd_panel *pd); +int dp_controller_init(struct s5p_dp_device *dp_device, unsigned *wait_ms); +int lcd_ctrl_init(vidinfo_t *panel_info, struct exynos5_fimd_panel *panel_data, void *lcdbase); +#endif /* _S5P_DP_CORE_H */ diff --git a/src/cpu/samsung/exynos5-common/s5p-dp-reg.c b/src/cpu/samsung/exynos5-common/s5p-dp-reg.c new file mode 100644 index 0000000000..adb64a8e10 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/s5p-dp-reg.c @@ -0,0 +1,481 @@ +/* + * Samsung DP (Display port) register interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@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. + */ + +#include <common.h> +#include <arch/io.h> +#include <cpu/samsung/exynos5-common/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/periph.h> +#include <cpu/samsung/exynos5250/s5p-dp.h> +#include "cpu/samsung/exynos5250/fimd.h" +#include "s5p-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; + u32 start; + 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); + + start = get_timer(0); + while (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + if (get_timer(start) > PLL_LOCK_TIMEOUT) { + debug("%s: PLL is not locked yet\n", __func__); + return -1; + } + } + } + + /* 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) { + 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) { + debug("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 + 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 + 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) { + 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; + u32 start; + struct exynos5_dp *base = dp->base; + + /* Wait for 4 VSYNC_DET interrupts */ + start = get_timer(0); + 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; + } while (get_timer(start) <= STREAM_ON_TIMEOUT); + + if (i != 4) { + 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); +} |