// SPDX-License-Identifier: MIT /* * Copyright 2022 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD * */ #include "dcn32_fpu.h" #include "dcn32/dcn32_resource.h" #include "dcn20/dcn20_resource.h" #include "display_mode_vba_util_32.h" #include "dml/dcn32/display_mode_vba_32.h" // We need this includes for WATERMARKS_* defines #include "clk_mgr/dcn32/dcn32_smu13_driver_if.h" #include "dcn30/dcn30_resource.h" #include "link.h" #include "dc_state_priv.h" #define DC_LOGGER_INIT(logger) … static const struct subvp_high_refresh_list subvp_high_refresh_list = …; static const struct subvp_active_margin_list subvp_active_margin_list = …; struct _vcs_dpi_ip_params_st dcn3_2_ip = …; struct _vcs_dpi_soc_bounding_box_st dcn3_2_soc = …; static bool dcn32_apply_merge_split_flags_helper(struct dc *dc, struct dc_state *context, bool *repopulate_pipes, int *split, bool *merge); void dcn32_build_wm_range_table_fpu(struct clk_mgr_internal *clk_mgr) { … } /* * Finds dummy_latency_index when MCLK switching using firmware based * vblank stretch is enabled. This function will iterate through the * table of dummy pstate latencies until the lowest value that allows * dm_allow_self_refresh_and_mclk_switch to happen is found */ int dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int pipe_cnt, int vlevel) { … } /** * dcn32_helper_populate_phantom_dlg_params - Get DLG params for phantom pipes * and populate pipe_ctx with those params. * @dc: [in] current dc state * @context: [in] new dc state * @pipes: [in] DML pipe params array * @pipe_cnt: [in] DML pipe count * * This function must be called AFTER the phantom pipes are added to context * and run through DML (so that the DLG params for the phantom pipes can be * populated), and BEFORE we program the timing for the phantom pipes. */ void dcn32_helper_populate_phantom_dlg_params(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int pipe_cnt) { … } static float calculate_net_bw_in_kbytes_sec(struct _vcs_dpi_voltage_scaling_st *entry) { … } static void get_optimal_ntuple(struct _vcs_dpi_voltage_scaling_st *entry) { … } static void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries, struct _vcs_dpi_voltage_scaling_st *entry) { … } /** * dcn32_set_phantom_stream_timing - Set timing params for the phantom stream * @dc: current dc state * @context: new dc state * @ref_pipe: Main pipe for the phantom stream * @phantom_stream: target phantom stream state * @pipes: DML pipe params * @pipe_cnt: number of DML pipes * @dc_pipe_idx: DC pipe index for the main pipe (i.e. ref_pipe) * * Set timing params of the phantom stream based on calculated output from DML. * This function first gets the DML pipe index using the DC pipe index, then * calls into DML (get_subviewport_lines_needed_in_mall) to get the number of * lines required for SubVP MCLK switching and assigns to the phantom stream * accordingly. * * - The number of SubVP lines calculated in DML does not take into account * FW processing delays and required pstate allow width, so we must include * that separately. * * - Set phantom backporch = vstartup of main pipe */ void dcn32_set_phantom_stream_timing(struct dc *dc, struct dc_state *context, struct pipe_ctx *ref_pipe, struct dc_stream_state *phantom_stream, display_e2e_pipe_params_st *pipes, unsigned int pipe_cnt, unsigned int dc_pipe_idx) { … } /** * dcn32_get_num_free_pipes - Calculate number of free pipes * @dc: current dc state * @context: new dc state * * This function assumes that a "used" pipe is a pipe that has * both a stream and a plane assigned to it. * * Return: Number of free pipes available in the context */ static unsigned int dcn32_get_num_free_pipes(struct dc *dc, struct dc_state *context) { … } /** * dcn32_assign_subvp_pipe - Function to decide which pipe will use Sub-VP. * @dc: current dc state * @context: new dc state * @index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned * * We enter this function if we are Sub-VP capable (i.e. enough pipes available) * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if * we are forcing SubVP P-State switching on the current config. * * The number of pipes used for the chosen surface must be less than or equal to the * number of free pipes available. * * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK). * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't * support MCLK switching naturally [i.e. ACTIVE or VBLANK]). * * Return: True if a valid pipe assignment was found for Sub-VP. Otherwise false. */ static bool dcn32_assign_subvp_pipe(struct dc *dc, struct dc_state *context, unsigned int *index) { … } /** * dcn32_enough_pipes_for_subvp - Function to check if there are "enough" pipes for SubVP. * @dc: current dc state * @context: new dc state * * This function returns true if there are enough free pipes * to create the required phantom pipes for any given stream * (that does not already have phantom pipe assigned). * * e.g. For a 2 stream config where the first stream uses one * pipe and the second stream uses 2 pipes (i.e. pipe split), * this function will return true because there is 1 remaining * pipe which can be used as the phantom pipe for the non pipe * split pipe. * * Return: * True if there are enough free pipes to assign phantom pipes to at least one * stream that does not already have phantom pipes assigned. Otherwise false. */ static bool dcn32_enough_pipes_for_subvp(struct dc *dc, struct dc_state *context) { … } /** * subvp_subvp_schedulable - Determine if SubVP + SubVP config is schedulable * @dc: current dc state * @context: new dc state * * High level algorithm: * 1. Find longest microschedule length (in us) between the two SubVP pipes * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both * pipes still allows for the maximum microschedule to fit in the active * region for both pipes. * * Return: True if the SubVP + SubVP config is schedulable, false otherwise */ static bool subvp_subvp_schedulable(struct dc *dc, struct dc_state *context) { … } /** * subvp_drr_schedulable() - Determine if SubVP + DRR config is schedulable * @dc: current dc state * @context: new dc state * * High level algorithm: * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching * (the margin is equal to the MALL region + DRR margin (500us)) * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame)) * then report the configuration as supported * * Return: True if the SubVP + DRR config is schedulable, false otherwise */ static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context) { … } /** * subvp_vblank_schedulable - Determine if SubVP + VBLANK config is schedulable * @dc: current dc state * @context: new dc state * * High level algorithm: * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time)) * then report the configuration as supported * 3. If the VBLANK display is DRR, then take the DRR static schedulability path * * Return: True if the SubVP + VBLANK/DRR config is schedulable, false otherwise */ static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context) { … } /** * subvp_subvp_admissable() - Determine if subvp + subvp config is admissible * * @dc: Current DC state * @context: New DC state to be programmed * * SubVP + SubVP is admissible under the following conditions: * - All SubVP pipes are < 120Hz OR * - All SubVP pipes are >= 120hz * * Return: True if admissible, false otherwise */ static bool subvp_subvp_admissable(struct dc *dc, struct dc_state *context) { … } /** * subvp_validate_static_schedulability - Check which SubVP case is calculated * and handle static analysis based on the case. * @dc: current dc state * @context: new dc state * @vlevel: Voltage level calculated by DML * * Three cases: * 1. SubVP + SubVP * 2. SubVP + VBLANK (DRR checked internally) * 3. SubVP + VACTIVE (currently unsupported) * * Return: True if statically schedulable, false otherwise */ static bool subvp_validate_static_schedulability(struct dc *dc, struct dc_state *context, int vlevel) { … } static void assign_subvp_index(struct dc *dc, struct dc_state *context) { … } struct pipe_slice_table { … }; static void update_slice_table_for_stream(struct pipe_slice_table *table, struct dc_stream_state *stream, int diff) { … } static void update_slice_table_for_plane(struct pipe_slice_table *table, struct pipe_ctx *dpp_pipe, struct dc_plane_state *plane, int diff) { … } static void init_pipe_slice_table_from_context( struct pipe_slice_table *table, struct dc_state *context) { … } static bool update_pipe_slice_table_with_split_flags( struct pipe_slice_table *table, struct dc *dc, struct dc_state *context, struct vba_vars_st *vba, int split[MAX_PIPES], bool merge[MAX_PIPES]) { … } static void update_pipes_with_slice_table(struct dc *dc, struct dc_state *context, struct pipe_slice_table *table) { … } static bool update_pipes_with_split_flags(struct dc *dc, struct dc_state *context, struct vba_vars_st *vba, int split[MAX_PIPES], bool merge[MAX_PIPES]) { … } static bool should_apply_odm_power_optimization(struct dc *dc, struct dc_state *context, struct vba_vars_st *v, int *split, bool *merge) { … } static void try_odm_power_optimization_and_revalidate( struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int *split, bool *merge, unsigned int *vlevel, int pipe_cnt) { … } static bool is_test_pattern_enabled( struct dc_state *context) { … } static bool dcn32_full_validate_bw_helper(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int *vlevel, int *split, bool *merge, int *pipe_cnt, bool *repopulate_pipes) { … } static bool is_dtbclk_required(struct dc *dc, struct dc_state *context) { … } static void dcn20_adjust_freesync_v_startup(const struct dc_crtc_timing *dc_crtc_timing, int *vstartup_start) { … } static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int pipe_cnt, int vlevel) { … } static struct pipe_ctx *dcn32_find_split_pipe( struct dc *dc, struct dc_state *context, int old_index) { … } static bool dcn32_split_stream_for_mpc_or_odm( const struct dc *dc, struct resource_context *res_ctx, struct pipe_ctx *pri_pipe, struct pipe_ctx *sec_pipe, bool odm) { … } static bool dcn32_apply_merge_split_flags_helper( struct dc *dc, struct dc_state *context, bool *repopulate_pipes, int *split, bool *merge) { … } bool dcn32_internal_validate_bw(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int *pipe_cnt_out, int *vlevel_out, bool fast_validate) { … } void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int pipe_cnt, int vlevel) { … } static void dcn32_get_optimal_dcfclk_fclk_for_uclk(unsigned int uclk_mts, unsigned int *optimal_dcfclk, unsigned int *optimal_fclk) { … } static void remove_entry_from_table_at_index(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries, unsigned int index) { … } void dcn32_patch_dpm_table(struct clk_bw_params *bw_params) { … } static void swap_table_entries(struct _vcs_dpi_voltage_scaling_st *first_entry, struct _vcs_dpi_voltage_scaling_st *second_entry) { … } /* * sort_entries_with_same_bw - Sort entries sharing the same bandwidth by DCFCLK */ static void sort_entries_with_same_bw(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) { … } /* * remove_inconsistent_entries - Ensure entries with the same bandwidth have MEMCLK and FCLK monotonically increasing * and remove entries that do not */ static void remove_inconsistent_entries(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) { … } /* * override_max_clk_values - Overwrite the max clock frequencies with the max DC mode timings * Input: * max_clk_limit - struct containing the desired clock timings * Output: * curr_clk_limit - struct containing the timings that need to be overwritten * Return: 0 upon success, non-zero for failure */ static int override_max_clk_values(struct clk_limit_table_entry *max_clk_limit, struct clk_limit_table_entry *curr_clk_limit) { … } static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk_bw_params *bw_params, struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) { … } /* * dcn32_update_bw_bounding_box * * This would override some dcn3_2 ip_or_soc initial parameters hardcoded from * spreadsheet with actual values as per dGPU SKU: * - with passed few options from dc->config * - with dentist_vco_frequency from Clk Mgr (currently hardcoded, but might * need to get it from PM FW) * - with passed latency values (passed in ns units) in dc-> bb override for * debugging purposes * - with passed latencies from VBIOS (in 100_ns units) if available for * certain dGPU SKU * - with number of DRAM channels from VBIOS (which differ for certain dGPU SKU * of the same ASIC) * - clocks levels with passed clk_table entries from Clk Mgr as reported by PM * FW for different clocks (which might differ for certain dGPU SKU of the * same ASIC) */ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { … } void dcn32_zero_pipe_dcc_fraction(display_e2e_pipe_params_st *pipes, int pipe_cnt) { … } bool dcn32_allow_subvp_with_active_margin(struct pipe_ctx *pipe) { … } /** * dcn32_allow_subvp_high_refresh_rate: Determine if the high refresh rate config will allow subvp * * @dc: Current DC state * @context: New DC state to be programmed * @pipe: Pipe to be considered for use in subvp * * On high refresh rate display configs, we will allow subvp under the following conditions: * 1. Resolution is 3840x2160, 3440x1440, or 2560x1440 * 2. Refresh rate is between 120hz - 165hz * 3. No scaling * 4. Freesync is inactive * 5. For single display cases, freesync must be disabled * * Return: True if pipe can be used for subvp, false otherwise */ bool dcn32_allow_subvp_high_refresh_rate(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe) { … } /** * dcn32_determine_max_vratio_prefetch: Determine max Vratio for prefetch by driver policy * * @dc: Current DC state * @context: New DC state to be programmed * * Return: Max vratio for prefetch */ double dcn32_determine_max_vratio_prefetch(struct dc *dc, struct dc_state *context) { … } /** * dcn32_assign_fpo_vactive_candidate - Assign the FPO stream candidate for FPO + VActive case * * This function chooses the FPO candidate stream for FPO + VActive cases (2 stream config). * For FPO + VAtive cases, the assumption is that one display has ActiveMargin > 0, and the * other display has ActiveMargin <= 0. This function will choose the pipe/stream that has * ActiveMargin <= 0 to be the FPO stream candidate if found. * * * @dc: current dc state * @context: new dc state * @fpo_candidate_stream: pointer to FPO stream candidate if one is found * * Return: void */ void dcn32_assign_fpo_vactive_candidate(struct dc *dc, const struct dc_state *context, struct dc_stream_state **fpo_candidate_stream) { … } /** * dcn32_find_vactive_pipe - Determines if the config has a pipe that can switch in VACTIVE * * @dc: current dc state * @context: new dc state * @fpo_candidate_stream: candidate stream to be chosen for FPO * @vactive_margin_req_us: The vactive marign required for a vactive pipe to be considered "found" * * Return: True if VACTIVE display is found, false otherwise */ bool dcn32_find_vactive_pipe(struct dc *dc, const struct dc_state *context, struct dc_stream_state *fpo_candidate_stream, uint32_t vactive_margin_req_us) { … } void dcn32_set_clock_limits(const struct _vcs_dpi_soc_bounding_box_st *soc_bb) { … } void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context) { … }