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);