summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c')
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c593
1 files changed, 267 insertions, 326 deletions
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 303083ad28e3..38fd8d5014d2 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1,19 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Analogix DP (Display port) core register interface driver.
*
* Copyright (C) 2012 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <jg1.han@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
#include <drm/bridge/analogix_dp.h>
@@ -125,9 +123,14 @@ void analogix_dp_reset(struct analogix_dp_device *dp)
analogix_dp_stop_video(dp);
analogix_dp_enable_video_mute(dp, 0);
- reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
- AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
- HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+ if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ reg = RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N |
+ SW_FUNC_EN_N;
+ else
+ reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
+ AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
+ HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
@@ -215,30 +218,32 @@ void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp)
writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
}
-enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp)
+int analogix_dp_wait_pll_locked(struct analogix_dp_device *dp)
{
- u32 reg;
+ u32 val;
- reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
- if (reg & PLL_LOCK)
- return PLL_LOCKED;
- else
- return PLL_UNLOCKED;
+ return readl_poll_timeout(dp->reg_base + ANALOGIX_DP_DEBUG_CTL, val,
+ val & PLL_LOCK, 120,
+ 120 * DP_TIMEOUT_LOOP_COUNT);
}
void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
{
u32 reg;
+ u32 mask = DP_PLL_PD;
+ u32 pd_addr = ANALOGIX_DP_PLL_CTL;
- if (enable) {
- reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
- reg |= DP_PLL_PD;
- writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
- } else {
- reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
- reg &= ~DP_PLL_PD;
- writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
+ if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
+ pd_addr = ANALOGIX_DP_PD;
+ mask = RK_PLL_PD;
}
+
+ reg = readl(dp->reg_base + pd_addr);
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, dp->reg_base + pd_addr);
}
void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
@@ -247,83 +252,98 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
{
u32 reg;
u32 phy_pd_addr = ANALOGIX_DP_PHY_PD;
+ u32 mask;
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
phy_pd_addr = ANALOGIX_DP_PD;
switch (block) {
case AUX_BLOCK:
- if (enable) {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg |= AUX_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- } else {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg &= ~AUX_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- }
+ if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ mask = RK_AUX_PD;
+ else
+ mask = AUX_PD;
+
+ reg = readl(dp->reg_base + phy_pd_addr);
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, dp->reg_base + phy_pd_addr);
break;
case CH0_BLOCK:
- if (enable) {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg |= CH0_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- } else {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg &= ~CH0_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- }
+ mask = CH0_PD;
+ reg = readl(dp->reg_base + phy_pd_addr);
+
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, dp->reg_base + phy_pd_addr);
break;
case CH1_BLOCK:
- if (enable) {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg |= CH1_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- } else {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg &= ~CH1_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- }
+ mask = CH1_PD;
+ reg = readl(dp->reg_base + phy_pd_addr);
+
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, dp->reg_base + phy_pd_addr);
break;
case CH2_BLOCK:
- if (enable) {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg |= CH2_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- } else {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg &= ~CH2_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- }
+ mask = CH2_PD;
+ reg = readl(dp->reg_base + phy_pd_addr);
+
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, dp->reg_base + phy_pd_addr);
break;
case CH3_BLOCK:
- if (enable) {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg |= CH3_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- } else {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg &= ~CH3_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- }
+ mask = CH3_PD;
+ reg = readl(dp->reg_base + phy_pd_addr);
+
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, dp->reg_base + phy_pd_addr);
break;
case ANALOG_TOTAL:
- if (enable) {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg |= DP_PHY_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- } else {
- reg = readl(dp->reg_base + phy_pd_addr);
- reg &= ~DP_PHY_PD;
- writel(reg, dp->reg_base + phy_pd_addr);
- }
+ /*
+ * There is no bit named DP_PHY_PD, so We used DP_INC_BG
+ * to power off everything instead of DP_PHY_PD in
+ * Rockchip
+ */
+ if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ mask = DP_INC_BG;
+ else
+ mask = DP_PHY_PD;
+
+ reg = readl(dp->reg_base + phy_pd_addr);
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+
+ writel(reg, dp->reg_base + phy_pd_addr);
+ if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ usleep_range(10, 15);
break;
case POWER_ALL:
if (enable) {
- reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
- CH1_PD | CH0_PD;
+ reg = DP_ALL_PD;
writel(reg, dp->reg_base + phy_pd_addr);
} else {
+ reg = DP_ALL_PD;
+ writel(reg, dp->reg_base + phy_pd_addr);
+ usleep_range(10, 15);
+ reg &= ~DP_INC_BG;
+ writel(reg, dp->reg_base + phy_pd_addr);
+ usleep_range(10, 15);
+
writel(0x00, dp->reg_base + phy_pd_addr);
}
break;
@@ -332,10 +352,9 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
}
}
-void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
+int analogix_dp_init_analog_func(struct analogix_dp_device *dp)
{
u32 reg;
- int timeout_loop = 0;
analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
@@ -347,31 +366,21 @@ void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
writel(reg, dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
/* Power up PLL */
- if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
- analogix_dp_set_pll_power_down(dp, 0);
-
- while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
- timeout_loop++;
- if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
- dev_err(dp->dev, "failed to get pll lock status\n");
- return;
- }
- usleep_range(10, 20);
- }
- }
+ analogix_dp_set_pll_power_down(dp, 0);
/* Enable Serdes FIFO function and Link symbol clock domain module */
reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
| AUX_FUNC_EN_N);
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+ return 0;
}
void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp)
{
u32 reg;
- if (gpio_is_valid(dp->hpd_gpio))
+ if (dp->hpd_gpiod)
return;
reg = HOTPLUG_CHG | HPD_LOST | PLUG;
@@ -385,7 +394,7 @@ void analogix_dp_init_hpd(struct analogix_dp_device *dp)
{
u32 reg;
- if (gpio_is_valid(dp->hpd_gpio))
+ if (dp->hpd_gpiod)
return;
analogix_dp_clear_hotplug_interrupts(dp);
@@ -408,8 +417,8 @@ enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp)
{
u32 reg;
- if (gpio_is_valid(dp->hpd_gpio)) {
- reg = gpio_get_value(dp->hpd_gpio);
+ if (dp->hpd_gpiod) {
+ reg = gpiod_get_value(dp->hpd_gpiod);
if (reg)
return DP_IRQ_TYPE_HP_CABLE_IN;
else
@@ -449,17 +458,22 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
reg = RPLY_RECEIV | AUX_ERR;
writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+ analogix_dp_set_analog_power_down(dp, AUX_BLOCK, true);
+ usleep_range(10, 11);
+ analogix_dp_set_analog_power_down(dp, AUX_BLOCK, false);
+
analogix_dp_reset_aux(dp);
- /* Disable AUX transaction H/W retry */
+ /* AUX_BIT_PERIOD_EXPECTED_DELAY doesn't apply to Rockchip IP */
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
- reg = AUX_BIT_PERIOD_EXPECTED_DELAY(0) |
- AUX_HW_RETRY_COUNT_SEL(3) |
- AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+ reg = 0;
else
- reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) |
- AUX_HW_RETRY_COUNT_SEL(0) |
- AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+ reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3);
+
+ /* Disable AUX transaction H/W retry */
+ reg |= AUX_HW_RETRY_COUNT_SEL(0) |
+ AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+
writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
@@ -476,8 +490,8 @@ int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp)
{
u32 reg;
- if (gpio_is_valid(dp->hpd_gpio)) {
- if (gpio_get_value(dp->hpd_gpio))
+ if (dp->hpd_gpiod) {
+ if (gpiod_get_value(dp->hpd_gpiod))
return 0;
} else {
reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
@@ -497,101 +511,27 @@ void analogix_dp_enable_sw_function(struct analogix_dp_device *dp)
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
}
-int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp)
-{
- int reg;
- int retval = 0;
- int timeout_loop = 0;
-
- /* Enable AUX CH operation */
- reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
- reg |= AUX_EN;
- writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
-
- /* Is AUX CH command reply received? */
- reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
- while (!(reg & RPLY_RECEIV)) {
- timeout_loop++;
- if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
- dev_err(dp->dev, "AUX CH command reply failed!\n");
- return -ETIMEDOUT;
- }
- reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
- usleep_range(10, 11);
- }
-
- /* Clear interrupt source for AUX CH command reply */
- writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA);
-
- /* Clear interrupt source for AUX CH access error */
- reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
- if (reg & AUX_ERR) {
- writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
- return -EREMOTEIO;
- }
-
- /* Check AUX CH error access status */
- reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
- if ((reg & AUX_STATUS_MASK) != 0) {
- dev_err(dp->dev, "AUX CH error happens: %d\n\n",
- reg & AUX_STATUS_MASK);
- return -EREMOTEIO;
- }
-
- return retval;
-}
-
-int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
- unsigned int reg_addr,
- unsigned char data)
-{
- u32 reg;
- int i;
- int retval;
-
- for (i = 0; i < 3; i++) {
- /* Clear AUX CH data buffer */
- reg = BUF_CLR;
- writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
- /* Select DPCD device address */
- reg = AUX_ADDR_7_0(reg_addr);
- writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
- reg = AUX_ADDR_15_8(reg_addr);
- writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
- reg = AUX_ADDR_19_16(reg_addr);
- writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
-
- /* Write data buffer */
- reg = (unsigned int)data;
- writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
-
- /*
- * Set DisplayPort transaction and write 1 byte
- * If bit 3 is 1, DisplayPort transaction.
- * If Bit 3 is 0, I2C transaction.
- */
- reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
- writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
- /* Start AUX transaction */
- retval = analogix_dp_start_aux_transaction(dp);
- if (retval == 0)
- break;
-
- dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
- }
-
- return retval;
-}
-
void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
{
u32 reg;
+ int ret;
reg = bwtype;
if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62))
writel(reg, dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+
+ if (dp->phy) {
+ union phy_configure_opts phy_cfg = {0};
+
+ phy_cfg.dp.link_rate =
+ drm_dp_bw_code_to_link_rate(dp->link_train.link_rate) / 100;
+ phy_cfg.dp.set_rate = true;
+ ret = phy_configure(dp->phy, &phy_cfg);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(dp->dev, "%s: phy_configure() failed: %d\n", __func__, ret);
+ return;
+ }
+ }
}
void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype)
@@ -605,9 +545,22 @@ void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype)
void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count)
{
u32 reg;
+ int ret;
reg = count;
writel(reg, dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+
+ if (dp->phy) {
+ union phy_configure_opts phy_cfg = {0};
+
+ phy_cfg.dp.lanes = dp->link_train.lane_count;
+ phy_cfg.dp.set_lanes = true;
+ ret = phy_configure(dp->phy, &phy_cfg);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(dp->dev, "%s: phy_configure() failed: %d\n", __func__, ret);
+ return;
+ }
+ }
}
void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
@@ -618,6 +571,44 @@ void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
*count = reg;
}
+void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp)
+{
+ u8 lane;
+ int ret;
+
+ for (lane = 0; lane < dp->link_train.lane_count; lane++)
+ writel(dp->link_train.training_lane[lane],
+ dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane);
+
+ if (dp->phy) {
+ union phy_configure_opts phy_cfg = {0};
+
+ for (lane = 0; lane < dp->link_train.lane_count; lane++) {
+ u8 training_lane = dp->link_train.training_lane[lane];
+ u8 vs, pe;
+
+ vs = (training_lane & DP_TRAIN_VOLTAGE_SWING_MASK) >>
+ DP_TRAIN_VOLTAGE_SWING_SHIFT;
+ pe = (training_lane & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ phy_cfg.dp.voltage[lane] = vs;
+ phy_cfg.dp.pre[lane] = pe;
+ }
+
+ phy_cfg.dp.set_voltages = true;
+ ret = phy_configure(dp->phy, &phy_cfg);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(dp->dev, "%s: phy_configure() failed: %d\n", __func__, ret);
+ return;
+ }
+ }
+}
+
+u32 analogix_dp_get_lane_link_training(struct analogix_dp_device *dp, u8 lane)
+{
+ return readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane);
+}
+
void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp,
bool enable)
{
@@ -667,106 +658,6 @@ void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
}
}
-void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp,
- u32 level)
-{
- u32 reg;
-
- reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
- reg &= ~PRE_EMPHASIS_SET_MASK;
- reg |= level << PRE_EMPHASIS_SET_SHIFT;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp,
- u32 level)
-{
- u32 reg;
-
- reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
- reg &= ~PRE_EMPHASIS_SET_MASK;
- reg |= level << PRE_EMPHASIS_SET_SHIFT;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp,
- u32 level)
-{
- u32 reg;
-
- reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
- reg &= ~PRE_EMPHASIS_SET_MASK;
- reg |= level << PRE_EMPHASIS_SET_SHIFT;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp,
- u32 level)
-{
- u32 reg;
-
- reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
- reg &= ~PRE_EMPHASIS_SET_MASK;
- reg |= level << PRE_EMPHASIS_SET_SHIFT;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
- u32 training_lane)
-{
- u32 reg;
-
- reg = training_lane;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
- u32 training_lane)
-{
- u32 reg;
-
- reg = training_lane;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
- u32 training_lane)
-{
- u32 reg;
-
- reg = training_lane;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
- u32 training_lane)
-{
- u32 reg;
-
- reg = training_lane;
- writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp)
-{
- return readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp)
-{
- return readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp)
-{
- return readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp)
-{
- return readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-}
-
void analogix_dp_reset_macro(struct analogix_dp_device *dp)
{
u32 reg;
@@ -946,8 +837,12 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
u32 reg;
reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
- reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
- reg |= MASTER_VID_FUNC_EN_N;
+ if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
+ reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N);
+ } else {
+ reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
+ reg |= MASTER_VID_FUNC_EN_N;
+ }
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
@@ -992,10 +887,25 @@ void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
}
-void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
- struct edp_vsc_psr *vsc)
+static ssize_t analogix_dp_get_psr_status(struct analogix_dp_device *dp)
+{
+ ssize_t val;
+ u8 status;
+
+ val = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &status);
+ if (val < 0) {
+ dev_err(dp->dev, "PSR_STATUS read failed ret=%zd", val);
+ return val;
+ }
+ return status;
+}
+
+int analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+ struct dp_sdp *vsc, bool blocking)
{
unsigned int val;
+ int ret;
+ ssize_t psr_status;
/* don't send info frame */
val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
@@ -1019,8 +929,8 @@ void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
/* configure DB0 / DB1 values */
- writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
- writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+ writel(vsc->db[0], dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+ writel(vsc->db[1], dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
/* set reuse spd inforframe */
val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
@@ -1036,6 +946,30 @@ void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
val |= IF_EN;
writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+ if (!blocking)
+ return 0;
+
+ /*
+ * db[1]!=0: entering PSR, wait for fully active remote frame buffer.
+ * db[1]==0: exiting PSR, wait for either
+ * (a) ACTIVE_RESYNC - the sink "must display the
+ * incoming active frames from the Source device with no visible
+ * glitches and/or artifacts", even though timings may still be
+ * re-synchronizing; or
+ * (b) INACTIVE - the transition is fully complete.
+ */
+ ret = readx_poll_timeout(analogix_dp_get_psr_status, dp, psr_status,
+ psr_status >= 0 &&
+ ((vsc->db[1] && psr_status == DP_PSR_SINK_ACTIVE_RFB) ||
+ (!vsc->db[1] && (psr_status == DP_PSR_SINK_ACTIVE_RESYNC ||
+ psr_status == DP_PSR_SINK_INACTIVE))),
+ 1500, DP_TIMEOUT_PSR_LOOP_MS * 1000);
+ if (ret) {
+ dev_warn(dp->dev, "Failed to apply PSR %d\n", ret);
+ return ret;
+ }
+ return 0;
}
ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
@@ -1043,9 +977,8 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
{
u32 reg;
u8 *buffer = msg->buffer;
- int timeout_loop = 0;
unsigned int i;
- int num_transferred = 0;
+ int ret;
/* Buffer size of AUX CH is 16 bytes */
if (WARN_ON(msg->size > 16))
@@ -1096,7 +1029,6 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
reg = buffer[i];
writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0 +
4 * i);
- num_transferred++;
}
}
@@ -1109,17 +1041,20 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
- /* Is AUX CH command reply received? */
+ ret = readx_poll_timeout(readl, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2,
+ reg, !(reg & AUX_EN), 25, 500 * 1000);
+ if (ret) {
+ dev_err(dp->dev, "AUX CH enable timeout!\n");
+ goto aux_error;
+ }
+
/* TODO: Wait for an interrupt instead of looping? */
- reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
- while (!(reg & RPLY_RECEIV)) {
- timeout_loop++;
- if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
- dev_err(dp->dev, "AUX CH command reply failed!\n");
- return -ETIMEDOUT;
- }
- reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
- usleep_range(10, 11);
+ /* Is AUX CH command reply received? */
+ ret = readx_poll_timeout(readl, dp->reg_base + ANALOGIX_DP_INT_STA,
+ reg, reg & RPLY_RECEIV, 10, 20 * 1000);
+ if (ret) {
+ dev_err(dp->dev, "AUX CH cmd reply timeout!\n");
+ goto aux_error;
}
/* Clear interrupt source for AUX CH command reply */
@@ -1127,17 +1062,18 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
/* Clear interrupt source for AUX CH access error */
reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
- if (reg & AUX_ERR) {
+ if ((reg & AUX_ERR)) {
+ u32 aux_status = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA) &
+ AUX_STATUS_MASK;
+
writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
- return -EREMOTEIO;
- }
- /* Check AUX CH error access status */
- reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
- if ((reg & AUX_STATUS_MASK)) {
- dev_err(dp->dev, "AUX CH error happened: %d\n\n",
- reg & AUX_STATUS_MASK);
- return -EREMOTEIO;
+ if (aux_status == AUX_STATUS_TIMEOUT_ERROR)
+ return -ETIMEDOUT;
+
+ dev_warn(dp->dev, "AUX CH error happened: %#x (%d)\n",
+ aux_status, !!(reg & AUX_ERR));
+ goto aux_error;
}
if (msg->request & DP_AUX_I2C_READ) {
@@ -1145,7 +1081,6 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0 +
4 * i);
buffer[i] = (unsigned char)reg;
- num_transferred++;
}
}
@@ -1162,5 +1097,11 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
(msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_READ)
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
- return num_transferred > 0 ? num_transferred : -EBUSY;
+ return msg->size;
+
+aux_error:
+ /* if aux err happen, reset aux */
+ analogix_dp_init_aux(dp);
+
+ return -EREMOTEIO;
}