| /* |
| * Copyright 2017 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 "display_watermark.h" |
| #include "display_mode_lib.h" |
| |
| static void get_bytes_per_pixel( |
| enum source_format_class format, |
| struct _vcs_dpi_wm_calc_pipe_params_st *plane) |
| { |
| switch (format) { |
| case dm_444_64: |
| plane->bytes_per_pixel_y = 8.0; |
| plane->bytes_per_pixel_c = 0.0; |
| break; |
| case dm_444_32: |
| plane->bytes_per_pixel_y = 4.0; |
| plane->bytes_per_pixel_c = 0.0; |
| break; |
| case dm_444_16: |
| plane->bytes_per_pixel_y = 2.0; |
| plane->bytes_per_pixel_c = 0.0; |
| break; |
| case dm_422_10: |
| plane->bytes_per_pixel_y = 4.0; |
| plane->bytes_per_pixel_c = 0.0; |
| break; |
| case dm_422_8: |
| plane->bytes_per_pixel_y = 2.0; |
| plane->bytes_per_pixel_c = 0.0; |
| break; |
| case dm_420_8: |
| plane->bytes_per_pixel_y = 1.0; |
| plane->bytes_per_pixel_c = 2.0; |
| break; |
| case dm_420_10: |
| plane->bytes_per_pixel_y = 4.0 / 3; |
| plane->bytes_per_pixel_c = 8.0 / 3; |
| break; |
| default: |
| BREAK_TO_DEBUGGER(); /* invalid format in get_bytes_per_pixel */ |
| } |
| } |
| |
| static unsigned int get_swath_width_y( |
| struct _vcs_dpi_display_pipe_source_params_st *src_param, |
| unsigned int num_dpp) |
| { |
| unsigned int val; |
| |
| /* note that we don't divide by num_dpp here because we have an interface which has already split |
| * any viewports |
| */ |
| if (src_param->source_scan == dm_horz) { |
| val = src_param->viewport_width; |
| } else { |
| val = src_param->viewport_height; |
| } |
| |
| return val; |
| } |
| |
| static void get_swath_height( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_pipe_source_params_st *src_param, |
| struct _vcs_dpi_wm_calc_pipe_params_st *plane, |
| unsigned int swath_width_y) |
| { |
| double buffer_width; |
| |
| if (src_param->source_format == dm_444_64 || src_param->source_format == dm_444_32 |
| || src_param->source_format == dm_444_16) { |
| if (src_param->sw_mode == dm_sw_linear) { |
| plane->swath_height_y = 1; |
| } else if (src_param->source_format == dm_444_64) { |
| plane->swath_height_y = 4; |
| } else { |
| plane->swath_height_y = 8; |
| } |
| |
| if (src_param->source_scan != dm_horz) { |
| plane->swath_height_y = 256 / (unsigned int) plane->bytes_per_pixel_y |
| / plane->swath_height_y; |
| } |
| |
| plane->swath_height_c = 0; |
| |
| } else { |
| if (src_param->sw_mode == dm_sw_linear) { |
| plane->swath_height_y = 1; |
| plane->swath_height_c = 1; |
| } else if (src_param->source_format == dm_420_8) { |
| plane->swath_height_y = 16; |
| plane->swath_height_c = 8; |
| } else { |
| plane->swath_height_y = 8; |
| plane->swath_height_c = 8; |
| } |
| |
| if (src_param->source_scan != dm_horz) { |
| double bytes_per_pixel_c_ceil; |
| |
| plane->swath_height_y = 256 / dml_ceil(plane->bytes_per_pixel_y) |
| / plane->swath_height_y; |
| |
| bytes_per_pixel_c_ceil = dml_ceil_2(plane->bytes_per_pixel_c); |
| |
| plane->swath_height_c = 256 / bytes_per_pixel_c_ceil |
| / plane->swath_height_c; |
| } |
| } |
| |
| /* use swath height min if buffer isn't big enough */ |
| |
| buffer_width = ((double) mode_lib->ip.det_buffer_size_kbytes * 1024.0 / 2.0) |
| / (plane->bytes_per_pixel_y * (double) plane->swath_height_y |
| + (plane->bytes_per_pixel_c / 2.0 |
| * (double) plane->swath_height_c)); |
| |
| if ((double) swath_width_y <= buffer_width) { |
| /* do nothing, just keep code structure from Gabes vba */ |
| } else { |
| /* substitute swath height with swath height min */ |
| if (src_param->source_format == dm_444_64 || src_param->source_format == dm_444_32 |
| || src_param->source_format == dm_444_16) { |
| if ((src_param->sw_mode == dm_sw_linear) |
| || (src_param->source_format == dm_444_64 |
| && (src_param->sw_mode == dm_sw_4kb_s |
| || src_param->sw_mode |
| == dm_sw_4kb_s_x |
| || src_param->sw_mode |
| == dm_sw_64kb_s |
| || src_param->sw_mode |
| == dm_sw_64kb_s_t |
| || src_param->sw_mode |
| == dm_sw_64kb_s_x |
| || src_param->sw_mode |
| == dm_sw_var_s |
| || src_param->sw_mode |
| == dm_sw_var_s_x) |
| && src_param->source_scan == dm_horz)) { |
| /* do nothing, just keep code structure from Gabes vba */ |
| } else { |
| plane->swath_height_y = plane->swath_height_y / 2; |
| } |
| } else { |
| if (src_param->sw_mode == dm_sw_linear) { |
| /* do nothing, just keep code structure from Gabes vba */ |
| } else if (src_param->source_format == dm_420_8 |
| && src_param->source_scan == dm_horz) { |
| plane->swath_height_y = plane->swath_height_y / 2; |
| } else if (src_param->source_format == dm_420_10 |
| && src_param->source_scan == dm_horz) { |
| plane->swath_height_c = plane->swath_height_c / 2; |
| } |
| } |
| } |
| |
| if (plane->swath_height_c == 0) { |
| plane->det_buffer_size_y = mode_lib->ip.det_buffer_size_kbytes * 1024.0; |
| } else if (plane->swath_height_c <= plane->swath_height_y) { |
| plane->det_buffer_size_y = mode_lib->ip.det_buffer_size_kbytes * 1024.0 / 2.0; |
| } else { |
| plane->det_buffer_size_y = mode_lib->ip.det_buffer_size_kbytes * 1024.0 * 2.0 / 3.0; |
| } |
| } |
| |
| static void calc_display_pipe_line_delivery_time( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].v_ratio <= 1.0) { |
| planes[i].display_pipe_line_delivery_time = planes[i].swath_width_y |
| * planes[i].num_dpp / planes[i].h_ratio |
| / planes[i].pixclk_mhz; |
| } else { |
| double dchub_pscl_bw_per_clk; |
| |
| if (planes[i].h_ratio > 1) { |
| double num_hscl_kernels; |
| |
| num_hscl_kernels = dml_ceil((double) planes[i].h_taps / 6); |
| dchub_pscl_bw_per_clk = |
| dml_min( |
| (double) mode_lib->ip.max_dchub_pscl_bw_pix_per_clk, |
| mode_lib->ip.max_pscl_lb_bw_pix_per_clk |
| * planes[i].h_ratio |
| / num_hscl_kernels); |
| } else { |
| dchub_pscl_bw_per_clk = |
| dml_min( |
| (double) mode_lib->ip.max_dchub_pscl_bw_pix_per_clk, |
| (double) mode_lib->ip.max_pscl_lb_bw_pix_per_clk); |
| } |
| |
| planes[i].display_pipe_line_delivery_time = planes[i].swath_width_y |
| / dchub_pscl_bw_per_clk / planes[i].dppclk_mhz; |
| } |
| } |
| } |
| |
| static double calc_total_data_read_bw( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| double val = 0.0; |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| double swath_width_y_plane = planes[i].swath_width_y * planes[i].num_dpp; |
| |
| planes[i].read_bw = swath_width_y_plane |
| * (dml_ceil(planes[i].bytes_per_pixel_y) |
| + dml_ceil_2(planes[i].bytes_per_pixel_c) / 2) |
| / (planes[i].h_total / planes[i].pixclk_mhz) * planes[i].v_ratio; |
| |
| val += planes[i].read_bw; |
| |
| DTRACE("plane[%d] start", i); |
| DTRACE("read_bw = %f", planes[i].read_bw); |
| DTRACE("plane[%d] end", i); |
| } |
| |
| return val; |
| } |
| |
| double dml_wm_calc_total_data_read_bw( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| return calc_total_data_read_bw(mode_lib, planes, num_planes); |
| } |
| |
| static double calc_dcfclk_mhz( |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| double dcfclk_mhz = -1.0; |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| /* voltage and dcfclk must be the same for all pipes */ |
| ASSERT(dcfclk_mhz == -1.0 || dcfclk_mhz == planes[i].dcfclk_mhz); |
| dcfclk_mhz = planes[i].dcfclk_mhz; |
| } |
| |
| return dcfclk_mhz; |
| } |
| |
| static enum voltage_state find_voltage( |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| int voltage = -1; |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| ASSERT(voltage == -1 || voltage == planes[i].voltage); |
| voltage = planes[i].voltage; |
| } |
| |
| return (enum voltage_state) voltage; |
| } |
| |
| static bool find_dcc_enable(struct _vcs_dpi_wm_calc_pipe_params_st *planes, unsigned int num_planes) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].dcc_enable) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static double calc_return_bw( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| struct _vcs_dpi_soc_bounding_box_st *soc; |
| double return_bw_mbps; |
| double dcfclk_mhz; |
| double return_bus_bw; |
| enum voltage_state voltage; |
| double return_bw_to_dcn; |
| bool dcc_enable; |
| double rob_chunk_diff; |
| double urgent_latency_traffic; |
| double critical_compression; |
| struct _vcs_dpi_voltage_scaling_st state; |
| |
| soc = &mode_lib->soc; |
| |
| dcfclk_mhz = calc_dcfclk_mhz(planes, num_planes); |
| return_bus_bw = dcfclk_mhz * soc->return_bus_width_bytes; |
| |
| DTRACE("INTERMEDIATE dcfclk_mhz = %f", dcfclk_mhz); |
| DTRACE("INTERMEDIATE return_bus_bw = %f", return_bus_bw); |
| |
| voltage = find_voltage(planes, num_planes); |
| return_bw_to_dcn = dml_socbb_return_bw_mhz(soc, voltage); |
| |
| dcc_enable = find_dcc_enable(planes, num_planes); |
| |
| return_bw_mbps = return_bw_to_dcn; |
| DTRACE("INTERMEDIATE return_bw_mbps = %f", return_bw_mbps); |
| |
| rob_chunk_diff = |
| (mode_lib->ip.rob_buffer_size_kbytes - mode_lib->ip.pixel_chunk_size_kbytes) |
| * 1024.0; |
| DTRACE("INTERMEDIATE rob_chunk_diff = %f", rob_chunk_diff); |
| |
| if (dcc_enable && return_bw_to_dcn > return_bus_bw / 4) { |
| double dcc_return_bw = |
| return_bw_to_dcn * 4.0 |
| * (1.0 |
| - soc->urgent_latency_us |
| / (rob_chunk_diff |
| / (return_bw_to_dcn |
| - return_bus_bw |
| / 4.0) |
| + soc->urgent_latency_us)); |
| return_bw_mbps = dml_min(return_bw_mbps, dcc_return_bw); |
| DTRACE("INTERMEDIATE dcc_return_bw = %f", dcc_return_bw); |
| } |
| |
| urgent_latency_traffic = return_bus_bw * soc->urgent_latency_us; |
| DTRACE("INTERMEDIATE urgent_latency_traffic = %f", urgent_latency_traffic); |
| critical_compression = 2.0 * urgent_latency_traffic |
| / (return_bw_to_dcn * soc->urgent_latency_us + rob_chunk_diff); |
| DTRACE("INTERMEDIATE critical_compression = %f", critical_compression); |
| |
| if (dcc_enable && critical_compression > 1.0 && critical_compression < 4.0) { |
| double crit_return_bw = (4 * return_bw_to_dcn * rob_chunk_diff |
| * urgent_latency_traffic); |
| crit_return_bw = crit_return_bw |
| / dml_pow( |
| return_bw_to_dcn * soc->urgent_latency_us |
| + rob_chunk_diff, |
| 2); |
| DTRACE("INTERMEDIATE critical_return_bw = %f", crit_return_bw); |
| return_bw_mbps = dml_min(return_bw_mbps, crit_return_bw); |
| } |
| |
| /* Gabe does this again for some reason using the value of return_bw_mpbs from the previous calculation |
| * and a lightly different return_bw_to_dcn |
| */ |
| |
| state = dml_socbb_voltage_scaling(soc, voltage); |
| return_bw_to_dcn = dml_min( |
| soc->return_bus_width_bytes * dcfclk_mhz, |
| state.dram_bw_per_chan_gbps * 1000.0 * (double) soc->num_chans); |
| |
| DTRACE("INTERMEDIATE rob_chunk_diff = %f", rob_chunk_diff); |
| |
| if (dcc_enable && return_bw_to_dcn > return_bus_bw / 4) { |
| double dcc_return_bw = |
| return_bw_to_dcn * 4.0 |
| * (1.0 |
| - soc->urgent_latency_us |
| / (rob_chunk_diff |
| / (return_bw_to_dcn |
| - return_bus_bw |
| / 4.0) |
| + soc->urgent_latency_us)); |
| return_bw_mbps = dml_min(return_bw_mbps, dcc_return_bw); |
| DTRACE("INTERMEDIATE dcc_return_bw = %f", dcc_return_bw); |
| } |
| |
| urgent_latency_traffic = return_bus_bw * soc->urgent_latency_us; |
| DTRACE("INTERMEDIATE urgent_latency_traffic = %f", urgent_latency_traffic); |
| critical_compression = 2.0 * urgent_latency_traffic |
| / (return_bw_to_dcn * soc->urgent_latency_us + rob_chunk_diff); |
| DTRACE("INTERMEDIATE critical_compression = %f", critical_compression); |
| |
| /* problem here? */ |
| if (dcc_enable && critical_compression > 1.0 && critical_compression < 4.0) { |
| double crit_return_bw = (4 * return_bw_to_dcn * rob_chunk_diff |
| * urgent_latency_traffic); |
| crit_return_bw = crit_return_bw |
| / dml_pow( |
| return_bw_to_dcn * soc->urgent_latency_us |
| + rob_chunk_diff, |
| 2); |
| DTRACE("INTERMEDIATE critical_return_bw = %f", crit_return_bw); |
| DTRACE("INTERMEDIATE return_bw_to_dcn = %f", return_bw_to_dcn); |
| DTRACE("INTERMEDIATE rob_chunk_diff = %f", rob_chunk_diff); |
| DTRACE("INTERMEDIATE urgent_latency_traffic = %f", urgent_latency_traffic); |
| |
| return_bw_mbps = dml_min(return_bw_mbps, crit_return_bw); |
| } |
| |
| DTRACE("INTERMEDIATE final return_bw_mbps = %f", return_bw_mbps); |
| return return_bw_mbps; |
| } |
| |
| double dml_wm_calc_return_bw( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| return calc_return_bw(mode_lib, planes, num_planes); |
| } |
| |
| static double calc_last_pixel_of_line_extra_wm_us( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| double val = 0.0; |
| double total_data_read_bw = calc_total_data_read_bw(mode_lib, planes, num_planes); |
| int voltage = -1; |
| unsigned int i; |
| double return_bw_mbps; |
| |
| for (i = 0; i < num_planes; i++) { |
| /* voltage mode must be the same for all pipes */ |
| ASSERT(voltage == -1 || voltage == planes[i].voltage); |
| voltage = planes[i].voltage; |
| } |
| return_bw_mbps = calc_return_bw(mode_lib, planes, num_planes); |
| |
| for (i = 0; i < num_planes; i++) { |
| double bytes_pp_y = dml_ceil(planes[i].bytes_per_pixel_y); |
| double bytes_pp_c = dml_ceil_2(planes[i].bytes_per_pixel_c); |
| double swath_bytes_y = (double) planes[i].swath_width_y |
| * (double) planes[i].swath_height_y * (double) bytes_pp_y; |
| double swath_bytes_c = ((double) planes[i].swath_width_y / 2.0) |
| * (double) planes[i].swath_height_c * (double) bytes_pp_c; |
| double data_fabric_line_delivery_time = (swath_bytes_y + swath_bytes_c) |
| / (return_bw_mbps * planes[i].read_bw / (double) planes[i].num_dpp |
| / total_data_read_bw); |
| |
| DTRACE( |
| "bytes_pp_y = %f, swath_width_y = %f, swath_height_y = %f, swath_bytes_y = %f", |
| bytes_pp_y, |
| (double) planes[i].swath_width_y, |
| (double) planes[i].swath_height_y, |
| swath_bytes_y); |
| DTRACE( |
| "bytes_pp_c = %f, swath_width_c = %f, swath_height_c = %f, swath_bytes_c = %f", |
| bytes_pp_c, |
| ((double) planes[i].swath_width_y / 2.0), |
| (double) planes[i].swath_height_c, |
| swath_bytes_c); |
| DTRACE( |
| "return_bw_mbps = %f, read_bw = %f, num_dpp = %d, total_data_read_bw = %f", |
| return_bw_mbps, |
| planes[i].read_bw, |
| planes[i].num_dpp, |
| total_data_read_bw); |
| DTRACE("data_fabric_line_delivery_time = %f", data_fabric_line_delivery_time); |
| DTRACE( |
| "display_pipe_line_delivery_time = %f", |
| planes[i].display_pipe_line_delivery_time); |
| |
| val = dml_max( |
| val, |
| data_fabric_line_delivery_time |
| - planes[i].display_pipe_line_delivery_time); |
| } |
| |
| DTRACE("last_pixel_of_line_extra_wm is %f us", val); |
| return val; |
| } |
| |
| static bool calc_pte_enable(struct _vcs_dpi_wm_calc_pipe_params_st *planes, unsigned int num_planes) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].pte_enable) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static void calc_lines_in_det_y(struct _vcs_dpi_wm_calc_pipe_params_st *plane) |
| { |
| plane->lines_in_det_y = plane->det_buffer_size_y / plane->bytes_per_pixel_y |
| / plane->swath_width_y; |
| plane->lines_in_det_y_rounded_down_to_swath = dml_floor( |
| (double) plane->lines_in_det_y / plane->swath_height_y) |
| * plane->swath_height_y; |
| plane->full_det_buffering_time = plane->lines_in_det_y_rounded_down_to_swath |
| * (plane->h_total / plane->pixclk_mhz); |
| } |
| |
| /* CHECKME: not obviously 1:1 with calculation described in architectural |
| * document or spreadsheet */ |
| static void calc_dcfclk_deepsleep_mhz_per_plane( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *plane) |
| { |
| double bus_width_per_pixel; |
| |
| if (plane->swath_height_c == 0) { |
| bus_width_per_pixel = dml_ceil(plane->bytes_per_pixel_y) / 64; |
| } else { |
| double bus_width_per_pixel_c; |
| |
| bus_width_per_pixel = dml_ceil(plane->bytes_per_pixel_y) / 32; |
| bus_width_per_pixel_c = dml_ceil(plane->bytes_per_pixel_c) / 32; |
| if (bus_width_per_pixel < bus_width_per_pixel_c) |
| bus_width_per_pixel = bus_width_per_pixel_c; |
| } |
| |
| if (plane->v_ratio <= 1) { |
| plane->dcfclk_deepsleep_mhz_per_plane = 1.1 * plane->pixclk_mhz / plane->num_dpp |
| * plane->h_ratio * bus_width_per_pixel; |
| } else if (plane->h_ratio > 1) { |
| double num_hscl_kernels = dml_ceil((double) plane->h_taps / 6); |
| double dchub_pscl_bw_per_clk = dml_min( |
| (double) mode_lib->ip.max_dchub_pscl_bw_pix_per_clk, |
| mode_lib->ip.max_pscl_lb_bw_pix_per_clk * plane->h_ratio |
| / num_hscl_kernels); |
| |
| plane->dcfclk_deepsleep_mhz_per_plane = 1.1 * plane->dppclk_mhz |
| * dchub_pscl_bw_per_clk * bus_width_per_pixel; |
| } else { |
| double dchub_pscl_bw_per_clk = dml_min( |
| (double) mode_lib->ip.max_dchub_pscl_bw_pix_per_clk, |
| (double) mode_lib->ip.max_pscl_lb_bw_pix_per_clk); |
| |
| plane->dcfclk_deepsleep_mhz_per_plane = 1.1 * plane->dppclk_mhz |
| * dchub_pscl_bw_per_clk * bus_width_per_pixel; |
| } |
| |
| plane->dcfclk_deepsleep_mhz_per_plane = dml_max( |
| plane->dcfclk_deepsleep_mhz_per_plane, |
| plane->pixclk_mhz / 16); |
| } |
| |
| /* Implementation of expected stutter efficiency from DCN1_Display_Mode.docx */ |
| double dml_wm_expected_stutter_eff_e2e( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *e2e, |
| unsigned int num_pipes) |
| { |
| double min_full_det_buffering_time_us; |
| double frame_time_for_min_full_det_buffering_time_us = 0.0; |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes = mode_lib->wm_param; |
| unsigned int num_planes; |
| unsigned int i; |
| double total_data_read_bw_mbps; |
| double average_read_bw_gbps; |
| double min_full_det_buffer_size_bytes; |
| double rob_fill_size_bytes; |
| double part_of_burst_that_fits_in_rob; |
| int voltage; |
| double dcfclk_mhz; |
| unsigned int total_writeback; |
| double return_bw_mbps; |
| double stutter_burst_time_us; |
| double stutter_eff_not_including_vblank; |
| double smallest_vblank_us; |
| double stutter_eff; |
| |
| memset(mode_lib->wm_param, 0, sizeof(mode_lib->wm_param)); |
| DTRACE("calculating expected stutter efficiency"); |
| |
| num_planes = dml_wm_e2e_to_wm(mode_lib, e2e, num_pipes, planes); |
| |
| for (i = 0; i < num_planes; i++) { |
| calc_lines_in_det_y(&planes[i]); |
| |
| DTRACE("swath width y plane %d = %d", i, planes[i].swath_width_y); |
| DTRACE("swath height y plane %d = %d", i, planes[i].swath_height_y); |
| DTRACE( |
| "bytes per pixel det y plane %d = %f", |
| i, |
| planes[i].bytes_per_pixel_y); |
| DTRACE( |
| "bytes per pixel det c plane %d = %f", |
| i, |
| planes[i].bytes_per_pixel_c); |
| DTRACE( |
| "det buffer size plane %d = %d", |
| i, |
| planes[i].det_buffer_size_y); |
| DTRACE("lines in det plane %d = %d", i, planes[i].lines_in_det_y); |
| DTRACE( |
| "lines in det rounded to swaths plane %d = %d", |
| i, |
| planes[i].lines_in_det_y_rounded_down_to_swath); |
| } |
| |
| min_full_det_buffering_time_us = 9999.0; |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].full_det_buffering_time < min_full_det_buffering_time_us) { |
| min_full_det_buffering_time_us = planes[i].full_det_buffering_time; |
| frame_time_for_min_full_det_buffering_time_us = (double) planes[i].v_total |
| * planes[i].h_total / planes[i].pixclk_mhz; |
| } |
| } |
| |
| DTRACE("INTERMEDIATE: min_full_det_buffering_time_us = %f", min_full_det_buffering_time_us); |
| |
| total_data_read_bw_mbps = calc_total_data_read_bw(mode_lib, planes, num_planes); |
| |
| average_read_bw_gbps = 0.0; |
| |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].dcc_enable) { |
| average_read_bw_gbps += planes[i].read_bw / planes[i].dcc_rate / 1000; |
| } else { |
| average_read_bw_gbps += planes[i].read_bw / 1000; |
| } |
| |
| if (planes[i].dcc_enable) { |
| average_read_bw_gbps += planes[i].read_bw / 1000 / 256; |
| } |
| |
| if (planes[i].pte_enable) { |
| average_read_bw_gbps += planes[i].read_bw / 1000 / 512; |
| } |
| } |
| |
| min_full_det_buffer_size_bytes = min_full_det_buffering_time_us * total_data_read_bw_mbps; |
| rob_fill_size_bytes = mode_lib->ip.rob_buffer_size_kbytes * 1024 * total_data_read_bw_mbps |
| / (average_read_bw_gbps * 1000); |
| part_of_burst_that_fits_in_rob = dml_min( |
| min_full_det_buffer_size_bytes, |
| rob_fill_size_bytes); |
| |
| voltage = -1; |
| dcfclk_mhz = -1.0; |
| total_writeback = 0; |
| |
| for (i = 0; i < num_pipes; i++) { |
| /* voltage and dcfclk must be the same for all pipes */ |
| ASSERT(voltage == -1 || voltage == e2e[i].clks_cfg.voltage); |
| voltage = e2e[i].clks_cfg.voltage; |
| ASSERT(dcfclk_mhz == -1.0 || dcfclk_mhz == e2e[i].clks_cfg.dcfclk_mhz); |
| dcfclk_mhz = e2e[i].clks_cfg.dcfclk_mhz; |
| |
| if (e2e[i].dout.output_type == dm_wb) |
| total_writeback++; |
| } |
| |
| return_bw_mbps = calc_return_bw(mode_lib, planes, num_planes); |
| |
| DTRACE("INTERMEDIATE: part_of_burst_that_fits_in_rob = %f", part_of_burst_that_fits_in_rob); |
| DTRACE("INTERMEDIATE: average_read_bw_gbps = %f", average_read_bw_gbps); |
| DTRACE("INTERMEDIATE: total_data_read_bw_mbps = %f", total_data_read_bw_mbps); |
| DTRACE("INTERMEDIATE: return_bw_mbps = %f", return_bw_mbps); |
| |
| stutter_burst_time_us = part_of_burst_that_fits_in_rob * (average_read_bw_gbps * 1000) |
| / total_data_read_bw_mbps / return_bw_mbps |
| + (min_full_det_buffering_time_us * total_data_read_bw_mbps |
| - part_of_burst_that_fits_in_rob) / (dcfclk_mhz * 64); |
| DTRACE("INTERMEDIATE: stutter_burst_time_us = %f", stutter_burst_time_us); |
| |
| if (total_writeback == 0) { |
| stutter_eff_not_including_vblank = (1.0 |
| - ((mode_lib->soc.sr_exit_time_us + stutter_burst_time_us) |
| / min_full_det_buffering_time_us)) * 100.0; |
| } else { |
| stutter_eff_not_including_vblank = 0.0; |
| } |
| |
| DTRACE("stutter_efficiency_not_including_vblank = %f", stutter_eff_not_including_vblank); |
| |
| smallest_vblank_us = 9999.0; |
| |
| for (i = 0; i < num_pipes; i++) { |
| double vblank_us; |
| if (e2e[i].pipe.dest.syncronized_vblank_all_planes != 0 || num_pipes == 1) { |
| vblank_us = (double) (e2e[i].pipe.dest.vtotal + 1 |
| - e2e[i].pipe.dest.vblank_start |
| + e2e[i].pipe.dest.vblank_end * e2e[i].pipe.dest.htotal) |
| / e2e[i].pipe.dest.pixel_rate_mhz; |
| } else { |
| vblank_us = 0.0; |
| } |
| |
| smallest_vblank_us = dml_min(smallest_vblank_us, vblank_us); |
| } |
| |
| DTRACE("smallest vblank = %f us", smallest_vblank_us); |
| |
| stutter_eff = 100.0 |
| * (((stutter_eff_not_including_vblank / 100.0) |
| * (frame_time_for_min_full_det_buffering_time_us |
| - smallest_vblank_us) + smallest_vblank_us) |
| / frame_time_for_min_full_det_buffering_time_us); |
| |
| DTRACE("stutter_efficiency = %f", stutter_eff); |
| |
| return stutter_eff_not_including_vblank; |
| } |
| |
| double dml_wm_expected_stutter_eff_e2e_with_vblank( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *e2e, |
| unsigned int num_pipes) |
| { |
| double min_full_det_buffering_time_us; |
| double frame_time_for_min_full_det_buffering_time_us = 0.0; |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes = mode_lib->wm_param; |
| unsigned int num_planes; |
| unsigned int i; |
| double total_data_read_bw_mbps; |
| double average_read_bw_gbps; |
| double min_full_det_buffer_size_bytes; |
| double rob_fill_size_bytes; |
| double part_of_burst_that_fits_in_rob; |
| int voltage; |
| double dcfclk_mhz; |
| unsigned int total_writeback; |
| double return_bw_mbps; |
| double stutter_burst_time_us; |
| double stutter_eff_not_including_vblank; |
| double smallest_vblank_us; |
| double stutter_eff; |
| |
| memset(mode_lib->wm_param, 0, sizeof(mode_lib->wm_param)); |
| num_planes = dml_wm_e2e_to_wm(mode_lib, e2e, num_pipes, planes); |
| |
| for (i = 0; i < num_planes; i++) { |
| calc_lines_in_det_y(&planes[i]); |
| } |
| |
| min_full_det_buffering_time_us = 9999.0; |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].full_det_buffering_time < min_full_det_buffering_time_us) { |
| min_full_det_buffering_time_us = planes[i].full_det_buffering_time; |
| frame_time_for_min_full_det_buffering_time_us = (double) planes[i].v_total |
| * planes[i].h_total / planes[i].pixclk_mhz; |
| } |
| } |
| |
| total_data_read_bw_mbps = calc_total_data_read_bw(mode_lib, planes, num_planes); |
| average_read_bw_gbps = 0.0; |
| |
| for (i = 0; i < num_planes; i++) { |
| if (planes[i].dcc_enable) { |
| average_read_bw_gbps += planes[i].read_bw / planes[i].dcc_rate / 1000; |
| } else { |
| average_read_bw_gbps += planes[i].read_bw / 1000; |
| } |
| |
| if (planes[i].dcc_enable) { |
| average_read_bw_gbps += planes[i].read_bw / 1000 / 256; |
| } |
| |
| if (planes[i].pte_enable) { |
| average_read_bw_gbps += planes[i].read_bw / 1000 / 512; |
| } |
| } |
| |
| min_full_det_buffer_size_bytes = min_full_det_buffering_time_us * total_data_read_bw_mbps; |
| rob_fill_size_bytes = mode_lib->ip.rob_buffer_size_kbytes * 1024 * total_data_read_bw_mbps |
| / (average_read_bw_gbps * 1000); |
| part_of_burst_that_fits_in_rob = dml_min( |
| min_full_det_buffer_size_bytes, |
| rob_fill_size_bytes); |
| |
| voltage = -1; |
| dcfclk_mhz = -1.0; |
| total_writeback = 0; |
| |
| for (i = 0; i < num_pipes; i++) { |
| /* voltage and dcfclk must be the same for all pipes */ |
| ASSERT(voltage == -1 || voltage == e2e[i].clks_cfg.voltage); |
| voltage = e2e[i].clks_cfg.voltage; |
| ASSERT(dcfclk_mhz == -1.0 || dcfclk_mhz == e2e[i].clks_cfg.dcfclk_mhz); |
| dcfclk_mhz = e2e[i].clks_cfg.dcfclk_mhz; |
| |
| if (e2e[i].dout.output_type == dm_wb) |
| total_writeback++; |
| } |
| |
| return_bw_mbps = calc_return_bw(mode_lib, planes, num_planes); |
| |
| stutter_burst_time_us = part_of_burst_that_fits_in_rob * (average_read_bw_gbps * 1000) |
| / total_data_read_bw_mbps / return_bw_mbps |
| + (min_full_det_buffering_time_us * total_data_read_bw_mbps |
| - part_of_burst_that_fits_in_rob) / (dcfclk_mhz * 64); |
| |
| if (total_writeback == 0) { |
| stutter_eff_not_including_vblank = (1.0 |
| - ((mode_lib->soc.sr_exit_time_us + stutter_burst_time_us) |
| / min_full_det_buffering_time_us)) * 100.0; |
| } else { |
| stutter_eff_not_including_vblank = 0.0; |
| } |
| |
| smallest_vblank_us = 9999.0; |
| |
| for (i = 0; i < num_pipes; i++) { |
| double vblank_us; |
| if (e2e[i].pipe.dest.syncronized_vblank_all_planes != 0 || num_pipes == 1) { |
| vblank_us = (double) (e2e[i].pipe.dest.vtotal + 1 |
| - e2e[i].pipe.dest.vblank_start |
| + e2e[i].pipe.dest.vblank_end * e2e[i].pipe.dest.htotal) |
| / e2e[i].pipe.dest.pixel_rate_mhz; |
| } else { |
| vblank_us = 0.0; |
| } |
| |
| smallest_vblank_us = dml_min(smallest_vblank_us, vblank_us); |
| } |
| |
| stutter_eff = 100.0 |
| * (((stutter_eff_not_including_vblank / 100.0) |
| * (frame_time_for_min_full_det_buffering_time_us |
| - smallest_vblank_us) + smallest_vblank_us) |
| / frame_time_for_min_full_det_buffering_time_us); |
| |
| |
| return stutter_eff; |
| } |
| |
| double urgent_extra_calc( |
| struct display_mode_lib *mode_lib, |
| double dcfclk_mhz, |
| double return_bw_mbps, |
| unsigned int total_active_dpp, |
| unsigned int total_dcc_active_dpp) |
| { |
| double urgent_extra_latency_us = 0.0; |
| double urgent_round_trip_ooo_latency_us; |
| |
| urgent_round_trip_ooo_latency_us = |
| (((double) mode_lib->soc.round_trip_ping_latency_dcfclk_cycles + 32) |
| / dcfclk_mhz) |
| + (((double) (mode_lib->soc.urgent_out_of_order_return_per_channel_bytes |
| * mode_lib->soc.num_chans)) / return_bw_mbps); |
| |
| DTRACE( |
| "INTERMEDIATE round_trip_ping_latency_dcfclk_cycles = %d", |
| mode_lib->soc.round_trip_ping_latency_dcfclk_cycles); |
| DTRACE("INTERMEDIATE dcfclk_mhz = %f", dcfclk_mhz); |
| DTRACE( |
| "INTERMEDIATE urgent_out_of_order_return_per_channel_bytes = %d", |
| mode_lib->soc.urgent_out_of_order_return_per_channel_bytes); |
| |
| urgent_extra_latency_us = urgent_round_trip_ooo_latency_us |
| + ((double) total_active_dpp * mode_lib->ip.pixel_chunk_size_kbytes |
| + (double) total_dcc_active_dpp |
| * mode_lib->ip.meta_chunk_size_kbytes) |
| * 1024.0 / return_bw_mbps; /* to us */ |
| |
| DTRACE( |
| "INTERMEDIATE urgent_round_trip_ooo_latency_us = %f", |
| urgent_round_trip_ooo_latency_us); |
| DTRACE("INTERMEDIATE total_active_dpp = %d", total_active_dpp); |
| DTRACE( |
| "INTERMEDIATE pixel_chunk_size_kbytes = %d", |
| mode_lib->ip.pixel_chunk_size_kbytes); |
| DTRACE("INTERMEDIATE total_dcc_active_dpp = %d", total_dcc_active_dpp); |
| DTRACE( |
| "INTERMEDIATE meta_chunk_size_kbyte = %d", |
| mode_lib->ip.meta_chunk_size_kbytes); |
| DTRACE("INTERMEDIATE return_bw_mbps = %f", return_bw_mbps); |
| |
| return urgent_extra_latency_us; |
| } |
| |
| double dml_wm_urgent_extra_max(struct display_mode_lib *mode_lib) |
| { |
| unsigned int total_active_dpp = DC__NUM_DPP; |
| unsigned int total_dcc_active_dpp = total_active_dpp; |
| double urgent_extra_latency_us = 0.0; |
| double dcfclk_mhz = 0.0; |
| double return_bw_mbps = 0.0; |
| int voltage = dm_vmin; |
| |
| /* use minimum voltage */ |
| return_bw_mbps = dml_socbb_return_bw_mhz(&mode_lib->soc, (enum voltage_state) voltage); |
| /* use minimum dcfclk */ |
| dcfclk_mhz = mode_lib->soc.vmin.dcfclk_mhz; |
| /* use max dpps and dpps with dcc */ |
| |
| urgent_extra_latency_us = urgent_extra_calc( |
| mode_lib, |
| dcfclk_mhz, |
| return_bw_mbps, |
| total_active_dpp, |
| total_dcc_active_dpp); |
| |
| DTRACE("urgent extra max = %f", urgent_extra_latency_us); |
| return urgent_extra_latency_us; |
| } |
| |
| double dml_wm_urgent_extra( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| unsigned int total_active_dpp = 0; |
| unsigned int total_dcc_active_dpp = 0; |
| double urgent_extra_latency_us = 0.0; |
| double dcfclk_mhz = 0.0; |
| double return_bw_mbps = 0.0; |
| int voltage = -1; |
| bool pte_enable = false; |
| unsigned int i; |
| |
| for (i = 0; i < num_pipes; i++) { |
| /* num_dpp must be greater than 0 */ |
| ASSERT(pipes[i].num_dpp > 0); |
| |
| /* voltage mode must be the same for all pipes */ |
| ASSERT(voltage == -1 || voltage == pipes[i].voltage); |
| voltage = pipes[i].voltage; |
| |
| /* dcfclk for all pipes must be the same */ |
| ASSERT(dcfclk_mhz == 0.0 || dcfclk_mhz == pipes[i].dcfclk_mhz); |
| dcfclk_mhz = pipes[i].dcfclk_mhz; |
| |
| total_active_dpp += pipes[i].num_dpp; |
| |
| if (pipes[i].dcc_enable) { |
| total_dcc_active_dpp += pipes[i].num_dpp; |
| } |
| } |
| |
| DTRACE("total active dpps %d", total_active_dpp); |
| DTRACE("total active dpps with dcc %d", total_dcc_active_dpp); |
| DTRACE("voltage state is %d", voltage); |
| |
| return_bw_mbps = calc_return_bw(mode_lib, pipes, num_pipes); |
| |
| DTRACE("return_bandwidth is %f MBps", return_bw_mbps); |
| |
| pte_enable = calc_pte_enable(pipes, num_pipes); |
| |
| /* calculate the maximum extra latency just for comparison purposes */ |
| /* dml_wm_urgent_extra_max(); */ |
| urgent_extra_latency_us = urgent_extra_calc( |
| mode_lib, |
| dcfclk_mhz, |
| return_bw_mbps, |
| total_active_dpp, |
| total_dcc_active_dpp); |
| |
| DTRACE("INTERMEDIATE urgent_extra_latency_us_before_pte = %f", urgent_extra_latency_us); |
| |
| if (pte_enable) { |
| urgent_extra_latency_us += total_active_dpp * mode_lib->ip.pte_chunk_size_kbytes |
| * 1024.0 / return_bw_mbps; |
| |
| DTRACE("INTERMEDIATE pte_enable = true"); |
| DTRACE("INTERMEDIATE total_active_dpp = %d", total_active_dpp); |
| DTRACE( |
| "INTERMEDIATE pte_chunk_size_kbytes = %d", |
| mode_lib->ip.pte_chunk_size_kbytes); |
| DTRACE("INTERMEDIATE return_bw_mbps = %f", return_bw_mbps); |
| } |
| |
| return urgent_extra_latency_us; |
| } |
| |
| double dml_wm_urgent_e2e( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| struct _vcs_dpi_wm_calc_pipe_params_st *wm = mode_lib->wm_param; |
| unsigned int combined_pipes; |
| double urgent_wm; |
| |
| memset(mode_lib->wm_param, 0, sizeof(mode_lib->wm_param)); |
| combined_pipes = dml_wm_e2e_to_wm(mode_lib, pipes, num_pipes, wm); |
| |
| urgent_wm = dml_wm_urgent(mode_lib, wm, combined_pipes); |
| |
| return urgent_wm; |
| } |
| |
| double dml_wm_urgent( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| double urgent_watermark; |
| double urgent_extra_latency_us; |
| double last_pixel_of_line_extra_wm_us = 0.0; |
| |
| DTRACE("calculating urgent watermark"); |
| calc_display_pipe_line_delivery_time(mode_lib, planes, num_planes); |
| urgent_extra_latency_us = dml_wm_urgent_extra(mode_lib, planes, num_planes); |
| |
| last_pixel_of_line_extra_wm_us = calc_last_pixel_of_line_extra_wm_us( |
| mode_lib, |
| planes, |
| num_planes); |
| |
| urgent_watermark = mode_lib->soc.urgent_latency_us + last_pixel_of_line_extra_wm_us |
| + urgent_extra_latency_us; |
| |
| DTRACE("INTERMEDIATE urgent_latency_us = %f", mode_lib->soc.urgent_latency_us); |
| DTRACE("INTERMEDIATE last_pixel_of_line_extra_wm_us = %f", last_pixel_of_line_extra_wm_us); |
| DTRACE("INTERMEDIATE urgent_extra_latency_us = %f", urgent_extra_latency_us); |
| |
| DTRACE("urgent_watermark_us = %f", urgent_watermark); |
| return urgent_watermark; |
| } |
| |
| double dml_wm_pte_meta_urgent(struct display_mode_lib *mode_lib, double urgent_wm_us) |
| { |
| double val; |
| |
| val = urgent_wm_us + 2.0 * mode_lib->soc.urgent_latency_us; |
| DTRACE("pte_meta_urgent_watermark_us = %f", val); |
| |
| return val; |
| } |
| |
| double dml_wm_dcfclk_deepsleep_mhz_e2e( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes = mode_lib->wm_param; |
| unsigned int num_planes; |
| double val; |
| |
| memset(mode_lib->wm_param, 0, sizeof(mode_lib->wm_param)); |
| num_planes = dml_wm_e2e_to_wm(mode_lib, pipes, num_pipes, planes); |
| |
| val = dml_wm_dcfclk_deepsleep_mhz(mode_lib, planes, num_planes); |
| |
| return val; |
| } |
| |
| double dml_wm_dcfclk_deepsleep_mhz( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *planes, |
| unsigned int num_planes) |
| { |
| double val = 8.0; |
| unsigned int i; |
| |
| for (i = 0; i < num_planes; i++) { |
| calc_dcfclk_deepsleep_mhz_per_plane(mode_lib, &planes[i]); |
| |
| if (val < planes[i].dcfclk_deepsleep_mhz_per_plane) { |
| val = planes[i].dcfclk_deepsleep_mhz_per_plane; |
| } |
| |
| DTRACE("plane[%d] start", i); |
| DTRACE("dcfclk_deepsleep_per_plane = %f", planes[i].dcfclk_deepsleep_mhz_per_plane); |
| DTRACE("plane[%d] end", i); |
| } |
| |
| DTRACE("dcfclk_deepsleep_mhz = %f", val); |
| |
| return val; |
| } |
| |
| struct _vcs_dpi_cstate_pstate_watermarks_st dml_wm_cstate_pstate_e2e( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| struct _vcs_dpi_wm_calc_pipe_params_st *wm = mode_lib->wm_param; |
| unsigned int combined_pipes; |
| struct _vcs_dpi_cstate_pstate_watermarks_st cstate_pstate_wm; |
| |
| memset(mode_lib->wm_param, 0, sizeof(mode_lib->wm_param)); |
| combined_pipes = dml_wm_e2e_to_wm(mode_lib, pipes, num_pipes, wm); |
| cstate_pstate_wm = dml_wm_cstate_pstate(mode_lib, wm, combined_pipes); |
| |
| |
| return cstate_pstate_wm; |
| } |
| |
| struct _vcs_dpi_cstate_pstate_watermarks_st dml_wm_cstate_pstate( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| struct _vcs_dpi_cstate_pstate_watermarks_st wm; |
| double urgent_extra_latency_us; |
| double urgent_watermark_us; |
| double last_pixel_of_line_extra_wm_us; |
| double dcfclk_deepsleep_freq; |
| |
| DTRACE("calculating cstate and pstate watermarks"); |
| urgent_extra_latency_us = dml_wm_urgent_extra(mode_lib, pipes, num_pipes); |
| urgent_watermark_us = dml_wm_urgent(mode_lib, pipes, num_pipes); |
| |
| last_pixel_of_line_extra_wm_us = calc_last_pixel_of_line_extra_wm_us( |
| mode_lib, |
| pipes, |
| num_pipes); |
| dcfclk_deepsleep_freq = dml_wm_dcfclk_deepsleep_mhz(mode_lib, pipes, num_pipes); |
| |
| wm.cstate_exit_us = mode_lib->soc.sr_exit_time_us + last_pixel_of_line_extra_wm_us |
| + urgent_extra_latency_us |
| + mode_lib->ip.dcfclk_cstate_latency / dcfclk_deepsleep_freq; |
| wm.cstate_enter_plus_exit_us = mode_lib->soc.sr_enter_plus_exit_time_us |
| + last_pixel_of_line_extra_wm_us + urgent_extra_latency_us; |
| wm.pstate_change_us = mode_lib->soc.dram_clock_change_latency_us + urgent_watermark_us; |
| |
| DTRACE("stutter_exit_watermark_us = %f", wm.cstate_exit_us); |
| DTRACE("stutter_enter_plus_exit_watermark_us = %f", wm.cstate_enter_plus_exit_us); |
| DTRACE("dram_clock_change_watermark_us = %f", wm.pstate_change_us); |
| |
| return wm; |
| } |
| |
| double dml_wm_writeback_pstate_e2e( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| struct _vcs_dpi_wm_calc_pipe_params_st *wm = mode_lib->wm_param; |
| unsigned int combined_pipes; |
| |
| memset(mode_lib->wm_param, 0, sizeof(mode_lib->wm_param)); |
| combined_pipes = dml_wm_e2e_to_wm(mode_lib, pipes, num_pipes, wm); |
| |
| |
| return dml_wm_writeback_pstate(mode_lib, wm, combined_pipes); |
| } |
| |
| double dml_wm_writeback_pstate( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_wm_calc_pipe_params_st *pipes, |
| unsigned int num_pipes) |
| { |
| unsigned int total_active_wb = 0; |
| double wm = 0.0; |
| double socclk_mhz = 0.0; |
| unsigned int i; |
| |
| DTRACE("calculating wb pstate watermark"); |
| for (i = 0; i < num_pipes; i++) { |
| if (pipes[i].output_type == dm_wb) |
| total_active_wb++; |
| ASSERT(socclk_mhz == 0.0 || socclk_mhz == pipes[i].socclk_mhz); |
| socclk_mhz = pipes[i].socclk_mhz; |
| } |
| |
| DTRACE("total wb outputs %d", total_active_wb); |
| DTRACE("socclk frequency %f Mhz", socclk_mhz); |
| |
| if (total_active_wb <= 1) { |
| wm = mode_lib->soc.writeback_dram_clock_change_latency_us; |
| } else { |
| wm = mode_lib->soc.writeback_dram_clock_change_latency_us |
| + (mode_lib->ip.writeback_chunk_size_kbytes * 1024.0) / 32.0 |
| / socclk_mhz; |
| } |
| |
| DTRACE("wb pstate watermark %f us", wm); |
| return wm; |
| } |
| |
| unsigned int dml_wm_e2e_to_wm( |
| struct display_mode_lib *mode_lib, |
| struct _vcs_dpi_display_e2e_pipe_params_st *e2e, |
| unsigned int num_pipes, |
| struct _vcs_dpi_wm_calc_pipe_params_st *wm) |
| { |
| unsigned int num_planes = 0; |
| bool visited[DC__NUM_PIPES]; |
| unsigned int i, j; |
| |
| for (i = 0; i < num_pipes; i++) { |
| visited[i] = false; |
| } |
| |
| for (i = 0; i < num_pipes; i++) { |
| unsigned int num_dpp = 1; |
| |
| if (visited[i]) { |
| continue; |
| } |
| |
| visited[i] = true; |
| |
| if (e2e[i].pipe.src.is_hsplit) { |
| for (j = i + 1; j < num_pipes; j++) { |
| if (e2e[j].pipe.src.is_hsplit && !visited[j] |
| && (e2e[i].pipe.src.hsplit_grp |
| == e2e[j].pipe.src.hsplit_grp)) { |
| num_dpp++; |
| visited[j] = true; |
| } |
| } |
| } |
| |
| wm[num_planes].num_dpp = num_dpp; |
| wm[num_planes].voltage = e2e[i].clks_cfg.voltage; |
| wm[num_planes].output_type = e2e[i].dout.output_type; |
| wm[num_planes].dcfclk_mhz = e2e[i].clks_cfg.dcfclk_mhz; |
| wm[num_planes].socclk_mhz = e2e[i].clks_cfg.socclk_mhz; |
| wm[num_planes].dppclk_mhz = e2e[i].clks_cfg.dppclk_mhz; |
| wm[num_planes].pixclk_mhz = e2e[i].pipe.dest.pixel_rate_mhz; |
| |
| wm[num_planes].pte_enable = e2e[i].pipe.src.vm; |
| wm[num_planes].dcc_enable = e2e[i].pipe.src.dcc; |
| wm[num_planes].dcc_rate = e2e[i].pipe.src.dcc_rate; |
| |
| get_bytes_per_pixel( |
| (enum source_format_class) e2e[i].pipe.src.source_format, |
| &wm[num_planes]); |
| wm[num_planes].swath_width_y = get_swath_width_y(&e2e[i].pipe.src, num_dpp); |
| get_swath_height( |
| mode_lib, |
| &e2e[i].pipe.src, |
| &wm[num_planes], |
| wm[num_planes].swath_width_y); |
| |
| wm[num_planes].interlace_en = e2e[i].pipe.dest.interlaced; |
| wm[num_planes].h_ratio = e2e[i].pipe.scale_ratio_depth.hscl_ratio; |
| wm[num_planes].v_ratio = e2e[i].pipe.scale_ratio_depth.vscl_ratio; |
| if (wm[num_planes].interlace_en) { |
| wm[num_planes].v_ratio = 2 * wm[num_planes].v_ratio; |
| } |
| wm[num_planes].h_taps = e2e[i].pipe.scale_taps.htaps; |
| wm[num_planes].h_total = e2e[i].pipe.dest.htotal; |
| wm[num_planes].v_total = e2e[i].pipe.dest.vtotal; |
| wm[num_planes].v_active = e2e[i].pipe.dest.vactive; |
| wm[num_planes].e2e_index = i; |
| num_planes++; |
| } |
| |
| for (i = 0; i < num_planes; i++) { |
| DTRACE("plane[%d] start", i); |
| DTRACE("voltage = %d", wm[i].voltage); |
| DTRACE("v_active = %d", wm[i].v_active); |
| DTRACE("h_total = %d", wm[i].h_total); |
| DTRACE("v_total = %d", wm[i].v_total); |
| DTRACE("pixclk_mhz = %f", wm[i].pixclk_mhz); |
| DTRACE("dcfclk_mhz = %f", wm[i].dcfclk_mhz); |
| DTRACE("dppclk_mhz = %f", wm[i].dppclk_mhz); |
| DTRACE("h_ratio = %f", wm[i].h_ratio); |
| DTRACE("v_ratio = %f", wm[i].v_ratio); |
| DTRACE("interlaced = %d", wm[i].interlace_en); |
| DTRACE("h_taps = %d", wm[i].h_taps); |
| DTRACE("num_dpp = %d", wm[i].num_dpp); |
| DTRACE("swath_width_y = %d", wm[i].swath_width_y); |
| DTRACE("swath_height_y = %d", wm[i].swath_height_y); |
| DTRACE("swath_height_c = %d", wm[i].swath_height_c); |
| DTRACE("det_buffer_size_y = %d", wm[i].det_buffer_size_y); |
| DTRACE("dcc_rate = %f", wm[i].dcc_rate); |
| DTRACE("dcc_enable = %s", wm[i].dcc_enable ? "true" : "false"); |
| DTRACE("pte_enable = %s", wm[i].pte_enable ? "true" : "false"); |
| DTRACE("plane[%d] end", i); |
| } |
| |
| return num_planes; |
| } |