From 7dd63e40109a54df517a52044dcbeca0ab53ebff Mon Sep 17 00:00:00 2001 From: Guochun Huang Date: Wed, 13 Nov 2019 17:19:26 +0800 Subject: [PATCH] drm/rockchip: calculate lanes and rate for Main Link Lanes the numbers and rate of Main Link lanes depend on the panel display resolution and corlor depth, it is recommended that the minimum number of lanes be used, using the minimum link rate allowed for that lane configuration. Change-Id: Id207410589e1f77ebff79615f3367f153a9b2755 Signed-off-by: Guochun Huang --- drivers/gpu/drm/rockchip/cdn-dp-core.c | 3 - drivers/gpu/drm/rockchip/cdn-dp-core.h | 3 + .../gpu/drm/rockchip/cdn-dp-link-training.c | 69 ++++++++++++++++--- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 68 +++++++++++------- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 1 + 5 files changed, 106 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 30104912e249..3514f230d315 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -313,9 +313,6 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, break; } - if (!IS_ALIGNED(mode->hdisplay * bpc * 3, 32)) - return MODE_H_ILLEGAL; - requested = mode->clock * bpc * 3 / 1000; source_max = dp->lanes; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 77a97934b4e9..b3788168fef1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -109,6 +109,9 @@ struct cdn_dp_device { int active_port; u8 train_set[4]; + u8 tu_size; + u64 tu_symbol; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; }; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c index 08962e96b17c..4292a4c5a74d 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -355,12 +355,66 @@ static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp) return 0; } +static int cdn_dp_main_link_config_cal(struct cdn_dp_device *dp) +{ + struct drm_display_info *display_info = &dp->connector.display_info; + struct drm_display_mode *mode = &dp->mode; + u32 rate, require_cap, bit_rate_cap, sink_max, source_max; + u8 num_lanes[] = {1, 2, 4}; + u16 link_rate[] = {1620, 2700, 5400}; + u8 bpc, x, y; + int ret; + + switch (display_info->bpc) { + case 10: + bpc = 10; + break; + case 6: + bpc = 6; + break; + default: + bpc = 8; + break; + } + + require_cap = mode->clock / 1000 * bpc * 3; + source_max = dp->lanes; + sink_max = drm_dp_max_lane_count(dp->dpcd); + dp->link.num_lanes = min(source_max, sink_max); + source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE); + sink_max = drm_dp_max_link_rate(dp->dpcd); + rate = min(source_max, sink_max); + dp->link.rate = drm_dp_link_rate_to_bw_code(rate); + + for (y = 0; y < ARRAY_SIZE(link_rate) && + link_rate[y] <= rate / 100; y++) { + for (x = 0; x < ARRAY_SIZE(num_lanes) && + num_lanes[x] <= dp->link.num_lanes; x++) { + bit_rate_cap = link_rate[y] * num_lanes[x] * 8 / 10; + if (require_cap < bit_rate_cap) { + rate = drm_dp_link_rate_to_bw_code( + link_rate[y] * 100); + ret = cdn_dp_tu_size_cal(dp, + num_lanes[x], rate); + if (ret) + continue; + dp->link.num_lanes = num_lanes[x]; + dp->link.rate = rate; + dev_info(dp->dev, "%dMbps x %dlanes\n", + link_rate[y], dp->link.num_lanes); + return ret; + } + } + } + ret = cdn_dp_tu_size_cal(dp, dp->link.num_lanes, dp->link.rate); + return ret; +} + int cdn_dp_software_train_link(struct cdn_dp_device *dp) { struct cdn_dp_port *port = dp->port[dp->active_port]; int ret, stop_err; u8 link_config[2]; - u32 rate, sink_max, source_max; bool ssc_on; ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd, @@ -370,14 +424,11 @@ int cdn_dp_software_train_link(struct cdn_dp_device *dp) return ret; } - source_max = dp->lanes; - sink_max = drm_dp_max_lane_count(dp->dpcd); - dp->link.num_lanes = min(source_max, sink_max); - - source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE); - sink_max = drm_dp_max_link_rate(dp->dpcd); - rate = min(source_max, sink_max); - dp->link.rate = drm_dp_link_rate_to_bw_code(rate); + ret = cdn_dp_main_link_config_cal(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to cal TU_SIZE %d\n", ret); + return ret; + } ssc_on = !!(dp->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5); link_config[0] = ssc_on ? DP_SPREAD_AMP_0_5 : 0; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index f243f1eddd38..c30174f40310 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -713,13 +713,52 @@ static int cdn_dp_get_msa_misc(struct video_info *video, return msa_misc; } +int cdn_dp_tu_size_cal(struct cdn_dp_device *dp, u8 lanes, u8 link_bw) +{ + struct video_info *video = &dp->video_info; + struct drm_display_mode *mode = &dp->mode; + u8 bit_per_pix, tu_size_reg = TU_SIZE; + u32 link_rate, rem; + u64 symbol; + int ret; + + bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? + (video->color_depth * 2) : (video->color_depth * 3); + + link_rate = drm_dp_bw_code_to_link_rate(link_bw) / 1000; + + /* + * get a best tu_size and valid symbol: + * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 + * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) + * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set + * TU += 2 and repeat 2nd step. + */ + do { + tu_size_reg += 2; + symbol = tu_size_reg * mode->clock * bit_per_pix; + do_div(symbol, lanes * link_rate * 8); + rem = do_div(symbol, 1000); + if (tu_size_reg > 64) { + ret = -EINVAL; + return ret; + } + } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || + (rem > 850) || (rem < 100)); + + dp->tu_size = tu_size_reg; + dp->tu_symbol = symbol; + return 0; +} + int cdn_dp_config_video(struct cdn_dp_device *dp) { struct video_info *video = &dp->video_info; struct drm_display_mode *mode = &dp->mode; - u64 symbol; - u32 val, link_rate, rem; - u8 bit_per_pix, tu_size_reg = TU_SIZE; + u8 tu_size_reg = dp->tu_size; + u64 symbol = dp->tu_symbol; + u32 val, link_rate; + u8 bit_per_pix; int ret; bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? @@ -735,29 +774,6 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) if (ret) goto err_config_video; - /* - * get a best tu_size and valid symbol: - * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 - * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) - * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set - * TU += 2 and repeat 2nd step. - */ - do { - tu_size_reg += 2; - symbol = tu_size_reg * mode->clock * bit_per_pix; - do_div(symbol, dp->link.num_lanes * link_rate * 8); - rem = do_div(symbol, 1000); - if (tu_size_reg > 64) { - ret = -EINVAL; - DRM_DEV_ERROR(dp->dev, - "tu error, clk:%d, lanes:%d, rate:%d\n", - mode->clock, dp->link.num_lanes, - link_rate); - goto err_config_video; - } - } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || - (rem > 850) || (rem < 100)); - val = symbol + (tu_size_reg << 8); val |= TU_CNT_RST_EN; ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h index 0d01d153ed61..03759d02a3c3 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -534,6 +534,7 @@ int cdn_dp_get_edid_block(void *dp, u8 *edid, unsigned int block, size_t length); int cdn_dp_train_link(struct cdn_dp_device *dp); int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active); +int cdn_dp_tu_size_cal(struct cdn_dp_device *dp, u8 lanes, u8 link_bw); int cdn_dp_config_video(struct cdn_dp_device *dp); int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio); int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);