/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #define ONE_BLOCK_SIZE 128 #define DP_LINK_CONSTANT_N_VALUE 0x8000 #define DP_LINK_STATUS_SIZE 6 #define DP_LANE0_1_STATUS 0x202 #define DP_LANE2_3_STATUS 0x203 #define DP_LANE_CR_DONE (1 << 0) #define DP_LANE_CHANNEL_EQ_DONE (1 << 1) #define DP_LANE_SYMBOL_LOCKED (1 << 2) #define DP_BRANCH_OUI_HEADER_SIZE 0xc #define DP_RECEIVER_CAP_SIZE 0xf #define DP_DSC_RECEIVER_CAP_SIZE 0xf #define EDP_PSR_RECEIVER_CAP_SIZE 2 #define EDP_DISPLAY_CTL_CAP_SIZE 3 #define DP_LTTPR_COMMON_CAP_SIZE 8 #define DP_LTTPR_PHY_CAP_SIZE 3 #define DP_TRAINING_AUX_RD_INTERVAL 0x00e #define DP_TRAINING_AUX_RD_MASK 0x7f #define DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT (1 << 7) #define DP_EDP_DPCD_REV 0x700 #define DP_EDP_11 0x00 #define DP_EDP_12 0x01 #define DP_EDP_13 0x02 #define DP_EDP_14 0x03 #define DP_EDP_14a 0x04 #define DP_EDP_14b 0x05 /* Receiver Capability */ #define DP_DPCD_REV 0x000 #define DP_DPCD_REV_10 0x10 #define DP_DPCD_REV_11 0x11 #define DP_DPCD_REV_12 0x12 #define DP_DPCD_REV_13 0x13 #define DP_DPCD_REV_14 0x14 #define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE | \ DP_LANE_CHANNEL_EQ_DONE | \ DP_LANE_SYMBOL_LOCKED) #define DP_LANE_ALIGN_STATUS_UPDATED 0x204 #define DP_INTERLANE_ALIGN_DONE (1 << 0) #define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) #define DP_LINK_STATUS_UPDATED (1 << 7) #define DP_SINK_STATUS 0x205 #define DP_RECEIVE_PORT_0_STATUS (1 << 0) #define DP_RECEIVE_PORT_1_STATUS (1 << 1) #define DP_STREAM_REGENERATION_STATUS (1 << 2) #define DP_AUX_MAX_PAYLOAD_BYTES 16 #define MAX_LANECOUNT 4 enum { DP_LANECOUNT_1 = 0x1, DP_LANECOUNT_2 = 0x2, DP_LANECOUNT_4 = 0x4, }; enum { DP_VERSION_11 = 0x11, DP_VERSION_12 = 0x12, DP_VERSION_14 = 0x14, DP_VERSION_12_14 = 0x16, DP_VERSION_14_14 = 0x17, DP_VERSION_MAX, }; enum { DPTX_SWING0 = 0x00, DPTX_SWING1 = 0x01, DPTX_SWING2 = 0x02, DPTX_SWING3 = 0x03, }; enum { DPTX_PREEMPHASIS0 = 0x00, DPTX_PREEMPHASIS1 = 0x01, DPTX_PREEMPHASIS2 = 0x02, DPTX_PREEMPHASIS3 = 0x03, }; enum { DPTX_PASS = 0, DPTX_PLUG_OUT = 1, DPTX_TIMEOUT = 2, DPTX_AUTH_FAIL = 3, DPTX_EDID_FAIL = 4, DPTX_TRANING_FAIL = 5, DPTX_TRANING_STATE_CHANGE = 6, }; enum { FEC_ERROR_COUNT_DISABLE = 0x0, FEC_UNCORRECTED_BLOCK_ERROR_COUNT = 0x1, FEC_CORRECTED_BLOCK_ERROR_COUNT = 0x2, FEC_BIT_ERROR_COUNT = 0x3, FEC_PARITY_BLOCK_ERROR_COUNT = 0x4, FEC_PARITY_BIT_ERROR_COUNT = 0x5, }; enum { DPTX_NTSTATE_STARTUP = 0x0, DPTX_NTSTATE_CHECKCAP = 0x1, DPTX_NTSTATE_CHECKEDID = 0x2, DPTX_NTSTATE_TRAINING_PRE = 0x3, DPTX_NTSTATE_TRAINING = 0x4, DPTX_NTSTATE_CHECKTIMING = 0x5, DPTX_NTSTATE_NORMAL = 0x6, DPTX_NTSTATE_POWERSAVE = 0x7, DPTX_NTSTATE_DPIDLE = 0x8, DPTX_NTSTATE_MAX, }; enum { DPTX_DISP_NONE = 0, DPTX_DISP_RESUME = 1, DPTX_DISP_SUSPEND = 2, }; enum { TRAIN_STEP_SUCCESS = 0, TRAIN_STEP_FAIL_BREAK = 1, TRAIN_STEP_FAIL_NOT_BREAK = 2, }; #define DPTX_TRAIN_RETRY_LIMIT 0x8 #define DPTX_TRAIN_MAX_ITERATION 0x5 #define HPD_INT_EVNET BIT(3) #define HPD_CONNECT BIT(2) #define HPD_DISCONNECT BIT(1) #define HPD_INITIAL_STATE 0 static bool dptx_auxwrite_bytes(struct mtk_dp *mtk_dp, u8 cmd, u32 dpcd_addr, size_t length, u8 *data) { if (retry(7, dptx_hal_auxwrite_bytes(mtk_dp, cmd, dpcd_addr, length, data), mdelay(1))) return true; printk(BIOS_ERR, "aux write fail: cmd = %d, addr = %#x, len = %ld\n", cmd, dpcd_addr, length); return false; } static bool dptx_auxwrite_dpcd(struct mtk_dp *mtk_dp, u8 cmd, u32 dpcd_addr, size_t length, u8 *data) { bool ret = true; size_t offset = 0; size_t len; while (offset < length) { len = MIN(length - offset, DP_AUX_MAX_PAYLOAD_BYTES); ret &= dptx_auxwrite_bytes(mtk_dp, cmd, dpcd_addr + offset, len, data + offset); offset += len; } return ret; } static bool dptx_auxread_bytes(struct mtk_dp *mtk_dp, u8 cmd, u32 dpcd_addr, size_t length, u8 *data) { if (retry(7, dptx_hal_auxread_bytes(mtk_dp, cmd, dpcd_addr, length, data), mdelay(1))) return true; printk(BIOS_ERR, "aux read fail: cmd = %d, addr = %#x, len = %ld\n", cmd, dpcd_addr, length); return false; } static bool dptx_auxread_dpcd(struct mtk_dp *mtk_dp, u8 cmd, u32 dpcd_addr, size_t length, u8 *rxbuf) { bool ret = true; size_t offset = 0; size_t len; while (offset < length) { len = MIN(length - offset, DP_AUX_MAX_PAYLOAD_BYTES); ret &= dptx_auxread_bytes(mtk_dp, cmd, dpcd_addr + offset, len, rxbuf + offset); offset += len; } return ret; } static int dptx_get_edid(struct mtk_dp *mtk_dp, struct edid *out) { int ret; u8 edid[ONE_BLOCK_SIZE]; u8 tmp = 0; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_I2C_WRITE, 0x50, 0x1, &tmp); for (tmp = 0; tmp < ONE_BLOCK_SIZE / DP_AUX_MAX_PAYLOAD_BYTES; tmp++) dptx_auxread_dpcd(mtk_dp, DP_AUX_I2C_READ, 0x50, DP_AUX_MAX_PAYLOAD_BYTES, edid + tmp * 16); ret = decode_edid(edid, ONE_BLOCK_SIZE, out); if (ret != EDID_CONFORMANT) { printk(BIOS_ERR, "failed to decode edid(%d).\n", ret); return -1; } mtk_dp->edid = out; return 0; } static u8 dptx_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) { return link_status[r - DP_LANE0_1_STATUS]; } static u8 dptx_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], int lane) { int i = DP_LANE0_1_STATUS + (lane >> 1); int s = (lane & 1) * 4; u8 l = dptx_link_status(link_status, i); return (l >> s) & 0xf; } static bool dptx_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], int lane_count) { int lane; u8 lane_status; for (lane = 0; lane < lane_count; lane++) { lane_status = dptx_get_lane_status(link_status, lane); if ((lane_status & DP_LANE_CR_DONE) == 0) return false; } return true; } static void dptx_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { u32 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & DP_TRAINING_AUX_RD_MASK; if (rd_interval > 4) printk(BIOS_ERR, "aux interval %d, out of range (max 4)\n", rd_interval); if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) rd_interval = 1; else rd_interval *= 4; mdelay(rd_interval); } static void dptx_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { u32 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & DP_TRAINING_AUX_RD_MASK; if (rd_interval > 4) printk(BIOS_ERR, "aux interval %d, out of range (max 4)\n", rd_interval); if (rd_interval == 0) rd_interval = 1; else rd_interval *= 4; mdelay(rd_interval); } static bool dptx_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], int lane_count) { u8 lane_align; u8 lane_status; int lane; lane_align = dptx_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) return false; for (lane = 0; lane < lane_count; lane++) { lane_status = dptx_get_lane_status(link_status, lane); if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) return false; } return true; } static void dptx_videomute(struct mtk_dp *mtk_dp, bool enable) { dptx_hal_videomute(mtk_dp, enable); } static void dptx_fec_ready(struct mtk_dp *mtk_dp, u8 err_cnt_sel) { u8 i, data[3]; dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, 0x90, 0x1, data); printk(BIOS_DEBUG, "FEC Capable[0], [0:3] should be 1: %#x\n", data[0]); /* * FEC error count select 120[3:1]: * 000b: FEC_ERROR_COUNT_DIS * 001b: UNCORRECTED_BLOCK_ERROR_COUNT * 010b: CORRECTED_BLOCK_ERROR_COUNT * 011b: BIT_ERROR_COUNT * 100b: PARITY_BLOCK_ERROR_COUNT * 101b: PARITY_BIT_ERROR_COUNT */ if (data[0] & BIT(0)) { mtk_dp->has_fec = true; data[0] = (err_cnt_sel << 1) | 0x1; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, 0x120, 0x1, data); dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, 0x280, 0x3, data); for (i = 0; i < 3; i++) printk(BIOS_DEBUG, "FEC status & error Count: %#x\n", data[i]); } } static void dptx_init_variable(struct mtk_dp *mtk_dp) { mtk_dp->regs = (void *)EDP_BASE; mtk_dp->train_info.sys_max_linkrate = DP_LINKRATE_HBR3; mtk_dp->train_info.linkrate = DP_LINKRATE_HBR2; mtk_dp->train_info.linklane_count = DP_LANECOUNT_4; mtk_dp->train_info.sink_extcap_en = false; mtk_dp->train_info.sink_ssc_en = false; mtk_dp->train_info.tps3 = true; mtk_dp->train_info.tps4 = true; mtk_dp->training_state = DPTX_NTSTATE_STARTUP; mtk_dp->info.format = DP_COLOR_FORMAT_RGB_444; mtk_dp->info.depth = DP_COLOR_DEPTH_8BIT; mtk_dp->power_on = false; mtk_dp->video_enable = false; mtk_dp->dp_ready = false; mtk_dp->has_dsc = false; mtk_dp->has_fec = false; mtk_dp->dsc_enable = false; } static inline bool dptx_check_res_sample_rate(const struct edid *edid) { return edid->mode.va + edid->mode.vbl <= 525; } static void dptx_setsdp_downcnt_init(struct mtk_dp *mtk_dp, u16 sram_read_start) { u32 count = 0; /* count: sdp_down_cnt_init */ u8 offset; if (mtk_dp->edid->mode.pixel_clock > 0) count = (sram_read_start * 2700 * 8 * mtk_dp->train_info.linkrate) / (mtk_dp->edid->mode.pixel_clock * 4); switch (mtk_dp->train_info.linklane_count) { case DP_LANECOUNT_1: count = (count > 0x1a) ? count : 0x1a; break; case DP_LANECOUNT_2: /* Case for LowResolution and High Audio Sample Rate. */ offset = dptx_check_res_sample_rate(mtk_dp->edid) ? 0x04 : 0x00; count = (count > 0x10) ? count : 0x10 + offset; break; case DP_LANECOUNT_4: default: count = (count > 0x06) ? count : 0x06; break; } printk(BIOS_DEBUG, "pixel rate(khz) = %d, sdp_dc_init = %#x\n", mtk_dp->edid->mode.pixel_clock, count); dptx_hal_setsdp_downcnt_init(mtk_dp, count); } static void dptx_setsdp_downcnt_init_inhblanking(struct mtk_dp *mtk_dp) { int pixclk_mhz = mtk_dp->edid->mode.pixel_clock / 1000; u8 offset; u16 count = 0; /* count: sdp_down_cnt_init*/ switch (mtk_dp->train_info.linklane_count) { case DP_LANECOUNT_1: count = 0x20; break; case DP_LANECOUNT_2: offset = dptx_check_res_sample_rate(mtk_dp->edid) ? 0x14 : 0x00; count = 0x0018 + offset; break; case DP_LANECOUNT_4: default: offset = dptx_check_res_sample_rate(mtk_dp->edid) ? 0x08 : 0x00; if (pixclk_mhz > mtk_dp->train_info.linkrate * 27) { count = 0x8; printk(BIOS_ERR, "Pixclk > LinkRateChange\n"); } else { count = 0x10 + offset; } break; } dptx_hal_setsdp_downcnt_init_inhblanking(mtk_dp, count); } static void dptx_set_tu(struct mtk_dp *mtk_dp) { u8 bpp; u16 sram_read_start = DPTX_TBC_BUF_READSTARTADRTHRD; int tu_size, n_value, f_value, pixclk_mhz; bpp = dptx_hal_get_colorbpp(mtk_dp); pixclk_mhz = mtk_dp->edid->mode.pixel_clock / 1000; tu_size = (640 * pixclk_mhz * bpp) / (mtk_dp->train_info.linkrate * 27 * mtk_dp->train_info.linklane_count * 8); n_value = tu_size / 10; f_value = tu_size % 10; printk(BIOS_DEBUG, "TU_size %d, FValue %d\n", tu_size, f_value); if (mtk_dp->train_info.linklane_count > 0) { sram_read_start = mtk_dp->edid->mode.ha / (mtk_dp->train_info.linklane_count * 4 * 2 * 2); sram_read_start = MIN(sram_read_start, DPTX_TBC_BUF_READSTARTADRTHRD); dptx_hal_settu_sramrd_start(mtk_dp, sram_read_start); } dptx_hal_settu_setencoder(mtk_dp); dptx_setsdp_downcnt_init_inhblanking(mtk_dp); dptx_setsdp_downcnt_init(mtk_dp, sram_read_start); } static void dptx_set_misc(struct mtk_dp *mtk_dp) { u8 format, depth; union misc_t dptx_misc; format = mtk_dp->info.format; depth = mtk_dp->info.depth; /* * MISC 0/1 reference to spec 1.4a p143 Table 2-96. * MISC0[7:5] color depth. */ dptx_misc.dp_misc.color_depth = depth; /* * MISC0[3]: 0->RGB, 1->YUV * MISC0[2:1]: 01b->4:2:2, 10b->4:4:4 */ switch (format) { case DP_COLOR_FORMAT_YUV_444: dptx_misc.dp_misc.color_format = 0x1; break; case DP_COLOR_FORMAT_YUV_422: dptx_misc.dp_misc.color_format = 0x2; break; case DP_COLOR_FORMAT_RAW: dptx_misc.dp_misc.color_format = 0x1; dptx_misc.dp_misc.spec_def2 = 0x1; break; case DP_COLOR_FORMAT_YONLY: dptx_misc.dp_misc.color_format = 0x0; dptx_misc.dp_misc.spec_def2 = 0x1; break; case DP_COLOR_FORMAT_YUV_420: case DP_COLOR_FORMAT_RGB_444: default: break; } dptx_hal_setmisc(mtk_dp, dptx_misc.cmisc); } static void dptx_set_dptxout(struct mtk_dp *mtk_dp) { dptx_hal_bypassmsa_en(mtk_dp, false); dptx_set_tu(mtk_dp); } static bool dptx_training_checkswingpre(struct mtk_dp *mtk_dp, u8 target_lane_count, const u8 *dpcp202_x, u8 *dpcp_buf) { bool ret = true; u8 swing_value, preemphasis; /* Lane 0 */ if (target_lane_count >= 0x1) { swing_value = (dpcp202_x[4] & 0x3); preemphasis = (dpcp202_x[4] & 0xc) >> 2; /* Adjust the swing and pre-emphasis */ ret &= dptx_hal_setswing_preemphasis(mtk_dp, DPTX_LANE0, swing_value, preemphasis); /* * Adjust the swing and pre-emphasis done, * and notify sink side. */ dpcp_buf[0] = swing_value | (preemphasis << 3); /* Max swing reached. */ if (swing_value == DPTX_SWING3) dpcp_buf[0] |= BIT(2); /* Max pre-emphasis reached. */ if (preemphasis == DPTX_PREEMPHASIS3) dpcp_buf[0] |= BIT(5); } /* Lane 1 */ if (target_lane_count >= 0x2) { swing_value = (dpcp202_x[4] & 0x30) >> 4; preemphasis = (dpcp202_x[4] & 0xc0) >> 6; /* Adjust the swing and pre-emphasis */ ret &= dptx_hal_setswing_preemphasis(mtk_dp, DPTX_LANE1, swing_value, preemphasis); /* * Adjust the swing and pre-emphasis done, * and notify sink side. */ dpcp_buf[1] = swing_value | (preemphasis << 3); /* Max swing reached. */ if (swing_value == DPTX_SWING3) dpcp_buf[1] |= BIT(2); /* Max pre-emphasis reached. */ if (preemphasis == DPTX_PREEMPHASIS3) dpcp_buf[1] |= BIT(5); } /* Lane 2 and Lane 3 */ if (target_lane_count == 0x4) { /* Lane 2 */ swing_value = (dpcp202_x[5] & 0x3); preemphasis = (dpcp202_x[5] & 0x0c) >> 2; /* Adjust the swing and pre-emphasis */ ret &= dptx_hal_setswing_preemphasis(mtk_dp, DPTX_LANE2, swing_value, preemphasis); /* * Adjust the swing and pre-emphasis done, * and notify sink side. */ dpcp_buf[2] = swing_value | (preemphasis << 3); /* Max swing reached. */ if (swing_value == DPTX_SWING3) dpcp_buf[2] |= BIT(2); /* Max pre-emphasis reached. */ if (preemphasis == DPTX_PREEMPHASIS3) dpcp_buf[2] |= BIT(5); /* Lane 3 */ swing_value = (dpcp202_x[5] & 0x30) >> 4; preemphasis = (dpcp202_x[5] & 0xc0) >> 6; /* Adjust the swing and pre-emphasis */ ret &= dptx_hal_setswing_preemphasis(mtk_dp, DPTX_LANE3, swing_value, preemphasis); /* * Adjust the swing and pre-emphasis done, * and notify sink side. */ dpcp_buf[0x3] = swing_value | (preemphasis << 3); /* Max swing reached. */ if (swing_value == DPTX_SWING3) dpcp_buf[3] |= BIT(2); /* Max pre-emphasis reached. */ if (preemphasis == DPTX_PREEMPHASIS3) dpcp_buf[3] |= BIT(5); } /* Wait signal stable enough */ mdelay(2); return ret; } static int dptx_train_tps1(struct mtk_dp *mtk_dp, u8 target_lanecount, int *status_ctrl, int *iteration, u8 *dpcp_buffer, u8 *dpcd206) { u8 tmp_val[6]; u8 dpcd200c[3]; printk(BIOS_INFO, "CR Training START\n"); dptx_hal_setscramble(mtk_dp, false); if (*status_ctrl == 0x0) { dptx_hal_set_txtrainingpattern(mtk_dp, BIT(4)); *status_ctrl = 0x1; tmp_val[0] = 0x21; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00102, 0x1, tmp_val); dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00206, 0x2, tmp_val + 4); *iteration = *iteration + 1; dptx_training_checkswingpre(mtk_dp, target_lanecount, tmp_val, dpcp_buffer); } dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00103, target_lanecount, dpcp_buffer); dptx_link_train_clock_recovery_delay(mtk_dp->rx_cap); dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00202, 0x6, tmp_val); if (mtk_dp->train_info.sink_extcap_en) { dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_0200C, 0x3, dpcd200c); tmp_val[0] = dpcd200c[0]; tmp_val[1] = dpcd200c[1]; tmp_val[2] = dpcd200c[2]; } if (dptx_clock_recovery_ok(tmp_val, target_lanecount)) { printk(BIOS_INFO, "CR Training Success\n"); mtk_dp->train_info.cr_done = true; *iteration = 0x1; return TRAIN_STEP_SUCCESS; } printk(BIOS_INFO, "CR Training Fail\n"); /* Requested swing and emp is the same with last time. */ if (*dpcd206 == tmp_val[4]) { *iteration = *iteration + 1; if (*dpcd206 & 0x3) return TRAIN_STEP_FAIL_BREAK; } else { *dpcd206 = tmp_val[4]; } return TRAIN_STEP_FAIL_NOT_BREAK; } static int dptx_train_tps2_3(struct mtk_dp *mtk_dp, u8 target_lanecount, int *status_ctrl, int *iteration, u8 *dpcp_buffer, u8 *dpcd206) { u8 tmp_val[6]; u8 dpcd200c[3]; printk(BIOS_INFO, "EQ Training START\n"); if (*status_ctrl == 0x1) { if (mtk_dp->train_info.tps4) dptx_hal_set_txtrainingpattern(mtk_dp, BIT(7)); else if (mtk_dp->train_info.tps3) dptx_hal_set_txtrainingpattern(mtk_dp, BIT(6)); else dptx_hal_set_txtrainingpattern(mtk_dp, BIT(5)); if (mtk_dp->train_info.tps4) { tmp_val[0] = 0x07; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00102, 0x1, tmp_val); } else if (mtk_dp->train_info.tps3) { tmp_val[0] = 0x23; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00102, 0x1, tmp_val); } else { tmp_val[0] = 0x22; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00102, 0x1, tmp_val); } *status_ctrl = 0x2; dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00206, 0x2, tmp_val + 4); *iteration = *iteration + 1; dptx_training_checkswingpre(mtk_dp, target_lanecount, tmp_val, dpcp_buffer); } dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00103, target_lanecount, dpcp_buffer); dptx_link_train_channel_eq_delay(mtk_dp->rx_cap); dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00202, 0x6, tmp_val); if (mtk_dp->train_info.sink_extcap_en) { dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_0200C, 0x3, dpcd200c); tmp_val[0] |= dpcd200c[0]; tmp_val[1] |= dpcd200c[1]; tmp_val[2] |= dpcd200c[2]; } if (!dptx_clock_recovery_ok(tmp_val, target_lanecount)) { printk(BIOS_INFO, "EQ Training Fail\n"); mtk_dp->train_info.cr_done = false; mtk_dp->train_info.eq_done = false; return TRAIN_STEP_FAIL_BREAK; } if (dptx_channel_eq_ok(tmp_val, target_lanecount)) { printk(BIOS_INFO, "EQ Training Success\n"); mtk_dp->train_info.eq_done = true; return TRAIN_STEP_SUCCESS; } printk(BIOS_INFO, "EQ Training Fail\n"); if (*dpcd206 == tmp_val[4]) *iteration = *iteration + 1; else *dpcd206 = tmp_val[4]; return TRAIN_STEP_FAIL_NOT_BREAK; } static int dptx_trainingflow(struct mtk_dp *mtk_dp, u8 lanerate, u8 lanecount) { u8 tmp_val[6]; u8 target_linkrate = lanerate; u8 target_lanecount = lanecount; u8 dpcp_buffer[4]; u8 dpcd206; bool pass_tps1 = false; bool pass_tps2_3 = false; int train_retry, status_ctrl, iteration; memset(tmp_val, 0, sizeof(tmp_val)); memset(dpcp_buffer, 0, sizeof(dpcp_buffer)); dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00600, 0x1, tmp_val); if (tmp_val[0] != 0x1) { tmp_val[0] = 0x1; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00600, 0x1, tmp_val); mdelay(1); } tmp_val[0] = target_linkrate; tmp_val[1] = target_lanecount | DPTX_AUX_SET_ENAHNCED_FRAME; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00100, 0x2, tmp_val); if (mtk_dp->train_info.sink_ssc_en) { tmp_val[0x0] = 0x10; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00107, 0x1, tmp_val); } train_retry = 0x0; status_ctrl = 0x0; iteration = 0x1; dpcd206 = 0xff; dptx_hal_set_txlane(mtk_dp, target_lanecount / 2); dptx_hal_set_txrate(mtk_dp, target_linkrate); do { train_retry++; if (!pass_tps1) { int ret = dptx_train_tps1(mtk_dp, target_lanecount, &status_ctrl, &iteration, dpcp_buffer, &dpcd206); if (ret == TRAIN_STEP_FAIL_BREAK) break; if (ret == TRAIN_STEP_SUCCESS) { pass_tps1 = true; train_retry = 0; } } else { int ret = dptx_train_tps2_3(mtk_dp, target_lanecount, &status_ctrl, &iteration, dpcp_buffer, &dpcd206); if (ret == TRAIN_STEP_FAIL_BREAK) break; if (ret == TRAIN_STEP_SUCCESS) { pass_tps2_3 = true; break; } } dptx_training_checkswingpre(mtk_dp, target_lanecount, tmp_val, dpcp_buffer); } while (train_retry < DPTX_TRAIN_RETRY_LIMIT && iteration < DPTX_TRAIN_MAX_ITERATION); tmp_val[0] = 0x0; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00102, 0x1, tmp_val); dptx_hal_set_txtrainingpattern(mtk_dp, 0); if (!pass_tps2_3) { printk(BIOS_ERR, "Link Training Fail\n"); return DPTX_TRANING_FAIL; } mtk_dp->train_info.linkrate = target_linkrate; mtk_dp->train_info.linklane_count = target_lanecount; dptx_hal_setscramble(mtk_dp, true); tmp_val[0] = target_lanecount | DPTX_AUX_SET_ENAHNCED_FRAME; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00101, 0x1, tmp_val); dptx_hal_set_ef_mode(mtk_dp, ENABLE_DPTX_EF_MODE); printk(BIOS_INFO, "Link Training Success\n"); return DPTX_PASS; } static void dptx_check_sinkcap(struct mtk_dp *mtk_dp) { u8 buffer[16]; memset(buffer, 0x0, sizeof(buffer)); buffer[0] = 0x1; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00600, 0x1, buffer); mdelay(2); dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00000, 0x10, buffer); mtk_dp->train_info.sink_extcap_en = false; mtk_dp->train_info.dpcd_rev = buffer[0]; printk(BIOS_INFO, "SINK DPCD version: %#x\n", mtk_dp->train_info.dpcd_rev); memcpy(mtk_dp->rx_cap, buffer, sizeof(mtk_dp->rx_cap)); mtk_dp->rx_cap[14] &= 0x7f; if (mtk_dp->train_info.dpcd_rev >= 0x14) dptx_fec_ready(mtk_dp, FEC_BIT_ERROR_COUNT); mtk_dp->train_info.linkrate = MIN(buffer[0x1], mtk_dp->train_info.sys_max_linkrate); mtk_dp->train_info.linklane_count = MIN(buffer[2] & 0x1F, MAX_LANECOUNT); mtk_dp->train_info.tps3 = (buffer[2] & BIT(6)) >> 0x6; mtk_dp->train_info.tps4 = (buffer[3] & BIT(7)) >> 0x7; mtk_dp->train_info.down_stream_port_present = (buffer[5] & BIT(0)); if ((buffer[3] & BIT(0)) == 0x1) { printk(BIOS_INFO, "SINK SUPPORT SSC!\n"); mtk_dp->train_info.sink_ssc_en = true; } else { printk(BIOS_INFO, "SINK NOT SUPPORT SSC!\n"); mtk_dp->train_info.sink_ssc_en = false; } dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00021, 0x1, buffer); mtk_dp->train_info.dp_mstcap = (buffer[0] & BIT(0)); mtk_dp->train_info.dp_mstbranch = false; if (mtk_dp->train_info.dp_mstcap == BIT(0)) { if (mtk_dp->train_info.down_stream_port_present == 0x1) mtk_dp->train_info.dp_mstbranch = true; dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_02003, 0x1, buffer); if (buffer[0] != 0x0) dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_02003, 0x1, buffer); } dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00600, 0x1, buffer); if (buffer[0] != 0x1) { buffer[0] = 0x1; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00600, 0x1, buffer); } dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00200, 0x2, buffer); } static void dptx_training_changemode(struct mtk_dp *mtk_dp) { dptx_hal_phyd_reset(mtk_dp); dptx_hal_reset_swing_preemphasis(mtk_dp); dptx_hal_ssc_en(mtk_dp, mtk_dp->train_info.sink_ssc_en); mdelay(2); } static int dptx_set_trainingstart(struct mtk_dp *mtk_dp) { int ret = DPTX_PASS; u8 lanecount; u8 linkrate; u8 buffer; u8 limit; u8 max_linkrate; buffer = 0x1; dptx_auxwrite_dpcd(mtk_dp, DP_AUX_NATIVE_WRITE, DPCD_00600, 0x1, &buffer); linkrate = mtk_dp->rx_cap[1]; lanecount = mtk_dp->rx_cap[2] & 0x1f; printk(BIOS_INFO, "RX support linkrate = %#x, lanecount = %#x\n", linkrate, lanecount); mtk_dp->train_info.linkrate = (linkrate >= mtk_dp->train_info.sys_max_linkrate) ? mtk_dp->train_info.sys_max_linkrate : linkrate; mtk_dp->train_info.linklane_count = (lanecount >= MAX_LANECOUNT) ? MAX_LANECOUNT : lanecount; if (mtk_dp->train_info.sink_extcap_en) dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_02002, 0x1, &buffer); else dptx_auxread_dpcd(mtk_dp, DP_AUX_NATIVE_READ, DPCD_00200, 0x1, &buffer); if ((buffer & 0xbf) != 0) mtk_dp->train_info.sink_count_num = buffer & 0xbf; linkrate = mtk_dp->train_info.linkrate; lanecount = mtk_dp->train_info.linklane_count; switch (linkrate) { case DP_LINKRATE_RBR: case DP_LINKRATE_HBR: case DP_LINKRATE_HBR2: case DP_LINKRATE_HBR25: case DP_LINKRATE_HBR3: break; default: mtk_dp->train_info.linkrate = DP_LINKRATE_HBR3; break; }; max_linkrate = linkrate; limit = 0x6; do { mtk_dp->train_info.cr_done = false; mtk_dp->train_info.eq_done = false; dptx_training_changemode(mtk_dp); ret = dptx_trainingflow(mtk_dp, linkrate, lanecount); if (!mtk_dp->train_info.cr_done) { /* CR fail and reduce link capability. */ switch (linkrate) { case DP_LINKRATE_RBR: lanecount = lanecount / 2; linkrate = max_linkrate; if (lanecount == 0x0) return DPTX_TRANING_FAIL; break; case DP_LINKRATE_HBR: linkrate = DP_LINKRATE_RBR; break; case DP_LINKRATE_HBR2: linkrate = DP_LINKRATE_HBR; break; case DP_LINKRATE_HBR3: linkrate = DP_LINKRATE_HBR2; break; default: return DPTX_TRANING_FAIL; }; } else if (!mtk_dp->train_info.eq_done) { /* EQ fail and reduce lane counts. */ if (lanecount == DP_LANECOUNT_4) lanecount = DP_LANECOUNT_2; else if (lanecount == DP_LANECOUNT_2) lanecount = DP_LANECOUNT_1; else return DPTX_TRANING_FAIL; } else { return DPTX_PASS; } } while (--limit > 0); return DPTX_TRANING_FAIL; } static void dptx_init_port(struct mtk_dp *mtk_dp) { dptx_hal_phy_setidlepattern(mtk_dp, true); dptx_hal_init_setting(mtk_dp); dptx_hal_aux_setting(mtk_dp); dptx_hal_digital_setting(mtk_dp); dptx_hal_phy_setting(mtk_dp); dptx_hal_hpd_detect_setting(mtk_dp); dptx_hal_digital_swreset(mtk_dp); dptx_hal_analog_power_en(mtk_dp, true); dptx_hal_hpd_int_en(mtk_dp, true); } static void dptx_video_enable(struct mtk_dp *mtk_dp, bool enable) { printk(BIOS_INFO, "Output Video %s!\n", enable ? "enable" : "disable"); if (enable) { dptx_set_dptxout(mtk_dp); dptx_videomute(mtk_dp, false); dptx_hal_verify_clock(mtk_dp); } else dptx_videomute(mtk_dp, true); } static void dptx_set_color_format(struct mtk_dp *mtk_dp, u8 color_format) { dptx_hal_set_color_format(mtk_dp, color_format); } static void dptx_set_color_depth(struct mtk_dp *mtk_dp, u8 color_depth) { dptx_hal_set_color_depth(mtk_dp, color_depth); } static void dptx_video_config(struct mtk_dp *mtk_dp) { u32 mvid = 0; bool overwrite = false; dptx_hal_overwrite_mn(mtk_dp, overwrite, mvid, 0x8000); /* Interlace does not support. */ dptx_hal_set_msa(mtk_dp); dptx_set_misc(mtk_dp); dptx_set_color_depth(mtk_dp, mtk_dp->info.depth); dptx_set_color_format(mtk_dp, mtk_dp->info.format); } int mtk_edp_init(struct edid *edid) { struct mtk_dp mtk_edp; dptx_init_variable(&mtk_edp); dptx_init_port(&mtk_edp); if (!dptx_hal_hpd_high(&mtk_edp)) { printk(BIOS_ERR, "HPD is low\n"); return -1; } dptx_check_sinkcap(&mtk_edp); dptx_get_edid(&mtk_edp, edid); dptx_set_trainingstart(&mtk_edp); dp_intf_config(edid); dptx_video_config(&mtk_edp); dptx_video_enable(&mtk_edp, true); return 0; }