summaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/exynos_dp.c925
-rw-r--r--drivers/video/exynos_dp_lowlevel.c1291
-rw-r--r--drivers/video/exynos_dp_lowlevel.h80
4 files changed, 2297 insertions, 0 deletions
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 2f8e2b521a..143bdc98e0 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -29,6 +29,7 @@ COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
COBJS-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o
+COBJS-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o
COBJS-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o
COBJS-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
exynos_mipi_dsi_lowlevel.o
diff --git a/drivers/video/exynos_dp.c b/drivers/video/exynos_dp.c
new file mode 100644
index 0000000000..53e410120a
--- /dev/null
+++ b/drivers/video/exynos_dp.c
@@ -0,0 +1,925 @@
+/*
+ * 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 <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <linux/err.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/dp_info.h>
+#include <asm/arch/dp.h>
+
+#include "exynos_dp_lowlevel.h"
+
+static struct exynos_dp_platform_data *dp_pd;
+
+static void exynos_dp_disp_info(struct edp_disp_info *disp_info)
+{
+ disp_info->h_total = disp_info->h_res + disp_info->h_sync_width +
+ disp_info->h_back_porch + disp_info->h_front_porch;
+ disp_info->v_total = disp_info->v_res + disp_info->v_sync_width +
+ disp_info->v_back_porch + disp_info->v_front_porch;
+
+ return;
+}
+
+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;
+
+ /*
+ * 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 */
+ exynos_dp_read_byte_from_i2c(I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG,
+ &extend_block);
+
+ if (extend_block > 0) {
+ printf("DP EDID data includes a single extension!\n");
+
+ /* 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) {
+ printf("DP EDID Read failed!\n");
+ return -1;
+ }
+ sum = exynos_dp_calc_edid_check_sum(edid);
+ if (sum != 0) {
+ printf("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) {
+ printf("DP EDID Read failed!\n");
+ return -1;
+ }
+ sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
+ if (sum != 0) {
+ printf("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 {
+ debug("DP EDID data does not include any extensions.\n");
+
+ /* 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) {
+ printf("DP EDID Read failed!\n");
+ return -1;
+ }
+ sum = exynos_dp_calc_edid_check_sum(edid);
+ if (sum != 0) {
+ printf("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);
+ }
+ }
+
+ debug("DP EDID Read success!\n");
+
+ 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, 16);
+ memset(lane_bw, 0, 16);
+ memset(lane_cnt, 0, 16);
+ memset(buf, 0, 12);
+
+ 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) {
+ printf("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 {
+ printf("DP Wrong DPCD Rev : %x\n", temp);
+ return -ENODEV;
+ }
+
+ 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 {
+ printf("DP Wrong MAX LINK RATE : %x\n", temp);
+ return -EINVAL;
+ }
+
+ /*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 {
+ printf("DP Wrong MAX LANE COUNT : %x\n", temp);
+ return -EINVAL;
+ }
+
+ ret = exynos_dp_read_edid();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP exynos_dp_read_edid() failed\n");
+ return -EINVAL;
+ }
+
+ 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 = 0;
+
+ debug("DP: %s was called\n", __func__);
+
+ 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) {
+ printf("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) {
+ printf("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) {
+ printf("DP write_dpcd_byte failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_training_pattern_dis(void)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+
+ 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) {
+ printf("DP requst_link_traninig_req failed\n");
+ return -EAGAIN;
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_enable_rx_to_enhanced_mode(unsigned char enable)
+{
+ unsigned char data;
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+
+ ret = exynos_dp_read_byte_from_dpcd(DPCD_LANE_COUNT_SET,
+ &data);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read_from_dpcd failed\n");
+ return -EAGAIN;
+ }
+
+ 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) {
+ printf("DP write_to_dpcd failed\n");
+ return -EAGAIN;
+
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_set_enhanced_mode(unsigned char enhance_mode)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+
+ ret = exynos_dp_enable_rx_to_enhanced_mode(enhance_mode);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP rx_enhance_mode failed\n");
+ return -EAGAIN;
+ }
+
+ 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,};
+ unsigned char shift_val[DP_LANE_CNT_4] = {0,};
+
+ shift_val[0] = 0;
+ shift_val[1] = 4;
+ shift_val[2] = 0;
+ shift_val[3] = 4;
+
+ ret = exynos_dp_read_bytes_from_dpcd(DPCD_LANE0_1_STATUS, 2, buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("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]) {
+ printf("Wrong lane status\n");
+ return -EINVAL;
+ }
+ }
+
+ *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)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+ unsigned char buf;
+ unsigned int dpcd_addr;
+ unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4};
+
+ /*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) {
+ printf("DP read adjust request failed\n");
+ return -EAGAIN;
+ }
+
+ *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) {
+ printf("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) {
+ printf("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;
+ printf("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)
+ printf("DP training_patter_disable() failed\n");
+
+ ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc);
+ if (ret != EXYNOS_DP_SUCCESS)
+ printf("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 = EXYNOS_DP_SUCCESS;
+ 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];
+
+ debug("DP: %s was called\n", __func__);
+ mdelay(1);
+
+ ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("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) {
+ 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) {
+ printf("DP write traning 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) {
+ printf("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) {
+ printf("DP write traning 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 = EXYNOS_DP_SUCCESS;
+ 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) {
+ printf("DP read lane status failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ }
+
+ debug("DP lane stat : %x\n", lane_stat);
+
+ if (lane_stat & DP_LANE_STAT_CR_DONE) {
+ 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;
+
+ return ret;
+ }
+
+ interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE);
+
+ 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) {
+ printf("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)) {
+ debug("DP Equalizer training succeed\n");
+
+ f_bw = exynos_dp_get_link_bandwidth();
+ f_lane_cnt = exynos_dp_get_lane_count();
+
+ debug("DP final BandWidth : %x\n", f_bw);
+ 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) {
+ printf("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)
+{
+ 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) {
+ printf("DP LT:link start failed\n");
+ return ret;
+ }
+ break;
+ case DP_LT_CR:
+ ret = exynos_dp_process_clock_recovery(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP LT:clock recovery failed\n");
+ return ret;
+ }
+ break;
+ case DP_LT_ET:
+ ret = exynos_dp_process_equalizer_training(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP LT:equalizer training failed\n");
+ return ret;
+ }
+ break;
+ case DP_LT_FINISHED:
+ training_finished = 1;
+ break;
+ case DP_LT_FAIL:
+ return -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)
+ printf("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) {
+ printf("DP does not support master mode\n");
+ return -ENODEV;
+ } else {
+ /* debug slave */
+ exynos_dp_config_video_slave_mode(&edp_info->video_info);
+ }
+
+ exynos_dp_set_video_color_format(&edp_info->video_info);
+
+ if (edp_info->video_info.bist_mode) {
+ if (exynos_dp_config_video_bist(edp_info) != 0)
+ return -1;
+ }
+
+ ret = exynos_dp_get_pll_lock_status();
+ if (ret != PLL_LOCKED) {
+ printf("DP PLL is not locked yet\n");
+ return -EIO;
+ }
+
+ 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) {
+ printf("DP stream_clock_on failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ mdelay(1);
+ } else
+ 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 */
+ exynos_dp_set_video_timing_mode(VIDEO_TIMING_FROM_CAPTURE);
+
+ /* Enable video bist */
+ if (edp_info->video_info.bist_pattern != COLOR_RAMP &&
+ edp_info->video_info.bist_pattern != BALCK_WHITE_V_LINES &&
+ edp_info->video_info.bist_pattern != COLOR_SQUARE)
+ exynos_dp_enable_video_bist(edp_info->video_info.bist_mode);
+ else
+ exynos_dp_enable_video_bist(DP_DISABLE);
+
+ /* 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) {
+ if (retry_cnt == 0) {
+ printf("DP Timeout of video stream\n");
+ return ret;
+ }
+ retry_cnt--;
+ mdelay(5);
+ } else
+ break;
+ }
+ }
+
+ return ret;
+}
+
+unsigned int exynos_init_dp(void)
+{
+ unsigned int ret;
+ struct edp_device_info *edp_info;
+ struct edp_disp_info disp_info;
+
+ edp_info = kzalloc(sizeof(struct edp_device_info), GFP_KERNEL);
+ if (!edp_info) {
+ debug("failed to allocate edp device object.\n");
+ return -EFAULT;
+ }
+
+ edp_info = dp_pd->edp_dev_info;
+ if (edp_info == NULL) {
+ debug("failed to get edp_info data.\n");
+ return -EFAULT;
+ }
+ disp_info = edp_info->disp_info;
+
+ exynos_dp_disp_info(&edp_info->disp_info);
+
+ if (dp_pd->phy_enable)
+ dp_pd->phy_enable(1);
+
+ ret = exynos_dp_init_dp();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP exynos_dp_init_dp() failed\n");
+ return ret;
+ }
+
+ ret = exynos_dp_handle_edid(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("EDP handle_edid fail\n");
+ return ret;
+ }
+
+ ret = exynos_dp_set_link_train(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP link training fail\n");
+ return ret;
+ }
+
+ 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) {
+ printf("Exynos DP init failed\n");
+ return ret;
+ }
+
+ printf("Exynos DP init done\n");
+
+ return ret;
+}
+
+void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd)
+{
+ if (pd == NULL) {
+ debug("pd is NULL\n");
+ return;
+ }
+
+ dp_pd = pd;
+}
diff --git a/drivers/video/exynos_dp_lowlevel.c b/drivers/video/exynos_dp_lowlevel.c
new file mode 100644
index 0000000000..7b54c8084c
--- /dev/null
+++ b/drivers/video/exynos_dp_lowlevel.c
@@ -0,0 +1,1291 @@
+/*
+ * 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 <config.h>
+#include <common.h>
+#include <linux/err.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/dp_info.h>
+#include <asm/arch/dp.h>
+
+static void exynos_dp_enable_video_input(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl1);
+ reg &= ~VIDEO_EN_MASK;
+
+ /* enable video input*/
+ if (enable)
+ reg |= VIDEO_EN_MASK;
+
+ writel(reg, &dp_regs->video_ctl1);
+
+ return;
+}
+
+void exynos_dp_enable_video_bist(unsigned int enable)
+{
+ /*enable video bist*/
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl4);
+ reg &= ~VIDEO_BIST_MASK;
+
+ /*enable video bist*/
+ if (enable)
+ reg |= VIDEO_BIST_MASK;
+
+ writel(reg, &dp_regs->video_ctl4);
+
+ return;
+}
+
+void exynos_dp_enable_video_mute(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl1);
+ reg &= ~(VIDEO_MUTE_MASK);
+ if (enable)
+ reg |= VIDEO_MUTE_MASK;
+
+ writel(reg, &dp_regs->video_ctl1);
+
+ return;
+}
+
+
+static void exynos_dp_init_analog_param(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /*
+ * Set termination
+ * Normal bandgap, Normal swing, Tx terminal registor 61 ohm
+ * 24M Phy clock, TX digital logic power is 100:1.0625V
+ */
+ reg = SEL_BG_NEW_BANDGAP | TX_TERMINAL_CTRL_61_OHM |
+ SWING_A_30PER_G_NORMAL;
+ writel(reg, &dp_regs->analog_ctl1);
+
+ reg = SEL_24M | TX_DVDD_BIT_1_0625V;
+ writel(reg, &dp_regs->analog_ctl2);
+
+ /*
+ * Set power source for internal clk driver to 1.0625v.
+ * Select current reference of TX driver current to 00:Ipp/2+Ic/2.
+ * Set VCO range of PLL +- 0uA
+ */
+ reg = DRIVE_DVDD_BIT_1_0625V | SEL_CURRENT_DEFAULT | VCO_BIT_000_MICRO;
+ writel(reg, &dp_regs->analog_ctl3);
+
+ /*
+ * Set AUX TX terminal resistor to 102 ohm
+ * Set AUX channel amplitude control
+ */
+ reg = PD_RING_OSC | AUX_TERMINAL_CTRL_52_OHM | TX_CUR1_2X | TX_CUR_4_MA;
+ writel(reg, &dp_regs->pll_filter_ctl1);
+
+ /*
+ * PLL loop filter bandwidth
+ * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz
+ * PLL digital power select: 1.2500V
+ */
+ reg = CH3_AMP_0_MV | CH2_AMP_0_MV | CH1_AMP_0_MV | CH0_AMP_0_MV;
+
+ writel(reg, &dp_regs->amp_tuning_ctl);
+
+ /*
+ * PLL loop filter bandwidth
+ * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz
+ * PLL digital power select: 1.1250V
+ */
+ reg = DP_PLL_LOOP_BIT_DEFAULT | DP_PLL_REF_BIT_1_1250V;
+ writel(reg, &dp_regs->pll_ctl);
+}
+
+static void exynos_dp_init_interrupt(void)
+{
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ /* Set interrupt registers to initial states */
+
+ /*
+ * Disable interrupt
+ * INT pin assertion polarity. It must be configured
+ * correctly according to ICU setting.
+ * 1 = assert high, 0 = assert low
+ */
+ writel(INT_POL, &dp_regs->int_ctl);
+
+ /* Clear pending regisers */
+ writel(0xff, &dp_regs->common_int_sta1);
+ writel(0xff, &dp_regs->common_int_sta2);
+ writel(0xff, &dp_regs->common_int_sta3);
+ writel(0xff, &dp_regs->common_int_sta4);
+ writel(0xff, &dp_regs->int_sta);
+
+ /* 0:mask,1: unmask */
+ writel(0x00, &dp_regs->int_sta_mask1);
+ writel(0x00, &dp_regs->int_sta_mask2);
+ writel(0x00, &dp_regs->int_sta_mask3);
+ writel(0x00, &dp_regs->int_sta_mask4);
+ writel(0x00, &dp_regs->int_sta_mask);
+}
+
+void exynos_dp_reset(void)
+{
+ unsigned int reg_func_1;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /*dp tx sw reset*/
+ writel(RESET_DP_TX, &dp_regs->tx_sw_reset);
+
+ exynos_dp_enable_video_input(DP_DISABLE);
+ exynos_dp_enable_video_bist(DP_DISABLE);
+ exynos_dp_enable_video_mute(DP_DISABLE);
+
+ /* software reset */
+ reg_func_1 = 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_func_1, &dp_regs->func_en1);
+ writel(reg_func_1, &dp_regs->func_en2);
+
+ mdelay(1);
+
+ exynos_dp_init_analog_param();
+ exynos_dp_init_interrupt();
+
+ return;
+}
+
+void exynos_dp_enable_sw_func(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->func_en1);
+ reg &= ~(SW_FUNC_EN_N);
+
+ if (!enable)
+ reg |= SW_FUNC_EN_N;
+
+ writel(reg, &dp_regs->func_en1);
+
+ return;
+}
+
+unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->phy_pd);
+ switch (block) {
+ case AUX_BLOCK:
+ reg &= ~(AUX_PD);
+ if (enable)
+ reg |= AUX_PD;
+ break;
+ case CH0_BLOCK:
+ reg &= ~(CH0_PD);
+ if (enable)
+ reg |= CH0_PD;
+ break;
+ case CH1_BLOCK:
+ reg &= ~(CH1_PD);
+ if (enable)
+ reg |= CH1_PD;
+ break;
+ case CH2_BLOCK:
+ reg &= ~(CH2_PD);
+ if (enable)
+ reg |= CH2_PD;
+ break;
+ case CH3_BLOCK:
+ reg &= ~(CH3_PD);
+ if (enable)
+ reg |= CH3_PD;
+ break;
+ case ANALOG_TOTAL:
+ reg &= ~PHY_PD;
+ if (enable)
+ reg |= PHY_PD;
+ break;
+ case POWER_ALL:
+ reg &= ~(PHY_PD | AUX_PD | CH0_PD | CH1_PD | CH2_PD |
+ CH3_PD);
+ if (enable)
+ reg |= (PHY_PD | AUX_PD | CH0_PD | CH1_PD |
+ CH2_PD | CH3_PD);
+ break;
+ default:
+ printf("DP undefined block number : %d\n", block);
+ return -1;
+ }
+
+ writel(reg, &dp_regs->phy_pd);
+
+ return 0;
+}
+
+unsigned int exynos_dp_get_pll_lock_status(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->debug_ctl);
+
+ if (reg & PLL_LOCK)
+ return PLL_LOCKED;
+ else
+ return PLL_UNLOCKED;
+}
+
+static void exynos_dp_set_pll_power(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->pll_ctl);
+ reg &= ~(DP_PLL_PD);
+
+ if (!enable)
+ reg |= DP_PLL_PD;
+
+ writel(reg, &dp_regs->pll_ctl);
+}
+
+int exynos_dp_init_analog_func(void)
+{
+ int ret = EXYNOS_DP_SUCCESS;
+ unsigned int retry_cnt = 10;
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /*Power On All Analog block */
+ exynos_dp_set_analog_power_down(POWER_ALL, DP_DISABLE);
+
+ reg = PLL_LOCK_CHG;
+ writel(reg, &dp_regs->common_int_sta1);
+
+ reg = readl(&dp_regs->debug_ctl);
+ reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+ writel(reg, &dp_regs->debug_ctl);
+
+ /*Assert DP PLL Reset*/
+ reg = readl(&dp_regs->pll_ctl);
+ reg |= DP_PLL_RESET;
+ writel(reg, &dp_regs->pll_ctl);
+
+ mdelay(1);
+
+ /*Deassert DP PLL Reset*/
+ reg = readl(&dp_regs->pll_ctl);
+ reg &= ~(DP_PLL_RESET);
+ writel(reg, &dp_regs->pll_ctl);
+
+ exynos_dp_set_pll_power(DP_ENABLE);
+
+ while (exynos_dp_get_pll_lock_status() == PLL_UNLOCKED) {
+ mdelay(1);
+ retry_cnt--;
+ if (retry_cnt == 0) {
+ printf("DP dp's pll lock failed : retry : %d\n",
+ retry_cnt);
+ return -EINVAL;
+ }
+ }
+
+ debug("dp's pll lock success(%d)\n", retry_cnt);
+
+ /* Enable Serdes FIFO function and Link symbol clock domain module */
+ reg = readl(&dp_regs->func_en2);
+ reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+ | AUX_FUNC_EN_N);
+ writel(reg, &dp_regs->func_en2);
+
+ return ret;
+}
+
+void exynos_dp_init_hpd(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear interrupts releated to Hot Plug Dectect */
+ reg = HOTPLUG_CHG | HPD_LOST | PLUG;
+ writel(reg, &dp_regs->common_int_sta4);
+
+ reg = INT_HPD;
+ writel(reg, &dp_regs->int_sta);
+
+ reg = readl(&dp_regs->sys_ctl3);
+ reg &= ~(F_HPD | HPD_CTRL);
+ writel(reg, &dp_regs->sys_ctl3);
+
+ return;
+}
+
+static inline void exynos_dp_reset_aux(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Disable AUX channel module */
+ reg = readl(&dp_regs->func_en2);
+ reg |= AUX_FUNC_EN_N;
+ writel(reg, &dp_regs->func_en2);
+
+ return;
+}
+
+void exynos_dp_init_aux(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear inerrupts related to AUX channel */
+ reg = RPLY_RECEIV | AUX_ERR;
+ writel(reg, &dp_regs->int_sta);
+
+ exynos_dp_reset_aux();
+
+ /* Disable AUX transaction H/W retry */
+ reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(3)|
+ AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+ writel(reg, &dp_regs->aux_hw_retry_ctl);
+
+ /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+ reg = DEFER_CTRL_EN | DEFER_COUNT(1);
+ writel(reg, &dp_regs->aux_ch_defer_ctl);
+
+ /* Enable AUX channel module */
+ reg = readl(&dp_regs->func_en2);
+ reg &= ~AUX_FUNC_EN_N;
+ writel(reg, &dp_regs->func_en2);
+
+ return;
+}
+
+void exynos_dp_config_interrupt(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* 0: mask, 1: unmask */
+ reg = COMMON_INT_MASK_1;
+ writel(reg, &dp_regs->common_int_mask1);
+
+ reg = COMMON_INT_MASK_2;
+ writel(reg, &dp_regs->common_int_mask2);
+
+ reg = COMMON_INT_MASK_3;
+ writel(reg, &dp_regs->common_int_mask3);
+
+ reg = COMMON_INT_MASK_4;
+ writel(reg, &dp_regs->common_int_mask4);
+
+ reg = INT_STA_MASK;
+ writel(reg, &dp_regs->int_sta_mask);
+
+ return;
+}
+
+unsigned int exynos_dp_get_plug_in_status(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->sys_ctl3);
+ if (reg & HPD_STATUS)
+ return 0;
+
+ return -1;
+}
+
+unsigned int exynos_dp_detect_hpd(void)
+{
+ int timeout_loop = DP_TIMEOUT_LOOP_COUNT;
+
+ mdelay(2);
+
+ while (exynos_dp_get_plug_in_status() != 0) {
+ if (timeout_loop == 0)
+ return -EINVAL;
+ mdelay(10);
+ timeout_loop--;
+ }
+
+ return EXYNOS_DP_SUCCESS;
+}
+
+unsigned int exynos_dp_start_aux_transaction(void)
+{
+ unsigned int reg;
+ unsigned int ret = 0;
+ unsigned int retry_cnt;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Enable AUX CH operation */
+ reg = readl(&dp_regs->aux_ch_ctl2);
+ reg |= AUX_EN;
+ writel(reg, &dp_regs->aux_ch_ctl2);
+
+ retry_cnt = 10;
+ while (retry_cnt) {
+ reg = readl(&dp_regs->int_sta);
+ if (!(reg & RPLY_RECEIV)) {
+ if (retry_cnt == 0) {
+ printf("DP Reply Timeout!!\n");
+ ret = -EAGAIN;
+ return ret;
+ }
+ mdelay(1);
+ retry_cnt--;
+ } else
+ break;
+ }
+
+ /* Clear interrupt source for AUX CH command reply */
+ writel(reg, &dp_regs->int_sta);
+
+ /* Clear interrupt source for AUX CH access error */
+ reg = readl(&dp_regs->int_sta);
+ if (reg & AUX_ERR) {
+ printf("DP Aux Access Error\n");
+ writel(AUX_ERR, &dp_regs->int_sta);
+ ret = -EAGAIN;
+ return ret;
+ }
+
+ /* Check AUX CH error access status */
+ reg = readl(&dp_regs->aux_ch_sta);
+ if ((reg & AUX_STATUS_MASK) != 0) {
+ debug("DP AUX CH error happens: %x\n", reg & AUX_STATUS_MASK);
+ ret = -EAGAIN;
+ return ret;
+ }
+
+ return EXYNOS_DP_SUCCESS;
+}
+
+unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr,
+ unsigned char data)
+{
+ unsigned int reg, ret;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr);
+ writel(reg, &dp_regs->aux_addr_19_16);
+
+ /* Write data buffer */
+ reg = (unsigned int)data;
+ writel(reg, &dp_regs->buf_data0);
+
+ /*
+ * 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, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ ret = exynos_dp_start_aux_transaction();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP Aux transaction failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr,
+ unsigned char *data)
+{
+ unsigned int reg;
+ int retval;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr);
+ writel(reg, &dp_regs->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, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (!retval)
+ debug("DP Aux Transaction fail!\n");
+
+ /* Read data buffer */
+ reg = readl(&dp_regs->buf_data0);
+ *data = (unsigned char)(reg & 0xff);
+
+ return retval;
+}
+
+unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[])
+{
+ unsigned int reg;
+ unsigned int start_offset;
+ unsigned int cur_data_count;
+ unsigned int cur_data_idx;
+ unsigned int retry_cnt;
+ unsigned int ret = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ start_offset = 0;
+ while (start_offset < count) {
+ /* Buffer size of AUX CH is 16 * 4bytes */
+ if ((count - start_offset) > 16)
+ cur_data_count = 16;
+ else
+ cur_data_count = count - start_offset;
+
+ retry_cnt = 5;
+ while (retry_cnt) {
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_19_16);
+
+ for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+ cur_data_idx++) {
+ reg = data[start_offset + cur_data_idx];
+ writel(reg, (unsigned int)&dp_regs->buf_data0 +
+ (4 * cur_data_idx));
+ }
+ /*
+ * Set DisplayPort transaction and write
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(cur_data_count) |
+ AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ ret = exynos_dp_start_aux_transaction();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP Aux Transaction failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ } else
+ break;
+ }
+ start_offset += cur_data_count;
+ }
+
+ return ret;
+}
+
+unsigned int exynos_dp_read_bytes_from_dpcd(unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[])
+{
+ unsigned int reg;
+ unsigned int start_offset;
+ unsigned int cur_data_count;
+ unsigned int cur_data_idx;
+ unsigned int retry_cnt;
+ unsigned int ret = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ start_offset = 0;
+ while (start_offset < count) {
+ /* Buffer size of AUX CH is 16 * 4bytes */
+ if ((count - start_offset) > 16)
+ cur_data_count = 16;
+ else
+ cur_data_count = count - start_offset;
+
+ retry_cnt = 5;
+ while (retry_cnt) {
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_19_16);
+ /*
+ * Set DisplayPort transaction and read
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(cur_data_count) |
+ AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ ret = exynos_dp_start_aux_transaction();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP Aux Transaction failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ } else
+ break;
+ }
+
+ for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+ cur_data_idx++) {
+ reg = readl((unsigned int)&dp_regs->buf_data0 +
+ 4 * cur_data_idx);
+ data[start_offset + cur_data_idx] = (unsigned char)reg;
+ }
+
+ start_offset += cur_data_count;
+ }
+
+ return ret;
+}
+
+int exynos_dp_select_i2c_device(unsigned int device_addr,
+ unsigned int reg_addr)
+{
+ unsigned int reg;
+ int retval;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Set EDID device address */
+ reg = device_addr;
+ writel(reg, &dp_regs->aux_addr_7_0);
+ writel(0x0, &dp_regs->aux_addr_15_8);
+ writel(0x0, &dp_regs->aux_addr_19_16);
+
+ /* Set offset from base address of EDID device */
+ writel(reg_addr, &dp_regs->buf_data0);
+
+ /*
+ * Set I2C transaction and write address
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+ AUX_TX_COMM_WRITE;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (retval != 0)
+ printf("%s: DP Aux Transaction fail!\n", __func__);
+
+ return retval;
+}
+
+int exynos_dp_read_byte_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr,
+ unsigned int *data)
+{
+ unsigned int reg;
+ int i;
+ int retval;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ for (i = 0; i < 10; i++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Select EDID device */
+ retval = exynos_dp_select_i2c_device(device_addr, reg_addr);
+ if (retval != 0) {
+ printf("DP Select EDID device fail. retry !\n");
+ continue;
+ }
+
+ /*
+ * Set I2C transaction and read data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (retval != EXYNOS_DP_SUCCESS)
+ printf("%s: DP Aux Transaction fail!\n", __func__);
+ }
+
+ /* Read data */
+ if (retval == 0)
+ *data = readl(&dp_regs->buf_data0);
+
+ return retval;
+}
+
+int exynos_dp_read_bytes_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr, unsigned int count, unsigned char edid[])
+{
+ unsigned int reg;
+ unsigned int i, j;
+ unsigned int cur_data_idx;
+ unsigned int defer = 0;
+ int retval = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ for (i = 0; i < count; i += 16) { /* use 16 burst */
+ for (j = 0; j < 100; j++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Set normal AUX CH command */
+ reg = readl(&dp_regs->aux_ch_ctl2);
+ reg &= ~ADDR_ONLY;
+ writel(reg, &dp_regs->aux_ch_ctl2);
+
+ /*
+ * If Rx sends defer, Tx sends only reads
+ * request without sending addres
+ */
+ if (!defer)
+ retval =
+ exynos_dp_select_i2c_device(device_addr,
+ reg_addr + i);
+ else
+ defer = 0;
+
+ if (retval == EXYNOS_DP_SUCCESS) {
+ /*
+ * Set I2C transaction and write data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(16) |
+ AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (retval == 0)
+ break;
+ else
+ printf("DP Aux Transaction fail!\n");
+ }
+ /* Check if Rx sends defer */
+ reg = readl(&dp_regs->aux_rx_comm);
+ if (reg == AUX_RX_COMM_AUX_DEFER ||
+ reg == AUX_RX_COMM_I2C_DEFER) {
+ printf("DP Defer: %d\n\n", reg);
+ defer = 1;
+ }
+ }
+
+ for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+ reg = readl((unsigned int)&dp_regs->buf_data0
+ + 4 * cur_data_idx);
+ edid[i + cur_data_idx] = (unsigned char)reg;
+ }
+ }
+
+ return retval;
+}
+
+void exynos_dp_reset_macro(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->phy_test);
+ reg |= MACRO_RST;
+ writel(reg, &dp_regs->phy_test);
+
+ /* 10 us is the minimum Macro reset time. */
+ mdelay(1);
+
+ reg &= ~MACRO_RST;
+ writel(reg, &dp_regs->phy_test);
+}
+
+void exynos_dp_set_link_bandwidth(unsigned char bwtype)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = (unsigned int)bwtype;
+
+ /* Set bandwidth to 2.7G or 1.62G */
+ if ((bwtype == DP_LANE_BW_1_62) || (bwtype == DP_LANE_BW_2_70))
+ writel(reg, &dp_regs->link_bw_set);
+}
+
+unsigned char exynos_dp_get_link_bandwidth(void)
+{
+ unsigned char ret;
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->link_bw_set);
+ ret = (unsigned char)reg;
+
+ return ret;
+}
+
+void exynos_dp_set_lane_count(unsigned char count)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = (unsigned int)count;
+
+ if ((count == DP_LANE_CNT_1) || (count == DP_LANE_CNT_2) ||
+ (count == DP_LANE_CNT_4))
+ writel(reg, &dp_regs->lane_count_set);
+}
+
+unsigned int exynos_dp_get_lane_count(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->lane_count_set);
+
+ return reg;
+}
+
+unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt)
+{
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ unsigned int reg_list[DP_LANE_CNT_4] = {
+ (unsigned int)&dp_regs->ln0_link_training_ctl,
+ (unsigned int)&dp_regs->ln1_link_training_ctl,
+ (unsigned int)&dp_regs->ln2_link_training_ctl,
+ (unsigned int)&dp_regs->ln3_link_training_ctl,
+ };
+
+ return readl(reg_list[lanecnt]);
+}
+
+void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val,
+ unsigned char lanecnt)
+{
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ unsigned int reg_list[DP_LANE_CNT_4] = {
+ (unsigned int)&dp_regs->ln0_link_training_ctl,
+ (unsigned int)&dp_regs->ln1_link_training_ctl,
+ (unsigned int)&dp_regs->ln2_link_training_ctl,
+ (unsigned int)&dp_regs->ln3_link_training_ctl,
+ };
+
+ writel(request_val, reg_list[lanecnt]);
+}
+
+void exynos_dp_set_lane_pre_emphasis(unsigned int level, unsigned char lanecnt)
+{
+ unsigned char i;
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ unsigned int reg_list[DP_LANE_CNT_4] = {
+ (unsigned int)&dp_regs->ln0_link_training_ctl,
+ (unsigned int)&dp_regs->ln1_link_training_ctl,
+ (unsigned int)&dp_regs->ln2_link_training_ctl,
+ (unsigned int)&dp_regs->ln3_link_training_ctl,
+ };
+ unsigned int reg_shift[DP_LANE_CNT_4] = {
+ PRE_EMPHASIS_SET_0_SHIFT,
+ PRE_EMPHASIS_SET_1_SHIFT,
+ PRE_EMPHASIS_SET_2_SHIFT,
+ PRE_EMPHASIS_SET_3_SHIFT
+ };
+
+ for (i = 0; i < lanecnt; i++) {
+ reg = level << reg_shift[i];
+ writel(reg, reg_list[i]);
+ }
+}
+
+void exynos_dp_set_training_pattern(unsigned int pattern)
+{
+ unsigned int reg = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ switch (pattern) {
+ case PRBS7:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+ break;
+ case D10_2:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+ break;
+ case TRAINING_PTN1:
+ reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+ break;
+ case TRAINING_PTN2:
+ reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+ break;
+ case DP_NONE:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_DISABLE |
+ SW_TRAINING_PATTERN_SET_NORMAL;
+ break;
+ default:
+ break;
+ }
+
+ writel(reg, &dp_regs->training_ptn_set);
+}
+
+void exynos_dp_enable_enhanced_mode(unsigned char enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->sys_ctl4);
+ reg &= ~ENHANCED;
+
+ if (enable)
+ reg |= ENHANCED;
+
+ writel(reg, &dp_regs->sys_ctl4);
+}
+
+void exynos_dp_enable_scrambling(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->training_ptn_set);
+ reg &= ~(SCRAMBLING_DISABLE);
+
+ if (!enable)
+ reg |= SCRAMBLING_DISABLE;
+
+ writel(reg, &dp_regs->training_ptn_set);
+}
+
+int exynos_dp_init_video(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear VID_CLK_CHG[1] and VID_FORMAT_CHG[3] and VSYNC_DET[7] */
+ reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+ writel(reg, &dp_regs->common_int_sta1);
+
+ /* I_STRM__CLK detect : DE_CTL : Auto detect */
+ reg &= ~DET_CTRL;
+ writel(reg, &dp_regs->sys_ctl1);
+
+ return 0;
+}
+
+void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Video Slave mode setting */
+ reg = readl(&dp_regs->func_en1);
+ reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
+ reg |= MASTER_VID_FUNC_EN_N;
+ writel(reg, &dp_regs->func_en1);
+
+ /* Configure Interlaced for slave mode video */
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~INTERACE_SCAN_CFG;
+ reg |= (video_info->interlaced << INTERACE_SCAN_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+
+ /* Configure V sync polarity for slave mode video */
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~VSYNC_POLARITY_CFG;
+ reg |= (video_info->v_sync_polarity << V_S_POLARITY_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+
+ /* Configure H sync polarity for slave mode video */
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~HSYNC_POLARITY_CFG;
+ reg |= (video_info->h_sync_polarity << H_S_POLARITY_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+
+ /*Set video mode to slave mode */
+ reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
+ writel(reg, &dp_regs->soc_general_ctl);
+}
+
+void exynos_dp_set_video_color_format(struct edp_video_info *video_info)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Configure the input color depth, color space, dynamic range */
+ reg = (video_info->dynamic_range << IN_D_RANGE_SHIFT) |
+ (video_info->color_depth << IN_BPC_SHIFT) |
+ (video_info->color_space << IN_COLOR_F_SHIFT);
+ writel(reg, &dp_regs->video_ctl2);
+
+ /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+ reg = readl(&dp_regs->video_ctl3);
+ reg &= ~IN_YC_COEFFI_MASK;
+ if (video_info->ycbcr_coeff)
+ reg |= IN_YC_COEFFI_ITU709;
+ else
+ reg |= IN_YC_COEFFI_ITU601;
+ writel(reg, &dp_regs->video_ctl3);
+}
+
+int exynos_dp_config_video_bist(struct edp_device_info *edp_info)
+{
+ unsigned int reg;
+ unsigned int bist_type = 0;
+ struct edp_video_info video_info = edp_info->video_info;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* For master mode, you don't need to set the video format */
+ if (video_info.master_mode == 0) {
+ writel(TOTAL_LINE_CFG_L(edp_info->disp_info.v_total),
+ &dp_regs->total_ln_cfg_l);
+ writel(TOTAL_LINE_CFG_H(edp_info->disp_info.v_total),
+ &dp_regs->total_ln_cfg_h);
+ writel(ACTIVE_LINE_CFG_L(edp_info->disp_info.v_res),
+ &dp_regs->active_ln_cfg_l);
+ writel(ACTIVE_LINE_CFG_H(edp_info->disp_info.v_res),
+ &dp_regs->active_ln_cfg_h);
+ writel(edp_info->disp_info.v_sync_width,
+ &dp_regs->vsw_cfg);
+ writel(edp_info->disp_info.v_back_porch,
+ &dp_regs->vbp_cfg);
+ writel(edp_info->disp_info.v_front_porch,
+ &dp_regs->vfp_cfg);
+
+ writel(TOTAL_PIXEL_CFG_L(edp_info->disp_info.h_total),
+ &dp_regs->total_pix_cfg_l);
+ writel(TOTAL_PIXEL_CFG_H(edp_info->disp_info.h_total),
+ &dp_regs->total_pix_cfg_h);
+ writel(ACTIVE_PIXEL_CFG_L(edp_info->disp_info.h_res),
+ &dp_regs->active_pix_cfg_l);
+ writel(ACTIVE_PIXEL_CFG_H(edp_info->disp_info.h_res),
+ &dp_regs->active_pix_cfg_h);
+ writel(H_F_PORCH_CFG_L(edp_info->disp_info.h_front_porch),
+ &dp_regs->hfp_cfg_l);
+ writel(H_F_PORCH_CFG_H(edp_info->disp_info.h_front_porch),
+ &dp_regs->hfp_cfg_h);
+ writel(H_SYNC_PORCH_CFG_L(edp_info->disp_info.h_sync_width),
+ &dp_regs->hsw_cfg_l);
+ writel(H_SYNC_PORCH_CFG_H(edp_info->disp_info.h_sync_width),
+ &dp_regs->hsw_cfg_h);
+ writel(H_B_PORCH_CFG_L(edp_info->disp_info.h_back_porch),
+ &dp_regs->hbp_cfg_l);
+ writel(H_B_PORCH_CFG_H(edp_info->disp_info.h_back_porch),
+ &dp_regs->hbp_cfg_h);
+
+ /*
+ * Set SLAVE_I_SCAN_CFG[2], VSYNC_P_CFG[1],
+ * HSYNC_P_CFG[0] properly
+ */
+ reg = (video_info.interlaced << INTERACE_SCAN_CFG_SHIFT |
+ video_info.v_sync_polarity << V_S_POLARITY_CFG_SHIFT |
+ video_info.h_sync_polarity << H_S_POLARITY_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+ }
+
+ /* BIST color bar width set--set to each bar is 32 pixel width */
+ switch (video_info.bist_pattern) {
+ case COLORBAR_32:
+ bist_type = BIST_WIDTH_BAR_32_PIXEL |
+ BIST_TYPE_COLOR_BAR;
+ break;
+ case COLORBAR_64:
+ bist_type = BIST_WIDTH_BAR_64_PIXEL |
+ BIST_TYPE_COLOR_BAR;
+ break;
+ case WHITE_GRAY_BALCKBAR_32:
+ bist_type = BIST_WIDTH_BAR_32_PIXEL |
+ BIST_TYPE_WHITE_GRAY_BLACK_BAR;
+ break;
+ case WHITE_GRAY_BALCKBAR_64:
+ bist_type = BIST_WIDTH_BAR_64_PIXEL |
+ BIST_TYPE_WHITE_GRAY_BLACK_BAR;
+ break;
+ case MOBILE_WHITEBAR_32:
+ bist_type = BIST_WIDTH_BAR_32_PIXEL |
+ BIST_TYPE_MOBILE_WHITE_BAR;
+ break;
+ case MOBILE_WHITEBAR_64:
+ bist_type = BIST_WIDTH_BAR_64_PIXEL |
+ BIST_TYPE_MOBILE_WHITE_BAR;
+ break;
+ default:
+ return -1;
+ }
+
+ reg = bist_type;
+ writel(reg, &dp_regs->video_ctl4);
+
+ return 0;
+}
+
+unsigned int exynos_dp_is_slave_video_stream_clock_on(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Update Video stream clk detect status */
+ reg = readl(&dp_regs->sys_ctl1);
+ writel(reg, &dp_regs->sys_ctl1);
+
+ reg = readl(&dp_regs->sys_ctl1);
+
+ if (!(reg & DET_STA)) {
+ debug("DP Input stream clock not detected.\n");
+ return -EIO;
+ }
+
+ return EXYNOS_DP_SUCCESS;
+}
+
+void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value,
+ unsigned int n_value)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ if (type == REGISTER_M) {
+ reg = readl(&dp_regs->sys_ctl4);
+ reg |= FIX_M_VID;
+ writel(reg, &dp_regs->sys_ctl4);
+ reg = M_VID0_CFG(m_value);
+ writel(reg, &dp_regs->m_vid0);
+ reg = M_VID1_CFG(m_value);
+ writel(reg, &dp_regs->m_vid1);
+ reg = M_VID2_CFG(m_value);
+ writel(reg, &dp_regs->m_vid2);
+
+ reg = N_VID0_CFG(n_value);
+ writel(reg, &dp_regs->n_vid0);
+ reg = N_VID1_CFG(n_value);
+ writel(reg, &dp_regs->n_vid1);
+ reg = N_VID2_CFG(n_value);
+ writel(reg, &dp_regs->n_vid2);
+ } else {
+ reg = readl(&dp_regs->sys_ctl4);
+ reg &= ~FIX_M_VID;
+ writel(reg, &dp_regs->sys_ctl4);
+ }
+}
+
+void exynos_dp_set_video_timing_mode(unsigned int type)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~FORMAT_SEL;
+
+ if (type != VIDEO_TIMING_FROM_CAPTURE)
+ reg |= FORMAT_SEL;
+
+ writel(reg, &dp_regs->video_ctl10);
+}
+
+void exynos_dp_enable_video_master(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->soc_general_ctl);
+ if (enable) {
+ reg &= ~VIDEO_MODE_MASK;
+ reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
+ } else {
+ reg &= ~VIDEO_MODE_MASK;
+ reg |= VIDEO_MODE_SLAVE_MODE;
+ }
+
+ writel(reg, &dp_regs->soc_general_ctl);
+}
+
+void exynos_dp_start_video(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Enable Video input and disable Mute */
+ reg = readl(&dp_regs->video_ctl1);
+ reg |= VIDEO_EN;
+ writel(reg, &dp_regs->video_ctl1);
+}
+
+unsigned int exynos_dp_is_video_stream_on(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Update STRM_VALID */
+ reg = readl(&dp_regs->sys_ctl3);
+ writel(reg, &dp_regs->sys_ctl3);
+
+ reg = readl(&dp_regs->sys_ctl3);
+ if (!(reg & STRM_VALID))
+ return -EIO;
+
+ return EXYNOS_DP_SUCCESS;
+}
diff --git a/drivers/video/exynos_dp_lowlevel.h b/drivers/video/exynos_dp_lowlevel.h
new file mode 100644
index 0000000000..a041a7ab50
--- /dev/null
+++ b/drivers/video/exynos_dp_lowlevel.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+ */
+
+#ifndef _EXYNOS_EDP_LOWLEVEL_H
+#define _EXYNOS_EDP_LOWLEVEL_H
+
+void exynos_dp_enable_video_bist(unsigned int enable);
+void exynos_dp_enable_video_mute(unsigned int enable);
+void exynos_dp_reset(void);
+void exynos_dp_enable_sw_func(unsigned int enable);
+unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable);
+unsigned int exynos_dp_get_pll_lock_status(void);
+int exynos_dp_init_analog_func(void);
+void exynos_dp_init_hpd(void);
+void exynos_dp_init_aux(void);
+void exynos_dp_config_interrupt(void);
+unsigned int exynos_dp_get_plug_in_status(void);
+unsigned int exynos_dp_detect_hpd(void);
+unsigned int exynos_dp_start_aux_transaction(void);
+unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr,
+ unsigned char data);
+unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr,
+ unsigned char *data);
+unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[]);
+unsigned int exynos_dp_read_bytes_from_dpcd( unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[]);
+int exynos_dp_select_i2c_device( unsigned int device_addr,
+ unsigned int reg_addr);
+int exynos_dp_read_byte_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr, unsigned int *data);
+int exynos_dp_read_bytes_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr, unsigned int count,
+ unsigned char edid[]);
+void exynos_dp_reset_macro(void);
+void exynos_dp_set_link_bandwidth(unsigned char bwtype);
+unsigned char exynos_dp_get_link_bandwidth(void);
+void exynos_dp_set_lane_count(unsigned char count);
+unsigned int exynos_dp_get_lane_count(void);
+unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt);
+void exynos_dp_set_lane_pre_emphasis(unsigned int level,
+ unsigned char lanecnt);
+void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val,
+ unsigned char lanecnt);
+void exynos_dp_set_training_pattern(unsigned int pattern);
+void exynos_dp_enable_enhanced_mode(unsigned char enable);
+void exynos_dp_enable_scrambling(unsigned int enable);
+int exynos_dp_init_video(void);
+void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info);
+void exynos_dp_set_video_color_format(struct edp_video_info *video_info);
+int exynos_dp_config_video_bist(struct edp_device_info *edp_info);
+unsigned int exynos_dp_is_slave_video_stream_clock_on(void);
+void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value,
+ unsigned int n_value);
+void exynos_dp_set_video_timing_mode(unsigned int type);
+void exynos_dp_enable_video_master(unsigned int enable);
+void exynos_dp_start_video(void);
+unsigned int exynos_dp_is_video_stream_on(void);
+
+#endif /* _EXYNOS_DP_LOWLEVEL_H */
OpenPOWER on IntegriCloud