diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/core/dc_stream.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_stream.c | 429 |
1 files changed, 380 insertions, 49 deletions
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 51a970fcb5d0..9a406d74c0dd 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -35,6 +35,10 @@ #include "dc_stream_priv.h" #define DC_LOGGER dc->ctx->logger +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(x, y) ((x > y) ? x : y) +#endif /******************************************************************************* * Private functions @@ -116,12 +120,7 @@ bool dc_stream_construct(struct dc_stream_state *stream, update_stream_signal(stream, dc_sink_data); - stream->out_transfer_func = dc_create_transfer_func(); - if (stream->out_transfer_func == NULL) { - dc_sink_release(dc_sink_data); - return false; - } - stream->out_transfer_func->type = TF_TYPE_BYPASS; + stream->out_transfer_func.type = TF_TYPE_BYPASS; dc_stream_assign_stream_id(stream); @@ -131,10 +130,6 @@ bool dc_stream_construct(struct dc_stream_state *stream, void dc_stream_destruct(struct dc_stream_state *stream) { dc_sink_release(stream->sink); - if (stream->out_transfer_func != NULL) { - dc_transfer_func_release(stream->out_transfer_func); - stream->out_transfer_func = NULL; - } } void dc_stream_assign_stream_id(struct dc_stream_state *stream) @@ -201,9 +196,6 @@ struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream) if (new_stream->sink) dc_sink_retain(new_stream->sink); - if (new_stream->out_transfer_func) - dc_transfer_func_retain(new_stream->out_transfer_func); - dc_stream_assign_stream_id(new_stream); /* If using dynamic encoder assignment, wait till stream committed to assign encoder. */ @@ -229,10 +221,9 @@ struct dc_stream_status *dc_stream_get_status( return dc_state_get_stream_status(dc->current_state, stream); } -static void program_cursor_attributes( +void program_cursor_attributes( struct dc *dc, - struct dc_stream_state *stream, - const struct dc_cursor_attributes *attributes) + struct dc_stream_state *stream) { int i; struct resource_context *res_ctx; @@ -278,7 +269,6 @@ bool dc_stream_set_cursor_attributes( const struct dc_cursor_attributes *attributes) { struct dc *dc; - bool reset_idle_optimizations = false; if (NULL == stream) { dm_error("DC: dc_stream is NULL!\n"); @@ -309,26 +299,41 @@ bool dc_stream_set_cursor_attributes( stream->cursor_attributes = *attributes; - dc_z10_restore(dc); - /* disable idle optimizations while updating cursor */ - if (dc->idle_optimizations_allowed) { - dc_allow_idle_optimizations(dc, false); - reset_idle_optimizations = true; - } + return true; +} - program_cursor_attributes(dc, stream, attributes); +bool dc_stream_program_cursor_attributes( + struct dc_stream_state *stream, + const struct dc_cursor_attributes *attributes) +{ + struct dc *dc; + bool reset_idle_optimizations = false; - /* re-enable idle optimizations if necessary */ - if (reset_idle_optimizations) - dc_allow_idle_optimizations(dc, true); + dc = stream ? stream->ctx->dc : NULL; - return true; + if (dc_stream_set_cursor_attributes(stream, attributes)) { + dc_z10_restore(dc); + /* disable idle optimizations while updating cursor */ + if (dc->idle_optimizations_allowed) { + dc_allow_idle_optimizations(dc, false); + reset_idle_optimizations = true; + } + + program_cursor_attributes(dc, stream); + + /* re-enable idle optimizations if necessary */ + if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle) + dc_allow_idle_optimizations(dc, true); + + return true; + } + + return false; } -static void program_cursor_position( +void program_cursor_position( struct dc *dc, - struct dc_stream_state *stream, - const struct dc_cursor_position *position) + struct dc_stream_state *stream) { int i; struct resource_context *res_ctx; @@ -367,9 +372,6 @@ bool dc_stream_set_cursor_position( struct dc_stream_state *stream, const struct dc_cursor_position *position) { - struct dc *dc; - bool reset_idle_optimizations = false; - if (NULL == stream) { dm_error("DC: dc_stream is NULL!\n"); return false; @@ -380,24 +382,75 @@ bool dc_stream_set_cursor_position( return false; } + stream->cursor_position = *position; + + + return true; +} + +bool dc_stream_program_cursor_position( + struct dc_stream_state *stream, + const struct dc_cursor_position *position) +{ + struct dc *dc; + bool reset_idle_optimizations = false; + const struct dc_cursor_position *old_position; + + if (!stream) + return false; + + old_position = &stream->cursor_position; dc = stream->ctx->dc; - dc_z10_restore(dc); - /* disable idle optimizations if enabling cursor */ - if (dc->idle_optimizations_allowed && (!stream->cursor_position.enable || dc->debug.exit_idle_opt_for_cursor_updates) - && position->enable) { - dc_allow_idle_optimizations(dc, false); - reset_idle_optimizations = true; - } + if (dc_stream_set_cursor_position(stream, position)) { + dc_z10_restore(dc); - stream->cursor_position = *position; + /* disable idle optimizations if enabling cursor */ + if (dc->idle_optimizations_allowed && + (!old_position->enable || dc->debug.exit_idle_opt_for_cursor_updates) && + position->enable) { + dc_allow_idle_optimizations(dc, false); + reset_idle_optimizations = true; + } - program_cursor_position(dc, stream, position); - /* re-enable idle optimizations if necessary */ - if (reset_idle_optimizations) - dc_allow_idle_optimizations(dc, true); + program_cursor_position(dc, stream); + /* re-enable idle optimizations if necessary */ + if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle) + dc_allow_idle_optimizations(dc, true); + + /* apply/update visual confirm */ + if (dc->debug.visual_confirm == VISUAL_CONFIRM_HW_CURSOR) { + /* update software state */ + uint32_t color_value = MAX_TG_COLOR_VALUE; + int i; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + + /* adjust visual confirm color for all pipes with current stream */ + if (stream == pipe_ctx->stream) { + if (stream->cursor_position.enable) { + pipe_ctx->visual_confirm_color.color_r_cr = color_value; + pipe_ctx->visual_confirm_color.color_g_y = 0; + pipe_ctx->visual_confirm_color.color_b_cb = 0; + } else { + pipe_ctx->visual_confirm_color.color_r_cr = 0; + pipe_ctx->visual_confirm_color.color_g_y = 0; + pipe_ctx->visual_confirm_color.color_b_cb = color_value; + } + + /* programming hardware */ + if (pipe_ctx->plane_state) + dc->hwss.update_visual_confirm_color(dc, pipe_ctx, + pipe_ctx->plane_res.hubp->mpcc_id); + } + } + } - return true; + return true; + } + + return false; } bool dc_stream_add_writeback(struct dc *dc, @@ -425,7 +478,7 @@ bool dc_stream_add_writeback(struct dc *dc, dc_exit_ips_for_hw_access(dc); - wb_info->dwb_params.out_transfer_func = stream->out_transfer_func; + wb_info->dwb_params.out_transfer_func = &stream->out_transfer_func; dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; dwb->dwb_is_drc = false; @@ -507,7 +560,7 @@ bool dc_stream_remove_writeback(struct dc *dc, struct dc_stream_state *stream, uint32_t dwb_pipe_inst) { - int i = 0, j = 0; + unsigned int i, j; if (stream == NULL) { dm_error("DC: dc_stream is NULL!\n"); return false; @@ -793,3 +846,281 @@ void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream) } } +/* + * Finds the greatest index in refresh_rate_hz that contains a value <= refresh + */ +static int dc_stream_get_nearest_smallest_index(struct dc_stream_state *stream, int refresh) +{ + for (int i = 0; i < (LUMINANCE_DATA_TABLE_SIZE - 1); ++i) { + if ((stream->lumin_data.refresh_rate_hz[i] <= refresh) && (refresh < stream->lumin_data.refresh_rate_hz[i + 1])) { + return i; + } + } + return 9; +} + +/* + * Finds a corresponding brightness for a given refresh rate between 2 given indices, where index1 < index2 + */ +static int dc_stream_get_brightness_millinits_linear_interpolation (struct dc_stream_state *stream, + int index1, + int index2, + int refresh_hz) +{ + long long slope = 0; + if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) { + slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) / + (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]); + } + + int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2]; + + return (y_intercept + refresh_hz * slope); +} + +/* + * Finds a corresponding refresh rate for a given brightness between 2 given indices, where index1 < index2 + */ +static int dc_stream_get_refresh_hz_linear_interpolation (struct dc_stream_state *stream, + int index1, + int index2, + int brightness_millinits) +{ + long long slope = 1; + if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) { + slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) / + (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]); + } + + int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2]; + + return ((int)div64_s64((brightness_millinits - y_intercept), slope)); +} + +/* + * Finds the current brightness in millinits given a refresh rate + */ +static int dc_stream_get_brightness_millinits_from_refresh (struct dc_stream_state *stream, int refresh_hz) +{ + int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, refresh_hz); + int nearest_smallest_value = stream->lumin_data.refresh_rate_hz[nearest_smallest_index]; + + if (nearest_smallest_value == refresh_hz) + return stream->lumin_data.luminance_millinits[nearest_smallest_index]; + + if (nearest_smallest_index >= 9) + return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index - 1, nearest_smallest_index, refresh_hz); + + if (nearest_smallest_value == stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1]) + return stream->lumin_data.luminance_millinits[nearest_smallest_index]; + + return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index, nearest_smallest_index + 1, refresh_hz); +} + +/* + * Finds the lowest/highest refresh rate (depending on search_for_max_increase) + * that can be achieved from starting_refresh_hz while staying + * within flicker criteria + */ +static int dc_stream_calculate_flickerless_refresh_rate(struct dc_stream_state *stream, + int current_brightness, + int starting_refresh_hz, + bool is_gaming, + bool search_for_max_increase) +{ + int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, starting_refresh_hz); + + int flicker_criteria_millinits = is_gaming ? + stream->lumin_data.flicker_criteria_milli_nits_GAMING : + stream->lumin_data.flicker_criteria_milli_nits_STATIC; + + int safe_upper_bound = current_brightness + flicker_criteria_millinits; + int safe_lower_bound = current_brightness - flicker_criteria_millinits; + int lumin_millinits_temp = 0; + + int offset = -1; + if (search_for_max_increase) { + offset = 1; + } + + /* + * Increments up or down by 1 depending on search_for_max_increase + */ + for (int i = nearest_smallest_index; (i > 0 && !search_for_max_increase) || (i < (LUMINANCE_DATA_TABLE_SIZE - 1) && search_for_max_increase); i += offset) { + + lumin_millinits_temp = stream->lumin_data.luminance_millinits[i + offset]; + + if ((lumin_millinits_temp >= safe_upper_bound) || (lumin_millinits_temp <= safe_lower_bound)) { + + if (stream->lumin_data.refresh_rate_hz[i + offset] == stream->lumin_data.refresh_rate_hz[i]) + return stream->lumin_data.refresh_rate_hz[i]; + + int target_brightness = (stream->lumin_data.luminance_millinits[i + offset] >= (current_brightness + flicker_criteria_millinits)) ? + current_brightness + flicker_criteria_millinits : + current_brightness - flicker_criteria_millinits; + + int refresh = 0; + + /* + * Need the second input to be < third input for dc_stream_get_refresh_hz_linear_interpolation + */ + if (search_for_max_increase) + refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i, i + offset, target_brightness); + else + refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i + offset, i, target_brightness); + + if (refresh == stream->lumin_data.refresh_rate_hz[i + offset]) + return stream->lumin_data.refresh_rate_hz[i + offset]; + + return refresh; + } + } + + if (search_for_max_increase) + return (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, stream->timing.v_total*(long long)stream->timing.h_total); + else + return stream->lumin_data.refresh_rate_hz[0]; +} + +/* + * Gets the max delta luminance within a specified refresh range + */ +static int dc_stream_get_max_delta_lumin_millinits(struct dc_stream_state *stream, int hz1, int hz2, bool isGaming) +{ + int lower_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz1); + int higher_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz2); + + int min = lower_refresh_brightness; + int max = higher_refresh_brightness; + + /* + * Static screen, therefore no need to scan through array + */ + if (!isGaming) { + if (lower_refresh_brightness >= higher_refresh_brightness) { + return lower_refresh_brightness - higher_refresh_brightness; + } + return higher_refresh_brightness - lower_refresh_brightness; + } + + min = MIN(lower_refresh_brightness, higher_refresh_brightness); + max = MAX(lower_refresh_brightness, higher_refresh_brightness); + + int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, hz1); + + for (; nearest_smallest_index < (LUMINANCE_DATA_TABLE_SIZE - 1) && + stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1] <= hz2 ; nearest_smallest_index++) { + min = MIN(min, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]); + max = MAX(max, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]); + } + + return (max - min); +} + +/* + * Determines the max flickerless instant vtotal delta for a stream. + * Determines vtotal increase/decrease based on the bool "increase" + */ +static unsigned int dc_stream_get_max_flickerless_instant_vtotal_delta(struct dc_stream_state *stream, bool is_gaming, bool increase) +{ + if (stream->timing.v_total * stream->timing.h_total == 0) + return 0; + + int current_refresh_hz = (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, stream->timing.v_total*(long long)stream->timing.h_total); + + int safe_refresh_hz = dc_stream_calculate_flickerless_refresh_rate(stream, + dc_stream_get_brightness_millinits_from_refresh(stream, current_refresh_hz), + current_refresh_hz, + is_gaming, + increase); + + int safe_refresh_v_total = (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, safe_refresh_hz*(long long)stream->timing.h_total); + + if (increase) + return (((int) stream->timing.v_total - safe_refresh_v_total) >= 0) ? (stream->timing.v_total - safe_refresh_v_total) : 0; + + return ((safe_refresh_v_total - (int) stream->timing.v_total) >= 0) ? (safe_refresh_v_total - stream->timing.v_total) : 0; +} + +/* + * Finds the highest refresh rate that can be achieved + * from starting_refresh_hz while staying within flicker criteria + */ +int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming) +{ + if (!stream->lumin_data.is_valid) + return 0; + + int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz); + + return dc_stream_calculate_flickerless_refresh_rate(stream, + current_brightness, + starting_refresh_hz, + is_gaming, + true); +} + +/* + * Finds the lowest refresh rate that can be achieved + * from starting_refresh_hz while staying within flicker criteria + */ +int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming) +{ + if (!stream->lumin_data.is_valid) + return 0; + + int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz); + + return dc_stream_calculate_flickerless_refresh_rate(stream, + current_brightness, + starting_refresh_hz, + is_gaming, + false); +} + +/* + * Determines if there will be a flicker when moving between 2 refresh rates + */ +bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream, int hz1, int hz2, bool is_gaming) +{ + + /* + * Assume that we wont flicker if there is invalid data + */ + if (!stream->lumin_data.is_valid) + return false; + + int dl = dc_stream_get_max_delta_lumin_millinits(stream, hz1, hz2, is_gaming); + + int flicker_criteria_millinits = (is_gaming) ? + stream->lumin_data.flicker_criteria_milli_nits_GAMING : + stream->lumin_data.flicker_criteria_milli_nits_STATIC; + + return (dl <= flicker_criteria_millinits); +} + +/* + * Determines the max instant vtotal delta increase that can be applied without + * flickering for a given stream + */ +unsigned int dc_stream_get_max_flickerless_instant_vtotal_decrease(struct dc_stream_state *stream, + bool is_gaming) +{ + if (!stream->lumin_data.is_valid) + return 0; + + return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, true); +} + +/* + * Determines the max instant vtotal delta decrease that can be applied without + * flickering for a given stream + */ +unsigned int dc_stream_get_max_flickerless_instant_vtotal_increase(struct dc_stream_state *stream, + bool is_gaming) +{ + if (!stream->lumin_data.is_valid) + return 0; + + return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, false); +} |