diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/core')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc.c | 175 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_debug.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link.c | 249 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c | 689 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c | 204 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_resource.c | 305 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_stream.c | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_surface.c | 3 |
9 files changed, 1140 insertions, 554 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index cbc480a33376..5d1adeda4d90 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -181,13 +181,25 @@ static bool create_links( link = link_create(&link_init_params); if (link) { - if (dc->config.edp_not_connected && - link->connector_signal == SIGNAL_TYPE_EDP) { - link_destroy(&link); - } else { + bool should_destory_link = false; + + if (link->connector_signal == SIGNAL_TYPE_EDP) { + if (dc->config.edp_not_connected) + should_destory_link = true; + else if (dc->debug.remove_disconnect_edp) { + enum dc_connection_type type; + dc_link_detect_sink(link, &type); + if (type == dc_connection_none) + should_destory_link = true; + } + } + + if (!should_destory_link) { dc->links[dc->link_count] = link; link->dc = dc; ++dc->link_count; + } else { + link_destroy(&link); } } } @@ -279,7 +291,9 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, dc->hwss.set_drr(&pipe, 1, adjust->v_total_min, - adjust->v_total_max); + adjust->v_total_max, + adjust->v_total_mid, + adjust->v_total_mid_frame_num); ret = true; } @@ -675,6 +689,11 @@ static bool construct(struct dc *dc, if (!dc->clk_mgr) goto fail; +#ifdef CONFIG_DRM_AMD_DC_DCN2_1 + if (dc->res_pool->funcs->update_bw_bounding_box) + dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); +#endif + /* Creation of current_state must occur after dc->dml * is initialized in dc_create_resource_pool because * on creation it copies the contents of dc->dml @@ -948,7 +967,7 @@ bool dc_validate_seamless_boot_timing(const struct dc *dc, { struct timing_generator *tg; struct dc_link *link = sink->link; - unsigned int inst; + unsigned int enc_inst, tg_inst; /* Check for enabled DIG to identify enabled display */ if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) @@ -960,13 +979,22 @@ bool dc_validate_seamless_boot_timing(const struct dc *dc, * current implementation always map 1-to-1, so this code makes * the same assumption and doesn't check OTG source. */ - inst = link->link_enc->funcs->get_dig_frontend(link->link_enc) - 1; + enc_inst = link->link_enc->funcs->get_dig_frontend(link->link_enc); /* Instance should be within the range of the pool */ - if (inst >= dc->res_pool->pipe_count) + if (enc_inst >= dc->res_pool->pipe_count) + return false; + + if (enc_inst >= dc->res_pool->stream_enc_count) + return false; + + tg_inst = dc->res_pool->stream_enc[enc_inst]->funcs->dig_source_otg( + dc->res_pool->stream_enc[enc_inst]); + + if (tg_inst >= dc->res_pool->timing_generator_count) return false; - tg = dc->res_pool->timing_generators[inst]; + tg = dc->res_pool->timing_generators[tg_inst]; if (!tg->funcs->is_matching_timing) return false; @@ -979,10 +1007,11 @@ bool dc_validate_seamless_boot_timing(const struct dc *dc, dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( dc->res_pool->dp_clock_source, - inst, &pix_clk_100hz); + tg_inst, &pix_clk_100hz); if (crtc_timing->pix_clk_100hz != pix_clk_100hz) return false; + } return true; @@ -1065,7 +1094,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c if (result != DC_OK) return result; - if (context->stream_count > 1) { + if (context->stream_count > 1 && !dc->debug.disable_timing_sync) { enable_timing_multisync(dc, context); program_timing_sync(dc, context); } @@ -1208,6 +1237,12 @@ struct dc_state *dc_copy_state(struct dc_state *src_ctx) if (cur_pipe->bottom_pipe) cur_pipe->bottom_pipe = &new_ctx->res_ctx.pipe_ctx[cur_pipe->bottom_pipe->pipe_idx]; + if (cur_pipe->prev_odm_pipe) + cur_pipe->prev_odm_pipe = &new_ctx->res_ctx.pipe_ctx[cur_pipe->prev_odm_pipe->pipe_idx]; + + if (cur_pipe->next_odm_pipe) + cur_pipe->next_odm_pipe = &new_ctx->res_ctx.pipe_ctx[cur_pipe->next_odm_pipe->pipe_idx]; + } for (i = 0; i < new_ctx->stream_count; i++) { @@ -1239,6 +1274,55 @@ void dc_release_state(struct dc_state *context) kref_put(&context->refcount, dc_state_free); } +bool dc_set_generic_gpio_for_stereo(bool enable, + struct gpio_service *gpio_service) +{ + enum gpio_result gpio_result = GPIO_RESULT_NON_SPECIFIC_ERROR; + struct gpio_pin_info pin_info; + struct gpio *generic; + struct gpio_generic_mux_config *config = kzalloc(sizeof(struct gpio_generic_mux_config), + GFP_KERNEL); + + if (!config) + return false; + pin_info = dal_gpio_get_generic_pin_info(gpio_service, GPIO_ID_GENERIC, 0); + + if (pin_info.mask == 0xFFFFFFFF || pin_info.offset == 0xFFFFFFFF) { + kfree(config); + return false; + } else { + generic = dal_gpio_service_create_generic_mux( + gpio_service, + pin_info.offset, + pin_info.mask); + } + + if (!generic) { + kfree(config); + return false; + } + + gpio_result = dal_gpio_open(generic, GPIO_MODE_OUTPUT); + + config->enable_output_from_mux = enable; + config->mux_select = GPIO_SIGNAL_SOURCE_PASS_THROUGH_STEREO_SYNC; + + if (gpio_result == GPIO_RESULT_OK) + gpio_result = dal_mux_setup_config(generic, config); + + if (gpio_result == GPIO_RESULT_OK) { + dal_gpio_close(generic); + dal_gpio_destroy_generic_mux(&generic); + kfree(config); + return true; + } else { + dal_gpio_close(generic); + dal_gpio_destroy_generic_mux(&generic); + kfree(config); + return false; + } +} + static bool is_surface_in_context( const struct dc_state *context, const struct dc_plane_state *plane_state) @@ -1305,8 +1389,8 @@ static enum surface_update_type get_plane_info_update_type(const struct dc_surfa } if (u->plane_info->dcc.enable != u->surface->dcc.enable - || u->plane_info->dcc.grph.independent_64b_blks != u->surface->dcc.grph.independent_64b_blks - || u->plane_info->dcc.grph.meta_pitch != u->surface->dcc.grph.meta_pitch) { + || u->plane_info->dcc.independent_64b_blks != u->surface->dcc.independent_64b_blks + || u->plane_info->dcc.meta_pitch != u->surface->dcc.meta_pitch) { update_flags->bits.dcc_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED); } @@ -1320,9 +1404,9 @@ static enum surface_update_type get_plane_info_update_type(const struct dc_surfa elevate_update_type(&update_type, UPDATE_TYPE_FULL); } - if (u->plane_info->plane_size.grph.surface_pitch != u->surface->plane_size.grph.surface_pitch - || u->plane_info->plane_size.video.luma_pitch != u->surface->plane_size.video.luma_pitch - || u->plane_info->plane_size.video.chroma_pitch != u->surface->plane_size.video.chroma_pitch) { + if (u->plane_info->plane_size.surface_pitch != u->surface->plane_size.surface_pitch + || u->plane_info->plane_size.surface_pitch != u->surface->plane_size.surface_pitch + || u->plane_info->plane_size.chroma_pitch != u->surface->plane_size.chroma_pitch) { update_flags->bits.plane_size_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED); } @@ -1542,6 +1626,9 @@ enum surface_update_type dc_check_update_surfaces_for_stream( for (i = 0; i < surface_count; i++) updates[i].surface->update_flags.raw = 0xFFFFFFFF; + if (type == UPDATE_TYPE_FAST && memcmp(&dc->current_state->bw_ctx.bw.dcn.clk, &dc->clk_mgr->clks, offsetof(struct dc_clocks, prev_p_state_change_support)) != 0) + dc->optimized_required = true; + return type; } @@ -1618,6 +1705,8 @@ static void copy_surface_update_to_plane( srf_update->plane_info->dcc; surface->sdr_white_level = srf_update->plane_info->sdr_white_level; + surface->layer_index = + srf_update->plane_info->layer_index; } if (srf_update->gamma && @@ -1784,9 +1873,7 @@ static void commit_planes_do_stream_update(struct dc *dc, for (j = 0; j < dc->res_pool->pipe_count; j++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; - if (!pipe_ctx->top_pipe && - pipe_ctx->stream && - pipe_ctx->stream == stream) { + if (!pipe_ctx->top_pipe && !pipe_ctx->prev_odm_pipe && pipe_ctx->stream == stream) { if (stream_update->periodic_interrupt0 && dc->hwss.setup_periodic_interrupt) @@ -1812,7 +1899,7 @@ static void commit_planes_do_stream_update(struct dc *dc, if (stream_update->dither_option) { #if defined(CONFIG_DRM_AMD_DC_DCN2_0) - struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; #endif resource_build_bit_depth_reduction_params(pipe_ctx->stream, &pipe_ctx->stream->bit_depth_params); @@ -1820,10 +1907,12 @@ static void commit_planes_do_stream_update(struct dc *dc, &stream->bit_depth_params, &stream->clamping); #if defined(CONFIG_DRM_AMD_DC_DCN2_0) - if (odm_pipe) + while (odm_pipe) { odm_pipe->stream_res.opp->funcs->opp_program_fmt(odm_pipe->stream_res.opp, &stream->bit_depth_params, &stream->clamping); + odm_pipe = odm_pipe->next_odm_pipe; + } #endif } @@ -1840,13 +1929,21 @@ static void commit_planes_do_stream_update(struct dc *dc, if (stream_update->dpms_off) { dc->hwss.pipe_control_lock(dc, pipe_ctx, true); + if (*stream_update->dpms_off) { - core_link_disable_stream(pipe_ctx, KEEP_ACQUIRED_RESOURCE); + core_link_disable_stream(pipe_ctx); + /* for dpms, keep acquired resources*/ + if (pipe_ctx->stream_res.audio && !dc->debug.az_endpoint_mute_only) + pipe_ctx->stream_res.audio->funcs->az_disable(pipe_ctx->stream_res.audio); + dc->hwss.optimize_bandwidth(dc, dc->current_state); } else { - dc->hwss.prepare_bandwidth(dc, dc->current_state); + if (!dc->optimize_seamless_boot) + dc->hwss.prepare_bandwidth(dc, dc->current_state); + core_link_enable_stream(dc->current_state, pipe_ctx); } + dc->hwss.pipe_control_lock(dc, pipe_ctx, false); } @@ -1936,6 +2033,7 @@ static void commit_planes_for_stream(struct dc *dc, struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; if (!pipe_ctx->top_pipe && + !pipe_ctx->prev_odm_pipe && pipe_ctx->stream && pipe_ctx->stream == stream) { struct dc_stream_status *stream_status = NULL; @@ -2050,7 +2148,7 @@ void dc_commit_updates_for_stream(struct dc *dc, enum surface_update_type update_type; struct dc_state *context; struct dc_context *dc_ctx = dc->ctx; - int i, j; + int i; stream_status = dc_stream_get_status(stream); context = dc->current_state; @@ -2088,16 +2186,6 @@ void dc_commit_updates_for_stream(struct dc *dc, copy_surface_update_to_plane(surface, &srf_updates[i]); - if (update_type >= UPDATE_TYPE_MED) { - for (j = 0; j < dc->res_pool->pipe_count; j++) { - struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; - - if (pipe_ctx->plane_state != surface) - continue; - - resource_build_scaling_params(pipe_ctx); - } - } } copy_stream_update_to_stream(dc, context, stream, stream_update); @@ -2187,6 +2275,14 @@ void dc_set_power_state( dc_resource_state_construct(dc, dc->current_state); dc->hwss.init_hw(dc); + +#ifdef CONFIG_DRM_AMD_DC_DCN2_0 + if (dc->hwss.init_sys_ctx != NULL && + dc->vm_pa_config.valid) { + dc->hwss.init_sys_ctx(dc->hwseq, dc, &dc->vm_pa_config); + } +#endif + break; default: ASSERT(dc->current_state->stream_count == 0); @@ -2387,3 +2483,14 @@ void get_clock_requirements_for_state(struct dc_state *state, struct AsicStateEx info->fClock = (unsigned int)state->bw_ctx.bw.dcn.clk.fclk_khz; info->phyClock = (unsigned int)state->bw_ctx.bw.dcn.clk.phyclk_khz; } +enum dc_status dc_set_clock(struct dc *dc, enum dc_clock_type clock_type, uint32_t clk_khz, uint32_t stepping) +{ + if (dc->hwss.set_clock) + return dc->hwss.set_clock(dc, clock_type, clk_khz, stepping); + return DC_ERROR_UNEXPECTED; +} +void dc_get_clock(struct dc *dc, enum dc_clock_type clock_type, struct dc_clock_config *clock_cfg) +{ + if (dc->hwss.get_clock) + dc->hwss.get_clock(dc, clock_type, clock_cfg); +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c index 5903e7822f98..b9227d5de3a3 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c @@ -115,16 +115,16 @@ void pre_surface_trace( plane_state->clip_rect.height); SURFACE_TRACE( - "plane_state->plane_size.grph.surface_size.x = %d;\n" - "plane_state->plane_size.grph.surface_size.y = %d;\n" - "plane_state->plane_size.grph.surface_size.width = %d;\n" - "plane_state->plane_size.grph.surface_size.height = %d;\n" - "plane_state->plane_size.grph.surface_pitch = %d;\n", - plane_state->plane_size.grph.surface_size.x, - plane_state->plane_size.grph.surface_size.y, - plane_state->plane_size.grph.surface_size.width, - plane_state->plane_size.grph.surface_size.height, - plane_state->plane_size.grph.surface_pitch); + "plane_state->plane_size.surface_size.x = %d;\n" + "plane_state->plane_size.surface_size.y = %d;\n" + "plane_state->plane_size.surface_size.width = %d;\n" + "plane_state->plane_size.surface_size.height = %d;\n" + "plane_state->plane_size.surface_pitch = %d;\n", + plane_state->plane_size.surface_size.x, + plane_state->plane_size.surface_size.y, + plane_state->plane_size.surface_size.width, + plane_state->plane_size.surface_size.height, + plane_state->plane_size.surface_pitch); SURFACE_TRACE( @@ -202,20 +202,20 @@ void update_surface_trace( SURFACE_TRACE( "plane_info->color_space = %d;\n" "plane_info->format = %d;\n" - "plane_info->plane_size.grph.surface_pitch = %d;\n" - "plane_info->plane_size.grph.surface_size.height = %d;\n" - "plane_info->plane_size.grph.surface_size.width = %d;\n" - "plane_info->plane_size.grph.surface_size.x = %d;\n" - "plane_info->plane_size.grph.surface_size.y = %d;\n" + "plane_info->plane_size.surface_pitch = %d;\n" + "plane_info->plane_size.surface_size.height = %d;\n" + "plane_info->plane_size.surface_size.width = %d;\n" + "plane_info->plane_size.surface_size.x = %d;\n" + "plane_info->plane_size.surface_size.y = %d;\n" "plane_info->rotation = %d;\n" "plane_info->stereo_format = %d;\n", update->plane_info->color_space, update->plane_info->format, - update->plane_info->plane_size.grph.surface_pitch, - update->plane_info->plane_size.grph.surface_size.height, - update->plane_info->plane_size.grph.surface_size.width, - update->plane_info->plane_size.grph.surface_size.x, - update->plane_info->plane_size.grph.surface_size.y, + update->plane_info->plane_size.surface_pitch, + update->plane_info->plane_size.surface_size.height, + update->plane_info->plane_size.surface_size.width, + update->plane_info->plane_size.surface_size.x, + update->plane_info->plane_size.surface_size.y, update->plane_info->rotation, update->plane_info->stereo_format); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 355b4ba12796..ca20b150afcc 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -45,10 +45,6 @@ #include "dpcd_defs.h" #include "dmcu.h" #include "hw/clk_mgr.h" -#if defined(CONFIG_DRM_AMD_DC_DCN2_0) -#include "resource.h" -#endif -#include "hw/clk_mgr.h" #define DC_LOGGER_INIT(logger) @@ -684,6 +680,56 @@ static bool is_same_edid(struct dc_edid *old_edid, struct dc_edid *new_edid) return (memcmp(old_edid->raw_edid, new_edid->raw_edid, new_edid->length) == 0); } +bool wait_for_alt_mode(struct dc_link *link) +{ + + /** + * something is terribly wrong if time out is > 200ms. (5Hz) + * 500 microseconds * 400 tries us 200 ms + **/ + unsigned int sleep_time_in_microseconds = 500; + unsigned int tries_allowed = 400; + bool is_in_alt_mode; + unsigned long long enter_timestamp; + unsigned long long finish_timestamp; + unsigned long long time_taken_in_ns; + int tries_taken; + + DC_LOGGER_INIT(link->ctx->logger); + + if (link->link_enc->funcs->is_in_alt_mode == NULL) + return true; + + is_in_alt_mode = link->link_enc->funcs->is_in_alt_mode(link->link_enc); + DC_LOG_WARNING("DP Alt mode state on HPD: %d\n", is_in_alt_mode); + + if (is_in_alt_mode) + return true; + + enter_timestamp = dm_get_timestamp(link->ctx); + + for (tries_taken = 0; tries_taken < tries_allowed; tries_taken++) { + udelay(sleep_time_in_microseconds); + /* ask the link if alt mode is enabled, if so return ok */ + if (link->link_enc->funcs->is_in_alt_mode(link->link_enc)) { + + finish_timestamp = dm_get_timestamp(link->ctx); + time_taken_in_ns = dm_get_elapse_time_in_ns( + link->ctx, finish_timestamp, enter_timestamp); + DC_LOG_WARNING("Alt mode entered finished after %llu ms\n", + div_u64(time_taken_in_ns, 1000000)); + return true; + } + + } + finish_timestamp = dm_get_timestamp(link->ctx); + time_taken_in_ns = dm_get_elapse_time_in_ns(link->ctx, finish_timestamp, + enter_timestamp); + DC_LOG_WARNING("Alt mode has timed out after %llu ms\n", + div_u64(time_taken_in_ns, 1000000)); + return false; +} + /** * dc_link_detect() - Detect if a sink is attached to a given link * @@ -772,6 +818,15 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) } case SIGNAL_TYPE_DISPLAY_PORT: { + /* wa HPD high coming too early*/ + if (link->link_enc->features.flags.bits.DP_IS_USB_C == 1) { + + /* if alt mode times out, return false */ + if (wait_for_alt_mode(link) == false) { + return false; + } + } + if (!detect_dp( link, &sink_caps, @@ -795,16 +850,9 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) dc_sink_release(prev_sink); } else { /* Empty dongle plug in */ - for (i = 0; i < LINK_TRAINING_MAX_VERIFY_RETRY; i++) { - int fail_count = 0; - - dp_verify_link_cap(link, - &link->reported_link_cap, - &fail_count); - - if (fail_count == 0) - break; - } + dp_verify_link_cap_with_retries(link, + &link->reported_link_cap, + LINK_TRAINING_MAX_VERIFY_RETRY); } return true; } @@ -908,17 +956,9 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) */ /* deal with non-mst cases */ - for (i = 0; i < LINK_TRAINING_MAX_VERIFY_RETRY; i++) { - int fail_count = 0; - - dp_verify_link_cap(link, - &link->reported_link_cap, - &fail_count); - - if (fail_count == 0) - break; - } - + dp_verify_link_cap_with_retries(link, + &link->reported_link_cap, + LINK_TRAINING_MAX_VERIFY_RETRY); } else { // If edid is the same, then discard new sink and revert back to original sink if (same_edid) { @@ -1188,6 +1228,9 @@ static bool construct( link->ctx = dc_ctx; link->link_index = init_params->link_index; + memset(&link->preferred_training_settings, 0, sizeof(struct dc_link_training_overrides)); + memset(&link->preferred_link_setting, 0, sizeof(struct dc_link_settings)); + link->link_id = bios->funcs->get_connector_id(bios, init_params->connector_index); if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { @@ -1384,57 +1427,6 @@ void link_destroy(struct dc_link **link) *link = NULL; } -static void dpcd_configure_panel_mode( - struct dc_link *link, - enum dp_panel_mode panel_mode) -{ - union dpcd_edp_config edp_config_set; - bool panel_mode_edp = false; - DC_LOGGER_INIT(link->ctx->logger); - - memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); - - if (DP_PANEL_MODE_DEFAULT != panel_mode) { - - switch (panel_mode) { - case DP_PANEL_MODE_EDP: - case DP_PANEL_MODE_SPECIAL: - panel_mode_edp = true; - break; - - default: - break; - } - - /*set edp panel mode in receiver*/ - core_link_read_dpcd( - link, - DP_EDP_CONFIGURATION_SET, - &edp_config_set.raw, - sizeof(edp_config_set.raw)); - - if (edp_config_set.bits.PANEL_MODE_EDP - != panel_mode_edp) { - enum ddc_result result = DDC_RESULT_UNKNOWN; - - edp_config_set.bits.PANEL_MODE_EDP = - panel_mode_edp; - result = core_link_write_dpcd( - link, - DP_EDP_CONFIGURATION_SET, - &edp_config_set.raw, - sizeof(edp_config_set.raw)); - - ASSERT(result == DDC_RESULT_SUCESSFULL); - } - } - DC_LOG_DETECTION_DP_CAPS("Link: %d eDP panel mode supported: %d " - "eDP panel mode enabled: %d \n", - link->link_index, - link->dpcd_caps.panel_mode_edp, - panel_mode_edp); -} - static void enable_stream_features(struct pipe_ctx *pipe_ctx) { struct dc_stream_state *stream = pipe_ctx->stream; @@ -1466,6 +1458,19 @@ static enum dc_status enable_link_dp( struct dc_link *link = stream->link; struct dc_link_settings link_settings = {0}; enum dp_panel_mode panel_mode; +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + bool fec_enable; +#endif + int i; + bool apply_seamless_boot_optimization = false; + + // check for seamless boot + for (i = 0; i < state->stream_count; i++) { + if (state->streams[i]->apply_seamless_boot_optimization) { + apply_seamless_boot_optimization = true; + break; + } + } /* get link settings for video mode timing */ decide_link_settings(stream, &link_settings); @@ -1487,7 +1492,8 @@ static enum dc_status enable_link_dp( pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = link_settings.link_rate * LINK_RATE_REF_FREQ_IN_KHZ; - state->clk_mgr->funcs->update_clocks(state->clk_mgr, state, false); + if (!apply_seamless_boot_optimization) + state->clk_mgr->funcs->update_clocks(state->clk_mgr, state, false); dp_enable_link_phy( link, @@ -1502,18 +1508,19 @@ static enum dc_status enable_link_dp( } panel_mode = dp_get_panel_mode(link); - dpcd_configure_panel_mode(link, panel_mode); + dp_set_panel_mode(link, panel_mode); skip_video_pattern = true; if (link_settings.link_rate == LINK_RATE_LOW) skip_video_pattern = false; -#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - dp_set_fec_ready(link, true); -#endif + if (link->aux_access_disabled) { + dc_link_dp_perform_link_training_skip_aux(link, &link_settings); - if (perform_link_training_with_retries( + link->cur_link_settings = link_settings; + status = DC_OK; + } else if (perform_link_training_with_retries( link, &link_settings, skip_video_pattern, @@ -1525,7 +1532,12 @@ static enum dc_status enable_link_dp( status = DC_FAIL_DP_LINK_TRAINING; #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - dp_set_fec_enable(link, true); + if (link->preferred_training_settings.fec_enable != NULL) + fec_enable = *link->preferred_training_settings.fec_enable; + else + fec_enable = true; + + dp_set_fec_enable(link, fec_enable); #endif return status; } @@ -2755,21 +2767,27 @@ void core_link_enable_stream( CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, COLOR_DEPTH_UNDEFINED); +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + if (pipe_ctx->stream->timing.flags.DSC) { + if (dc_is_dp_signal(pipe_ctx->stream->signal) || + dc_is_virtual_signal(pipe_ctx->stream->signal)) + dp_set_dsc_enable(pipe_ctx, true); + } +#endif core_dc->hwss.enable_stream(pipe_ctx); - if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) - allocate_mst_payload(pipe_ctx); - #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - if (pipe_ctx->stream->timing.flags.DSC && - (dc_is_dp_signal(pipe_ctx->stream->signal) || - dc_is_virtual_signal(pipe_ctx->stream->signal))) { - dp_set_dsc_enable(pipe_ctx, true); - pipe_ctx->stream_res.tg->funcs->wait_for_state( - pipe_ctx->stream_res.tg, - CRTC_STATE_VBLANK); + /* Set DPS PPS SDP (AKA "info frames") */ + if (pipe_ctx->stream->timing.flags.DSC) { + if (dc_is_dp_signal(pipe_ctx->stream->signal) || + dc_is_virtual_signal(pipe_ctx->stream->signal)) + dp_set_dsc_pps_sdp(pipe_ctx, true); } #endif + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + allocate_mst_payload(pipe_ctx); + core_dc->hwss.unblank_stream(pipe_ctx, &pipe_ctx->stream->link->cur_link_settings); @@ -2786,7 +2804,7 @@ void core_link_enable_stream( #endif } -void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option) +void core_link_disable_stream(struct pipe_ctx *pipe_ctx) { struct dc *core_dc = pipe_ctx->stream->ctx->dc; struct dc_stream_state *stream = pipe_ctx->stream; @@ -2821,13 +2839,13 @@ void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option) write_i2c_redriver_setting(pipe_ctx, false); } } - core_dc->hwss.disable_stream(pipe_ctx, option); + core_dc->hwss.disable_stream(pipe_ctx); disable_link(pipe_ctx->stream->link, pipe_ctx->stream->signal); #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - if (pipe_ctx->stream->timing.flags.DSC && - dc_is_dp_signal(pipe_ctx->stream->signal)) { - dp_set_dsc_enable(pipe_ctx, false); + if (pipe_ctx->stream->timing.flags.DSC) { + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + dp_set_dsc_enable(pipe_ctx, false); } #endif } @@ -2836,7 +2854,7 @@ void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) { struct dc *core_dc = pipe_ctx->stream->ctx->dc; - if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) + if (!dc_is_hdmi_signal(pipe_ctx->stream->signal)) return; core_dc->hwss.set_avmute(pipe_ctx, enable); @@ -2999,8 +3017,10 @@ void dc_link_set_preferred_link_settings(struct dc *dc, for (i = 0; i < MAX_PIPES; i++) { pipe = &dc->current_state->res_ctx.pipe_ctx[i]; if (pipe->stream && pipe->stream->link) { - if (pipe->stream->link == link) + if (pipe->stream->link == link) { + link_stream = pipe->stream; break; + } } } @@ -3008,20 +3028,40 @@ void dc_link_set_preferred_link_settings(struct dc *dc, if (i == MAX_PIPES) return; - link_stream = link->dc->current_state->res_ctx.pipe_ctx[i].stream; - /* Cannot retrain link if backend is off */ if (link_stream->dpms_off) return; - if (link_stream) - decide_link_settings(link_stream, &store_settings); + decide_link_settings(link_stream, &store_settings); if ((store_settings.lane_count != LANE_COUNT_UNKNOWN) && (store_settings.link_rate != LINK_RATE_UNKNOWN)) dp_retrain_link_dp_test(link, &store_settings, false); } +void dc_link_set_preferred_training_settings(struct dc *dc, + struct dc_link_settings *link_setting, + struct dc_link_training_overrides *lt_overrides, + struct dc_link *link, + bool skip_immediate_retrain) +{ + if (lt_overrides != NULL) + link->preferred_training_settings = *lt_overrides; + else + memset(&link->preferred_training_settings, 0, sizeof(link->preferred_training_settings)); + + if (link_setting != NULL) { + link->preferred_link_setting = *link_setting; + } else { + link->preferred_link_setting.lane_count = LANE_COUNT_UNKNOWN; + link->preferred_link_setting.link_rate = LINK_RATE_UNKNOWN; + } + + /* Retrain now, or wait until next stream update to apply */ + if (skip_immediate_retrain == false) + dc_link_set_preferred_link_settings(dc, &link->preferred_link_setting, link); +} + void dc_link_enable_hpd(const struct dc_link *link) { dc_link_dp_enable_hpd(link); @@ -3032,7 +3072,6 @@ void dc_link_disable_hpd(const struct dc_link *link) dc_link_dp_disable_hpd(link); } - void dc_link_set_test_pattern(struct dc_link *link, enum dp_test_pattern test_pattern, const struct link_training_settings *p_link_settings, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c index e6da8506128b..505967b48e14 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c @@ -294,7 +294,7 @@ static uint32_t defer_delay_converter_wa( { struct dc_link *link = ddc->link; - if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_4 && + if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 && !memcmp(link->dpcd_caps.branch_dev_name, DP_DVI_CONVERTER_ID_4, sizeof(link->dpcd_caps.branch_dev_name))) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index 2c7aaed907b9..f5742719b5d9 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -49,7 +49,7 @@ static struct dc_link_settings get_common_supported_link_settings( struct dc_link_settings link_setting_a, struct dc_link_settings link_setting_b); -static void wait_for_training_aux_rd_interval( +static uint32_t get_training_aux_rd_interval( struct dc_link *link, uint32_t default_wait_in_micro_secs) { @@ -68,15 +68,21 @@ static void wait_for_training_aux_rd_interval( sizeof(training_rd_interval)); if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) - default_wait_in_micro_secs = - training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; + default_wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; } - udelay(default_wait_in_micro_secs); + return default_wait_in_micro_secs; +} + +static void wait_for_training_aux_rd_interval( + struct dc_link *link, + uint32_t wait_in_micro_secs) +{ + udelay(wait_in_micro_secs); DC_LOG_HW_LINK_TRAINING("%s:\n wait = %d\n", __func__, - default_wait_in_micro_secs); + wait_in_micro_secs); } static void dpcd_set_training_pattern( @@ -95,27 +101,27 @@ static void dpcd_set_training_pattern( dpcd_pattern.v1_4.TRAINING_PATTERN_SET); } -static enum hw_dp_training_pattern get_supported_tp(struct dc_link *link) +static enum dc_dp_training_pattern get_supported_tp(struct dc_link *link) { - enum hw_dp_training_pattern highest_tp = HW_DP_TRAINING_PATTERN_2; + enum dc_dp_training_pattern highest_tp = DP_TRAINING_PATTERN_SEQUENCE_2; struct encoder_feature_support *features = &link->link_enc->features; struct dpcd_caps *dpcd_caps = &link->dpcd_caps; if (features->flags.bits.IS_TPS3_CAPABLE) - highest_tp = HW_DP_TRAINING_PATTERN_3; + highest_tp = DP_TRAINING_PATTERN_SEQUENCE_3; if (features->flags.bits.IS_TPS4_CAPABLE) - highest_tp = HW_DP_TRAINING_PATTERN_4; + highest_tp = DP_TRAINING_PATTERN_SEQUENCE_4; if (dpcd_caps->max_down_spread.bits.TPS4_SUPPORTED && - highest_tp >= HW_DP_TRAINING_PATTERN_4) - return HW_DP_TRAINING_PATTERN_4; + highest_tp >= DP_TRAINING_PATTERN_SEQUENCE_4) + return DP_TRAINING_PATTERN_SEQUENCE_4; if (dpcd_caps->max_ln_count.bits.TPS3_SUPPORTED && - highest_tp >= HW_DP_TRAINING_PATTERN_3) - return HW_DP_TRAINING_PATTERN_3; + highest_tp >= DP_TRAINING_PATTERN_SEQUENCE_3) + return DP_TRAINING_PATTERN_SEQUENCE_3; - return HW_DP_TRAINING_PATTERN_2; + return DP_TRAINING_PATTERN_SEQUENCE_2; } static void dpcd_set_link_settings( @@ -126,7 +132,7 @@ static void dpcd_set_link_settings( union down_spread_ctrl downspread = { {0} }; union lane_count_set lane_count_set = { {0} }; - enum hw_dp_training_pattern hw_tr_pattern; + enum dc_dp_training_pattern dp_tr_pattern; downspread.raw = (uint8_t) (lt_settings->link_settings.link_spread); @@ -134,21 +140,21 @@ static void dpcd_set_link_settings( lane_count_set.bits.LANE_COUNT_SET = lt_settings->link_settings.lane_count; - lane_count_set.bits.ENHANCED_FRAMING = 1; - + lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; - hw_tr_pattern = get_supported_tp(link); - if (hw_tr_pattern != HW_DP_TRAINING_PATTERN_4) { + dp_tr_pattern = get_supported_tp(link); + + if (dp_tr_pattern != DP_TRAINING_PATTERN_SEQUENCE_4) { lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED; } core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, - &downspread.raw, sizeof(downspread)); + &downspread.raw, sizeof(downspread)); core_link_write_dpcd(link, DP_LANE_COUNT_SET, - &lane_count_set.raw, 1); + &lane_count_set.raw, 1); if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 && lt_settings->link_settings.use_link_rate_set == true) { @@ -162,46 +168,47 @@ static void dpcd_set_link_settings( } if (rate) { - DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n", + DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = %x\n %x spread = %x\n", __func__, DP_LINK_BW_SET, lt_settings->link_settings.link_rate, DP_LANE_COUNT_SET, lt_settings->link_settings.lane_count, + lt_settings->enhanced_framing, DP_DOWNSPREAD_CTRL, lt_settings->link_settings.link_spread); } else { - DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x\n %x spread = %x\n", + DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x framing = %x\n %x spread = %x\n", __func__, DP_LINK_RATE_SET, lt_settings->link_settings.link_rate_set, DP_LANE_COUNT_SET, lt_settings->link_settings.lane_count, + lt_settings->enhanced_framing, DP_DOWNSPREAD_CTRL, lt_settings->link_settings.link_spread); } - } static enum dpcd_training_patterns - hw_training_pattern_to_dpcd_training_pattern( + dc_dp_training_pattern_to_dpcd_training_pattern( struct dc_link *link, - enum hw_dp_training_pattern pattern) + enum dc_dp_training_pattern pattern) { enum dpcd_training_patterns dpcd_tr_pattern = DPCD_TRAINING_PATTERN_VIDEOIDLE; switch (pattern) { - case HW_DP_TRAINING_PATTERN_1: + case DP_TRAINING_PATTERN_SEQUENCE_1: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1; break; - case HW_DP_TRAINING_PATTERN_2: + case DP_TRAINING_PATTERN_SEQUENCE_2: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2; break; - case HW_DP_TRAINING_PATTERN_3: + case DP_TRAINING_PATTERN_SEQUENCE_3: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3; break; - case HW_DP_TRAINING_PATTERN_4: + case DP_TRAINING_PATTERN_SEQUENCE_4: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_4; break; default: @@ -212,13 +219,12 @@ static enum dpcd_training_patterns } return dpcd_tr_pattern; - } static void dpcd_set_lt_pattern_and_lane_settings( struct dc_link *link, const struct link_training_settings *lt_settings, - enum hw_dp_training_pattern pattern) + enum dc_dp_training_pattern pattern) { union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = { { {0} } }; const uint32_t dpcd_base_lt_offset = @@ -233,7 +239,7 @@ static void dpcd_set_lt_pattern_and_lane_settings( * DpcdAddress_TrainingPatternSet *****************************************************************/ dpcd_pattern.v1_4.TRAINING_PATTERN_SET = - hw_training_pattern_to_dpcd_training_pattern(link, pattern); + dc_dp_training_pattern_to_dpcd_training_pattern(link, pattern); dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - dpcd_base_lt_offset] = dpcd_pattern.raw; @@ -346,12 +352,20 @@ static void update_drive_settings( { uint32_t lane; for (lane = 0; lane < src.link_settings.lane_count; lane++) { - dest->lane_settings[lane].VOLTAGE_SWING = - src.lane_settings[lane].VOLTAGE_SWING; - dest->lane_settings[lane].PRE_EMPHASIS = - src.lane_settings[lane].PRE_EMPHASIS; - dest->lane_settings[lane].POST_CURSOR2 = - src.lane_settings[lane].POST_CURSOR2; + if (dest->voltage_swing == NULL) + dest->lane_settings[lane].VOLTAGE_SWING = src.lane_settings[lane].VOLTAGE_SWING; + else + dest->lane_settings[lane].VOLTAGE_SWING = *dest->voltage_swing; + + if (dest->pre_emphasis == NULL) + dest->lane_settings[lane].PRE_EMPHASIS = src.lane_settings[lane].PRE_EMPHASIS; + else + dest->lane_settings[lane].PRE_EMPHASIS = *dest->pre_emphasis; + + if (dest->post_cursor2 == NULL) + dest->lane_settings[lane].POST_CURSOR2 = src.lane_settings[lane].POST_CURSOR2; + else + dest->lane_settings[lane].POST_CURSOR2 = *dest->post_cursor2; } } @@ -754,15 +768,15 @@ static enum link_training_result perform_channel_equalization_sequence( struct link_training_settings *lt_settings) { struct link_training_settings req_settings; - enum hw_dp_training_pattern hw_tr_pattern; + enum dc_dp_training_pattern tr_pattern; uint32_t retries_ch_eq; enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; union lane_align_status_updated dpcd_lane_status_updated = { {0} }; union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = { { {0} } }; - hw_tr_pattern = get_supported_tp(link); + tr_pattern = lt_settings->pattern_for_eq; - dp_set_hw_training_pattern(link, hw_tr_pattern); + dp_set_hw_training_pattern(link, tr_pattern); for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; retries_ch_eq++) { @@ -776,12 +790,12 @@ static enum link_training_result perform_channel_equalization_sequence( dpcd_set_lt_pattern_and_lane_settings( link, lt_settings, - hw_tr_pattern); + tr_pattern); else dpcd_set_lane_settings(link, lt_settings); /* 3. wait for receiver to lock-on*/ - wait_for_training_aux_rd_interval(link, 400); + wait_for_training_aux_rd_interval(link, lt_settings->eq_pattern_time); /* 4. Read lane status and requested * drive settings as set by the sink*/ @@ -817,27 +831,16 @@ static enum link_training_result perform_clock_recovery_sequence( { uint32_t retries_cr; uint32_t retry_count; - uint32_t lane; struct link_training_settings req_settings; - enum dc_lane_count lane_count = - lt_settings->link_settings.lane_count; - enum hw_dp_training_pattern hw_tr_pattern = HW_DP_TRAINING_PATTERN_1; + enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; + enum dc_dp_training_pattern tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_1; union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; union lane_align_status_updated dpcd_lane_status_updated; retries_cr = 0; retry_count = 0; - /* initial drive setting (VS/PE/PC2)*/ - for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { - lt_settings->lane_settings[lane].VOLTAGE_SWING = - VOLTAGE_SWING_LEVEL0; - lt_settings->lane_settings[lane].PRE_EMPHASIS = - PRE_EMPHASIS_DISABLED; - lt_settings->lane_settings[lane].POST_CURSOR2 = - POST_CURSOR2_DISABLED; - } - dp_set_hw_training_pattern(link, hw_tr_pattern); + dp_set_hw_training_pattern(link, tr_pattern); /* najeeb - The synaptics MST hub can put the LT in * infinite loop by switching the VS @@ -845,7 +848,7 @@ static enum link_training_result perform_clock_recovery_sequence( /* between level 0 and level 1 continuously, here * we try for CR lock for LinkTrainingMaxCRRetry count*/ while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && - (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { + (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status)); memset(&dpcd_lane_status_updated, '\0', @@ -863,7 +866,7 @@ static enum link_training_result perform_clock_recovery_sequence( dpcd_set_lt_pattern_and_lane_settings( link, lt_settings, - hw_tr_pattern); + tr_pattern); else dpcd_set_lane_settings( link, @@ -872,7 +875,7 @@ static enum link_training_result perform_clock_recovery_sequence( /* 3. wait receiver to lock-on*/ wait_for_training_aux_rd_interval( link, - 100); + lt_settings->cr_pattern_time); /* 4. Read lane status and requested drive * settings as set by the sink @@ -939,7 +942,7 @@ static inline enum link_training_result perform_link_training_int( * TPS4 must be used instead of POST_LT_ADJ_REQ. */ if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED != 1 || - get_supported_tp(link) == HW_DP_TRAINING_PATTERN_4) + get_supported_tp(link) == DP_TRAINING_PATTERN_SEQUENCE_4) return status; if (status == LINK_TRAINING_SUCCESS && @@ -947,7 +950,7 @@ static inline enum link_training_result perform_link_training_int( status = LINK_TRAINING_LQA_FAIL; lane_count_set.bits.LANE_COUNT_SET = lt_settings->link_settings.lane_count; - lane_count_set.bits.ENHANCED_FRAMING = 1; + lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; core_link_write_dpcd( @@ -959,24 +962,29 @@ static inline enum link_training_result perform_link_training_int( return status; } -enum link_training_result dc_link_dp_perform_link_training( - struct dc_link *link, +static void initialize_training_settings( + struct dc_link *link, const struct dc_link_settings *link_setting, - bool skip_video_pattern) + const struct dc_link_training_overrides *overrides, + struct link_training_settings *lt_settings) { - enum link_training_result status = LINK_TRAINING_SUCCESS; + uint32_t lane; - char *link_rate = "Unknown"; - char *lt_result = "Unknown"; + memset(lt_settings, '\0', sizeof(struct link_training_settings)); - struct link_training_settings lt_settings; + /* Initialize link settings */ + lt_settings->link_settings.use_link_rate_set = link_setting->use_link_rate_set; + lt_settings->link_settings.link_rate_set = link_setting->link_rate_set; - memset(<_settings, '\0', sizeof(lt_settings)); + if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) + lt_settings->link_settings.link_rate = link->preferred_link_setting.link_rate; + else + lt_settings->link_settings.link_rate = link_setting->link_rate; - lt_settings.link_settings.link_rate = link_setting->link_rate; - lt_settings.link_settings.lane_count = link_setting->lane_count; - lt_settings.link_settings.use_link_rate_set = link_setting->use_link_rate_set; - lt_settings.link_settings.link_rate_set = link_setting->link_rate_set; + if (link->preferred_link_setting.lane_count != LANE_COUNT_UNKNOWN) + lt_settings->link_settings.lane_count = link->preferred_link_setting.lane_count; + else + lt_settings->link_settings.lane_count = link_setting->lane_count; /*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/ @@ -987,31 +995,75 @@ enum link_training_result dc_link_dp_perform_link_training( * LINK_SPREAD_05_DOWNSPREAD_30KHZ : * LINK_SPREAD_DISABLED; */ + /* Initialize link spread */ if (link->dp_ss_off) - lt_settings.link_settings.link_spread = LINK_SPREAD_DISABLED; + lt_settings->link_settings.link_spread = LINK_SPREAD_DISABLED; + else if (overrides->downspread != NULL) + lt_settings->link_settings.link_spread + = *overrides->downspread + ? LINK_SPREAD_05_DOWNSPREAD_30KHZ + : LINK_SPREAD_DISABLED; else - lt_settings.link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; + lt_settings->link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; - /* 1. set link rate, lane count and spread*/ - dpcd_set_link_settings(link, <_settings); + /* Initialize lane settings overrides */ + if (overrides->voltage_swing != NULL) + lt_settings->voltage_swing = overrides->voltage_swing; - /* 2. perform link training (set link training done - * to false is done as well)*/ - status = perform_clock_recovery_sequence(link, <_settings); - if (status == LINK_TRAINING_SUCCESS) { - status = perform_channel_equalization_sequence(link, - <_settings); - } + if (overrides->pre_emphasis != NULL) + lt_settings->pre_emphasis = overrides->pre_emphasis; - if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { - status = perform_link_training_int(link, - <_settings, - status); + if (overrides->post_cursor2 != NULL) + lt_settings->post_cursor2 = overrides->post_cursor2; + + /* Initialize lane settings (VS/PE/PC2) */ + for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { + lt_settings->lane_settings[lane].VOLTAGE_SWING = + lt_settings->voltage_swing != NULL ? + *lt_settings->voltage_swing : + VOLTAGE_SWING_LEVEL0; + lt_settings->lane_settings[lane].PRE_EMPHASIS = + lt_settings->pre_emphasis != NULL ? + *lt_settings->pre_emphasis + : PRE_EMPHASIS_DISABLED; + lt_settings->lane_settings[lane].POST_CURSOR2 = + lt_settings->post_cursor2 != NULL ? + *lt_settings->post_cursor2 + : POST_CURSOR2_DISABLED; } - /* 6. print status message*/ - switch (lt_settings.link_settings.link_rate) { + /* Initialize training timings */ + if (overrides->cr_pattern_time != NULL) + lt_settings->cr_pattern_time = *overrides->cr_pattern_time; + else + lt_settings->cr_pattern_time = get_training_aux_rd_interval(link, 100); + + if (overrides->eq_pattern_time != NULL) + lt_settings->eq_pattern_time = *overrides->eq_pattern_time; + else + lt_settings->eq_pattern_time = get_training_aux_rd_interval(link, 400); + + if (overrides->pattern_for_eq != NULL) + lt_settings->pattern_for_eq = *overrides->pattern_for_eq; + else + lt_settings->pattern_for_eq = get_supported_tp(link); + + if (overrides->enhanced_framing != NULL) + lt_settings->enhanced_framing = *overrides->enhanced_framing; + else + lt_settings->enhanced_framing = 1; +} + +static void print_status_message( + struct dc_link *link, + const struct link_training_settings *lt_settings, + enum link_training_result status) +{ + char *link_rate = "Unknown"; + char *lt_result = "Unknown"; + char *lt_spread = "Disabled"; + switch (lt_settings->link_settings.link_rate) { case LINK_RATE_LOW: link_rate = "RBR"; break; @@ -1057,13 +1109,122 @@ enum link_training_result dc_link_dp_perform_link_training( break; } + switch (lt_settings->link_settings.link_spread) { + case LINK_SPREAD_DISABLED: + lt_spread = "Disabled"; + break; + case LINK_SPREAD_05_DOWNSPREAD_30KHZ: + lt_spread = "0.5% 30KHz"; + break; + case LINK_SPREAD_05_DOWNSPREAD_33KHZ: + lt_spread = "0.5% 33KHz"; + break; + default: + break; + } + /* Connectivity log: link training */ - CONN_MSG_LT(link, "%sx%d %s VS=%d, PE=%d", - link_rate, - lt_settings.link_settings.lane_count, - lt_result, - lt_settings.lane_settings[0].VOLTAGE_SWING, - lt_settings.lane_settings[0].PRE_EMPHASIS); + CONN_MSG_LT(link, "%sx%d %s VS=%d, PE=%d, DS=%s", + link_rate, + lt_settings->link_settings.lane_count, + lt_result, + lt_settings->lane_settings[0].VOLTAGE_SWING, + lt_settings->lane_settings[0].PRE_EMPHASIS, + lt_spread); +} + +bool dc_link_dp_perform_link_training_skip_aux( + struct dc_link *link, + const struct dc_link_settings *link_setting) +{ + struct link_training_settings lt_settings; + enum dc_dp_training_pattern pattern_for_cr = DP_TRAINING_PATTERN_SEQUENCE_1; + + initialize_training_settings( + link, + link_setting, + &link->preferred_training_settings, + <_settings); + + /* 1. Perform_clock_recovery_sequence. */ + + /* transmit training pattern for clock recovery */ + dp_set_hw_training_pattern(link, pattern_for_cr); + + /* call HWSS to set lane settings*/ + dp_set_hw_lane_settings(link, <_settings); + + /* wait receiver to lock-on*/ + wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time); + + /* 2. Perform_channel_equalization_sequence. */ + + /* transmit training pattern for channel equalization. */ + dp_set_hw_training_pattern(link, lt_settings.pattern_for_eq); + + /* call HWSS to set lane settings*/ + dp_set_hw_lane_settings(link, <_settings); + + /* wait receiver to lock-on. */ + wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time); + + /* 3. Perform_link_training_int. */ + + /* Mainlink output idle pattern. */ + dp_set_hw_test_pattern(link, DP_TEST_PATTERN_VIDEO_MODE, NULL, 0); + + print_status_message(link, <_settings, LINK_TRAINING_SUCCESS); + + return true; +} + +enum link_training_result dc_link_dp_perform_link_training( + struct dc_link *link, + const struct dc_link_settings *link_setting, + bool skip_video_pattern) +{ + enum link_training_result status = LINK_TRAINING_SUCCESS; + struct link_training_settings lt_settings; +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + bool fec_enable; +#endif + + initialize_training_settings( + link, + link_setting, + &link->preferred_training_settings, + <_settings); + + /* 1. set link rate, lane count and spread. */ + dpcd_set_link_settings(link, <_settings); + +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + if (link->preferred_training_settings.fec_enable != NULL) + fec_enable = *link->preferred_training_settings.fec_enable; + else + fec_enable = true; + + dp_set_fec_ready(link, fec_enable); +#endif + + + /* 2. perform link training (set link training done + * to false is done as well) + */ + status = perform_clock_recovery_sequence(link, <_settings); + if (status == LINK_TRAINING_SUCCESS) { + status = perform_channel_equalization_sequence(link, + <_settings); + } + + if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { + status = perform_link_training_int(link, + <_settings, + status); + } + + /* 6. print status message*/ + print_status_message(link, <_settings, status); if (status != LINK_TRAINING_SUCCESS) link->ctx->dc->debug_data.ltFailCount++; @@ -1071,7 +1232,6 @@ enum link_training_result dc_link_dp_perform_link_training( return status; } - bool perform_link_training_with_retries( struct dc_link *link, const struct dc_link_settings *link_setting, @@ -1096,6 +1256,146 @@ bool perform_link_training_with_retries( return false; } +static enum clock_source_id get_clock_source_id(struct dc_link *link) +{ + enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_UNDEFINED; + struct clock_source *dp_cs = link->dc->res_pool->dp_clock_source; + + if (dp_cs != NULL) { + dp_cs_id = dp_cs->id; + } else { + /* + * dp clock source is not initialized for some reason. + * Should not happen, CLOCK_SOURCE_ID_EXTERNAL will be used + */ + ASSERT(dp_cs); + } + + return dp_cs_id; +} + +static void set_dp_mst_mode(struct dc_link *link, bool mst_enable) +{ + if (mst_enable == false && + link->type == dc_connection_mst_branch) { + /* Disable MST on link. Use only local sink. */ + dp_disable_link_phy_mst(link, link->connector_signal); + + link->type = dc_connection_single; + link->local_sink = link->remote_sinks[0]; + link->local_sink->sink_signal = SIGNAL_TYPE_DISPLAY_PORT; + } else if (mst_enable == true && + link->type == dc_connection_single && + link->remote_sinks[0] != NULL) { + /* Re-enable MST on link. */ + dp_disable_link_phy(link, link->connector_signal); + dp_enable_mst_on_sink(link, true); + + link->type = dc_connection_mst_branch; + link->local_sink->sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + } +} + +bool dc_link_dp_sync_lt_begin(struct dc_link *link) +{ + /* Begin Sync LT. During this time, + * DPCD:600h must not be powered down. + */ + link->sync_lt_in_progress = true; + + /*Clear any existing preferred settings.*/ + memset(&link->preferred_training_settings, 0, + sizeof(struct dc_link_training_overrides)); + memset(&link->preferred_link_setting, 0, + sizeof(struct dc_link_settings)); + + return true; +} + +enum link_training_result dc_link_dp_sync_lt_attempt( + struct dc_link *link, + struct dc_link_settings *link_settings, + struct dc_link_training_overrides *lt_overrides) +{ + struct link_training_settings lt_settings; + enum link_training_result lt_status = LINK_TRAINING_SUCCESS; + enum dp_panel_mode panel_mode = DP_PANEL_MODE_DEFAULT; + enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + bool fec_enable = false; +#endif + + initialize_training_settings( + link, + link_settings, + lt_overrides, + <_settings); + + /* Setup MST Mode */ + if (lt_overrides->mst_enable) + set_dp_mst_mode(link, *lt_overrides->mst_enable); + + /* Disable link */ + dp_disable_link_phy(link, link->connector_signal); + + /* Enable link */ + dp_cs_id = get_clock_source_id(link); + dp_enable_link_phy(link, link->connector_signal, + dp_cs_id, link_settings); + +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + /* Set FEC enable */ + fec_enable = lt_overrides->fec_enable && *lt_overrides->fec_enable; + dp_set_fec_ready(link, fec_enable); +#endif + + if (lt_overrides->alternate_scrambler_reset) { + if (*lt_overrides->alternate_scrambler_reset) + panel_mode = DP_PANEL_MODE_EDP; + else + panel_mode = DP_PANEL_MODE_DEFAULT; + } else + panel_mode = dp_get_panel_mode(link); + + dp_set_panel_mode(link, panel_mode); + + /* Attempt to train with given link training settings */ + + /* Set link rate, lane count and spread. */ + dpcd_set_link_settings(link, <_settings); + + /* 2. perform link training (set link training done + * to false is done as well) + */ + lt_status = perform_clock_recovery_sequence(link, <_settings); + if (lt_status == LINK_TRAINING_SUCCESS) { + lt_status = perform_channel_equalization_sequence(link, + <_settings); + } + + /* 3. Sync LT must skip TRAINING_PATTERN_SET:0 (video pattern)*/ + /* 4. print status message*/ + print_status_message(link, <_settings, lt_status); + + return lt_status; +} + +bool dc_link_dp_sync_lt_end(struct dc_link *link, bool link_down) +{ + /* If input parameter is set, shut down phy. + * Still shouldn't turn off dp_receiver (DPCD:600h) + */ + if (link_down == true) { + dp_disable_link_phy(link, link->connector_signal); +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + dp_set_fec_ready(link, false); +#endif + } + + link->sync_lt_in_progress = false; + return true; +} + static struct dc_link_settings get_max_link_cap(struct dc_link *link) { /* Set Default link settings */ @@ -1250,7 +1550,6 @@ bool dp_verify_link_cap( bool success; bool skip_link_training; bool skip_video_pattern; - struct clock_source *dp_cs; enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; enum link_training_result status; union hpd_irq_data irq_data; @@ -1274,17 +1573,7 @@ bool dp_verify_link_cap( /* disable PHY done possible by BIOS, will be done by driver itself */ dp_disable_link_phy(link, link->connector_signal); - dp_cs = link->dc->res_pool->dp_clock_source; - - if (dp_cs) - dp_cs_id = dp_cs->id; - else { - /* - * dp clock source is not initialized for some reason. - * Should not happen, CLOCK_SOURCE_ID_EXTERNAL will be used - */ - ASSERT(dp_cs); - } + dp_cs_id = get_clock_source_id(link); /* link training starts with the maximum common settings * supported by both sink and ASIC. @@ -1354,6 +1643,33 @@ bool dp_verify_link_cap( return success; } +bool dp_verify_link_cap_with_retries( + struct dc_link *link, + struct dc_link_settings *known_limit_link_setting, + int attempts) +{ + uint8_t i = 0; + bool success = false; + + for (i = 0; i < attempts; i++) { + int fail_count = 0; + enum dc_connection_type type; + + memset(&link->verified_link_cap, 0, + sizeof(struct dc_link_settings)); + if (!dc_link_detect_sink(link, &type)) { + break; + } else if (dp_verify_link_cap(link, + &link->reported_link_cap, + &fail_count) && fail_count == 0) { + success = true; + break; + } + msleep(10); + } + return success; +} + static struct dc_link_settings get_common_supported_link_settings( struct dc_link_settings link_setting_a, struct dc_link_settings link_setting_b) @@ -2156,6 +2472,11 @@ bool is_mst_supported(struct dc_link *link) union dpcd_rev rev; union mstm_cap cap; + if (link->preferred_training_settings.mst_enable && + *link->preferred_training_settings.mst_enable == false) { + return false; + } + rev.raw = 0; cap.raw = 0; @@ -2363,13 +2684,13 @@ static void dp_wa_power_up_0010FA(struct dc_link *link, uint8_t *dpcd_data, if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) { switch (link->dpcd_caps.branch_dev_id) { - /* Some active dongles (DP-VGA, DP-DLDVI converters) power down + /* 0010FA active dongles (DP-VGA, DP-DLDVI converters) power down * all internal circuits including AUX communication preventing * reading DPCD table and EDID (spec violation). * Encoder will skip DP RX power down on disable_output to * keep receiver powered all the time.*/ - case DP_BRANCH_DEVICE_ID_1: - case DP_BRANCH_DEVICE_ID_4: + case DP_BRANCH_DEVICE_ID_0010FA: + case DP_BRANCH_DEVICE_ID_0080E1: link->wa_flags.dp_keep_receiver_powered = true; break; @@ -2774,14 +3095,19 @@ static void set_crtc_test_pattern(struct dc_link *link, controller_test_pattern, color_depth); #if defined(CONFIG_DRM_AMD_DC_DCN2_0) else if (opp->funcs->opp_set_disp_pattern_generator) { - struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + struct pipe_ctx *odm_pipe; + int opp_cnt = 1; + + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; - if (bot_odm_pipe) { - struct output_pixel_processor *bot_opp = bot_odm_pipe->stream_res.opp; + width /= opp_cnt; - bot_opp->funcs->opp_program_bit_depth_reduction(bot_opp, ¶ms); - width /= 2; - bot_opp->funcs->opp_set_disp_pattern_generator(bot_opp, + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp; + + odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); + odm_opp->funcs->opp_set_disp_pattern_generator(odm_opp, controller_test_pattern, color_depth, NULL, @@ -2810,14 +3136,18 @@ static void set_crtc_test_pattern(struct dc_link *link, color_depth); #if defined(CONFIG_DRM_AMD_DC_DCN2_0) else if (opp->funcs->opp_set_disp_pattern_generator) { - struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + struct pipe_ctx *odm_pipe; + int opp_cnt = 1; + + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; - if (bot_odm_pipe) { - struct output_pixel_processor *bot_opp = bot_odm_pipe->stream_res.opp; + width /= opp_cnt; + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp; - bot_opp->funcs->opp_program_bit_depth_reduction(bot_opp, ¶ms); - width /= 2; - bot_opp->funcs->opp_set_disp_pattern_generator(bot_opp, + odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); + odm_opp->funcs->opp_set_disp_pattern_generator(odm_opp, CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, color_depth, NULL, @@ -2858,7 +3188,7 @@ bool dc_link_dp_set_test_pattern( memset(&training_pattern, 0, sizeof(training_pattern)); for (i = 0; i < MAX_PIPES; i++) { - if (pipes[i].stream->link == link) { + if (pipes[i].stream->link == link && !pipes[i].top_pipe && !pipes[i].prev_odm_pipe) { pipe_ctx = &pipes[i]; break; } @@ -3007,6 +3337,105 @@ void dp_enable_mst_on_sink(struct dc_link *link, bool enable) core_link_write_dpcd(link, DP_MSTM_CTRL, &mstmCntl, 1); } +void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode) +{ + union dpcd_edp_config edp_config_set; + bool panel_mode_edp = false; + + memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); + + if (panel_mode != DP_PANEL_MODE_DEFAULT) { + + switch (panel_mode) { + case DP_PANEL_MODE_EDP: + case DP_PANEL_MODE_SPECIAL: + panel_mode_edp = true; + break; + + default: + break; + } + + /*set edp panel mode in receiver*/ + core_link_read_dpcd( + link, + DP_EDP_CONFIGURATION_SET, + &edp_config_set.raw, + sizeof(edp_config_set.raw)); + + if (edp_config_set.bits.PANEL_MODE_EDP + != panel_mode_edp) { + enum ddc_result result = DDC_RESULT_UNKNOWN; + + edp_config_set.bits.PANEL_MODE_EDP = + panel_mode_edp; + result = core_link_write_dpcd( + link, + DP_EDP_CONFIGURATION_SET, + &edp_config_set.raw, + sizeof(edp_config_set.raw)); + + ASSERT(result == DDC_RESULT_SUCESSFULL); + } + } + DC_LOG_DETECTION_DP_CAPS("Link: %d eDP panel mode supported: %d " + "eDP panel mode enabled: %d \n", + link->link_index, + link->dpcd_caps.panel_mode_edp, + panel_mode_edp); +} + +enum dp_panel_mode dp_get_panel_mode(struct dc_link *link) +{ + /* We need to explicitly check that connector + * is not DP. Some Travis_VGA get reported + * by video bios as DP. + */ + if (link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) { + + switch (link->dpcd_caps.branch_dev_id) { + case DP_BRANCH_DEVICE_ID_0022B9: + /* alternate scrambler reset is required for Travis + * for the case when external chip does not + * provide sink device id, alternate scrambler + * scheme will be overriden later by querying + * Encoder features + */ + if (strncmp( + link->dpcd_caps.branch_dev_name, + DP_VGA_LVDS_CONVERTER_ID_2, + sizeof( + link->dpcd_caps. + branch_dev_name)) == 0) { + return DP_PANEL_MODE_SPECIAL; + } + break; + case DP_BRANCH_DEVICE_ID_00001A: + /* alternate scrambler reset is required for Travis + * for the case when external chip does not provide + * sink device id, alternate scrambler scheme will + * be overriden later by querying Encoder feature + */ + if (strncmp(link->dpcd_caps.branch_dev_name, + DP_VGA_LVDS_CONVERTER_ID_3, + sizeof( + link->dpcd_caps. + branch_dev_name)) == 0) { + return DP_PANEL_MODE_SPECIAL; + } + break; + default: + break; + } + } + + if (link->dpcd_caps.panel_mode_edp) { + return DP_PANEL_MODE_EDP; + } + + return DP_PANEL_MODE_DEFAULT; +} + #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT void dp_set_fec_ready(struct dc_link *link, bool ready) { @@ -3024,7 +3453,7 @@ void dp_set_fec_ready(struct dc_link *link, bool ready) if (link_enc->funcs->fec_set_ready && link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) { - if (link->fec_state == dc_link_fec_not_ready && ready) { + if (ready) { fec_config = 1; if (core_link_write_dpcd(link, DP_FEC_CONFIGURATION, @@ -3033,9 +3462,11 @@ void dp_set_fec_ready(struct dc_link *link, bool ready) link_enc->funcs->fec_set_ready(link_enc, true); link->fec_state = dc_link_fec_ready; } else { + link->link_enc->funcs->fec_set_ready(link->link_enc, false); + link->fec_state = dc_link_fec_not_ready; dm_error("dpcd write failed to set fec_ready"); } - } else if (link->fec_state == dc_link_fec_ready && !ready) { + } else if (link->fec_state == dc_link_fec_ready) { fec_config = 0; core_link_write_dpcd(link, DP_FEC_CONFIGURATION, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c index 2d019e1f6135..79438c4f1e20 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c @@ -25,10 +25,11 @@ enum dc_status core_link_read_dpcd( uint8_t *data, uint32_t size) { - if (!dm_helpers_dp_read_dpcd(link->ctx, - link, - address, data, size)) - return DC_ERROR_UNEXPECTED; + if (!link->aux_access_disabled && + !dm_helpers_dp_read_dpcd(link->ctx, + link, address, data, size)) { + return DC_ERROR_UNEXPECTED; + } return DC_OK; } @@ -39,10 +40,11 @@ enum dc_status core_link_write_dpcd( const uint8_t *data, uint32_t size) { - if (!dm_helpers_dp_write_dpcd(link->ctx, - link, - address, data, size)) - return DC_ERROR_UNEXPECTED; + if (!link->aux_access_disabled && + !dm_helpers_dp_write_dpcd(link->ctx, + link, address, data, size)) { + return DC_ERROR_UNEXPECTED; + } return DC_OK; } @@ -53,6 +55,9 @@ void dp_receiver_power_ctrl(struct dc_link *link, bool on) state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3; + if (link->sync_lt_in_progress) + return; + core_link_write_dpcd(link, DP_SET_POWER, &state, sizeof(state)); } @@ -160,6 +165,10 @@ bool edp_receiver_ready_T7(struct dc_link *link) break; udelay(25); //MAx T7 is 50ms } while (++tries < 300); + + if (link->local_sink->edid_caps.panel_patch.extra_t7_ms > 0) + udelay(link->local_sink->edid_caps.panel_patch.extra_t7_ms * 1000); + return result; } @@ -203,21 +212,21 @@ void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal) bool dp_set_hw_training_pattern( struct dc_link *link, - enum hw_dp_training_pattern pattern) + enum dc_dp_training_pattern pattern) { enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; switch (pattern) { - case HW_DP_TRAINING_PATTERN_1: + case DP_TRAINING_PATTERN_SEQUENCE_1: test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN1; break; - case HW_DP_TRAINING_PATTERN_2: + case DP_TRAINING_PATTERN_SEQUENCE_2: test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN2; break; - case HW_DP_TRAINING_PATTERN_3: + case DP_TRAINING_PATTERN_SEQUENCE_3: test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN3; break; - case HW_DP_TRAINING_PATTERN_4: + case DP_TRAINING_PATTERN_SEQUENCE_4: test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN4; break; default: @@ -239,46 +248,6 @@ void dp_set_hw_lane_settings( encoder->funcs->dp_set_lane_settings(encoder, link_settings); } -enum dp_panel_mode dp_get_panel_mode(struct dc_link *link) -{ - /* We need to explicitly check that connector - * is not DP. Some Travis_VGA get reported - * by video bios as DP. - */ - if (link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) { - - switch (link->dpcd_caps.branch_dev_id) { - case DP_BRANCH_DEVICE_ID_2: - if (strncmp( - link->dpcd_caps.branch_dev_name, - DP_VGA_LVDS_CONVERTER_ID_2, - sizeof( - link->dpcd_caps. - branch_dev_name)) == 0) { - return DP_PANEL_MODE_SPECIAL; - } - break; - case DP_BRANCH_DEVICE_ID_3: - if (strncmp(link->dpcd_caps.branch_dev_name, - DP_VGA_LVDS_CONVERTER_ID_3, - sizeof( - link->dpcd_caps. - branch_dev_name)) == 0) { - return DP_PANEL_MODE_SPECIAL; - } - break; - default: - break; - } - } - - if (link->dpcd_caps.panel_mode_edp) { - return DP_PANEL_MODE_EDP; - } - - return DP_PANEL_MODE_DEFAULT; -} - void dp_set_hw_test_pattern( struct dc_link *link, enum dp_test_pattern test_pattern, @@ -306,7 +275,7 @@ void dp_retrain_link_dp_test(struct dc_link *link, for (i = 0; i < MAX_PIPES; i++) { if (pipes[i].stream != NULL && - !pipes[i].top_pipe && + !pipes[i].top_pipe && !pipes[i].prev_odm_pipe && pipes[i].stream->link != NULL && pipes[i].stream_res.stream_enc != NULL) { udelay(100); @@ -320,7 +289,9 @@ void dp_retrain_link_dp_test(struct dc_link *link, dp_receiver_power_ctrl(link, false); - link->dc->hwss.disable_stream(&pipes[i], KEEP_ACQUIRED_RESOURCE); + link->dc->hwss.disable_stream(&pipes[i]); + if ((&pipes[i])->stream_res.audio && !link->dc->debug.az_endpoint_mute_only) + (&pipes[i])->stream_res.audio->funcs->az_disable((&pipes[i])->stream_res.audio); link->link_enc->funcs->disable_output( link->link_enc, @@ -373,10 +344,22 @@ void dp_retrain_link_dp_test(struct dc_link *link, static void dsc_optc_config_log(struct display_stream_compressor *dsc, struct dsc_optc_config *config) { - DC_LOG_DSC("Setting optc DSC config at DSC inst %d", dsc->inst); - DC_LOG_DSC("\n\tbytes_per_pixel %d\n\tis_pixel_format_444 %d\n\tslice_width %d", - config->bytes_per_pixel, - config->is_pixel_format_444, config->slice_width); + uint32_t precision = 1 << 28; + uint32_t bytes_per_pixel_int = config->bytes_per_pixel / precision; + uint32_t bytes_per_pixel_mod = config->bytes_per_pixel % precision; + uint64_t ll_bytes_per_pix_fraq = bytes_per_pixel_mod; + + /* 7 fractional digits decimal precision for bytes per pixel is enough because DSC + * bits per pixel precision is 1/16th of a pixel, which means bytes per pixel precision is + * 1/16/8 = 1/128 of a byte, or 0.0078125 decimal + */ + ll_bytes_per_pix_fraq *= 10000000; + ll_bytes_per_pix_fraq /= precision; + + DC_LOG_DSC("\tbytes_per_pixel 0x%08x (%d.%07d)", + config->bytes_per_pixel, bytes_per_pixel_int, (uint32_t)ll_bytes_per_pix_fraq); + DC_LOG_DSC("\tis_pixel_format_444 %d", config->is_pixel_format_444); + DC_LOG_DSC("\tslice_width %d", config->slice_width); } static bool dp_set_dsc_on_rx(struct pipe_ctx *pipe_ctx, bool enable) @@ -392,55 +375,62 @@ static bool dp_set_dsc_on_rx(struct pipe_ctx *pipe_ctx, bool enable) return result; } -/* This has to be done after DSC was enabled on RX first, i.e. after dp_enable_dsc_on_rx() had been called +/* 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 */ -static void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) +void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) { struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; struct dc *core_dc = pipe_ctx->stream->ctx->dc; struct dc_stream_state *stream = pipe_ctx->stream; - struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + struct pipe_ctx *odm_pipe; + int opp_cnt = 1; + + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; if (enable) { - /* TODO proper function */ struct dsc_config dsc_cfg; struct dsc_optc_config dsc_optc_cfg; enum optc_dsc_mode optc_dsc_mode; - uint8_t dsc_packed_pps[128]; /* Enable DSC hw block */ - dsc_cfg.pic_width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right; + dsc_cfg.pic_width = (stream->timing.h_addressable + 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.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->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg, &dsc_packed_pps[0]); - if (odm_pipe) { - struct display_stream_compressor *bot_dsc = odm_pipe->stream_res.dsc; - uint8_t dsc_packed_pps_odm[128]; - - dsc_cfg.pic_width /= 2; - ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % 2 == 0); - dsc_cfg.dc_dsc_cfg.num_slices_h /= 2; - dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg, &dsc_packed_pps_odm[0]); - bot_dsc->funcs->dsc_set_config(bot_dsc, &dsc_cfg, &dsc_optc_cfg, &dsc_packed_pps_odm[0]); - bot_dsc->funcs->dsc_enable(bot_dsc, odm_pipe->stream_res.opp->inst); - } + dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg); dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst); + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + struct display_stream_compressor *odm_dsc = odm_pipe->stream_res.dsc; + + odm_dsc->funcs->dsc_set_config(odm_dsc, &dsc_cfg, &dsc_optc_cfg); + 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; optc_dsc_mode = dsc_optc_cfg.is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; - dsc_optc_config_log(dsc, &dsc_optc_cfg); /* Enable DSC in encoder */ - if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment) && pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config) + if (dc_is_dp_signal(stream->signal) && !IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { + 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); 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, - &dsc_packed_pps[0]); + dsc_optc_cfg.slice_width); + + /* PPS SDP is set elsewhere because it has to be done after DIG FE is connected to DIG BE */ + } /* 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, @@ -452,15 +442,18 @@ static void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) OPTC_DSC_DISABLED, 0, 0); /* disable DSC in stream encoder */ - if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { + if (dc_is_dp_signal(stream->signal) && !IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config( pipe_ctx->stream_res.stream_enc, - OPTC_DSC_DISABLED, 0, 0, NULL); + 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); } /* disable DSC block */ pipe_ctx->stream_res.dsc->funcs->dsc_disable(pipe_ctx->stream_res.dsc); - if (odm_pipe) + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc); } } @@ -489,6 +482,47 @@ out: return result; } +bool dp_set_dsc_pps_sdp(struct pipe_ctx *pipe_ctx, bool enable) +{ + struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; + struct dc *core_dc = pipe_ctx->stream->ctx->dc; + struct dc_stream_state *stream = pipe_ctx->stream; + + if (!pipe_ctx->stream->timing.flags.DSC || !dsc) + return false; + + if (enable) { + struct dsc_config dsc_cfg; + uint8_t dsc_packed_pps[128]; + + /* Enable DSC hw block */ + dsc_cfg.pic_width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right; + 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.dc_dsc_cfg = stream->timing.dsc_cfg; + + DC_LOG_DSC(" "); + dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, &dsc_packed_pps[0]); + if (dc_is_dp_signal(stream->signal) && !IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { + DC_LOG_DSC("Setting stream encoder DSC PPS SDP for engine %d\n", (int)pipe_ctx->stream_res.stream_enc->id); + pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet( + pipe_ctx->stream_res.stream_enc, + true, + &dsc_packed_pps[0]); + } + } else { + /* disable DSC PPS in stream encoder */ + if (dc_is_dp_signal(stream->signal) && !IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { + pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet( + pipe_ctx->stream_res.stream_enc, false, NULL); + } + } + + return true; +} + + bool dp_update_dsc_config(struct pipe_ctx *pipe_ctx) { struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; @@ -499,8 +533,8 @@ bool dp_update_dsc_config(struct pipe_ctx *pipe_ctx) return false; dp_set_dsc_on_stream(pipe_ctx, true); + dp_set_dsc_pps_sdp(pipe_ctx, true); return true; } - #endif diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 2ceaab4fb5de..8f70295179ff 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -52,6 +52,9 @@ #if defined(CONFIG_DRM_AMD_DC_DCN2_0) #include "dcn20/dcn20_resource.h" #endif +#if defined(CONFIG_DRM_AMD_DC_DCN2_1) +#include "dcn21/dcn21_resource.h" +#endif #include "dce120/dce120_resource.h" #define DC_LOGGER_INIT(logger) @@ -101,6 +104,10 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) dc_version = DCN_VERSION_1_0; if (ASICREV_IS_RAVEN2(asic_id.hw_internal_rev)) dc_version = DCN_VERSION_1_01; +#if defined(CONFIG_DRM_AMD_DC_DCN2_1) + if (ASICREV_IS_RENOIR(asic_id.hw_internal_rev)) + dc_version = DCN_VERSION_2_1; +#endif break; #endif @@ -168,17 +175,20 @@ struct resource_pool *dc_create_resource_pool(struct dc *dc, res_pool = dcn20_create_resource_pool(init_data, dc); break; #endif +#if defined(CONFIG_DRM_AMD_DC_DCN2_1) + case DCN_VERSION_2_1: + res_pool = dcn21_create_resource_pool(init_data, dc); + break; +#endif default: break; } - if (res_pool != NULL) { - struct dc_firmware_info fw_info = { { 0 } }; - if (dc->ctx->dc_bios->funcs->get_firmware_info(dc->ctx->dc_bios, - &fw_info) == BP_RESULT_OK) { + if (res_pool != NULL) { + if (dc->ctx->dc_bios->fw_info_valid) { res_pool->ref_clocks.xtalin_clock_inKhz = - fw_info.pll_info.crystal_frequency; + dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; /* initialize with firmware data first, no all * ASIC have DCCG SW component. FPGA or * simulation need initialization of @@ -265,12 +275,10 @@ bool resource_construct( DC_ERR("DC: failed to create audio!\n"); return false; } - if (!aud->funcs->endpoint_valid(aud)) { aud->funcs->destroy(&aud); break; } - pool->audios[i] = aud; pool->audio_count++; } @@ -940,7 +948,14 @@ static void calculate_inits_and_adj_vp(struct pipe_ctx *pipe_ctx) data->inits.v_c_bot = dc_fixpt_add(data->inits.v_c, data->ratios.vert_c); } +static bool are_rect_integer_multiples(struct rect src, struct rect dest) +{ + if (dest.width >= src.width && dest.width % src.width == 0 && + dest.height >= src.height && dest.height % src.height == 0) + return true; + return false; +} bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) { const struct dc_plane_state *plane_state = pipe_ctx->plane_state; @@ -983,6 +998,15 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) if (pipe_ctx->plane_res.dpp != NULL) res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps( pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); + + if (res && + plane_state->scaling_quality.integer_scaling && + are_rect_integer_multiples(pipe_ctx->plane_res.scl_data.viewport, + pipe_ctx->plane_res.scl_data.recout)) { + pipe_ctx->plane_res.scl_data.taps.v_taps = 1; + pipe_ctx->plane_res.scl_data.taps.h_taps = 1; + } + if (!res) { /* Try 24 bpp linebuffer */ pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_24BPP; @@ -1103,25 +1127,21 @@ struct pipe_ctx *resource_get_head_pipe_for_stream( struct dc_stream_state *stream) { int i; + for (i = 0; i < MAX_PIPES; i++) { - if (res_ctx->pipe_ctx[i].stream == stream && - !res_ctx->pipe_ctx[i].top_pipe) { + if (res_ctx->pipe_ctx[i].stream == stream + && !res_ctx->pipe_ctx[i].top_pipe + && !res_ctx->pipe_ctx[i].prev_odm_pipe) return &res_ctx->pipe_ctx[i]; - break; - } } return NULL; } -static struct pipe_ctx *resource_get_tail_pipe_for_stream( +static struct pipe_ctx *resource_get_tail_pipe( struct resource_context *res_ctx, - struct dc_stream_state *stream) + struct pipe_ctx *head_pipe) { - struct pipe_ctx *head_pipe, *tail_pipe; - head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); - - if (!head_pipe) - return NULL; + struct pipe_ctx *tail_pipe; tail_pipe = head_pipe->bottom_pipe; @@ -1137,31 +1157,20 @@ static struct pipe_ctx *resource_get_tail_pipe_for_stream( * A free_pipe for a stream is defined here as a pipe * that has no surface attached yet */ -static struct pipe_ctx *acquire_free_pipe_for_stream( +static struct pipe_ctx *acquire_free_pipe_for_head( struct dc_state *context, const struct resource_pool *pool, - struct dc_stream_state *stream) + struct pipe_ctx *head_pipe) { int i; struct resource_context *res_ctx = &context->res_ctx; - struct pipe_ctx *head_pipe = NULL; - - /* Find head pipe, which has the back end set up*/ - - head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); - - if (!head_pipe) { - ASSERT(0); - return NULL; - } - if (!head_pipe->plane_state) return head_pipe; /* Re-use pipe already acquired for this stream if available*/ for (i = pool->pipe_count - 1; i >= 0; i--) { - if (res_ctx->pipe_ctx[i].stream == stream && + if (res_ctx->pipe_ctx[i].stream == head_pipe->stream && !res_ctx->pipe_ctx[i].plane_state) { return &res_ctx->pipe_ctx[i]; } @@ -1175,8 +1184,7 @@ static struct pipe_ctx *acquire_free_pipe_for_stream( if (!pool->funcs->acquire_idle_pipe_for_layer) return NULL; - return pool->funcs->acquire_idle_pipe_for_layer(context, pool, stream); - + return pool->funcs->acquire_idle_pipe_for_layer(context, pool, head_pipe->stream); } #if defined(CONFIG_DRM_AMD_DC_DCN1_0) @@ -1190,7 +1198,7 @@ static int acquire_first_split_pipe( for (i = 0; i < pool->pipe_count; i++) { struct pipe_ctx *split_pipe = &res_ctx->pipe_ctx[i]; - if (split_pipe->top_pipe && !dc_res_is_odm_head_pipe(split_pipe) && + if (split_pipe->top_pipe && split_pipe->top_pipe->plane_state == split_pipe->plane_state) { split_pipe->top_pipe->bottom_pipe = split_pipe->bottom_pipe; if (split_pipe->bottom_pipe) @@ -1251,39 +1259,41 @@ bool dc_add_plane_to_context( return false; } - tail_pipe = resource_get_tail_pipe_for_stream(&context->res_ctx, stream); - ASSERT(tail_pipe); + /* retain new surface, but only once per stream */ + dc_plane_state_retain(plane_state); - free_pipe = acquire_free_pipe_for_stream(context, pool, stream); + while (head_pipe) { + tail_pipe = resource_get_tail_pipe(&context->res_ctx, head_pipe); + ASSERT(tail_pipe); -#if defined(CONFIG_DRM_AMD_DC_DCN1_0) - if (!free_pipe) { - int pipe_idx = acquire_first_split_pipe(&context->res_ctx, pool, stream); - if (pipe_idx >= 0) - free_pipe = &context->res_ctx.pipe_ctx[pipe_idx]; - } -#endif - if (!free_pipe) - return false; + free_pipe = acquire_free_pipe_for_head(context, pool, head_pipe); - /* retain new surfaces */ - dc_plane_state_retain(plane_state); - free_pipe->plane_state = plane_state; - - if (head_pipe != free_pipe) { - free_pipe->stream_res.tg = tail_pipe->stream_res.tg; - free_pipe->stream_res.abm = tail_pipe->stream_res.abm; - free_pipe->stream_res.opp = tail_pipe->stream_res.opp; - free_pipe->stream_res.stream_enc = tail_pipe->stream_res.stream_enc; - free_pipe->stream_res.audio = tail_pipe->stream_res.audio; - free_pipe->clock_source = tail_pipe->clock_source; - free_pipe->top_pipe = tail_pipe; - tail_pipe->bottom_pipe = free_pipe; - } else if (free_pipe->bottom_pipe && free_pipe->bottom_pipe->plane_state == NULL) { - ASSERT(free_pipe->bottom_pipe->stream_res.opp != free_pipe->stream_res.opp); - free_pipe->bottom_pipe->plane_state = plane_state; - } + #if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (!free_pipe) { + int pipe_idx = acquire_first_split_pipe(&context->res_ctx, pool, stream); + if (pipe_idx >= 0) + free_pipe = &context->res_ctx.pipe_ctx[pipe_idx]; + } + #endif + if (!free_pipe) { + dc_plane_state_release(plane_state); + return false; + } + free_pipe->plane_state = plane_state; + + if (head_pipe != free_pipe) { + free_pipe->stream_res.tg = tail_pipe->stream_res.tg; + free_pipe->stream_res.abm = tail_pipe->stream_res.abm; + free_pipe->stream_res.opp = tail_pipe->stream_res.opp; + free_pipe->stream_res.stream_enc = tail_pipe->stream_res.stream_enc; + free_pipe->stream_res.audio = tail_pipe->stream_res.audio; + free_pipe->clock_source = tail_pipe->clock_source; + free_pipe->top_pipe = tail_pipe; + tail_pipe->bottom_pipe = free_pipe; + } + head_pipe = head_pipe->next_odm_pipe; + } /* assign new surfaces*/ stream_status->plane_states[stream_status->plane_count] = plane_state; @@ -1292,35 +1302,6 @@ bool dc_add_plane_to_context( return true; } -struct pipe_ctx *dc_res_get_odm_bottom_pipe(struct pipe_ctx *pipe_ctx) -{ - struct pipe_ctx *bottom_pipe = pipe_ctx->bottom_pipe; - - /* ODM should only be updated once per otg */ - if (pipe_ctx->top_pipe) - return NULL; - - while (bottom_pipe) { - if (bottom_pipe->stream_res.opp != pipe_ctx->stream_res.opp) - break; - bottom_pipe = bottom_pipe->bottom_pipe; - } - - return bottom_pipe; -} - -bool dc_res_is_odm_head_pipe(struct pipe_ctx *pipe_ctx) -{ - struct pipe_ctx *top_pipe = pipe_ctx->top_pipe; - - if (!top_pipe) - return false; - if (top_pipe && top_pipe->stream_res.opp == pipe_ctx->stream_res.opp) - return false; - - return true; -} - bool dc_remove_plane_from_context( const struct dc *dc, struct dc_stream_state *stream, @@ -1347,12 +1328,6 @@ bool dc_remove_plane_from_context( struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; if (pipe_ctx->plane_state == plane_state) { - if (dc_res_is_odm_head_pipe(pipe_ctx)) { - pipe_ctx->plane_state = NULL; - pipe_ctx->bottom_pipe = NULL; - continue; - } - if (pipe_ctx->top_pipe) pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe; @@ -1367,13 +1342,10 @@ bool dc_remove_plane_from_context( * For head pipe detach surfaces from pipe for tail * pipe just zero it out */ - if (!pipe_ctx->top_pipe) { + if (!pipe_ctx->top_pipe) pipe_ctx->plane_state = NULL; - if (!dc_res_get_odm_bottom_pipe(pipe_ctx)) - pipe_ctx->bottom_pipe = NULL; - } else { + else memset(pipe_ctx, 0, sizeof(*pipe_ctx)); - } } } @@ -1659,24 +1631,25 @@ static struct audio *find_first_free_audio( const struct resource_pool *pool, enum engine_id id) { - int i; - for (i = 0; i < pool->audio_count; i++) { + int i, available_audio_count; + + available_audio_count = pool->audio_count; + + for (i = 0; i < available_audio_count; i++) { if ((res_ctx->is_audio_acquired[i] == false) && (res_ctx->is_stream_enc_acquired[i] == true)) { /*we have enough audio endpoint, find the matching inst*/ if (id != i) continue; - return pool->audios[i]; } } - /* use engine id to find free audio */ - if ((id < pool->audio_count) && (res_ctx->is_audio_acquired[id] == false)) { + /* use engine id to find free audio */ + if ((id < available_audio_count) && (res_ctx->is_audio_acquired[id] == false)) { return pool->audios[id]; } - /*not found the matching one, first come first serve*/ - for (i = 0; i < pool->audio_count; i++) { + for (i = 0; i < available_audio_count; i++) { if (res_ctx->is_audio_acquired[i] == false) { return pool->audios[i]; } @@ -1736,50 +1709,45 @@ enum dc_status dc_remove_stream_from_ctx( { int i; struct dc_context *dc_ctx = dc->ctx; - struct pipe_ctx *del_pipe = NULL; + struct pipe_ctx *del_pipe = resource_get_head_pipe_for_stream(&new_ctx->res_ctx, stream); + struct pipe_ctx *odm_pipe; - /* Release primary pipe */ - for (i = 0; i < MAX_PIPES; i++) { - if (new_ctx->res_ctx.pipe_ctx[i].stream == stream && - !new_ctx->res_ctx.pipe_ctx[i].top_pipe) { - struct pipe_ctx *odm_pipe = - dc_res_get_odm_bottom_pipe(&new_ctx->res_ctx.pipe_ctx[i]); + if (!del_pipe) { + DC_ERROR("Pipe not found for stream %p !\n", stream); + return DC_ERROR_UNEXPECTED; + } - del_pipe = &new_ctx->res_ctx.pipe_ctx[i]; + odm_pipe = del_pipe->next_odm_pipe; - ASSERT(del_pipe->stream_res.stream_enc); - update_stream_engine_usage( - &new_ctx->res_ctx, - dc->res_pool, - del_pipe->stream_res.stream_enc, - false); + /* Release primary pipe */ + ASSERT(del_pipe->stream_res.stream_enc); + update_stream_engine_usage( + &new_ctx->res_ctx, + dc->res_pool, + del_pipe->stream_res.stream_enc, + false); - if (del_pipe->stream_res.audio) - update_audio_usage( - &new_ctx->res_ctx, - dc->res_pool, - del_pipe->stream_res.audio, - false); + if (del_pipe->stream_res.audio) + update_audio_usage( + &new_ctx->res_ctx, + dc->res_pool, + del_pipe->stream_res.audio, + false); - resource_unreference_clock_source(&new_ctx->res_ctx, - dc->res_pool, - del_pipe->clock_source); + resource_unreference_clock_source(&new_ctx->res_ctx, + dc->res_pool, + del_pipe->clock_source); - if (dc->res_pool->funcs->remove_stream_from_ctx) - dc->res_pool->funcs->remove_stream_from_ctx(dc, new_ctx, stream); + if (dc->res_pool->funcs->remove_stream_from_ctx) + dc->res_pool->funcs->remove_stream_from_ctx(dc, new_ctx, stream); - memset(del_pipe, 0, sizeof(*del_pipe)); - if (odm_pipe) - memset(odm_pipe, 0, sizeof(*odm_pipe)); + while (odm_pipe) { + struct pipe_ctx *next_odm_pipe = odm_pipe->next_odm_pipe; - break; - } - } - - if (!del_pipe) { - DC_ERROR("Pipe not found for stream %p !\n", stream); - return DC_ERROR_UNEXPECTED; + memset(odm_pipe, 0, sizeof(*odm_pipe)); + odm_pipe = next_odm_pipe; } + memset(del_pipe, 0, sizeof(*del_pipe)); for (i = 0; i < new_ctx->stream_count; i++) if (new_ctx->streams[i] == stream) @@ -1880,7 +1848,7 @@ static int acquire_resource_from_hw_enabled_state( struct dc_stream_state *stream) { struct dc_link *link = stream->link; - unsigned int inst; + unsigned int inst, tg_inst; /* Check for enabled DIG to identify enabled display */ if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) @@ -1892,28 +1860,37 @@ static int acquire_resource_from_hw_enabled_state( * current implementation always map 1-to-1, so this code makes * the same assumption and doesn't check OTG source. */ - inst = link->link_enc->funcs->get_dig_frontend(link->link_enc) - 1; + inst = link->link_enc->funcs->get_dig_frontend(link->link_enc); /* Instance should be within the range of the pool */ if (inst >= pool->pipe_count) return -1; - if (!res_ctx->pipe_ctx[inst].stream) { - struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[inst]; + if (inst >= pool->stream_enc_count) + return -1; + + tg_inst = pool->stream_enc[inst]->funcs->dig_source_otg(pool->stream_enc[inst]); - pipe_ctx->stream_res.tg = pool->timing_generators[inst]; - pipe_ctx->plane_res.mi = pool->mis[inst]; - pipe_ctx->plane_res.hubp = pool->hubps[inst]; - pipe_ctx->plane_res.ipp = pool->ipps[inst]; - pipe_ctx->plane_res.xfm = pool->transforms[inst]; - pipe_ctx->plane_res.dpp = pool->dpps[inst]; - pipe_ctx->stream_res.opp = pool->opps[inst]; - if (pool->dpps[inst]) - pipe_ctx->plane_res.mpcc_inst = pool->dpps[inst]->inst; - pipe_ctx->pipe_idx = inst; + if (tg_inst >= pool->timing_generator_count) + return false; + + if (!res_ctx->pipe_ctx[tg_inst].stream) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[tg_inst]; + + pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst]; + pipe_ctx->plane_res.mi = pool->mis[tg_inst]; + pipe_ctx->plane_res.hubp = pool->hubps[tg_inst]; + pipe_ctx->plane_res.ipp = pool->ipps[tg_inst]; + pipe_ctx->plane_res.xfm = pool->transforms[tg_inst]; + pipe_ctx->plane_res.dpp = pool->dpps[tg_inst]; + pipe_ctx->stream_res.opp = pool->opps[tg_inst]; + + if (pool->dpps[tg_inst]) + pipe_ctx->plane_res.mpcc_inst = pool->dpps[tg_inst]->inst; + pipe_ctx->pipe_idx = tg_inst; pipe_ctx->stream = stream; - return inst; + return tg_inst; } return -1; @@ -2475,6 +2452,12 @@ void dc_resource_state_copy_construct( if (cur_pipe->bottom_pipe) cur_pipe->bottom_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->bottom_pipe->pipe_idx]; + + if (cur_pipe->next_odm_pipe) + cur_pipe->next_odm_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->next_odm_pipe->pipe_idx]; + + if (cur_pipe->prev_odm_pipe) + cur_pipe->prev_odm_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->prev_odm_pipe->pipe_idx]; } for (i = 0; i < dst_ctx->stream_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index 352862370390..bf1d7bb90e0f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -566,6 +566,7 @@ bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream, return ret; } + #if defined(CONFIG_DRM_AMD_DC_DCN2_0) bool dc_stream_dmdata_status_done(struct dc *dc, struct dc_stream_state *stream) { @@ -597,6 +598,14 @@ bool dc_stream_set_dynamic_metadata(struct dc *dc, struct hubp *hubp; int i; + /* Dynamic metadata is only supported on HDMI or DP */ + if (!dc_is_hdmi_signal(stream->signal) && !dc_is_dp_signal(stream->signal)) + return false; + + /* Check hardware support */ + if (!dc->hwss.program_dmdata_engine) + return false; + for (i = 0; i < MAX_PIPES; i++) { pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; if (pipe_ctx->stream == stream) @@ -612,23 +621,7 @@ bool dc_stream_set_dynamic_metadata(struct dc *dc, pipe_ctx->stream->dmdata_address = attr->address; - if (pipe_ctx->stream_res.stream_enc && - pipe_ctx->stream_res.stream_enc->funcs->set_dynamic_metadata != NULL) { - if (pipe_ctx->stream->dmdata_address.quad_part != 0) { - /* if using dynamic meta, don't set up generic infopackets */ - pipe_ctx->stream_res.encoder_info_frame.hdrsmd.valid = false; - pipe_ctx->stream_res.stream_enc->funcs->set_dynamic_metadata( - pipe_ctx->stream_res.stream_enc, - true, pipe_ctx->plane_res.hubp->inst, - dc_is_dp_signal(pipe_ctx->stream->signal) ? - dmdata_dp : dmdata_hdmi); - } else - pipe_ctx->stream_res.stream_enc->funcs->set_dynamic_metadata( - pipe_ctx->stream_res.stream_enc, - false, pipe_ctx->plane_res.hubp->inst, - dc_is_dp_signal(pipe_ctx->stream->signal) ? - dmdata_dp : dmdata_hdmi); - } + dc->hwss.program_dmdata_engine(pipe_ctx); if (hubp->funcs->dmdata_set_attributes != NULL && pipe_ctx->stream->dmdata_address.quad_part != 0) { diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c index f40e4fd52fa2..b9d6a5bd8522 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c @@ -60,7 +60,6 @@ static void construct(struct dc_context *ctx, struct dc_plane_state *plane_state plane_state->lut3d_func = dc_create_3dlut_func(); if (plane_state->lut3d_func != NULL) { plane_state->lut3d_func->ctx = ctx; - plane_state->lut3d_func->initialized = false; } plane_state->blend_tf = dc_create_transfer_func(); if (plane_state->blend_tf != NULL) { @@ -279,7 +278,7 @@ struct dc_3dlut *dc_create_3dlut_func(void) goto alloc_fail; kref_init(&lut->refcount); - lut->initialized = false; + lut->state.raw = 0; return lut; |