coreboot-kgpe-d16/src/soc/rockchip/rk3288/edp.c
huang lin c16ba0aeaa rk3288: send correct EDID buffer size
decode_edid() parses the whole EDID buffer, regardless of whether there
is an extension buffer, so we pass the size of the EDID actually read to
prevent EDID parser getting the wrong data.

BUG=chrome-os-partner:35053
TEST=Boot from jerry
BRANCH=veyron

Change-Id: I5951b670f129cf4765a5199cb58ac6abff5478a6
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
Original-Commit-Id: 4d508647efc0a9d48b2a4b23c12a54b63af2813e
Original-Change-Id: I8cd8e09025520322461fe940b01e4af3995b5ecd
Original-Signed-off-by: huang lin <hl@rock-chips.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/240643
Original-Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>
Reviewed-on: http://review.coreboot.org/9645
Tested-by: build bot (Jenkins)
Reviewed-by: Marc Jones <marc.jones@se-eng.com>
2015-04-15 16:48:24 +02:00

991 lines
23 KiB
C

/*
* 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_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];
u32 edid_size = EDID_LENGTH;
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]) {
edid_size += EDID_LENGTH;
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, edid_size, 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_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);
}