aboutsummaryrefslogtreecommitdiff
path: root/src/soc/rockchip/rk3288/edp.c
diff options
context:
space:
mode:
authorhuang lin <hl@rock-chips.com>2014-09-19 14:51:52 +0800
committerPatrick Georgi <pgeorgi@google.com>2015-04-10 20:50:53 +0200
commit40f558e8f4f77ab70a8a2eb9bdfa850e362cb553 (patch)
tree6ef4fd3fca8bbf8f0e07070b224ba29dccec8021 /src/soc/rockchip/rk3288/edp.c
parent1c8f2a6f968bec72a7060ba264f44fbea96d68e9 (diff)
rockchip: support display
Implement VOP and eDP drivers, vop and edp clock configuration, framebuffer allocation and display configuration logic. The eDP driver reads panel EDID to determine panel dimensions and the pixel clock used by the VOP. The pixel clock is generating using the NPLL. BUG=chrome-os-partner:31897 TEST=Booted Veyron Pinky and display normal BRANCH=None Change-Id: I01b5c347a3433a108806aec61aa3a875cab8c129 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: e4f863b0b57f2f5293ea8015db86cf7f8acc5853 Original-Change-Id: I61214f55e96bc1dcda9b0f700e5db11e49e5e533 Original-Signed-off-by: huang lin <hl@rock-chips.com> Original-Reviewed-on: https://chromium-review.googlesource.com/219050 Original-Reviewed-by: Julius Werner <jwerner@chromium.org> Reviewed-on: http://review.coreboot.org/9553 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'src/soc/rockchip/rk3288/edp.c')
-rw-r--r--src/soc/rockchip/rk3288/edp.c1014
1 files changed, 1014 insertions, 0 deletions
diff --git a/src/soc/rockchip/rk3288/edp.c b/src/soc/rockchip/rk3288/edp.c
new file mode 100644
index 0000000000..556dc16a39
--- /dev/null
+++ b/src/soc/rockchip/rk3288/edp.c
@@ -0,0 +1,1014 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Rockchip 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 <arch/io.h>
+#include <assert.h>
+#include <console/console.h>
+#include <delay.h>
+#include <edid.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <soc/addressmap.h>
+#include <soc/edp.h>
+#include <soc/grf.h>
+#include <soc/vop.h>
+#include <timer.h>
+
+#include "chip.h"
+
+#define edp_debug(x...) do {if (0) printk(BIOS_DEBUG, x); } while (0)
+
+static struct rk_edp rk_edp;
+
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+#define DP_LINK_STATUS_SIZE 6
+
+static const char *voltage_names[] = {
+ "0.4V", "0.6V", "0.8V", "1.2V"
+};
+static const char *pre_emph_names[] = {
+ "0dB", "3.5dB", "6dB", "9.5dB"
+};
+
+#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200
+#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5
+
+static void rk_edp_reset(struct rk_edp *edp)
+{
+ u32 reg;
+
+ /* Stop Video */
+ clrbits_le32(&edp->regs->video_ctl_1, VIDEO_EN);
+ setbits_le32(&edp->regs->video_ctl_1, VIDEO_MUTE);
+
+ reg = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N |
+ AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+ writel(reg, &edp->regs->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, &edp->regs->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, &edp->regs->lane_map);
+}
+
+static void rk_edp_init_refclk(struct rk_edp *edp)
+{
+ writel(SEL_24M, &edp->regs->analog_ctl_2);
+ writel(REF_CLK_24M, &edp->regs->pll_reg_1);
+
+ /*initial value*/
+ writel(LDO_OUTPUT_V_SEL_145 |
+ KVCO_DEFALUT |
+ CHG_PUMP_CUR_SEL_5US |
+ V2L_CUR_SEL_1MA, &edp->regs->pll_reg_2);
+
+ writel(LOCK_DET_CNT_SEL_256 |
+ LOOP_FILTER_RESET |
+ PALL_SSC_RESET |
+ LOCK_DET_BYPASS |
+ PLL_LOCK_DET_MODE |
+ PLL_LOCK_DET_FORCE, &edp->regs->pll_reg_3);
+
+ writel(REGULATOR_V_SEL_950MV |
+ STANDBY_CUR_SEL |
+ CHG_PUMP_INOUT_CTRL_1200MV |
+ CHG_PUMP_INPUT_CTRL_OP, &edp->regs->pll_reg_5);
+
+ writel(SSC_OFFSET | SSC_MODE | SSC_DEPTH, &edp->regs->ssc_reg);
+
+ writel(TX_SWING_PRE_EMP_MODE |
+ PRE_DRIVER_PW_CTRL1 |
+ LP_MODE_CLK_REGULATOR |
+ RESISTOR_MSB_CTRL |
+ RESISTOR_CTRL, &edp->regs->tx_common);
+
+ writel(DP_AUX_COMMON_MODE |
+ DP_AUX_EN |
+ AUX_TERM_50OHM, &edp->regs->dp_aux);
+
+ writel(DP_BG_OUT_SEL |
+ DP_DB_CUR_CTRL |
+ DP_BG_SEL |
+ DP_RESISTOR_TUNE_BG, &edp->regs->dp_bias);
+
+ writel(CH1_CH3_SWING_EMP_CTRL |
+ CH0_CH2_SWING_EMP_CTRL, &edp->regs->dp_reserv2);
+}
+
+static void rk_edp_init_interrupt(struct rk_edp *edp)
+{
+ /* Set interrupt pin assertion polarity as high */
+ writel(INT_POL, &edp->regs->int_ctl);
+
+ /* Clear pending registers */
+ writel(0xff, &edp->regs->common_int_sta_1);
+ writel(0x4f, &edp->regs->common_int_sta_2);
+ writel(0xff, &edp->regs->common_int_sta_3);
+ writel(0x27, &edp->regs->common_int_sta_4);
+ writel(0x7f, &edp->regs->dp_int_sta);
+
+ /* 0:mask,1: unmask */
+ writel(0x00, &edp->regs->common_int_mask_1);
+ writel(0x00, &edp->regs->common_int_mask_2);
+ writel(0x00, &edp->regs->common_int_mask_3);
+ writel(0x00, &edp->regs->common_int_mask_4);
+ writel(0x00, &edp->regs->int_sta_mask);
+}
+
+static void rk_edp_enable_sw_function(struct rk_edp *edp)
+{
+ clrbits_le32(&edp->regs->func_en_1, SW_FUNC_EN_N);
+}
+
+static int rk_edp_get_pll_lock_status(struct rk_edp *edp)
+{
+ u32 val;
+
+ val = readl(&edp->regs->dp_debug_ctl);
+ return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED;
+}
+
+static void rk_edp_init_analog_func(struct rk_edp *edp)
+{
+ struct stopwatch sw;
+
+ writel(0x00, &edp->regs->dp_pd);
+
+ writel(PLL_LOCK_CHG, &edp->regs->common_int_sta_1);
+
+ clrbits_le32(&edp->regs->dp_debug_ctl, F_PLL_LOCK | PLL_LOCK_CTRL);
+
+ stopwatch_init_msecs_expire(&sw, PLL_LOCK_TIMEOUT);
+
+ while (rk_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) {
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR, "%s: PLL is not locked\n", __func__);
+ return;
+ }
+ }
+
+ /* Enable Serdes FIFO function and Link symbol clock domain module */
+ clrbits_le32(&edp->regs->func_en_2, SERDES_FIFO_FUNC_EN_N |
+ LS_CLK_DOMAIN_FUNC_EN_N | AUX_FUNC_EN_N |
+ SSC_FUNC_EN_N);
+}
+
+static void rk_edp_init_aux(struct rk_edp *edp)
+{
+ /* Clear inerrupts related to AUX channel */
+ writel(AUX_FUNC_EN_N, &edp->regs->dp_int_sta);
+
+ /* Disable AUX channel module */
+ setbits_le32(&edp->regs->func_en_2, AUX_FUNC_EN_N);
+
+ /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+ writel(DEFER_CTRL_EN | DEFER_COUNT(1), &edp->regs->aux_ch_defer_dtl);
+
+ /* Enable AUX channel module */
+ clrbits_le32(&edp->regs->func_en_2, AUX_FUNC_EN_N);
+}
+
+static int rk_edp_aux_enable(struct rk_edp *edp)
+{
+ struct stopwatch sw;
+
+ setbits_le32(&edp->regs->aux_ch_ctl_2, AUX_EN);
+ stopwatch_init_msecs_expire(&sw, 20);
+ do {
+ if (!(readl(&edp->regs->aux_ch_ctl_2) & AUX_EN))
+ return 0;
+ } while (!stopwatch_expired(&sw));
+
+ return -1;
+
+}
+
+static int rk_edp_is_aux_reply(struct rk_edp *edp)
+{
+ struct stopwatch sw;
+
+ stopwatch_init_msecs_expire(&sw, 10);
+
+ while (!(readl(&edp->regs->dp_int_sta) & RPLY_RECEIV)) {
+ if (stopwatch_expired(&sw))
+ return -1;
+ }
+
+ writel(RPLY_RECEIV, &edp->regs->dp_int_sta);
+
+ return 0;
+}
+
+static int rk_edp_start_aux_transaction(struct rk_edp *edp)
+{
+ int val;
+
+ /* Enable AUX CH operation */
+ if (rk_edp_aux_enable(edp)) {
+ edp_debug("AUX CH enable timeout!\n");
+ return -1;
+ }
+
+ /* Is AUX CH command reply received? */
+ if (rk_edp_is_aux_reply(edp)) {
+ edp_debug("AUX CH command reply failed!\n");
+ return -1;
+ }
+
+ /* Clear interrupt source for AUX CH access error */
+ val = readl(&edp->regs->dp_int_sta);
+ if (val & AUX_ERR) {
+ writel(AUX_ERR, &edp->regs->dp_int_sta);
+ return -1;
+ }
+
+ /* Check AUX CH error access status */
+ val = readl(&edp->regs->dp_int_sta);
+ if ((val & AUX_STATUS_MASK) != 0) {
+ edp_debug("AUX CH error happens: %d\n\n",
+ val & AUX_STATUS_MASK);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rk_edp_dpcd_transfer(struct rk_edp *edp,
+ unsigned int val_addr, u8 *data,
+ unsigned int length,
+ enum dpcd_request request)
+{
+ int val;
+ int i, try_times;
+ int retval = 0;
+ u32 len = 0;
+
+ while (length) {
+ len = MIN(length, 16);
+ for (try_times = 0; try_times < 10; try_times++) {
+
+ /* Clear AUX CH data buffer */
+ val = BUF_CLR;
+ writel(val, &edp->regs->buf_data_ctl);
+
+ /* Select DPCD device address */
+ val = AUX_ADDR_7_0(val_addr);
+ writel(val, &edp->regs->aux_addr_7_0);
+ val = AUX_ADDR_15_8(val_addr);
+ writel(val, &edp->regs->aux_addr_15_8);
+ val = AUX_ADDR_19_16(val_addr);
+ writel(val, &edp->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.
+ */
+ if (request == DPCD_WRITE) {
+ val = AUX_LENGTH(len) |
+ AUX_TX_COMM_DP_TRANSACTION |
+ AUX_TX_COMM_WRITE;
+ for (i = 0; i < len; i++)
+ writel(*data++, &edp->regs->buf_data[i]);
+ } else
+ val = AUX_LENGTH(len) |
+ AUX_TX_COMM_DP_TRANSACTION |
+ AUX_TX_COMM_READ;
+
+ writel(val, &edp->regs->aux_ch_ctl_1);
+
+ /* Start AUX transaction */
+ retval = rk_edp_start_aux_transaction(edp);
+ if (retval == 0)
+ break;
+ else
+ printk(BIOS_WARNING, "read dpcd Aux Transaction fail!\n");
+
+ }
+
+ if (retval)
+ return -1;
+
+ if (request == DPCD_READ) {
+ for (i = 0; i < len; i++)
+ *data++ = (u8)readl(&edp->regs->buf_data[i]);
+ }
+
+ length -= len;
+ val_addr += 16;
+ }
+ return 0;
+}
+
+static int rk_edp_dpcd_read(struct rk_edp *edp, u32 addr, u8 *values, size_t size)
+{
+ return rk_edp_dpcd_transfer(edp, addr, values, size, DPCD_READ);
+}
+
+static int rk_edp_dpcd_write(struct rk_edp *edp, u32 addr, u8 *values, size_t size)
+{
+ return rk_edp_dpcd_transfer(edp, addr, values, size, DPCD_WRITE);
+}
+
+
+static int rk_edp_link_power_up(struct rk_edp *edp)
+{
+ u8 value;
+ int err;
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (edp->link_train.revision < 0x11)
+ return 0;
+
+ err = rk_edp_dpcd_read(edp, DPCD_LINK_POWER_STATE, &value, 1);
+ if (err < 0)
+ return err;
+
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D0;
+
+ err = rk_edp_dpcd_write(edp, DPCD_LINK_POWER_STATE, &value, 1);
+ if (err < 0)
+ return err;
+
+ /*
+ * According to the DP 1.1 specification, a "Sink Device must exit the
+ * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+ * Control Field" (register 0x600).
+ */
+ mdelay(1);
+
+ return 0;
+}
+
+static int rk_edp_link_configure(struct rk_edp *edp)
+{
+ u8 values[2];
+
+ values[0] = edp->link_train.link_rate;
+ values[1] = edp->link_train.lane_count;
+
+ return rk_edp_dpcd_write(edp, DPCD_LINK_BW_SET, values, sizeof(values));
+}
+
+static void rk_edp_set_link_training(struct rk_edp *edp,
+ const u8 *training_values)
+{
+ int i;
+
+ for (i = 0; i < edp->link_train.lane_count; i++)
+ writel(training_values[i], &edp->regs->ln_link_trn_ctl[i]);
+}
+
+static u8 edp_link_status(const u8 *link_status, int r)
+{
+ return link_status[r - DPCD_LANE0_1_STATUS];
+}
+
+static int rk_edp_dpcd_read_link_status(struct rk_edp *edp, u8 *link_status)
+{
+ return rk_edp_dpcd_read(edp, DPCD_LANE0_1_STATUS, link_status,
+ DP_LINK_STATUS_SIZE);
+}
+
+static u8 edp_get_lane_status(const u8 *link_status, int lane)
+{
+ int i = DPCD_LANE0_1_STATUS + (lane >> 1);
+ int s = (lane & 1) * 4;
+ u8 l = edp_link_status(link_status, i);
+ return (l >> s) & 0xf;
+}
+
+static int rk_edp_clock_recovery_ok(const u8 *link_status, int lane_count)
+{
+ int lane;
+ u8 lane_status;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ lane_status = edp_get_lane_status(link_status, lane);
+ if ((lane_status & DP_LANE_CR_DONE) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static int rk_edp_channel_eq_ok(const u8 *link_status, int lane_count)
+{
+ u8 lane_align;
+ u8 lane_status;
+ int lane;
+
+ lane_align = edp_link_status(link_status,
+ DPCD_LANE_ALIGN_STATUS_UPDATED);
+ if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
+ return 0;
+ for (lane = 0; lane < lane_count; lane++) {
+ lane_status = edp_get_lane_status(link_status, lane);
+ if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
+ return 0;
+ }
+ return 1;
+}
+
+static u8
+rk_edp_get_adjust_request_voltage(const u8 *link_status, int lane)
+{
+ int i = DPCD_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+ int s = ((lane & 1) ?
+ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+ u8 l = edp_link_status(link_status, i);
+
+ return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+static u8 rk_edp_get_adjust_request_pre_emphasis(const u8 *link_status,
+ int lane)
+{
+ int i = DPCD_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+ int s = ((lane & 1) ?
+ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+ u8 l = edp_link_status(link_status, i);
+
+ return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+static void edp_get_adjust_train(const u8 *link_status, int lane_count,
+ u8 train_set[])
+{
+ u8 v = 0;
+ u8 p = 0;
+ int lane;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ u8 this_v =
+ rk_edp_get_adjust_request_voltage(link_status, lane);
+ u8 this_p =
+ rk_edp_get_adjust_request_pre_emphasis(link_status,
+ lane);
+
+ printk(BIOS_DEBUG, "requested signal parameters: lane %d "
+ "voltage %s pre_emph %s\n", lane,
+ voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
+ pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
+
+ if (this_v > v)
+ v = this_v;
+ if (this_p > p)
+ p = this_p;
+ }
+
+ if (v >= DP_VOLTAGE_MAX)
+ v |= DP_TRAIN_MAX_SWING_REACHED;
+
+ if (p >= DP_PRE_EMPHASIS_MAX)
+ p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ printk(BIOS_DEBUG, "using signal parameters: voltage %s pre_emph %s\n",
+ voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK)
+ >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
+ pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK)
+ >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
+
+ for (lane = 0; lane < 4; lane++)
+ train_set[lane] = v | p;
+}
+
+static int rk_edp_link_train_cr(struct rk_edp *edp)
+{
+ int clock_recovery;
+ u8 voltage, tries = 0;
+ u8 status[DP_LINK_STATUS_SIZE];
+ int i;
+ u8 value;
+
+ value = DP_TRAINING_PATTERN_1;
+ writel(value, &edp->regs->dp_training_ptn_set);
+ rk_edp_dpcd_write(edp, DPCD_TRAINING_PATTERN_SET, &value, 1);
+ memset(edp->train_set, 0, 4);
+
+ /* clock recovery loop */
+ clock_recovery = 0;
+ tries = 0;
+ voltage = 0xff;
+
+ while (1) {
+ rk_edp_set_link_training(edp, edp->train_set);
+ rk_edp_dpcd_write(edp, DPCD_TRAINING_LANE0_SET,
+ edp->train_set,
+ edp->link_train.lane_count);
+
+ mdelay(1);
+
+ if (rk_edp_dpcd_read_link_status(edp, status) < 0) {
+ printk(BIOS_ERR, "displayport link status failed\n");
+ break;
+ }
+
+ if (rk_edp_clock_recovery_ok(status,
+ edp->link_train.lane_count)) {
+ clock_recovery = 1;
+ break;
+ }
+
+ for (i = 0; i < edp->link_train.lane_count; i++) {
+ if ((edp->train_set[i] &
+ DP_TRAIN_MAX_SWING_REACHED) == 0)
+ break;
+ }
+ if (i == edp->link_train.lane_count) {
+ printk(BIOS_ERR, "clock recovery reached max voltage\n");
+ break;
+ }
+
+ if ((edp->train_set[0] &
+ DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+ ++tries;
+ if (tries == MAX_CR_LOOP) {
+ printk(BIOS_ERR, "clock recovery tried 5 times\n");
+ break;
+ }
+ } else
+ tries = 0;
+
+ voltage = edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+ /* Compute new train_set as requested by sink */
+ edp_get_adjust_train(status, edp->link_train.lane_count,
+ edp->train_set);
+ }
+ if (!clock_recovery) {
+ printk(BIOS_ERR, "clock recovery failed\n");
+ return -1;
+ } else {
+ printk(BIOS_DEBUG, "clock recovery at voltage %d pre-emphasis %d\n",
+ edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+ (edp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT);
+ return 0;
+ }
+}
+
+static int rk_edp_link_train_ce(struct rk_edp *edp)
+{
+ int channel_eq;
+ u8 value, tries = 0;
+ u8 status[DP_LINK_STATUS_SIZE];
+
+ value = DP_TRAINING_PATTERN_2;
+ writel(value, &edp->regs->dp_training_ptn_set);
+ rk_edp_dpcd_write(edp, DPCD_TRAINING_PATTERN_SET, &value, 1);
+
+ /* channel equalization loop */
+ channel_eq = 0;
+ for (tries = 0; tries < 5; tries++) {
+ rk_edp_set_link_training(edp, edp->train_set);
+ udelay(400);
+
+ if (rk_edp_dpcd_read_link_status(edp, status) < 0) {
+ printk(BIOS_ERR, "displayport link status failed\n");
+ return -1;
+ }
+
+ if (rk_edp_channel_eq_ok(status,
+ edp->link_train.lane_count)) {
+ channel_eq = 1;
+ break;
+ }
+ edp_get_adjust_train(status,
+ edp->link_train.lane_count,
+ edp->train_set);
+ }
+
+ if (!channel_eq) {
+ printk(BIOS_ERR, "channel eq failed\n");
+ return -1;
+ } else {
+ printk(BIOS_DEBUG, "channel eq at voltage %d pre-emphasis %d\n",
+ edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+ (edp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+ >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
+ return 0;
+ }
+}
+
+static int rk_edp_init_training(struct rk_edp *edp)
+{
+ u8 values[3];
+ int err;
+
+ err = rk_edp_dpcd_read(edp, DPCD_DPCD_REV, values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ edp->link_train.revision = values[0];
+ edp->link_train.link_rate = values[1];
+ edp->link_train.lane_count = values[2] & DP_MAX_LANE_COUNT_MASK;
+
+ edp_debug("max link rate:%d.%dGps max number of lanes:%d\n",
+ edp->link_train.link_rate * 27 / 100,
+ edp->link_train.link_rate * 27 % 100,
+ edp->link_train.lane_count);
+
+ if ((edp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
+ (edp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
+ edp_debug("Rx Max Link Rate is abnormal :%x\n",
+ edp->link_train.link_rate);
+ return -1;
+ }
+
+ if (edp->link_train.lane_count == 0) {
+ edp_debug("Rx Max Lane count is abnormal :%x\n",
+ edp->link_train.lane_count);
+ return -1;
+ }
+
+ rk_edp_link_power_up(edp);
+ rk_edp_link_configure(edp);
+ return 0;
+}
+
+static int rk_edp_hw_link_training(struct rk_edp *edp)
+{
+ u32 val;
+ struct stopwatch sw;
+
+ /* Set link rate and count as you want to establish*/
+ writel(edp->link_train.link_rate, &edp->regs->link_bw_set);
+ writel(edp->link_train.lane_count, &edp->regs->lane_count_set);
+
+ if (rk_edp_link_train_cr(edp))
+ return -1;
+ if (rk_edp_link_train_ce(edp))
+ return -1;
+
+ writel(HW_LT_EN, &edp->regs->dp_hw_link_training);
+ stopwatch_init_msecs_expire(&sw, 10);
+ do {
+ val = readl(&edp->regs->dp_hw_link_training);
+ if (!(val & HW_LT_EN))
+ break;
+ } while (!stopwatch_expired(&sw));
+ if (val & HW_LT_ERR_CODE_MASK) {
+ printk(BIOS_ERR, "edp hw link training error: %d\n",
+ val >> HW_LT_ERR_CODE_SHIFT);
+ return -1;
+ }
+ return 0;
+
+}
+
+static int rk_edp_select_i2c_device(struct rk_edp *edp,
+ unsigned int device_addr,
+ unsigned int val_addr)
+{
+ u32 val;
+ int retval;
+
+ /* Set EDID device address */
+ val = device_addr;
+ writel(val, &edp->regs->aux_addr_7_0);
+ writel(0x0, &edp->regs->aux_addr_15_8);
+ writel(0x0, &edp->regs->aux_addr_19_16);
+
+ /* Set offset from base address of EDID device */
+ writel(val_addr, &edp->regs->buf_data[0]);
+
+ /*
+ * Set I2C transaction and write address
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+ AUX_TX_COMM_WRITE;
+ writel(val, &edp->regs->aux_ch_ctl_1);
+
+ /* Start AUX transaction */
+ retval = rk_edp_start_aux_transaction(edp);
+ if (retval != 0)
+ edp_debug("select_i2c_device Aux Transaction fail!\n");
+
+ return retval;
+}
+
+static int rk_edp_read_bytes_from_i2c(struct rk_edp *edp,
+ unsigned int device_addr,
+ unsigned int val_addr,
+ unsigned int count,
+ u8 edid[])
+{
+ u32 val;
+ unsigned int i, j;
+ unsigned int cur_data_idx;
+ unsigned int defer = 0;
+ int retval = 0;
+
+ for (i = 0; i < count; i += 16) {
+ for (j = 0; j < 10; j++) { /* try 10 times */
+ /* Clear AUX CH data buffer */
+ val = BUF_CLR;
+ writel(val, &edp->regs->buf_data_ctl);
+
+ /* Set normal AUX CH command */
+ clrbits_le32(&edp->regs->aux_ch_ctl_2, ADDR_ONLY);
+
+ /*
+ * If Rx sends defer, Tx sends only reads
+ * request without sending addres
+ */
+ if (!defer)
+ retval = rk_edp_select_i2c_device(edp,
+ device_addr, val_addr + i);
+ else
+ defer = 0;
+
+ /*
+ * Set I2C transaction and write data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(val, &edp->regs->aux_ch_ctl_1);
+
+ /* Start AUX transaction */
+ retval = rk_edp_start_aux_transaction(edp);
+ if (retval == 0)
+ break;
+ else {
+ edp_debug("Aux Transaction fail!\n");
+ continue;
+ }
+
+ /* Check if Rx sends defer */
+ val = readl(&edp->regs->aux_rx_comm);
+ if (val == AUX_RX_COMM_AUX_DEFER ||
+ val == AUX_RX_COMM_I2C_DEFER) {
+ edp_debug("Defer: %d\n\n", val);
+ defer = 1;
+ }
+ }
+
+ if (retval)
+ return -1;
+
+ for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+ val = readl(&edp->regs->buf_data[cur_data_idx]);
+ edid[i + cur_data_idx] = (u8)val;
+ }
+ }
+
+ return retval;
+}
+
+static int rk_edp_read_edid(struct rk_edp *edp, struct edid *edid)
+{
+ u8 buf[EDID_LENGTH * 2];
+ int retval;
+
+ /* Read EDID data */
+ retval = rk_edp_read_bytes_from_i2c(edp, EDID_ADDR,
+ EDID_HEADER, EDID_LENGTH,
+ &buf[EDID_HEADER]);
+ if (retval != 0) {
+ printk(BIOS_ERR, "EDID Read failed!\n");
+ return -1;
+ }
+
+ /* check if edid have extension flag, and read additional EDID data */
+ if (buf[EDID_EXTENSION_FLAG]) {
+ retval = rk_edp_read_bytes_from_i2c(edp, EDID_ADDR,
+ EDID_LENGTH, EDID_LENGTH,
+ &buf[EDID_LENGTH]);
+ if (retval != 0) {
+ printk(BIOS_ERR, "EDID Read failed!\n");
+ return -1;
+ }
+ }
+
+ if (decode_edid(buf, sizeof(buf), edid)) {
+ printk(BIOS_ERR, "%s: Failed to decode EDID.\n",
+ __func__);
+ return -1;
+ }
+
+ edp_debug("EDID Read success!\n");
+ return 0;
+}
+
+static int rk_edp_set_link_train(struct rk_edp *edp)
+{
+ int retval;
+
+ if (rk_edp_init_training(edp)) {
+ printk(BIOS_ERR, "DP LT init failed!\n");
+ return -1;
+ }
+
+ retval = rk_edp_hw_link_training(edp);
+
+ return retval;
+}
+
+static void rk_edp_init_video(struct rk_edp *edp)
+{
+ u32 val;
+
+ val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+ writel(val, &edp->regs->common_int_sta_1);
+
+ val = CHA_CRI(4) | CHA_CTRL;
+ writel(val, &edp->regs->sys_ctl_2);
+
+ val = VID_HRES_TH(2) | VID_VRES_TH(0);
+ writel(val, &edp->regs->video_ctl_8);
+}
+
+static void rk_edp_config_video_slave_mode(struct rk_edp *edp)
+{
+ clrbits_le32(&edp->regs->func_en_1,
+ VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N);
+}
+
+static void rk_edp_set_video_cr_mn(struct rk_edp *edp,
+ enum clock_recovery_m_value_type type,
+ u32 m_value,
+ u32 n_value)
+{
+ u32 val;
+
+ if (type == REGISTER_M) {
+ setbits_le32(&edp->regs->sys_ctl_4, FIX_M_VID);
+ val = m_value & 0xff;
+ writel(val, &edp->regs->m_vid_0);
+ val = (m_value >> 8) & 0xff;
+ writel(val, &edp->regs->m_vid_1);
+ val = (m_value >> 16) & 0xff;
+ writel(val, &edp->regs->m_vid_2);
+
+ val = n_value & 0xff;
+ writel(val, &edp->regs->n_vid_0);
+ val = (n_value >> 8) & 0xff;
+ writel(val, &edp->regs->n_vid_1);
+ val = (n_value >> 16) & 0xff;
+ writel(val, &edp->regs->n_vid_2);
+ } else {
+ clrbits_le32(&edp->regs->sys_ctl_4, FIX_M_VID);
+
+ writel(0x00, &edp->regs->n_vid_0);
+ writel(0x80, &edp->regs->n_vid_1);
+ writel(0x00, &edp->regs->n_vid_2);
+ }
+}
+
+static int rk_edp_is_video_stream_clock_on(struct rk_edp *edp)
+{
+ u32 val;
+ struct stopwatch sw;
+
+ stopwatch_init_msecs_expire(&sw, 100);
+ do {
+ val = readl(&edp->regs->sys_ctl_1);
+
+ /*must write value to update DET_STA bit status*/
+ writel(val, &edp->regs->sys_ctl_1);
+ val = readl(&edp->regs->sys_ctl_1);
+ if (!(val & DET_STA))
+ continue;
+
+ val = readl(&edp->regs->sys_ctl_2);
+
+ /*must write value to update CHA_STA bit status*/
+ writel(val, &edp->regs->sys_ctl_2);
+ val = readl(&edp->regs->sys_ctl_2);
+ if (!(val & CHA_STA))
+ return 0;
+ } while (!stopwatch_expired(&sw));
+
+ return -1;
+}
+
+static int rk_edp_is_video_stream_on(struct rk_edp *edp)
+{
+ u32 val;
+ struct stopwatch sw;
+
+ stopwatch_init_msecs_expire(&sw, 100);
+ do {
+ val = readl(&edp->regs->sys_ctl_3);
+
+ /*must write value to update STRM_VALID bit status*/
+ writel(val, &edp->regs->sys_ctl_3);
+
+ val = readl(&edp->regs->sys_ctl_3);
+ if (!(val & STRM_VALID))
+ return 0;
+ } while (!stopwatch_expired(&sw));
+
+ return -1;
+}
+
+static int rk_edp_config_video(struct rk_edp *edp)
+{
+ rk_edp_config_video_slave_mode(edp);
+
+ if (rk_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) {
+ edp_debug("PLL is not locked yet.\n");
+ return -1;
+ }
+
+ if (rk_edp_is_video_stream_clock_on(edp))
+ return -1;
+
+ /* Set to use the register calculated M/N video */
+ rk_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+
+ /* For video bist, Video timing must be generated by register */
+ clrbits_le32(&edp->regs->video_ctl_10, F_SEL);
+
+ /* Disable video mute */
+ clrbits_le32(&edp->regs->video_ctl_1, VIDEO_MUTE);
+
+ /* Enable video at next frame */
+ setbits_le32(&edp->regs->video_ctl_1, VIDEO_EN);
+
+ return rk_edp_is_video_stream_on(edp);
+}
+
+int rk_edp_get_edid(struct edid *edid)
+{
+ int i;
+ int retval;
+
+ /* Read EDID */
+ for (i = 0; i < 3; i++) {
+ retval = rk_edp_read_edid(&rk_edp, edid);
+ if (retval == 0)
+ break;
+ }
+
+ return retval;
+}
+
+int rk_edp_enable(void)
+{
+ int ret = 0;
+
+ if (rk_edp_set_link_train(&rk_edp)) {
+ printk(BIOS_ERR, "link train failed!\n");
+ return -1;
+ }
+
+ rk_edp_init_video(&rk_edp);
+ ret = rk_edp_config_video(&rk_edp);
+ if (ret)
+ printk(BIOS_ERR, "config video failed\n");
+
+ return ret;
+}
+
+void rk_edp_init(u32 vop_id)
+{
+ u32 val;
+
+ rk_edp.regs = (struct rk3288_edp_regs *)EDP_BASE;
+
+ /* grf_edp_ref_clk_sel: from internal 24MHz or 27MHz clock */
+ writel(RK_SETBITS(1 << 4), &rk3288_grf->soc_con12);
+
+ /* select epd signal from vop0 or vop1 */
+ val = (vop_id == 1) ? RK_SETBITS(1 << 5) : RK_CLRBITS(1 << 5);
+ writel(val, &rk3288_grf->soc_con6);
+
+ rk_edp_reset(&rk_edp);
+ rk_edp_init_refclk(&rk_edp);
+ rk_edp_init_interrupt(&rk_edp);
+ rk_edp_enable_sw_function(&rk_edp);
+ rk_edp_init_analog_func(&rk_edp);
+ rk_edp_init_aux(&rk_edp);
+}