diff options
author | Animesh Kishore <ankishore@nvidia.com> | 2014-02-20 16:19:41 +0530 |
---|---|---|
committer | Animesh Kishore <ankishore@nvidia.com> | 2014-02-27 02:24:00 -0800 |
commit | 6a4305ffaeb59f8e14624d13b78fee50588e15de (patch) | |
tree | 11ee7a37a03d066079548d84c61a27d54cd0122b /drivers/video/tegra/dc/dp.c | |
parent | 3143f5615d9c60d1fb8b05322b88330d00a659c8 (diff) |
video: tegra: dp: Optimize link training
- Explore link config only during boot.
- Restructure the code for better code reuse
between fast and full link training.
Bug 1453404
Bug 1453390
Change-Id: I3a2679338ee8fed2f4a88a50510db9298123f951
Signed-off-by: Animesh Kishore <ankishore@nvidia.com>
Reviewed-on: http://git-master/r/369885
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Diffstat (limited to 'drivers/video/tegra/dc/dp.c')
-rw-r--r-- | drivers/video/tegra/dc/dp.c | 794 |
1 files changed, 388 insertions, 406 deletions
diff --git a/drivers/video/tegra/dc/dp.c b/drivers/video/tegra/dc/dp.c index 5baf6c5bda1b..79c737e10726 100644 --- a/drivers/video/tegra/dc/dp.c +++ b/drivers/video/tegra/dc/dp.c @@ -14,7 +14,6 @@ * */ - #include <linux/err.h> #include <linux/gpio.h> #include <linux/wait.h> @@ -24,6 +23,7 @@ #include <linux/debugfs.h> #include <linux/tegra-soc.h> #include <linux/clk/tegra.h> +#include <linux/moduleparam.h> #include <mach/dc.h> #include <mach/fb.h> @@ -35,7 +35,22 @@ #include "dc_priv.h" #include "edid.h" +static bool tegra_dp_debug; +module_param(tegra_dp_debug, bool, 0644); +MODULE_PARM_DESC(tegra_dp_debug, "Enable to print all link configs"); + static int tegra_dp_lt(struct tegra_dc_dp_data *dp); +static int tegra_dp_fast_lt(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *cfg); +static void tegra_dp_link_config(struct tegra_dc_dp_data *dp); +static void tegra_dp_tpg(struct tegra_dc_dp_data *dp, u32 tp, u32 n_lanes); +static void tegra_dp_lt_config(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4]); +static bool tegra_dp_clock_recovery_status(struct tegra_dc_dp_data *dp); +static bool tegra_dp_channel_eq_status(struct tegra_dc_dp_data *dp); +static void tegra_dp_set_tx_pu(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4]); +static int tegra_dp_full_lt(struct tegra_dc_dp_data *dp); static inline u32 tegra_dpaux_readl(struct tegra_dc_dp_data *dp, u32 reg) { @@ -602,6 +617,21 @@ static int tegra_dc_dp_dpcd_write(struct tegra_dc_dp_data *dp, u32 cmd, return ret; } +static inline int tegra_dp_dpcd_write_field(struct tegra_dc_dp_data *dp, + u32 cmd, u8 mask, u8 data) +{ + u8 dpcd_data; + int ret; + + might_sleep(); + + CHECK_RET(tegra_dc_dp_dpcd_read(dp, cmd, &dpcd_data)); + dpcd_data &= ~mask; + dpcd_data |= data; + CHECK_RET(tegra_dc_dp_dpcd_write(dp, cmd, dpcd_data)); + + return 0; +} static inline u64 tegra_div64(u64 dividend, u32 divisor) { @@ -707,6 +737,9 @@ static int tegra_dp_panel_power_state(struct tegra_dc_dp_data *dp, u8 state) static void tegra_dc_dp_dump_link_cfg(struct tegra_dc_dp_data *dp, const struct tegra_dc_dp_link_config *cfg) { + if (!tegra_dp_debug) + return; + BUG_ON(!cfg); dev_info(&dp->dc->ndev->dev, "DP config: cfg_name " @@ -916,6 +949,7 @@ static bool tegra_dc_dp_calc_config(struct tegra_dc_dp_data *dp, cfg->vblank_sym = 0; cfg->is_valid = true; + tegra_dc_dp_dump_link_cfg(dp, cfg); return true; @@ -924,8 +958,8 @@ static bool tegra_dc_dp_calc_config(struct tegra_dc_dp_data *dp, static int tegra_dp_init_max_link_cfg(struct tegra_dc_dp_data *dp, struct tegra_dc_dp_link_config *cfg) { - u8 dpcd_data; - int ret; + u8 dpcd_data; + int ret; CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_LANE_COUNT, &dpcd_data)); @@ -966,11 +1000,13 @@ static int tegra_dp_init_max_link_cfg(struct tegra_dc_dp_data *dp, cfg->edp_cap = (dpcd_data & NV_DPCD_EDP_CONFIG_CAP_DISPLAY_CONTROL_CAP_YES) ? true : false; - cfg->lane_count = cfg->max_lane_count; - cfg->link_bw = cfg->max_link_bw; + cfg->lane_count = cfg->max_lane_count; + cfg->link_bw = cfg->max_link_bw; cfg->enhanced_framing = cfg->support_enhanced_framing; tegra_dc_dp_calc_config(dp, dp->mode, cfg); + + dp->max_link_cfg = *cfg; return 0; } @@ -999,137 +1035,141 @@ static int tegra_dp_set_link_bandwidth(struct tegra_dc_dp_data *dp, u8 link_bw) return tegra_dc_dp_dpcd_write(dp, NV_DPCD_LINK_BANDWIDTH_SET, link_bw); } -static int tegra_dp_set_lane_count(struct tegra_dc_dp_data *dp, - const struct tegra_dc_dp_link_config *cfg) +static int tegra_dp_set_enhanced_framing(struct tegra_dc_dp_data *dp, + bool enable) { - u8 dpcd_data; int ret; - /* check if panel support enhanched_framing */ - dpcd_data = cfg->lane_count; - if (cfg->enhanced_framing) - dpcd_data |= NV_DPCD_LANE_COUNT_SET_ENHANCEDFRAMING_T; - CHECK_RET(tegra_dc_dp_dpcd_write(dp, NV_DPCD_LANE_COUNT_SET, - dpcd_data)); + if (enable) { + tegra_sor_write_field(dp->sor, + NV_SOR_DP_LINKCTL(dp->sor->portnum), + NV_SOR_DP_LINKCTL_ENHANCEDFRAME_ENABLE, + NV_SOR_DP_LINKCTL_ENHANCEDFRAME_ENABLE); - tegra_dc_sor_set_lane_count(dp->sor, cfg->lane_count); + CHECK_RET(tegra_dp_dpcd_write_field(dp, NV_DPCD_LANE_COUNT_SET, + NV_DPCD_LANE_COUNT_SET_ENHANCEDFRAMING_T, + NV_DPCD_LANE_COUNT_SET_ENHANCEDFRAMING_T)); + } - /* Also power down lanes that will not be used */ return 0; } -static bool tegra_dc_dp_link_trained(struct tegra_dc_dp_data *dp, - const struct tegra_dc_dp_link_config *cfg) +static int tegra_dp_set_lane_count(struct tegra_dc_dp_data *dp, u8 lane_cnt) { - u32 lane; - u8 mask; - u8 data; int ret; - for (lane = 0; lane < cfg->lane_count; ++lane) { - CHECK_RET(tegra_dc_dp_dpcd_read(dp, (lane/2) ? - NV_DPCD_LANE2_3_STATUS : NV_DPCD_LANE0_1_STATUS, - &data)); - mask = (lane & 1) ? - NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES | - NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES | - NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES : - NV_DPCD_STATUS_LANEX_CR_DONE_YES | - NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES | - NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES; - if ((data & mask) != mask) - return false; - } - return true; + tegra_sor_power_dp_lanes(dp->sor, lane_cnt, true); + + CHECK_RET(tegra_dp_dpcd_write_field(dp, NV_DPCD_LANE_COUNT_SET, + NV_DPCD_LANE_COUNT_SET_MASK, + lane_cnt)); + + return 0; } +static bool tegra_dp_lt_status(struct tegra_dc_dp_data *dp) +{ + bool cr_done, ce_done; -static int tegra_dc_dp_fast_link_training(struct tegra_dc_dp_data *dp, - const struct tegra_dc_dp_link_config *cfg) + cr_done = tegra_dp_clock_recovery_status(dp); + if (!cr_done) + return false; + + ce_done = tegra_dp_channel_eq_status(dp); + + return ce_done; +} + +void tegra_dp_lt_cal(struct tegra_dc_dp_data *dp) { struct tegra_dc_sor_data *sor = dp->sor; - u8 link_bw; - u8 lane_count; - u32 data; - u32 size; - u32 status; - int j; - u32 mask = 0xffff >> ((4 - cfg->lane_count) * 4); + struct tegra_dc_dp_link_config *cfg = &dp->link_cfg; + u32 load_adj; + + switch (cfg->link_bw) { + case SOR_LINK_SPEED_G1_62: + load_adj = 0x3; + break; + case SOR_LINK_SPEED_G2_7: + load_adj = 0x4; + break; + case SOR_LINK_SPEED_G5_4: + load_adj = 0x6; + break; + default: + BUG(); + } + + tegra_sor_write_field(sor, NV_SOR_PLL1, + NV_SOR_PLL1_LOADADJ_DEFAULT_MASK, + load_adj << NV_SOR_PLL1_LOADADJ_SHIFT); + + tegra_dp_set_tx_pu(dp, cfg->preemphasis, + cfg->drive_current, cfg->postcursor); +} + +static int tegra_dp_fast_lt(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *cfg) +{ + bool cr_done; + bool lt_done; + int ret = 0; BUG_ON(!cfg || !cfg->is_valid); - tegra_dc_sor_set_lane_parm(sor, cfg); - tegra_dc_sor_set_dp_linkctl(dp->sor, true, trainingPattern_None, cfg); - tegra_dc_sor_set_dp_mode(dp->sor, cfg); - tegra_dp_set_lane_count(dp, cfg); - tegra_dp_set_link_bandwidth(dp, cfg->link_bw); + cfg->lt_data_valid = false; - tegra_dc_dp_dpcd_write(dp, NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET, - NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET_ANSI_8B10B); - - /* Send TP1 */ - tegra_dc_sor_set_dp_linkctl(sor, true, trainingPattern_1, cfg); - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, - NV_DPCD_TRAINING_PATTERN_SET_TPS_TP1); - - for (j = 0; j < cfg->lane_count; ++j) - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_LANE0_SET + j, - 0x24); - usleep_range(500, 1000); - size = 2; - tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, - NV_DPCD_LANE0_1_STATUS, (u8 *)&data, &size, &status); - status = mask & 0x1111; - if ((data & status) != status) { - dev_err(&dp->dc->ndev->dev, - "dp: Link training error for TP1 (0x%x)\n", data); - return -EFAULT; - } + tegra_dp_lt_cal(dp); - /* enable ASSR */ - tegra_dc_dp_set_assr(dp, true); - tegra_dc_sor_set_dp_linkctl(sor, true, trainingPattern_3, cfg); - - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, - cfg->link_bw == 20 ? 0x23 : 0x22); - for (j = 0; j < cfg->lane_count; ++j) - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_LANE0_SET + j, - 0x24); - usleep_range(500, 1000); - - size = 2; - tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, - NV_DPCD_LANE0_1_STATUS, (u8 *)&data, &size, &status); - if ((data & mask) != (0x7777 & mask)) { - dev_info(&dp->dc->ndev->dev, - "dp: Link training error for TP2/3 (0x%x)\n", data); - return -EFAULT; - } + tegra_sor_precharge_lanes(dp->sor); - tegra_dc_sor_set_dp_linkctl(sor, true, trainingPattern_Disabled, cfg); - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, 0); + tegra_dp_lt_config(dp, cfg->preemphasis, + cfg->drive_current, cfg->postcursor); - if (!tegra_dc_dp_link_trained(dp, cfg)) { - tegra_dc_sor_read_link_config(dp->sor, &link_bw, - &lane_count); - dev_err(&dp->dc->ndev->dev, - "Fast link trainging failed, link bw %d, lane # %d\n", - link_bw, lane_count); - return -EFAULT; - } else - dev_info(&dp->dc->ndev->dev, - "Fast link trainging succeeded, link bw %d, lane %d\n", - cfg->link_bw, cfg->lane_count); + tegra_dp_tpg(dp, TRAINING_PATTERN_1, cfg->lane_count); + tegra_dp_wait_aux_training(dp, true); - return 0; + cr_done = tegra_dp_clock_recovery_status(dp); + cr_done ? : ({ret = -EINVAL; goto fail; }); + + if (cfg->tps3_supported) + tegra_dp_tpg(dp, TRAINING_PATTERN_3, cfg->lane_count); + else + tegra_dp_tpg(dp, TRAINING_PATTERN_2, cfg->lane_count); + tegra_dp_wait_aux_training(dp, false); + + lt_done = tegra_dp_lt_status(dp); + lt_done ? : ({ret = -EINVAL; goto fail; }); + + cfg->lt_data_valid = true; + tegra_dc_dp_dump_link_cfg(dp, cfg); +fail: + tegra_dp_tpg(dp, TRAINING_PATTERN_DISABLE, cfg->lane_count); + return ret; +} + +static inline void tegra_dp_save_link_config(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *old_cfg) +{ + *old_cfg = dp->link_cfg; } -static bool tegra_dc_dp_lower_config(struct tegra_dc_dp_data *dp, +static inline void tegra_dp_restore_link_config(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *old_cfg) +{ + dp->link_cfg = *old_cfg; + tegra_dp_link_config(dp); +} + +static bool tegra_dp_lower_link_config(struct tegra_dc_dp_data *dp, struct tegra_dc_dp_link_config *cfg) { + struct tegra_dc_dp_link_config tmp_cfg; BUG_ON(!cfg); + tegra_dp_save_link_config(dp, &tmp_cfg); cfg->is_valid = false; + if (cfg->link_bw == SOR_LINK_SPEED_G1_62) { if (cfg->max_link_bw > SOR_LINK_SPEED_G1_62) cfg->link_bw = SOR_LINK_SPEED_G2_7; @@ -1155,100 +1195,78 @@ static bool tegra_dc_dp_lower_config(struct tegra_dc_dp_data *dp, goto fail; cfg->is_valid = true; + + tegra_dp_link_config(dp); + return true; fail: + tegra_dp_restore_link_config(dp, &tmp_cfg); return false; } -static int tegra_dp_link_config(struct tegra_dc_dp_data *dp, - struct tegra_dc_dp_link_config *cfg) +static int tegra_dp_lt(struct tegra_dc_dp_data *dp) { - u8 link_bw; - u8 lane_count; - int ret; - struct tegra_dc_dp_link_config cur_cfg; - - if (cfg->lane_count == 0) { - /* TODO: shutdown the link */ - return 0; - } - - /* Enable ASSR if possible */ - if (cfg->alt_scramber_reset_cap) - CHECK_RET(tegra_dc_dp_set_assr(dp, true)); - - /* Link training rules: always try fast link training for eDP panel - when LT data is provided, or DP panel with valid config values */ - ret = -1; - if ((cfg->edp_cap && dp->pdata->n_lt_settings) - || (cfg->support_fast_lt && cfg->vs_pe_valid)) { - cur_cfg.is_valid = false; - while (!(ret = tegra_dc_dp_fast_link_training(dp, cfg))) { - cur_cfg = *cfg; - if (!tegra_dc_dp_lower_config(dp, cfg)) + int ret = -EFAULT; + struct tegra_dc_dp_link_config *cfg = &dp->link_cfg; + struct tegra_dp_out *dp_pdata = dp->pdata; + + if (cfg->support_fast_lt && cfg->lt_data_valid) { + ret = tegra_dp_fast_lt(dp, cfg); + if (!ret) + goto lt_success; + } else if (dp->pdata && dp->pdata->n_lt_settings) { + size_t cnt = 0; + struct tegra_dc_dp_link_config lt_pass_cfg; + size_t copy_bytes = sizeof( + dp_pdata->lt_settings[0].lane_preemphasis[0]) * 4; + + for (; cnt < dp->pdata->n_lt_settings; cnt++) { + struct tegra_dc_dp_lt_settings *lt_data = + &dp_pdata->lt_settings[cnt]; + memcpy(cfg->preemphasis, + lt_data->lane_preemphasis, copy_bytes); + memcpy(cfg->drive_current, + lt_data->drive_current, copy_bytes); + memcpy(cfg->postcursor, + lt_data->post_cursor, copy_bytes); + ret = tegra_dp_fast_lt(dp, cfg); + if (!ret) break; } - if (cur_cfg.is_valid) { - *cfg = cur_cfg; + /* Fast link training failed with platform data */ + if (ret) + goto try_full_lt; + + /* Try lower link config */ + do { + lt_pass_cfg = *cfg; + if (!tegra_dp_lower_link_config(dp, cfg)) + break; + } while (!(ret = tegra_dp_fast_lt(dp, cfg))); - /* redo fast link training if needed */ - if (unlikely(ret)) - ret = tegra_dc_dp_fast_link_training(dp, cfg); + /* Try last known good link config */ + if (ret) { + tegra_dp_restore_link_config(dp, <_pass_cfg); + ret = tegra_dp_fast_lt(dp, cfg); } + /* Fast link training failed with known good link config */ if (unlikely(ret)) { - dev_WARN(&dp->dc->ndev->dev, - "dp: fast link training failed\n"); - /* retstoring the max lane count and bw for Full LT */ - cfg->lane_count = cfg->max_lane_count; - cfg->link_bw = cfg->max_link_bw; - cfg->vs_pe_valid = false; - } - } - /* Fall back to full link training otherwise */ - if (ret) { - tegra_dc_sor_set_dp_linkctl(dp->sor, true, - trainingPattern_None, cfg); - tegra_dc_sor_set_dp_mode(dp->sor, cfg); - tegra_dp_set_lane_count(dp, cfg); - tegra_dp_set_link_bandwidth(dp, cfg->link_bw); - - ret = tegra_dp_lt(dp); - if (ret < 0) { - dev_err(&dp->dc->ndev->dev, "dp: link training failed\n"); - cfg->vs_pe_valid = false; - return ret; + dev_err(&dp->dc->ndev->dev, + "dp: fast lt failed, trying full lt\n"); + goto try_full_lt; } } - /* Everything goes well, double check the link config */ - /* TODO: record edc/c2 data for debugging */ - tegra_dc_sor_read_link_config(dp->sor, &link_bw, &lane_count); - - if ((cfg->link_bw == link_bw) && (cfg->lane_count == lane_count)) - return 0; - else - return -EFAULT; -} - -static int tegra_dc_dp_explore_link_cfg(struct tegra_dc_dp_data *dp, - struct tegra_dc_dp_link_config *cfg, struct tegra_dc_mode *mode) -{ - int ret; - - if (!mode->pclk || !mode->h_active || !mode->v_active) { - dev_err(&dp->dc->ndev->dev, - "dp: error mode configuration"); - return -EINVAL; - } - if (!cfg->max_link_bw || !cfg->max_lane_count) { - dev_err(&dp->dc->ndev->dev, - "dp: error link configuration"); - return -EINVAL; +try_full_lt: + if (ret) { + tegra_dp_restore_link_config(dp, &dp->max_link_cfg); + ret = tegra_dp_full_lt(dp); + if (ret < 0) + dev_err(&dp->dc->ndev->dev, "dp: full lt failed.\n"); } - - ret = tegra_dp_link_config(dp, cfg); +lt_success: return ret; } @@ -1264,18 +1282,7 @@ static void tegra_dc_dp_lt_worker(struct work_struct *work) tegra_dc_dp_dpcd_write(dp, NV_DPCD_DEVICE_SERVICE_IRQ_VECTOR, NV_DPCD_DEVICE_SERVICE_IRQ_VECTOR_AUTO_TEST_YES); - if (!dp->link_cfg.is_valid || - tegra_dp_link_config(dp, &dp->link_cfg)) { - /* If current config is not valid or cannot be trained, - needs to re-explore the possilbe config */ - if (tegra_dp_init_max_link_cfg(dp, &dp->link_cfg)) - dev_err(&dp->dc->ndev->dev, - "dp: failed to init link configuration\n"); - else if (tegra_dc_dp_explore_link_cfg(dp, &dp->link_cfg, - dp->mode)) - dev_err(&dp->dc->ndev->dev, - "dp irq: cannot get working config\n"); - } + tegra_dp_lt(dp); tegra_dc_enable(dp->dc); } @@ -1517,18 +1524,15 @@ static void tegra_dp_set_tx_pu(struct tegra_dc_dp_data *dp, u32 pe[4], NV_SOR_DP_PADCTL_TX_PU_ENABLE)); } -static int _tegra_dp_clk_recovery(struct tegra_dc_dp_data *dp, u32 pe[4], - u32 vs[4], u32 pc[4], bool pc_supported, - u32 n_lanes) +static void tegra_dp_lt_config(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4]) { struct tegra_dc_sor_data *sor = dp->sor; + u32 n_lanes = dp->link_cfg.lane_count; + bool pc_supported = dp->link_cfg.tps3_supported; u32 cnt; - bool cr_done = true; - u8 data_ptr; - u32 pe_temp[4], vs_temp[4]; - u32 retry_cnt = 1; u32 val; -retry: + for (cnt = 0; cnt < n_lanes; cnt++) { u32 mask = 0; u32 pe_reg, vs_reg, pc_reg; @@ -1556,7 +1560,7 @@ retry: } pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]]; vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]]; - pc_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]]; tegra_sor_write_field(sor, NV_SOR_PR(sor->portnum), mask, (pe_reg << shift)); tegra_sor_write_field(sor, NV_SOR_DC(sor->portnum), @@ -1602,7 +1606,13 @@ retry: (NV_DPCD_TRAINING_LANE0_1_SET2 + cnt), val); } } - tegra_dp_wait_aux_training(dp, true); +} + +static bool tegra_dp_clock_recovery_status(struct tegra_dc_dp_data *dp) +{ + u32 cnt; + u32 n_lanes = dp->link_cfg.lane_count; + u8 data_ptr; for (cnt = 0; cnt < n_lanes / 2; cnt++) { tegra_dc_dp_dpcd_read(dp, @@ -1610,16 +1620,21 @@ retry: if (!(data_ptr & 0x1) || !(data_ptr & (0x1 << NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT))) { - cr_done = false; - break; + return false; } } - if (cr_done) - return 0; + return true; +} + +static void tegra_dp_lt_adjust(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4], + bool pc_supported) +{ + size_t cnt; + u8 data_ptr; + u32 n_lanes = dp->link_cfg.lane_count; - memcpy(pe_temp, pe, sizeof(pe_temp)); - memcpy(vs_temp, vs, sizeof(vs_temp)); for (cnt = 0; cnt < n_lanes / 2; cnt++) { tegra_dc_dp_dpcd_read(dp, (NV_DPCD_LANE0_1_ADJUST_REQ + cnt), &data_ptr); @@ -1643,9 +1658,28 @@ retry: NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK; } } +} - if (!memcmp(pe_temp, pe, sizeof(pe_temp)) && - !memcmp(vs_temp, vs, sizeof(vs_temp))) { +static int _tegra_dp_clk_recovery(struct tegra_dc_dp_data *dp, u32 pe[4], + u32 vs[4], u32 pc[4], bool pc_supported, + u32 n_lanes) +{ + bool cr_done = true; + u32 vs_temp[4]; + u32 retry_cnt = 1; +retry: + tegra_dp_lt_config(dp, pe, vs, pc); + tegra_dp_wait_aux_training(dp, true); + + cr_done = tegra_dp_clock_recovery_status(dp); + if (cr_done) + return 0; + + memcpy(vs_temp, vs, sizeof(vs_temp)); + + tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported); + + if (!memcmp(vs_temp, vs, sizeof(vs_temp))) { if (retry_cnt++ >= 5) return -EBUSY; goto retry; @@ -1654,30 +1688,6 @@ retry: return _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes); } -static void tegra_dp_tpg(struct tegra_dc_dp_data *dp, u32 tp, u32 n_lanes) -{ - struct tegra_dc_sor_data *sor = dp->sor; - u32 const tbl[][2] = { - /* ansi8b/10b encoded, scrambled */ - {1, 1}, /* no pattern */ - {1, 0}, /* training pattern 1 */ - {1, 0}, /* training pattern 2 */ - }; - u32 cnt; - u32 val = 0; - - for (cnt = 0; cnt < n_lanes; cnt++) { - u32 tp_shift = NV_SOR_DP_TPG_LANE1_PATTERN_SHIFT * cnt; - val |= tp << tp_shift | - tbl[tp][0] << (tp_shift + - NV_SOR_DP_TPG_LANE0_CHANNELCODING_SHIFT) | - tbl[tp][1] << (tp_shift + - NV_SOR_DP_TPG_LANE0_SCRAMBLEREN_SHIFT); - } - - tegra_sor_writel(sor, NV_SOR_DP_TPG, val); -} - static int tegra_dp_clk_recovery(struct tegra_dc_dp_data *dp, u32 pe[4], u32 vs[4], u32 pc[4]) { @@ -1685,41 +1695,25 @@ static int tegra_dp_clk_recovery(struct tegra_dc_dp_data *dp, bool pc_supported = dp->link_cfg.tps3_supported; int err; - tegra_dp_tpg(dp, trainingPattern_1, n_lanes); - - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, - (NV_DPCD_TRAINING_PATTERN_SET_TPS_TP1 | - NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T)); + tegra_dp_tpg(dp, TRAINING_PATTERN_1, n_lanes); err = _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes); + if (err < 0) + tegra_dp_tpg(dp, TRAINING_PATTERN_DISABLE, n_lanes); return err; } -static int _tegra_dp_channel_eq(struct tegra_dc_dp_data *dp, u32 pe[4], - u32 vs[4], u32 pc[4], bool pc_supported, - u32 n_lanes) +static bool tegra_dp_channel_eq_status(struct tegra_dc_dp_data *dp) { - struct tegra_dc_sor_data *sor = dp->sor; u32 cnt; + u32 n_lanes = dp->link_cfg.lane_count; u8 data_ptr; - bool cr_done = true; bool ce_done = true; - u32 retry_cnt = 1; - u32 val; - -retry: - tegra_dp_wait_aux_training(dp, false); for (cnt = 0; cnt < n_lanes / 2; cnt++) { tegra_dc_dp_dpcd_read(dp, (NV_DPCD_LANE0_1_STATUS + cnt), &data_ptr); - if (!(data_ptr & 0x1) || - !(data_ptr & - (0x1 << NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT))) { - cr_done = false; - break; - } if (!(data_ptr & (0x1 << NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) || !(data_ptr & @@ -1732,7 +1726,7 @@ retry: break; } } - if (cr_done && ce_done) { + if (ce_done) { tegra_dc_dp_dpcd_read(dp, NV_DPCD_LANE_ALIGN_STATUS_UPDATED, &data_ptr); if (!(data_ptr & @@ -1740,6 +1734,22 @@ retry: ce_done = false; } + return ce_done; +} + +static int _tegra_dp_channel_eq(struct tegra_dc_dp_data *dp, u32 pe[4], + u32 vs[4], u32 pc[4], bool pc_supported, + u32 n_lanes) +{ + bool cr_done = true; + bool ce_done = true; + u32 retry_cnt = 1; +retry: + tegra_dp_wait_aux_training(dp, false); + + cr_done = tegra_dp_clock_recovery_status(dp); + ce_done = tegra_dp_channel_eq_status(dp); + if (!cr_done) goto fail; @@ -1749,108 +1759,13 @@ retry: if (++retry_cnt > 5) goto fail; - for (cnt = 0; cnt < n_lanes / 2; cnt++) { - tegra_dc_dp_dpcd_read(dp, - (NV_DPCD_LANE0_1_ADJUST_REQ + cnt), &data_ptr); - pe[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_PE_MASK) >> - NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT; - vs[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_DC_MASK) >> - NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT; - pe[1 + 2 * cnt] = - (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK) >> - NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT; - vs[1 + 2 * cnt] = - (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK) >> - NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT; - } - if (pc_supported) { - tegra_dc_dp_dpcd_read(dp, - NV_DPCD_ADJUST_REQ_POST_CURSOR2, &data_ptr); - for (cnt = 0; cnt < n_lanes; cnt++) { - pc[cnt] = (data_ptr >> - NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(cnt)) & - NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK; - } - } + tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported); - for (cnt = 0; cnt < n_lanes; cnt++) { - u32 mask = 0; - u32 pe_reg, vs_reg, pc_reg; - u32 shift = 0; - switch (cnt) { - case 0: - mask = NV_SOR_PR_LANE2_DP_LANE0_MASK; - shift = NV_SOR_PR_LANE2_DP_LANE0_SHIFT; - break; - case 1: - mask = NV_SOR_PR_LANE1_DP_LANE1_MASK; - shift = NV_SOR_PR_LANE1_DP_LANE1_SHIFT; - break; - case 2: - mask = NV_SOR_PR_LANE0_DP_LANE2_MASK; - shift = NV_SOR_PR_LANE0_DP_LANE2_SHIFT; - break; - case 3: - mask = NV_SOR_PR_LANE3_DP_LANE3_MASK; - shift = NV_SOR_PR_LANE3_DP_LANE3_SHIFT; - break; - default: - dev_err(&dp->dc->ndev->dev, - "dp: incorrect lane cnt\n"); - } - pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]]; - vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]]; - pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]]; - tegra_sor_write_field(sor, NV_SOR_PR(sor->portnum), - mask, (pe_reg << shift)); - tegra_sor_write_field(sor, NV_SOR_DC(sor->portnum), - mask, (vs_reg << shift)); - if (pc_supported) { - tegra_sor_write_field( - sor, NV_SOR_POSTCURSOR(sor->portnum), - mask, (pc_reg << shift)); - } - } - tegra_dp_set_tx_pu(dp, pe, vs, pc); - usleep_range(15, 20); - - for (cnt = 0; cnt < n_lanes; cnt++) { - u32 max_vs_flag = tegra_dp_is_max_vs(pe[cnt], vs[cnt]); - u32 max_pe_flag = tegra_dp_is_max_pe(pe[cnt], vs[cnt]); - - val = (vs[cnt] << NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT) | - (max_vs_flag ? - NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T : - NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F) | - (pe[cnt] << NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT) | - (max_pe_flag ? - NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T : - NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F); - tegra_dc_dp_dpcd_write(dp, - (NV_DPCD_TRAINING_LANE0_SET + cnt), val); - } - if (pc_supported) { - for (cnt = 0; cnt < n_lanes / 2; cnt++) { - u32 max_pc_flag0 = tegra_dp_is_max_pc(pc[cnt]); - u32 max_pc_flag1 = tegra_dp_is_max_pc(pc[cnt + 1]); - val = (pc[cnt] << NV_DPCD_LANEX_SET2_PC2_SHIFT) | - (max_pc_flag0 ? - NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T : - NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F) | - (pc[cnt + 1] << - NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT) | - (max_pc_flag1 ? - NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T : - NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F); - tegra_dc_dp_dpcd_write(dp, - (NV_DPCD_TRAINING_LANE0_1_SET2 + cnt), val); - } - } - usleep_range(150, 200); + tegra_dp_lt_config(dp, pe, vs, pc); goto retry; fail: - if (tegra_dc_dp_lower_config(dp, &dp->link_cfg)) + if (tegra_dp_lower_link_config(dp, &dp->link_cfg)) return -EAGAIN; return -EBUSY; } @@ -1861,67 +1776,57 @@ static int tegra_dp_channel_eq(struct tegra_dc_dp_data *dp, u32 n_lanes = dp->link_cfg.lane_count; bool pc_supported = dp->link_cfg.tps3_supported; int err; - u32 tp_src = trainingPattern_2; - u32 tp_sink = NV_DPCD_TRAINING_PATTERN_SET_TPS_TP2; + u32 tp_src = TRAINING_PATTERN_2; - if (pc_supported) { - tp_src = trainingPattern_3; - tp_sink = NV_DPCD_TRAINING_PATTERN_SET_TPS_TP3; - } + if (pc_supported) + tp_src = TRAINING_PATTERN_3; tegra_dp_tpg(dp, tp_src, n_lanes); - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, - (tp_sink | - NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T)); - err = _tegra_dp_channel_eq(dp, pe, vs, pc, pc_supported, n_lanes); + tegra_dp_tpg(dp, TRAINING_PATTERN_DISABLE, n_lanes); + return err; } -static int tegra_dp_lt(struct tegra_dc_dp_data *dp) +static int tegra_dp_full_lt(struct tegra_dc_dp_data *dp) { struct tegra_dc_sor_data *sor = dp->sor; int err; u32 pe[4] = { - preEmphasis_Disabled, - preEmphasis_Disabled, - preEmphasis_Disabled, - preEmphasis_Disabled + PRE_EMPHASIS_L0, + PRE_EMPHASIS_L0, + PRE_EMPHASIS_L0, + PRE_EMPHASIS_L0 }; u32 vs[4] = { - driveCurrent_Level0, - driveCurrent_Level0, - driveCurrent_Level0, - driveCurrent_Level0 + DRIVE_CURRENT_L0, + DRIVE_CURRENT_L0, + DRIVE_CURRENT_L0, + DRIVE_CURRENT_L0 }; u32 pc[4] = { - postCursor2_Level0, - postCursor2_Level0, - postCursor2_Level0, - postCursor2_Level0 + POST_CURSOR2_L0, + POST_CURSOR2_L0, + POST_CURSOR2_L0, + POST_CURSOR2_L0 }; + size_t copy_bytes = sizeof(pe[0]) * 4; + struct tegra_dc_dp_link_config *cfg = &dp->link_cfg; - tegra_sor_precharge_lanes(sor); - - tegra_dc_dp_dpcd_write(dp, NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET, - NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET_ANSI_8B10B); + cfg->lt_data_valid = false; - tegra_dp_set_lane_count(dp, &dp->link_cfg); - tegra_dp_set_link_bandwidth(dp, dp->link_cfg.max_link_bw); + tegra_sor_precharge_lanes(sor); retry_cr: - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, - NV_DPCD_TRAINING_PATTERN_SET_TPS_NONE); - tegra_dp_tpg(dp, trainingPattern_Disabled, dp->link_cfg.lane_count); - memset(pe, preEmphasis_Disabled, sizeof(pe)); - memset(vs, driveCurrent_Level0, sizeof(vs)); - memset(pc, postCursor2_Level0, sizeof(pc)); + memset(pe, PRE_EMPHASIS_L0, sizeof(pe)); + memset(vs, DRIVE_CURRENT_L0, sizeof(vs)); + memset(pc, POST_CURSOR2_L0, sizeof(pc)); err = tegra_dp_clk_recovery(dp, pe, vs, pc); if (err < 0) { - if (tegra_dc_dp_lower_config(dp, &dp->link_cfg)) + if (tegra_dp_lower_link_config(dp, &dp->link_cfg)) goto retry_cr; dev_err(&dp->dc->ndev->dev, "dp: clk recovery failed\n"); @@ -1938,38 +1843,113 @@ retry_cr: goto fail; } - tegra_dp_tpg(dp, trainingPattern_Disabled, dp->link_cfg.lane_count); + memcpy(cfg->preemphasis, pe, copy_bytes); + memcpy(cfg->drive_current, vs, copy_bytes); + memcpy(cfg->postcursor, pc, copy_bytes); - tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, - NV_DPCD_TRAINING_PATTERN_SET_TPS_NONE); + dp->link_cfg.lt_data_valid = true; - /* update link config with new voltage swing and pre-emphasis */ - dp->link_cfg.preemphasis = - tegra_sor_readl(sor, NV_SOR_PR(sor->portnum)); - dp->link_cfg.drive_current = - tegra_sor_readl(sor, NV_SOR_DC(sor->portnum)); - dp->link_cfg.postcursor = - tegra_sor_readl(sor, NV_SOR_POSTCURSOR(sor->portnum)); - dp->link_cfg.vs_pe_valid = true; + tegra_dc_dp_dump_link_cfg(dp, &dp->link_cfg); return 0; fail: - dp->link_cfg.vs_pe_valid = false; return err; } static void tegra_dp_dpcd_init(struct tegra_dc_dp_data *dp) { + struct tegra_dc_dp_link_config *cfg = &dp->link_cfg; + + if (cfg->is_valid) + return; + /* Check DP version */ if (tegra_dc_dp_dpcd_read(dp, NV_DPCD_REV, &dp->revision)) dev_err(&dp->dc->ndev->dev, "dp: failed to read the revision number from sink\n"); - if (tegra_dp_init_max_link_cfg(dp, &dp->link_cfg)) + if (tegra_dp_init_max_link_cfg(dp, cfg)) dev_err(&dp->dc->ndev->dev, "dp: failed to init link configuration\n"); } +static void tegra_dp_tpg(struct tegra_dc_dp_data *dp, u32 tp, u32 n_lanes) +{ + tegra_sor_tpg(dp->sor, tp, n_lanes); + + tegra_dc_dp_dpcd_write(dp, NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET, + NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET_ANSI_8B10B); + + if (tp == TRAINING_PATTERN_DISABLE) + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, + (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F)); + else + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, + (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T)); +} + +static void tegra_dp_tu_config(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *cfg) +{ + struct tegra_dc_sor_data *sor = dp->sor; + u32 reg_val; + + tegra_sor_write_field(sor, NV_SOR_DP_LINKCTL(sor->portnum), + NV_SOR_DP_LINKCTL_TUSIZE_MASK, + (cfg->tu_size << NV_SOR_DP_LINKCTL_TUSIZE_SHIFT)); + + tegra_sor_write_field(sor, NV_SOR_DP_CONFIG(sor->portnum), + NV_SOR_DP_CONFIG_WATERMARK_MASK, + cfg->watermark); + + tegra_sor_write_field(sor, NV_SOR_DP_CONFIG(sor->portnum), + NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_MASK, + (cfg->active_count << + NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_SHIFT)); + + tegra_sor_write_field(sor, NV_SOR_DP_CONFIG(sor->portnum), + NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_MASK, + (cfg->active_frac << + NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_SHIFT)); + + reg_val = cfg->activepolarity ? + NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE : + NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_NEGATIVE; + tegra_sor_write_field(sor, NV_SOR_DP_CONFIG(sor->portnum), + NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE, + reg_val); + + tegra_sor_write_field(sor, NV_SOR_DP_CONFIG(sor->portnum), + NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_ENABLE, + NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_ENABLE); + + tegra_sor_write_field(sor, NV_SOR_DP_CONFIG(sor->portnum), + NV_SOR_DP_CONFIG_RD_RESET_VAL_NEGATIVE, + NV_SOR_DP_CONFIG_RD_RESET_VAL_NEGATIVE); +} + +static void tegra_dp_link_config(struct tegra_dc_dp_data *dp) +{ + struct tegra_dc_dp_link_config *cfg = &dp->link_cfg; + struct tegra_dc_sor_data *sor = dp->sor; + + BUG_ON(!cfg || !cfg->is_valid || !dp->mode); + + tegra_dp_set_link_bandwidth(dp, cfg->link_bw); + tegra_dp_set_lane_count(dp, cfg->lane_count); + tegra_dp_set_enhanced_framing(dp, cfg->enhanced_framing); + + if (cfg->alt_scramber_reset_cap) + tegra_dc_dp_set_assr(dp, true); + + tegra_dp_tu_config(dp, cfg); + tegra_sor_writel(sor, NV_SOR_LVDS, 0); + + tegra_dp_tpg(dp, TRAINING_PATTERN_DISABLE, cfg->lane_count); + + tegra_sor_port_enable(sor, true); +} + static void tegra_dc_dp_enable(struct tegra_dc *dc) { struct tegra_dc_dp_data *dp = tegra_dc_get_outdata(dc); @@ -2003,7 +1983,9 @@ static void tegra_dc_dp_enable(struct tegra_dc *dc) tegra_dc_sor_enable_dp(dp->sor); - tegra_dc_dp_explore_link_cfg(dp, &dp->link_cfg, dp->mode); + tegra_dp_link_config(dp); + + tegra_dp_lt(dp); tegra_dc_sor_set_power_state(dp->sor, 1); tegra_dc_sor_attach(dp->sor); |