From: George Shen <[email protected]> [Why] DSC block programming should be independent from link programming. This should be refactored such that DSC block programming is in HWSS.
[How] The first step to the refactor is to split the existing logic into the encoder portion and the DSC block portion. Reviewed-by: Dillon Varone <[email protected]> Signed-off-by: George Shen <[email protected]> Signed-off-by: Ivan Lipski <[email protected]> --- .../gpu/drm/amd/display/dc/link/link_dpms.c | 275 ++++++++++-------- 1 file changed, 159 insertions(+), 116 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index 0d3b65d40807..ef577e0e5215 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -717,21 +717,94 @@ static bool dp_set_hblank_reduction_on_rx(struct pipe_ctx *pipe_ctx) return result; } +/* TODO - There is overlapping functionality between link_set_dsc_pps_packet, + * and set_encoder_dsc_config, the two functions should be refactored to avoid code duplication. + */ +static void set_encoder_dsc_config(struct pipe_ctx *pipe_ctx, + struct dsc_config *dsc_cfg, + struct dsc_optc_config *dsc_optc_cfg, + bool enable) +{ + struct dc *dc = pipe_ctx->stream->ctx->dc; + struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; + struct dc_stream_state *stream = pipe_ctx->stream; + enum optc_dsc_mode optc_dsc_mode; + int odm_count = 1; + struct pipe_ctx *odm_pipe; + DC_LOGGER_INIT(dsc->ctx->logger); -/* The stream with these settings can be sent (unblanked) only after DSC was enabled on RX first, - * i.e. after dp_enable_dsc_on_rx() had been called + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + odm_count++; + + if (enable) { + optc_dsc_mode = dsc_optc_cfg->is_pixel_format_444 ? + OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; + + if (dc_is_dp_signal(stream->signal) && !dp_is_128b_132b_signal(pipe_ctx)) { + DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.stream_enc->id); + dsc_optc_config_log(dsc, dsc_optc_cfg); + if (pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config) + pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config(pipe_ctx->stream_res.stream_enc, + optc_dsc_mode, + dsc_optc_cfg->bytes_per_pixel, + dsc_optc_cfg->slice_width); + + /* PPS SDP is set elsewhere because it has to be done after DIG FE is connected to DIG BE */ + } else if (dc_is_hdmi_frl_signal(stream->signal)) { + uint8_t dsc_packed_pps[128]; + struct dc_crtc_timing patched_crtc_timing = stream->timing; + + DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id); + dsc_optc_config_log(dsc, dsc_optc_cfg); + dsc_cfg->dc_dsc_cfg.num_slices_h *= odm_count; + dsc_cfg->pic_width *= odm_count; + dsc_cfg->dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding; + if (dc->debug.enable_hblank_borrow) + dsc_cfg->pic_width = stream->timing.h_addressable; + dsc->funcs->dsc_get_packed_pps(dsc, dsc_cfg, &dsc_packed_pps[0]); + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( + pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing, &dsc_packed_pps[0]); + } + + return; + } + + if (dc_is_dp_signal(stream->signal)) { + if (dp_is_128b_132b_signal(pipe_ctx)) + pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_set_dsc_pps_info_packet( + pipe_ctx->stream_res.hpo_dp_stream_enc, + false, + NULL, + true); + else { + if (pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config) + pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config( + pipe_ctx->stream_res.stream_enc, + OPTC_DSC_DISABLED, 0, 0); + pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet( + pipe_ctx->stream_res.stream_enc, false, NULL, true); + } + } else if (dc_is_hdmi_frl_signal(stream->signal)) { + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( + pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL); + } +} + +/* Legacy implementation of DSC configuration sequence. DSC HW block programming + * is independent of stream encoder and link configuration operations, and should + * be configured separately during front-end programming, not within link DPMS sequences. */ -void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) +static bool configure_dsc_hw_block(struct pipe_ctx *pipe_ctx, bool enable, + struct dsc_config *dsc_cfg, struct dsc_optc_config *dsc_optc_cfg) { - /* TODO: Move this to HWSS as this is hardware programming sequence not a - * link layer sequence - */ struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; struct dc *dc = pipe_ctx->stream->ctx->dc; struct dc_stream_state *stream = pipe_ctx->stream; struct pipe_ctx *odm_pipe; - int opp_cnt = 1; struct dccg *dccg = dc->res_pool->dccg; + int opp_cnt = 1; + DC_LOGGER_INIT(dsc->ctx->logger); + /* It has been found that when DSCCLK is lower than 16Mhz, we will get DCN * register access hung. When DSCCLk is based on refclk, DSCCLk is always a * fixed value higher than 16Mhz so the issue doesn't occur. When DSCCLK is @@ -741,35 +814,34 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) * based on fixed value refclk when timing is smaller than 3x16Mhz (i.e * 48Mhz) pixel clock to avoid hitting this problem. */ - bool should_use_dto_dscclk = (dccg->funcs->set_dto_dscclk != NULL) && + bool should_use_dto_dscclk = dccg->funcs->set_dto_dscclk && stream->timing.pix_clk_100hz > 480000; - DC_LOGGER_INIT(dsc->ctx->logger); for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) opp_cnt++; if (enable) { - struct dsc_config dsc_cfg; - struct dsc_optc_config dsc_optc_cfg = {0}; enum optc_dsc_mode optc_dsc_mode; + memset(dsc_cfg, 0, sizeof(*dsc_cfg)); + memset(dsc_optc_cfg, 0, sizeof(*dsc_optc_cfg)); /* Enable DSC hw block */ - dsc_cfg.pic_width = (stream->timing.h_addressable + pipe_ctx->dsc_padding_params.dsc_hactive_padding + + dsc_cfg->pic_width = (stream->timing.h_addressable + pipe_ctx->dsc_padding_params.dsc_hactive_padding + stream->timing.h_border_left + stream->timing.h_border_right) / opp_cnt; - dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom; - dsc_cfg.pixel_encoding = stream->timing.pixel_encoding; - dsc_cfg.color_depth = stream->timing.display_color_depth; - dsc_cfg.is_odm = pipe_ctx->next_odm_pipe ? true : false; - dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg; - ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0); - dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt; - dsc_cfg.dsc_padding = 0; + dsc_cfg->pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom; + dsc_cfg->pixel_encoding = stream->timing.pixel_encoding; + dsc_cfg->color_depth = stream->timing.display_color_depth; + dsc_cfg->is_odm = pipe_ctx->next_odm_pipe != NULL; + dsc_cfg->dc_dsc_cfg = stream->timing.dsc_cfg; + ASSERT(dsc_cfg->dc_dsc_cfg.num_slices_h % opp_cnt == 0); + dsc_cfg->dc_dsc_cfg.num_slices_h /= opp_cnt; + dsc_cfg->dsc_padding = 0; if (should_use_dto_dscclk) - dccg->funcs->set_dto_dscclk(dccg, dsc->inst, dsc_cfg.dc_dsc_cfg.num_slices_h); - if (!dsc->funcs->dsc_prepare_config(dsc, &dsc_cfg, &dsc_optc_cfg)) { + dccg->funcs->set_dto_dscclk(dccg, dsc->inst, dsc_cfg->dc_dsc_cfg.num_slices_h); + if (!dsc->funcs->dsc_prepare_config(dsc, dsc_cfg, dsc_optc_cfg)) { ASSERT(false); - return; + return false; } dsc->funcs->dsc_set_config(dsc); dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst); @@ -778,113 +850,84 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) ASSERT(odm_dsc); if (!odm_dsc) - return; + return false; if (should_use_dto_dscclk) - dccg->funcs->set_dto_dscclk(dccg, odm_dsc->inst, dsc_cfg.dc_dsc_cfg.num_slices_h); - if (!odm_dsc->funcs->dsc_prepare_config(odm_dsc, &dsc_cfg, &dsc_optc_cfg)) { + dccg->funcs->set_dto_dscclk(dccg, odm_dsc->inst, + dsc_cfg->dc_dsc_cfg.num_slices_h); + if (!odm_dsc->funcs->dsc_prepare_config(odm_dsc, dsc_cfg, dsc_optc_cfg)) { ASSERT(false); - return; + return false; } odm_dsc->funcs->dsc_set_config(odm_dsc); odm_dsc->funcs->dsc_enable(odm_dsc, odm_pipe->stream_res.opp->inst); } - dsc_cfg.dc_dsc_cfg.num_slices_h *= opp_cnt; - dsc_cfg.pic_width *= opp_cnt; - dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding; + optc_dsc_mode = dsc_optc_cfg->is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; - optc_dsc_mode = dsc_optc_cfg.is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; + /* Enable DSC in OPTC */ + DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst); + dsc_optc_config_log(dsc, dsc_optc_cfg); + pipe_ctx->stream_res.tg->funcs->set_dsc_config(pipe_ctx->stream_res.tg, + optc_dsc_mode, + dsc_optc_cfg->bytes_per_pixel, + dsc_optc_cfg->slice_width); + return true; + } - /* Enable DSC in encoder */ - if (dc_is_dp_signal(stream->signal) && !dp_is_128b_132b_signal(pipe_ctx)) { - DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.stream_enc->id); - dsc_optc_config_log(dsc, &dsc_optc_cfg); - if (pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config) - pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config(pipe_ctx->stream_res.stream_enc, - optc_dsc_mode, - dsc_optc_cfg.bytes_per_pixel, - dsc_optc_cfg.slice_width); + /* disable DSC in OPTC */ + pipe_ctx->stream_res.tg->funcs->set_dsc_config( + pipe_ctx->stream_res.tg, + OPTC_DSC_DISABLED, 0, 0); - /* PPS SDP is set elsewhere because it has to be done after DIG FE is connected to DIG BE */ - } - else if (dc_is_hdmi_frl_signal(stream->signal)) { - uint8_t dsc_packed_pps[128]; - struct dc_crtc_timing patched_crtc_timing = stream->timing; + for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + odm_pipe->stream_res.dsc->funcs->dsc_disconnect(odm_pipe->stream_res.dsc); - DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id); - dsc_optc_config_log(dsc, &dsc_optc_cfg); + /* + * dsc_disconnect is a double buffered register. + * by the time we call dsc_disable, dsc may still remain + * connected to OPP. In this case OPTC will no longer + * get correct pixel data because DSCC is off. However + * we also can't wait for the disconnect pending + * complete, because this function can be called + * with/without OTG master lock acquired. When the lock + * is acquired we will never get pending complete until + * we release the lock later. So there is no easy way to + * solve this problem especially when the lock is + * acquired. DSC is a front end hw block it should be + * programmed as part of front end sequence, where the + * commit sequence without lock and update sequence + * with lock are completely separated. However because + * we are programming dsc as part of back end link + * programming sequence, we don't know if front end OPTC + * master lock is acquired. The back end should be + * agnostic to front end lock. DSC programming shouldn't + * belong to this sequence. + */ + odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc); + if (dccg->funcs->set_ref_dscclk) + dccg->funcs->set_ref_dscclk(dccg, odm_pipe->stream_res.dsc->inst); + } + return true; +} - /* if we are borrowing from hblank, h_addressable and pic_width need to be adjusted */ - if (dc->debug.enable_hblank_borrow) { - dsc_cfg.pic_width = stream->timing.h_addressable; - } +/* The stream with these settings can be sent (unblanked) only after DSC was enabled on RX first, + * i.e. after dp_enable_dsc_on_rx() had been called + */ +void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) +{ + struct dsc_config dsc_cfg = {0}; + struct dsc_optc_config dsc_optc_cfg = {0}; - dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, &dsc_packed_pps[0]); - pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( - pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing, &dsc_packed_pps[0]); - } + if (!pipe_ctx->stream_res.dsc) { + ASSERT(false); + return; + } - /* Enable DSC in OPTC */ - DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst); - dsc_optc_config_log(dsc, &dsc_optc_cfg); - pipe_ctx->stream_res.tg->funcs->set_dsc_config(pipe_ctx->stream_res.tg, - optc_dsc_mode, - dsc_optc_cfg.bytes_per_pixel, - dsc_optc_cfg.slice_width); + if (enable) { + if (configure_dsc_hw_block(pipe_ctx, true, &dsc_cfg, &dsc_optc_cfg)) + set_encoder_dsc_config(pipe_ctx, &dsc_cfg, &dsc_optc_cfg, true); } else { - /* disable DSC in OPTC */ - pipe_ctx->stream_res.tg->funcs->set_dsc_config( - pipe_ctx->stream_res.tg, - OPTC_DSC_DISABLED, 0, 0); - - /* disable DSC in stream encoder */ - if (dc_is_dp_signal(stream->signal)) { - if (dp_is_128b_132b_signal(pipe_ctx)) - pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_set_dsc_pps_info_packet( - pipe_ctx->stream_res.hpo_dp_stream_enc, - false, - NULL, - true); - else { - if (pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config) - pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config( - pipe_ctx->stream_res.stream_enc, - OPTC_DSC_DISABLED, 0, 0); - pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet( - pipe_ctx->stream_res.stream_enc, false, NULL, true); - } - } - else if (dc_is_hdmi_frl_signal(stream->signal)) - pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( - pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL); - - /* disable DSC block */ - for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { - odm_pipe->stream_res.dsc->funcs->dsc_disconnect(odm_pipe->stream_res.dsc); - /* - * TODO - dsc_disconnect is a double buffered register. - * by the time we call dsc_disable, dsc may still remain - * connected to OPP. In this case OPTC will no longer - * get correct pixel data because DSCC is off. However - * we also can't wait for the disconnect pending - * complete, because this function can be called - * with/without OTG master lock acquired. When the lock - * is acquired we will never get pending complete until - * we release the lock later. So there is no easy way to - * solve this problem especially when the lock is - * acquired. DSC is a front end hw block it should be - * programmed as part of front end sequence, where the - * commit sequence without lock and update sequence - * with lock are completely separated. However because - * we are programming dsc as part of back end link - * programming sequence, we don't know if front end OPTC - * master lock is acquired. The back end should be - * agnostic to front end lock. DSC programming shouldn't - * belong to this sequence. - */ - odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc); - if (dccg->funcs->set_ref_dscclk) - dccg->funcs->set_ref_dscclk(dccg, odm_pipe->stream_res.dsc->inst); - } + set_encoder_dsc_config(pipe_ctx, &dsc_cfg, &dsc_optc_cfg, false); + configure_dsc_hw_block(pipe_ctx, false, &dsc_cfg, &dsc_optc_cfg); } } -- 2.43.0
