mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
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 <hero.huang@rock-chips.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user