summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/display/dc/dsc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
commitace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch)
treeb2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/gpu/drm/amd/display/dc/dsc
parentInitial commit. (diff)
downloadlinux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz
linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dsc')
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/Makefile8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c1228
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dscc_types.h55
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c64
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h35
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c123
6 files changed, 1513 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/Makefile b/drivers/gpu/drm/amd/display/dc/dsc/Makefile
new file mode 100644
index 0000000000..a2537229ee
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dsc/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: MIT
+#
+# Makefile for the 'dsc' sub-component of DAL.
+DSC = dc_dsc.o rc_calc.o rc_calc_dpi.o
+
+AMD_DAL_DSC = $(addprefix $(AMDDALPATH)/dc/dsc/,$(DSC))
+
+AMD_DISPLAY_FILES += $(AMD_DAL_DSC)
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
new file mode 100644
index 0000000000..3966845c76
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
@@ -0,0 +1,1228 @@
+/*
+ * Copyright 2019 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.
+ *
+ * Author: AMD
+ */
+
+#include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_dsc_helper.h>
+#include "dc_hw_types.h"
+#include "dsc.h"
+#include "dc.h"
+#include "rc_calc.h"
+#include "fixed31_32.h"
+
+/* This module's internal functions */
+
+/* default DSC policy target bitrate limit is 16bpp */
+static uint32_t dsc_policy_max_target_bpp_limit = 16;
+
+/* default DSC policy enables DSC only when needed */
+static bool dsc_policy_enable_dsc_when_not_needed;
+
+static bool dsc_policy_disable_dsc_stream_overhead;
+
+static bool disable_128b_132b_stream_overhead;
+
+#ifndef MAX
+#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+#endif
+#ifndef MIN
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+#endif
+
+/* Need to account for padding due to pixel-to-symbol packing
+ * for uncompressed 128b/132b streams.
+ */
+static uint32_t apply_128b_132b_stream_overhead(
+ const struct dc_crtc_timing *timing, const uint32_t kbps)
+{
+ uint32_t total_kbps = kbps;
+
+ if (disable_128b_132b_stream_overhead)
+ return kbps;
+
+ if (!timing->flags.DSC) {
+ struct fixed31_32 bpp;
+ struct fixed31_32 overhead_factor;
+
+ bpp = dc_fixpt_from_int(kbps);
+ bpp = dc_fixpt_div_int(bpp, timing->pix_clk_100hz / 10);
+
+ /* Symbols_per_HActive = HActive * bpp / (4 lanes * 32-bit symbol size)
+ * Overhead_factor = ceil(Symbols_per_HActive) / Symbols_per_HActive
+ */
+ overhead_factor = dc_fixpt_from_int(timing->h_addressable);
+ overhead_factor = dc_fixpt_mul(overhead_factor, bpp);
+ overhead_factor = dc_fixpt_div_int(overhead_factor, 128);
+ overhead_factor = dc_fixpt_div(
+ dc_fixpt_from_int(dc_fixpt_ceil(overhead_factor)),
+ overhead_factor);
+
+ total_kbps = dc_fixpt_ceil(
+ dc_fixpt_mul_int(overhead_factor, total_kbps));
+ }
+
+ return total_kbps;
+}
+
+uint32_t dc_bandwidth_in_kbps_from_timing(
+ const struct dc_crtc_timing *timing,
+ const enum dc_link_encoding_format link_encoding)
+{
+ uint32_t bits_per_channel = 0;
+ uint32_t kbps;
+
+ if (timing->flags.DSC)
+ return dc_dsc_stream_bandwidth_in_kbps(timing,
+ timing->dsc_cfg.bits_per_pixel,
+ timing->dsc_cfg.num_slices_h,
+ timing->dsc_cfg.is_dp);
+
+ switch (timing->display_color_depth) {
+ case COLOR_DEPTH_666:
+ bits_per_channel = 6;
+ break;
+ case COLOR_DEPTH_888:
+ bits_per_channel = 8;
+ break;
+ case COLOR_DEPTH_101010:
+ bits_per_channel = 10;
+ break;
+ case COLOR_DEPTH_121212:
+ bits_per_channel = 12;
+ break;
+ case COLOR_DEPTH_141414:
+ bits_per_channel = 14;
+ break;
+ case COLOR_DEPTH_161616:
+ bits_per_channel = 16;
+ break;
+ default:
+ ASSERT(bits_per_channel != 0);
+ bits_per_channel = 8;
+ break;
+ }
+
+ kbps = timing->pix_clk_100hz / 10;
+ kbps *= bits_per_channel;
+
+ if (timing->flags.Y_ONLY != 1) {
+ /*Only YOnly make reduce bandwidth by 1/3 compares to RGB*/
+ kbps *= 3;
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
+ kbps /= 2;
+ else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422)
+ kbps = kbps * 2 / 3;
+ }
+
+ if (link_encoding == DC_LINK_ENCODING_DP_128b_132b)
+ kbps = apply_128b_132b_stream_overhead(timing, kbps);
+
+ return kbps;
+}
+
+
+/* Forward Declerations */
+static bool decide_dsc_bandwidth_range(
+ const uint32_t min_bpp_x16,
+ const uint32_t max_bpp_x16,
+ const uint32_t num_slices_h,
+ const struct dsc_enc_caps *dsc_caps,
+ const struct dc_crtc_timing *timing,
+ const enum dc_link_encoding_format link_encoding,
+ struct dc_dsc_bw_range *range);
+
+static uint32_t compute_bpp_x16_from_target_bandwidth(
+ const uint32_t bandwidth_in_kbps,
+ const struct dc_crtc_timing *timing,
+ const uint32_t num_slices_h,
+ const uint32_t bpp_increment_div,
+ const bool is_dp);
+
+static void get_dsc_enc_caps(
+ const struct display_stream_compressor *dsc,
+ struct dsc_enc_caps *dsc_enc_caps,
+ int pixel_clock_100Hz);
+
+static bool intersect_dsc_caps(
+ const struct dsc_dec_dpcd_caps *dsc_sink_caps,
+ const struct dsc_enc_caps *dsc_enc_caps,
+ enum dc_pixel_encoding pixel_encoding,
+ struct dsc_enc_caps *dsc_common_caps);
+
+static bool setup_dsc_config(
+ const struct dsc_dec_dpcd_caps *dsc_sink_caps,
+ const struct dsc_enc_caps *dsc_enc_caps,
+ int target_bandwidth_kbps,
+ const struct dc_crtc_timing *timing,
+ const struct dc_dsc_config_options *options,
+ const enum dc_link_encoding_format link_encoding,
+ struct dc_dsc_config *dsc_cfg);
+
+static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int *buff_block_size)
+{
+
+ switch (dpcd_buff_block_size) {
+ case DP_DSC_RC_BUF_BLK_SIZE_1:
+ *buff_block_size = 1024;
+ break;
+ case DP_DSC_RC_BUF_BLK_SIZE_4:
+ *buff_block_size = 4 * 1024;
+ break;
+ case DP_DSC_RC_BUF_BLK_SIZE_16:
+ *buff_block_size = 16 * 1024;
+ break;
+ case DP_DSC_RC_BUF_BLK_SIZE_64:
+ *buff_block_size = 64 * 1024;
+ break;
+ default: {
+ dm_error("%s: DPCD DSC buffer size not recognized.\n", __func__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *line_buff_bit_depth)
+{
+ if (0 <= dpcd_line_buff_bit_depth && dpcd_line_buff_bit_depth <= 7)
+ *line_buff_bit_depth = dpcd_line_buff_bit_depth + 9;
+ else if (dpcd_line_buff_bit_depth == 8)
+ *line_buff_bit_depth = 8;
+ else {
+ dm_error("%s: DPCD DSC buffer depth not recognized.\n", __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput)
+{
+ switch (dpcd_throughput) {
+ case DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED:
+ *throughput = 0;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_170:
+ *throughput = 170;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_340:
+ *throughput = 340;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_400:
+ *throughput = 400;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_450:
+ *throughput = 450;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_500:
+ *throughput = 500;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_550:
+ *throughput = 550;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_600:
+ *throughput = 600;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_650:
+ *throughput = 650;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_700:
+ *throughput = 700;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_750:
+ *throughput = 750;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_800:
+ *throughput = 800;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_850:
+ *throughput = 850;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_900:
+ *throughput = 900;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_950:
+ *throughput = 950;
+ break;
+ case DP_DSC_THROUGHPUT_MODE_0_1000:
+ *throughput = 1000;
+ break;
+ default: {
+ dm_error("%s: DPCD DSC throughput mode not recognized.\n", __func__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+static bool dsc_bpp_increment_div_from_dpcd(uint8_t bpp_increment_dpcd, uint32_t *bpp_increment_div)
+{
+ // Mask bpp increment dpcd field to avoid reading other fields
+ bpp_increment_dpcd &= 0x7;
+
+ switch (bpp_increment_dpcd) {
+ case 0:
+ *bpp_increment_div = 16;
+ break;
+ case 1:
+ *bpp_increment_div = 8;
+ break;
+ case 2:
+ *bpp_increment_div = 4;
+ break;
+ case 3:
+ *bpp_increment_div = 2;
+ break;
+ case 4:
+ *bpp_increment_div = 1;
+ break;
+ default: {
+ dm_error("%s: DPCD DSC bits-per-pixel increment not recognized.\n", __func__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
+ const uint8_t *dpcd_dsc_basic_data,
+ const uint8_t *dpcd_dsc_branch_decoder_caps,
+ struct dsc_dec_dpcd_caps *dsc_sink_caps)
+{
+ if (!dpcd_dsc_basic_data)
+ return false;
+
+ dsc_sink_caps->is_dsc_supported =
+ (dpcd_dsc_basic_data[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_DECOMPRESSION_IS_SUPPORTED) != 0;
+ if (!dsc_sink_caps->is_dsc_supported)
+ return false;
+
+ dsc_sink_caps->dsc_version = dpcd_dsc_basic_data[DP_DSC_REV - DP_DSC_SUPPORT];
+
+ {
+ int buff_block_size;
+ int buff_size;
+
+ if (!dsc_buff_block_size_from_dpcd(dpcd_dsc_basic_data[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT],
+ &buff_block_size))
+ return false;
+
+ buff_size = dpcd_dsc_basic_data[DP_DSC_RC_BUF_SIZE - DP_DSC_SUPPORT] + 1;
+ dsc_sink_caps->rc_buffer_size = buff_size * buff_block_size;
+ }
+
+ dsc_sink_caps->slice_caps1.raw = dpcd_dsc_basic_data[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT];
+ if (!dsc_line_buff_depth_from_dpcd(dpcd_dsc_basic_data[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT],
+ &dsc_sink_caps->lb_bit_depth))
+ return false;
+
+ dsc_sink_caps->is_block_pred_supported =
+ (dpcd_dsc_basic_data[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT] &
+ DP_DSC_BLK_PREDICTION_IS_SUPPORTED) != 0;
+
+ dsc_sink_caps->edp_max_bits_per_pixel =
+ dpcd_dsc_basic_data[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] |
+ dpcd_dsc_basic_data[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] << 8;
+
+ dsc_sink_caps->color_formats.raw = dpcd_dsc_basic_data[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT];
+ dsc_sink_caps->color_depth.raw = dpcd_dsc_basic_data[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT];
+
+ {
+ int dpcd_throughput = dpcd_dsc_basic_data[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT];
+
+ if (!dsc_throughput_from_dpcd(dpcd_throughput & DP_DSC_THROUGHPUT_MODE_0_MASK,
+ &dsc_sink_caps->throughput_mode_0_mps))
+ return false;
+
+ dpcd_throughput = (dpcd_throughput & DP_DSC_THROUGHPUT_MODE_1_MASK) >> DP_DSC_THROUGHPUT_MODE_1_SHIFT;
+ if (!dsc_throughput_from_dpcd(dpcd_throughput, &dsc_sink_caps->throughput_mode_1_mps))
+ return false;
+ }
+
+ dsc_sink_caps->max_slice_width = dpcd_dsc_basic_data[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * 320;
+ dsc_sink_caps->slice_caps2.raw = dpcd_dsc_basic_data[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT];
+
+ if (!dsc_bpp_increment_div_from_dpcd(dpcd_dsc_basic_data[DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT],
+ &dsc_sink_caps->bpp_increment_div))
+ return false;
+
+ if (dc->debug.dsc_bpp_increment_div) {
+ /* dsc_bpp_increment_div should onl be 1, 2, 4, 8 or 16, but rather than rejecting invalid values,
+ * we'll accept all and get it into range. This also makes the above check against 0 redundant,
+ * but that one stresses out the override will be only used if it's not 0.
+ */
+ if (dc->debug.dsc_bpp_increment_div >= 1)
+ dsc_sink_caps->bpp_increment_div = 1;
+ if (dc->debug.dsc_bpp_increment_div >= 2)
+ dsc_sink_caps->bpp_increment_div = 2;
+ if (dc->debug.dsc_bpp_increment_div >= 4)
+ dsc_sink_caps->bpp_increment_div = 4;
+ if (dc->debug.dsc_bpp_increment_div >= 8)
+ dsc_sink_caps->bpp_increment_div = 8;
+ if (dc->debug.dsc_bpp_increment_div >= 16)
+ dsc_sink_caps->bpp_increment_div = 16;
+ }
+
+ /* Extended caps */
+ if (dpcd_dsc_branch_decoder_caps == NULL) { // branch decoder DPCD DSC data can be null for non branch device
+ dsc_sink_caps->branch_overall_throughput_0_mps = 0;
+ dsc_sink_caps->branch_overall_throughput_1_mps = 0;
+ dsc_sink_caps->branch_max_line_width = 0;
+ return true;
+ }
+
+ dsc_sink_caps->branch_overall_throughput_0_mps =
+ dpcd_dsc_branch_decoder_caps[DP_DSC_BRANCH_OVERALL_THROUGHPUT_0 - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0];
+ if (dsc_sink_caps->branch_overall_throughput_0_mps == 0)
+ dsc_sink_caps->branch_overall_throughput_0_mps = 0;
+ else if (dsc_sink_caps->branch_overall_throughput_0_mps == 1)
+ dsc_sink_caps->branch_overall_throughput_0_mps = 680;
+ else {
+ dsc_sink_caps->branch_overall_throughput_0_mps *= 50;
+ dsc_sink_caps->branch_overall_throughput_0_mps += 600;
+ }
+
+ dsc_sink_caps->branch_overall_throughput_1_mps =
+ dpcd_dsc_branch_decoder_caps[DP_DSC_BRANCH_OVERALL_THROUGHPUT_1 - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0];
+ if (dsc_sink_caps->branch_overall_throughput_1_mps == 0)
+ dsc_sink_caps->branch_overall_throughput_1_mps = 0;
+ else if (dsc_sink_caps->branch_overall_throughput_1_mps == 1)
+ dsc_sink_caps->branch_overall_throughput_1_mps = 680;
+ else {
+ dsc_sink_caps->branch_overall_throughput_1_mps *= 50;
+ dsc_sink_caps->branch_overall_throughput_1_mps += 600;
+ }
+
+ dsc_sink_caps->branch_max_line_width =
+ dpcd_dsc_branch_decoder_caps[DP_DSC_BRANCH_MAX_LINE_WIDTH - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0] * 320;
+ ASSERT(dsc_sink_caps->branch_max_line_width == 0 || dsc_sink_caps->branch_max_line_width >= 5120);
+
+ dsc_sink_caps->is_dp = true;
+ return true;
+}
+
+
+/* If DSC is possbile, get DSC bandwidth range based on [min_bpp, max_bpp] target bitrate range and
+ * timing's pixel clock and uncompressed bandwidth.
+ * If DSC is not possible, leave '*range' untouched.
+ */
+bool dc_dsc_compute_bandwidth_range(
+ const struct display_stream_compressor *dsc,
+ uint32_t dsc_min_slice_height_override,
+ uint32_t min_bpp_x16,
+ uint32_t max_bpp_x16,
+ const struct dsc_dec_dpcd_caps *dsc_sink_caps,
+ const struct dc_crtc_timing *timing,
+ const enum dc_link_encoding_format link_encoding,
+ struct dc_dsc_bw_range *range)
+{
+ bool is_dsc_possible = false;
+ struct dsc_enc_caps dsc_enc_caps;
+ struct dsc_enc_caps dsc_common_caps;
+ struct dc_dsc_config config;
+ struct dc_dsc_config_options options = {0};
+
+ options.dsc_min_slice_height_override = dsc_min_slice_height_override;
+ options.max_target_bpp_limit_override_x16 = max_bpp_x16;
+ options.slice_height_granularity = 1;
+
+ get_dsc_enc_caps(dsc, &dsc_enc_caps, timing->pix_clk_100hz);
+
+ is_dsc_possible = intersect_dsc_caps(dsc_sink_caps, &dsc_enc_caps,
+ timing->pixel_encoding, &dsc_common_caps);
+
+ if (is_dsc_possible)
+ is_dsc_possible = setup_dsc_config(dsc_sink_caps, &dsc_enc_caps, 0, timing,
+ &options, link_encoding, &config);
+
+ if (is_dsc_possible)
+ is_dsc_possible = decide_dsc_bandwidth_range(min_bpp_x16, max_bpp_x16,
+ config.num_slices_h, &dsc_common_caps, timing, link_encoding, range);
+
+ return is_dsc_possible;
+}
+
+static void get_dsc_enc_caps(
+ const struct display_stream_compressor *dsc,
+ struct dsc_enc_caps *dsc_enc_caps,
+ int pixel_clock_100Hz)
+{
+ // This is a static HW query, so we can use any DSC
+
+ memset(dsc_enc_caps, 0, sizeof(struct dsc_enc_caps));
+ if (dsc) {
+ if (!dsc->ctx->dc->debug.disable_dsc)
+ dsc->funcs->dsc_get_enc_caps(dsc_enc_caps, pixel_clock_100Hz);
+ if (dsc->ctx->dc->debug.native422_support)
+ dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1;
+ }
+}
+
+/* Returns 'false' if no intersection was found for at least one capability.
+ * It also implicitly validates some sink caps against invalid value of zero.
+ */
+static bool intersect_dsc_caps(
+ const struct dsc_dec_dpcd_caps *dsc_sink_caps,
+ const struct dsc_enc_caps *dsc_enc_caps,
+ enum dc_pixel_encoding pixel_encoding,
+ struct dsc_enc_caps *dsc_common_caps)
+{
+ int32_t max_slices;
+ int32_t total_sink_throughput;
+
+ memset(dsc_common_caps, 0, sizeof(struct dsc_enc_caps));
+
+ dsc_common_caps->dsc_version = min(dsc_sink_caps->dsc_version, dsc_enc_caps->dsc_version);
+ if (!dsc_common_caps->dsc_version)
+ return false;
+
+ dsc_common_caps->slice_caps.bits.NUM_SLICES_1 =
+ dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 && dsc_enc_caps->slice_caps.bits.NUM_SLICES_1;
+ dsc_common_caps->slice_caps.bits.NUM_SLICES_2 =
+ dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 && dsc_enc_caps->slice_caps.bits.NUM_SLICES_2;
+ dsc_common_caps->slice_caps.bits.NUM_SLICES_4 =
+ dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 && dsc_enc_caps->slice_caps.bits.NUM_SLICES_4;
+ dsc_common_caps->slice_caps.bits.NUM_SLICES_8 =
+ dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 && dsc_enc_caps->slice_caps.bits.NUM_SLICES_8;
+ if (!dsc_common_caps->slice_caps.raw)
+ return false;
+
+ dsc_common_caps->lb_bit_depth = min(dsc_sink_caps->lb_bit_depth, dsc_enc_caps->lb_bit_depth);
+ if (!dsc_common_caps->lb_bit_depth)
+ return false;
+
+ dsc_common_caps->is_block_pred_supported =
+ dsc_sink_caps->is_block_pred_supported && dsc_enc_caps->is_block_pred_supported;
+
+ dsc_common_caps->color_formats.raw = dsc_sink_caps->color_formats.raw & dsc_enc_caps->color_formats.raw;
+ if (!dsc_common_caps->color_formats.raw)
+ return false;
+
+ dsc_common_caps->color_depth.raw = dsc_sink_caps->color_depth.raw & dsc_enc_caps->color_depth.raw;
+ if (!dsc_common_caps->color_depth.raw)
+ return false;
+
+ max_slices = 0;
+ if (dsc_common_caps->slice_caps.bits.NUM_SLICES_1)
+ max_slices = 1;
+
+ if (dsc_common_caps->slice_caps.bits.NUM_SLICES_2)
+ max_slices = 2;
+
+ if (dsc_common_caps->slice_caps.bits.NUM_SLICES_4)
+ max_slices = 4;
+
+ total_sink_throughput = max_slices * dsc_sink_caps->throughput_mode_0_mps;
+ if (pixel_encoding == PIXEL_ENCODING_YCBCR422 || pixel_encoding == PIXEL_ENCODING_YCBCR420)
+ total_sink_throughput = max_slices * dsc_sink_caps->throughput_mode_1_mps;
+
+ dsc_common_caps->max_total_throughput_mps = min(total_sink_throughput, dsc_enc_caps->max_total_throughput_mps);
+
+ dsc_common_caps->max_slice_width = min(dsc_sink_caps->max_slice_width, dsc_enc_caps->max_slice_width);
+ if (!dsc_common_caps->max_slice_width)
+ return false;
+
+ dsc_common_caps->bpp_increment_div = min(dsc_sink_caps->bpp_increment_div, dsc_enc_caps->bpp_increment_div);
+
+ // TODO DSC: Remove this workaround for N422 and 420 once it's fixed, or move it to get_dsc_encoder_caps()
+ if (pixel_encoding == PIXEL_ENCODING_YCBCR422 || pixel_encoding == PIXEL_ENCODING_YCBCR420)
+ dsc_common_caps->bpp_increment_div = min(dsc_common_caps->bpp_increment_div, (uint32_t)8);
+
+ dsc_common_caps->edp_sink_max_bits_per_pixel = dsc_sink_caps->edp_max_bits_per_pixel;
+ dsc_common_caps->is_dp = dsc_sink_caps->is_dp;
+ return true;
+}
+
+static inline uint32_t dsc_div_by_10_round_up(uint32_t value)
+{
+ return (value + 9) / 10;
+}
+
+static uint32_t compute_bpp_x16_from_target_bandwidth(
+ const uint32_t bandwidth_in_kbps,
+ const struct dc_crtc_timing *timing,
+ const uint32_t num_slices_h,
+ const uint32_t bpp_increment_div,
+ const bool is_dp)
+{
+ uint32_t overhead_in_kbps;
+ struct fixed31_32 effective_bandwidth_in_kbps;
+ struct fixed31_32 bpp_x16;
+
+ overhead_in_kbps = dc_dsc_stream_bandwidth_overhead_in_kbps(
+ timing, num_slices_h, is_dp);
+ effective_bandwidth_in_kbps = dc_fixpt_from_int(bandwidth_in_kbps);
+ effective_bandwidth_in_kbps = dc_fixpt_sub_int(effective_bandwidth_in_kbps,
+ overhead_in_kbps);
+ bpp_x16 = dc_fixpt_mul_int(effective_bandwidth_in_kbps, 10);
+ bpp_x16 = dc_fixpt_div_int(bpp_x16, timing->pix_clk_100hz);
+ bpp_x16 = dc_fixpt_from_int(dc_fixpt_floor(dc_fixpt_mul_int(bpp_x16, bpp_increment_div)));
+ bpp_x16 = dc_fixpt_div_int(bpp_x16, bpp_increment_div);
+ bpp_x16 = dc_fixpt_mul_int(bpp_x16, 16);
+ return dc_fixpt_floor(bpp_x16);
+}
+
+/* Decide DSC bandwidth range based on signal, timing, specs specific and input min and max
+ * requirements.
+ * The range output includes decided min/max target bpp, the respective bandwidth requirements
+ * and native timing bandwidth requirement when DSC is not used.
+ */
+static bool decide_dsc_bandwidth_range(
+ const uint32_t min_bpp_x16,
+ const uint32_t max_bpp_x16,
+ const uint32_t num_slices_h,
+ const struct dsc_enc_caps *dsc_caps,
+ const struct dc_crtc_timing *timing,
+ const enum dc_link_encoding_format link_encoding,
+ struct dc_dsc_bw_range *range)
+{
+ uint32_t preferred_bpp_x16 = timing->dsc_fixed_bits_per_pixel_x16;
+
+ memset(range, 0, sizeof(*range));
+
+ /* apply signal, timing, specs and explicitly specified DSC range requirements */
+ if (preferred_bpp_x16) {
+ if (preferred_bpp_x16 <= max_bpp_x16 &&
+ preferred_bpp_x16 >= min_bpp_x16) {
+ range->max_target_bpp_x16 = preferred_bpp_x16;
+ range->min_target_bpp_x16 = preferred_bpp_x16;
+ }
+ }
+ /* TODO - make this value generic to all signal types */
+ else if (dsc_caps->edp_sink_max_bits_per_pixel) {
+ /* apply max bpp limitation from edp sink */
+ range->max_target_bpp_x16 = MIN(dsc_caps->edp_sink_max_bits_per_pixel,
+ max_bpp_x16);
+ range->min_target_bpp_x16 = min_bpp_x16;
+ }
+ else {
+ range->max_target_bpp_x16 = max_bpp_x16;
+ range->min_target_bpp_x16 = min_bpp_x16;
+ }
+
+ /* populate output structure */
+ if (range->max_target_bpp_x16 >= range->min_target_bpp_x16 && range->min_target_bpp_x16 > 0) {
+ /* native stream bandwidth */
+ range->stream_kbps = dc_bandwidth_in_kbps_from_timing(timing, link_encoding);
+
+ /* max dsc target bpp */
+ range->max_kbps = dc_dsc_stream_bandwidth_in_kbps(timing,
+ range->max_target_bpp_x16, num_slices_h, dsc_caps->is_dp);
+
+ /* min dsc target bpp */
+ range->min_kbps = dc_dsc_stream_bandwidth_in_kbps(timing,
+ range->min_target_bpp_x16, num_slices_h, dsc_caps->is_dp);
+ }
+
+ return range->max_kbps >= range->min_kbps && range->min_kbps > 0;
+}
+
+/* Decides if DSC should be used and calculates target bpp if it should, applying DSC policy.
+ *
+ * Returns:
+ * - 'true' if target bpp is decided
+ * - 'false' if target bpp cannot be decided (e.g. cannot fit even with min DSC bpp),
+ */
+static bool decide_dsc_target_bpp_x16(
+ const struct dc_dsc_policy *policy,
+ const struct dsc_enc_caps *dsc_common_caps,
+ const int target_bandwidth_kbps,
+ const struct dc_crtc_timing *timing,
+ const int num_slices_h,
+ const enum dc_link_encoding_format link_encoding,
+ int *target_bpp_x16)
+{
+ struct dc_dsc_bw_range range;
+
+ *target_bpp_x16 = 0;
+
+ if (decide_dsc_bandwidth_range(policy->min_target_bpp * 16, policy->max_target_bpp * 16,
+ num_slices_h, dsc_common_caps, timing, link_encoding, &range)) {
+ if (target_bandwidth_kbps >= range.stream_kbps) {
+ if (policy->enable_dsc_when_not_needed)
+ /* enable max bpp even dsc is not needed */
+ *target_bpp_x16 = range.max_target_bpp_x16;
+ } else if (target_bandwidth_kbps >= range.max_kbps) {
+ /* use max target bpp allowed */
+ *target_bpp_x16 = range.max_target_bpp_x16;
+ } else if (target_bandwidth_kbps >= range.min_kbps) {
+ /* use target bpp that can take entire target bandwidth */
+ *target_bpp_x16 = compute_bpp_x16_from_target_bandwidth(
+ target_bandwidth_kbps, timing, num_slices_h,
+ dsc_common_caps->bpp_increment_div,
+ dsc_common_caps->is_dp);
+ }
+ }
+
+ return *target_bpp_x16 != 0;
+}
+
+#define MIN_AVAILABLE_SLICES_SIZE 6
+
+static int get_available_dsc_slices(union dsc_enc_slice_caps slice_caps, int *available_slices)
+{
+ int idx = 0;
+
+ if (slice_caps.bits.NUM_SLICES_1)
+ available_slices[idx++] = 1;
+
+ if (slice_caps.bits.NUM_SLICES_2)
+ available_slices[idx++] = 2;
+
+ if (slice_caps.bits.NUM_SLICES_4)
+ available_slices[idx++] = 4;
+
+ if (slice_caps.bits.NUM_SLICES_8)
+ available_slices[idx++] = 8;
+
+ return idx;
+}
+
+
+static int get_max_dsc_slices(union dsc_enc_slice_caps slice_caps)
+{
+ int max_slices = 0;
+ int available_slices[MIN_AVAILABLE_SLICES_SIZE];
+ int end_idx = get_available_dsc_slices(slice_caps, &available_slices[0]);
+
+ if (end_idx > 0)
+ max_slices = available_slices[end_idx - 1];
+
+ return max_slices;
+}
+
+
+// Increment slice number in available slice numbers stops if possible, or just increment if not
+static int inc_num_slices(union dsc_enc_slice_caps slice_caps, int num_slices)
+{
+ // Get next bigger num slices available in common caps
+ int available_slices[MIN_AVAILABLE_SLICES_SIZE];
+ int end_idx;
+ int i;
+ int new_num_slices = num_slices;
+
+ end_idx = get_available_dsc_slices(slice_caps, &available_slices[0]);
+ if (end_idx == 0) {
+ // No available slices found
+ new_num_slices++;
+ return new_num_slices;
+ }
+
+ // Numbers of slices found - get the next bigger number
+ for (i = 0; i < end_idx; i++) {
+ if (new_num_slices < available_slices[i]) {
+ new_num_slices = available_slices[i];
+ break;
+ }
+ }
+
+ if (new_num_slices == num_slices) // No bigger number of slices found
+ new_num_slices++;
+
+ return new_num_slices;
+}
+
+
+// Decrement slice number in available slice numbers stops if possible, or just decrement if not. Stop at zero.
+static int dec_num_slices(union dsc_enc_slice_caps slice_caps, int num_slices)
+{
+ // Get next bigger num slices available in common caps
+ int available_slices[MIN_AVAILABLE_SLICES_SIZE];
+ int end_idx;
+ int i;
+ int new_num_slices = num_slices;
+
+ end_idx = get_available_dsc_slices(slice_caps, &available_slices[0]);
+ if (end_idx == 0 && new_num_slices > 0) {
+ // No numbers of slices found
+ new_num_slices++;
+ return new_num_slices;
+ }
+
+ // Numbers of slices found - get the next smaller number
+ for (i = end_idx - 1; i >= 0; i--) {
+ if (new_num_slices > available_slices[i]) {
+ new_num_slices = available_slices[i];
+ break;
+ }
+ }
+
+ if (new_num_slices == num_slices) {
+ // No smaller number of slices found
+ new_num_slices--;
+ if (new_num_slices < 0)
+ new_num_slices = 0;
+ }
+
+ return new_num_slices;
+}
+
+
+// Choose next bigger number of slices if the requested number of slices is not available
+static int fit_num_slices_up(union dsc_enc_slice_caps slice_caps, int num_slices)
+{
+ // Get next bigger num slices available in common caps
+ int available_slices[MIN_AVAILABLE_SLICES_SIZE];
+ int end_idx;
+ int i;
+ int new_num_slices = num_slices;
+
+ end_idx = get_available_dsc_slices(slice_caps, &available_slices[0]);
+ if (end_idx == 0) {
+ // No available slices found
+ new_num_slices++;
+ return new_num_slices;
+ }
+
+ // Numbers of slices found - get the equal or next bigger number
+ for (i = 0; i < end_idx; i++) {
+ if (new_num_slices <= available_slices[i]) {
+ new_num_slices = available_slices[i];
+ break;
+ }
+ }
+
+ return new_num_slices;
+}
+
+
+/* Attempts to set DSC configuration for the stream, applying DSC policy.
+ * Returns 'true' if successful or 'false' if not.
+ *
+ * Parameters:
+ *
+ * dsc_sink_caps - DSC sink decoder capabilities (from DPCD)
+ *
+ * dsc_enc_caps - DSC encoder capabilities
+ *
+ * target_bandwidth_kbps - Target bandwidth to fit the stream into.
+ * If 0, do not calculate target bpp.
+ *
+ * timing - The stream timing to fit into 'target_bandwidth_kbps' or apply
+ * maximum compression to, if 'target_badwidth == 0'
+ *
+ * dsc_cfg - DSC configuration to use if it was possible to come up with
+ * one for the given inputs.
+ * The target bitrate after DSC can be calculated by multiplying
+ * dsc_cfg.bits_per_pixel (in U6.4 format) by pixel rate, e.g.
+ *
+ * dsc_stream_bitrate_kbps = (int)ceil(timing->pix_clk_khz * dsc_cfg.bits_per_pixel / 16.0);
+ */
+static bool setup_dsc_config(
+ const struct dsc_dec_dpcd_caps *dsc_sink_caps,
+ const struct dsc_enc_caps *dsc_enc_caps,
+ int target_bandwidth_kbps,
+ const struct dc_crtc_timing *timing,
+ const struct dc_dsc_config_options *options,
+ const enum dc_link_encoding_format link_encoding,
+ struct dc_dsc_config *dsc_cfg)
+{
+ struct dsc_enc_caps dsc_common_caps;
+ int max_slices_h;
+ int min_slices_h;
+ int num_slices_h;
+ int pic_width;
+ int slice_width;
+ int target_bpp;
+ int sink_per_slice_throughput_mps;
+ int branch_max_throughput_mps = 0;
+ bool is_dsc_possible = false;
+ int pic_height;
+ int slice_height;
+ struct dc_dsc_policy policy;
+
+ memset(dsc_cfg, 0, sizeof(struct dc_dsc_config));
+
+ dc_dsc_get_policy_for_timing(timing, options->max_target_bpp_limit_override_x16, &policy);
+ pic_width = timing->h_addressable + timing->h_border_left + timing->h_border_right;
+ pic_height = timing->v_addressable + timing->v_border_top + timing->v_border_bottom;
+
+ if (!dsc_sink_caps->is_dsc_supported)
+ goto done;
+
+ if (dsc_sink_caps->branch_max_line_width && dsc_sink_caps->branch_max_line_width < pic_width)
+ goto done;
+
+ // Intersect decoder with encoder DSC caps and validate DSC settings
+ is_dsc_possible = intersect_dsc_caps(dsc_sink_caps, dsc_enc_caps, timing->pixel_encoding, &dsc_common_caps);
+ if (!is_dsc_possible)
+ goto done;
+
+ sink_per_slice_throughput_mps = 0;
+
+ // Validate available DSC settings against the mode timing
+
+ // Validate color format (and pick up the throughput values)
+ dsc_cfg->ycbcr422_simple = false;
+ switch (timing->pixel_encoding) {
+ case PIXEL_ENCODING_RGB:
+ is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.RGB;
+ sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps;
+ branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_0_mps;
+ break;
+ case PIXEL_ENCODING_YCBCR444:
+ is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_444;
+ sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps;
+ branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_0_mps;
+ break;
+ case PIXEL_ENCODING_YCBCR422:
+ is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422;
+ sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps;
+ branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps;
+ if (!is_dsc_possible) {
+ is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_SIMPLE_422;
+ dsc_cfg->ycbcr422_simple = is_dsc_possible;
+ sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps;
+ }
+ break;
+ case PIXEL_ENCODING_YCBCR420:
+ is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_420;
+ sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps;
+ branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps;
+ break;
+ default:
+ is_dsc_possible = false;
+ }
+
+ // Validate branch's maximum throughput
+ if (branch_max_throughput_mps && dsc_div_by_10_round_up(timing->pix_clk_100hz) > branch_max_throughput_mps * 1000)
+ is_dsc_possible = false;
+
+ if (!is_dsc_possible)
+ goto done;
+
+ // Color depth
+ switch (timing->display_color_depth) {
+ case COLOR_DEPTH_888:
+ is_dsc_possible = (bool)dsc_common_caps.color_depth.bits.COLOR_DEPTH_8_BPC;
+ break;
+ case COLOR_DEPTH_101010:
+ is_dsc_possible = (bool)dsc_common_caps.color_depth.bits.COLOR_DEPTH_10_BPC;
+ break;
+ case COLOR_DEPTH_121212:
+ is_dsc_possible = (bool)dsc_common_caps.color_depth.bits.COLOR_DEPTH_12_BPC;
+ break;
+ default:
+ is_dsc_possible = false;
+ }
+
+ if (!is_dsc_possible)
+ goto done;
+
+ // Slice width (i.e. number of slices per line)
+ max_slices_h = get_max_dsc_slices(dsc_common_caps.slice_caps);
+
+ while (max_slices_h > 0) {
+ if (pic_width % max_slices_h == 0)
+ break;
+
+ max_slices_h = dec_num_slices(dsc_common_caps.slice_caps, max_slices_h);
+ }
+
+ is_dsc_possible = (dsc_common_caps.max_slice_width > 0);
+ if (!is_dsc_possible)
+ goto done;
+
+ min_slices_h = pic_width / dsc_common_caps.max_slice_width;
+ if (pic_width % dsc_common_caps.max_slice_width)
+ min_slices_h++;
+
+ min_slices_h = fit_num_slices_up(dsc_common_caps.slice_caps, min_slices_h);
+
+ while (min_slices_h <= max_slices_h) {
+ int pix_clk_per_slice_khz = dsc_div_by_10_round_up(timing->pix_clk_100hz) / min_slices_h;
+ if (pix_clk_per_slice_khz <= sink_per_slice_throughput_mps * 1000)
+ break;
+
+ min_slices_h = inc_num_slices(dsc_common_caps.slice_caps, min_slices_h);
+ }
+
+ is_dsc_possible = (min_slices_h <= max_slices_h);
+
+ if (pic_width % min_slices_h != 0)
+ min_slices_h = 0; // DSC TODO: Maybe try increasing the number of slices first?
+
+ if (min_slices_h == 0 && max_slices_h == 0)
+ is_dsc_possible = false;
+
+ if (!is_dsc_possible)
+ goto done;
+
+ if (policy.use_min_slices_h) {
+ if (min_slices_h > 0)
+ num_slices_h = min_slices_h;
+ else if (max_slices_h > 0) { // Fall back to max slices if min slices is not working out
+ if (policy.max_slices_h)
+ num_slices_h = min(policy.max_slices_h, max_slices_h);
+ else
+ num_slices_h = max_slices_h;
+ } else
+ is_dsc_possible = false;
+ } else {
+ if (max_slices_h > 0) {
+ if (policy.max_slices_h)
+ num_slices_h = min(policy.max_slices_h, max_slices_h);
+ else
+ num_slices_h = max_slices_h;
+ } else if (min_slices_h > 0) // Fall back to min slices if max slices is not possible
+ num_slices_h = min_slices_h;
+ else
+ is_dsc_possible = false;
+ }
+ // When we force 2:1 ODM, we can't have 1 slice to divide amongst 2 separate DSC instances
+ // need to enforce at minimum 2 horizontal slices
+ if (options->dsc_force_odm_hslice_override) {
+ num_slices_h = fit_num_slices_up(dsc_common_caps.slice_caps, 2);
+ if (num_slices_h == 0)
+ is_dsc_possible = false;
+ }
+
+ if (!is_dsc_possible)
+ goto done;
+
+ dsc_cfg->num_slices_h = num_slices_h;
+ slice_width = pic_width / num_slices_h;
+
+ is_dsc_possible = slice_width <= dsc_common_caps.max_slice_width;
+ if (!is_dsc_possible)
+ goto done;
+
+ // Slice height (i.e. number of slices per column): start with policy and pick the first one that height is divisible by.
+ // For 4:2:0 make sure the slice height is divisible by 2 as well.
+ if (options->dsc_min_slice_height_override == 0)
+ slice_height = min(policy.min_slice_height, pic_height);
+ else
+ slice_height = min((int)(options->dsc_min_slice_height_override), pic_height);
+
+ while (slice_height < pic_height && (pic_height % slice_height != 0 ||
+ slice_height % options->slice_height_granularity != 0 ||
+ (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 && slice_height % 2 != 0)))
+ slice_height++;
+
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) // For the case when pic_height < dsc_policy.min_sice_height
+ is_dsc_possible = (slice_height % 2 == 0);
+
+ if (!is_dsc_possible)
+ goto done;
+
+ dsc_cfg->num_slices_v = pic_height/slice_height;
+
+ if (target_bandwidth_kbps > 0) {
+ is_dsc_possible = decide_dsc_target_bpp_x16(
+ &policy,
+ &dsc_common_caps,
+ target_bandwidth_kbps,
+ timing,
+ num_slices_h,
+ link_encoding,
+ &target_bpp);
+ dsc_cfg->bits_per_pixel = target_bpp;
+ }
+ if (!is_dsc_possible)
+ goto done;
+
+ // Final decission: can we do DSC or not?
+ if (is_dsc_possible) {
+ // Fill out the rest of DSC settings
+ dsc_cfg->block_pred_enable = dsc_common_caps.is_block_pred_supported;
+ dsc_cfg->linebuf_depth = dsc_common_caps.lb_bit_depth;
+ dsc_cfg->version_minor = (dsc_common_caps.dsc_version & 0xf0) >> 4;
+ dsc_cfg->is_dp = dsc_sink_caps->is_dp;
+ }
+
+done:
+ if (!is_dsc_possible)
+ memset(dsc_cfg, 0, sizeof(struct dc_dsc_config));
+
+ return is_dsc_possible;
+}
+
+bool dc_dsc_compute_config(
+ const struct display_stream_compressor *dsc,
+ const struct dsc_dec_dpcd_caps *dsc_sink_caps,
+ const struct dc_dsc_config_options *options,
+ uint32_t target_bandwidth_kbps,
+ const struct dc_crtc_timing *timing,
+ const enum dc_link_encoding_format link_encoding,
+ struct dc_dsc_config *dsc_cfg)
+{
+ bool is_dsc_possible = false;
+ struct dsc_enc_caps dsc_enc_caps;
+
+ get_dsc_enc_caps(dsc, &dsc_enc_caps, timing->pix_clk_100hz);
+ is_dsc_possible = setup_dsc_config(dsc_sink_caps,
+ &dsc_enc_caps,
+ target_bandwidth_kbps,
+ timing, options, link_encoding, dsc_cfg);
+ return is_dsc_possible;
+}
+
+uint32_t dc_dsc_stream_bandwidth_in_kbps(const struct dc_crtc_timing *timing,
+ uint32_t bpp_x16, uint32_t num_slices_h, bool is_dp)
+{
+ uint32_t overhead_in_kbps;
+ struct fixed31_32 bpp;
+ struct fixed31_32 actual_bandwidth_in_kbps;
+
+ overhead_in_kbps = dc_dsc_stream_bandwidth_overhead_in_kbps(
+ timing, num_slices_h, is_dp);
+ bpp = dc_fixpt_from_fraction(bpp_x16, 16);
+ actual_bandwidth_in_kbps = dc_fixpt_from_fraction(timing->pix_clk_100hz, 10);
+ actual_bandwidth_in_kbps = dc_fixpt_mul(actual_bandwidth_in_kbps, bpp);
+ actual_bandwidth_in_kbps = dc_fixpt_add_int(actual_bandwidth_in_kbps, overhead_in_kbps);
+ return dc_fixpt_ceil(actual_bandwidth_in_kbps);
+}
+
+uint32_t dc_dsc_stream_bandwidth_overhead_in_kbps(
+ const struct dc_crtc_timing *timing,
+ const int num_slices_h,
+ const bool is_dp)
+{
+ struct fixed31_32 max_dsc_overhead;
+ struct fixed31_32 refresh_rate;
+
+ if (dsc_policy_disable_dsc_stream_overhead || !is_dp)
+ return 0;
+
+ /* use target bpp that can take entire target bandwidth */
+ refresh_rate = dc_fixpt_from_int(timing->pix_clk_100hz);
+ refresh_rate = dc_fixpt_div_int(refresh_rate, timing->h_total);
+ refresh_rate = dc_fixpt_div_int(refresh_rate, timing->v_total);
+ refresh_rate = dc_fixpt_mul_int(refresh_rate, 100);
+
+ max_dsc_overhead = dc_fixpt_from_int(num_slices_h);
+ max_dsc_overhead = dc_fixpt_mul_int(max_dsc_overhead, timing->v_total);
+ max_dsc_overhead = dc_fixpt_mul_int(max_dsc_overhead, 256);
+ max_dsc_overhead = dc_fixpt_div_int(max_dsc_overhead, 1000);
+ max_dsc_overhead = dc_fixpt_mul(max_dsc_overhead, refresh_rate);
+
+ return dc_fixpt_ceil(max_dsc_overhead);
+}
+
+void dc_dsc_get_policy_for_timing(const struct dc_crtc_timing *timing,
+ uint32_t max_target_bpp_limit_override_x16,
+ struct dc_dsc_policy *policy)
+{
+ uint32_t bpc = 0;
+
+ policy->min_target_bpp = 0;
+ policy->max_target_bpp = 0;
+
+ /* DSC Policy: Use minimum number of slices that fits the pixel clock */
+ policy->use_min_slices_h = true;
+
+ /* DSC Policy: Use max available slices
+ * (in our case 4 for or 8, depending on the mode)
+ */
+ policy->max_slices_h = 0;
+
+ /* DSC Policy: Use slice height recommended
+ * by VESA DSC Spreadsheet user guide
+ */
+ policy->min_slice_height = 108;
+
+ /* DSC Policy: follow DP specs with an internal upper limit to 16 bpp
+ * for better interoperability
+ */
+ switch (timing->display_color_depth) {
+ case COLOR_DEPTH_888:
+ bpc = 8;
+ break;
+ case COLOR_DEPTH_101010:
+ bpc = 10;
+ break;
+ case COLOR_DEPTH_121212:
+ bpc = 12;
+ break;
+ default:
+ return;
+ }
+ switch (timing->pixel_encoding) {
+ case PIXEL_ENCODING_RGB:
+ case PIXEL_ENCODING_YCBCR444:
+ case PIXEL_ENCODING_YCBCR422: /* assume no YCbCr422 native support */
+ /* DP specs limits to 8 */
+ policy->min_target_bpp = 8;
+ /* DP specs limits to 3 x bpc */
+ policy->max_target_bpp = 3 * bpc;
+ break;
+ case PIXEL_ENCODING_YCBCR420:
+ /* DP specs limits to 6 */
+ policy->min_target_bpp = 6;
+ /* DP specs limits to 1.5 x bpc assume bpc is an even number */
+ policy->max_target_bpp = bpc * 3 / 2;
+ break;
+ default:
+ return;
+ }
+
+ /* internal upper limit, default 16 bpp */
+ if (policy->max_target_bpp > dsc_policy_max_target_bpp_limit)
+ policy->max_target_bpp = dsc_policy_max_target_bpp_limit;
+
+ /* apply override */
+ if (max_target_bpp_limit_override_x16 && policy->max_target_bpp > max_target_bpp_limit_override_x16 / 16)
+ policy->max_target_bpp = max_target_bpp_limit_override_x16 / 16;
+
+ /* enable DSC when not needed, default false */
+ if (dsc_policy_enable_dsc_when_not_needed)
+ policy->enable_dsc_when_not_needed = dsc_policy_enable_dsc_when_not_needed;
+ else
+ policy->enable_dsc_when_not_needed = false;
+}
+
+void dc_dsc_policy_set_max_target_bpp_limit(uint32_t limit)
+{
+ dsc_policy_max_target_bpp_limit = limit;
+}
+
+void dc_dsc_policy_set_enable_dsc_when_not_needed(bool enable)
+{
+ dsc_policy_enable_dsc_when_not_needed = enable;
+}
+
+void dc_dsc_policy_set_disable_dsc_stream_overhead(bool disable)
+{
+ dsc_policy_disable_dsc_stream_overhead = disable;
+}
+
+void dc_set_disable_128b_132b_stream_overhead(bool disable)
+{
+ disable_128b_132b_stream_overhead = disable;
+}
+
+void dc_dsc_get_default_config_option(const struct dc *dc, struct dc_dsc_config_options *options)
+{
+ options->dsc_min_slice_height_override = dc->debug.dsc_min_slice_height_override;
+ options->dsc_force_odm_hslice_override = dc->debug.force_odm_combine;
+ options->max_target_bpp_limit_override_x16 = 0;
+ options->slice_height_granularity = 1;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dscc_types.h b/drivers/gpu/drm/amd/display/dc/dsc/dscc_types.h
new file mode 100644
index 0000000000..31574940cc
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dscc_types.h
@@ -0,0 +1,55 @@
+
+/*
+ * 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
+ *
+ */
+#ifndef __DSCC_TYPES_H__
+#define __DSCC_TYPES_H__
+
+#include <drm/display/drm_dsc.h>
+
+#ifndef NUM_BUF_RANGES
+#define NUM_BUF_RANGES 15
+#endif
+
+struct dsc_pps_rc_range {
+ int range_min_qp;
+ int range_max_qp;
+ int range_bpg_offset;
+};
+
+struct dsc_parameters {
+ struct drm_dsc_config pps;
+
+ /* Additional parameters for register programming */
+ uint32_t bytes_per_pixel; /* In u3.28 format */
+ uint32_t rc_buffer_model_size;
+};
+
+struct rc_params;
+
+int dscc_compute_dsc_parameters(const struct drm_dsc_config *pps,
+ const struct rc_params *rc,
+ struct dsc_parameters *dsc_params);
+#endif
+
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c
new file mode 100644
index 0000000000..64cee8c801
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c
@@ -0,0 +1,64 @@
+
+/*
+ * 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 "rc_calc.h"
+
+/**
+ * calc_rc_params - reads the user's cmdline mode
+ * @rc: DC internal DSC parameters
+ * @pps: DRM struct with all required DSC values
+ *
+ * This function expects a drm_dsc_config data struct with all the required DSC
+ * values previously filled out by our driver and based on this information it
+ * computes some of the DSC values.
+ *
+ * @note This calculation requires float point operation, most of it executes
+ * under kernel_fpu_{begin,end}.
+ */
+void calc_rc_params(struct rc_params *rc, const struct drm_dsc_config *pps)
+{
+#if defined(CONFIG_DRM_AMD_DC_FP)
+ enum colour_mode mode;
+ enum bits_per_comp bpc;
+ bool is_navite_422_or_420;
+ u16 drm_bpp = pps->bits_per_pixel;
+ int slice_width = pps->slice_width;
+ int slice_height = pps->slice_height;
+
+ mode = pps->convert_rgb ? CM_RGB : (pps->simple_422 ? CM_444 :
+ (pps->native_422 ? CM_422 :
+ pps->native_420 ? CM_420 : CM_444));
+ bpc = (pps->bits_per_component == 8) ? BPC_8 : (pps->bits_per_component == 10)
+ ? BPC_10 : BPC_12;
+
+ is_navite_422_or_420 = pps->native_422 || pps->native_420;
+
+ DC_FP_START();
+ _do_calc_rc_params(rc, mode, bpc, drm_bpp, is_navite_422_or_420,
+ slice_width, slice_height,
+ pps->dsc_version_minor);
+ DC_FP_END();
+#endif
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h
new file mode 100644
index 0000000000..80921c1c0d
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h
@@ -0,0 +1,35 @@
+
+/*
+ * 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
+ *
+ */
+
+#ifndef __RC_CALC_H__
+#define __RC_CALC_H__
+
+#include "dml/dsc/rc_calc_fpu.h"
+
+void calc_rc_params(struct rc_params *rc, const struct drm_dsc_config *pps);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c
new file mode 100644
index 0000000000..36d6c1646a
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2012-17 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 <drm/display/drm_dsc_helper.h>
+#include "dscc_types.h"
+#include "rc_calc.h"
+
+static void copy_pps_fields(struct drm_dsc_config *to, const struct drm_dsc_config *from)
+{
+ to->line_buf_depth = from->line_buf_depth;
+ to->bits_per_component = from->bits_per_component;
+ to->convert_rgb = from->convert_rgb;
+ to->slice_width = from->slice_width;
+ to->slice_height = from->slice_height;
+ to->simple_422 = from->simple_422;
+ to->native_422 = from->native_422;
+ to->native_420 = from->native_420;
+ to->pic_width = from->pic_width;
+ to->pic_height = from->pic_height;
+ to->rc_tgt_offset_high = from->rc_tgt_offset_high;
+ to->rc_tgt_offset_low = from->rc_tgt_offset_low;
+ to->bits_per_pixel = from->bits_per_pixel;
+ to->rc_edge_factor = from->rc_edge_factor;
+ to->rc_quant_incr_limit1 = from->rc_quant_incr_limit1;
+ to->rc_quant_incr_limit0 = from->rc_quant_incr_limit0;
+ to->initial_xmit_delay = from->initial_xmit_delay;
+ to->initial_dec_delay = from->initial_dec_delay;
+ to->block_pred_enable = from->block_pred_enable;
+ to->first_line_bpg_offset = from->first_line_bpg_offset;
+ to->second_line_bpg_offset = from->second_line_bpg_offset;
+ to->initial_offset = from->initial_offset;
+ memcpy(&to->rc_buf_thresh, &from->rc_buf_thresh, sizeof(from->rc_buf_thresh));
+ memcpy(&to->rc_range_params, &from->rc_range_params, sizeof(from->rc_range_params));
+ to->rc_model_size = from->rc_model_size;
+ to->flatness_min_qp = from->flatness_min_qp;
+ to->flatness_max_qp = from->flatness_max_qp;
+ to->initial_scale_value = from->initial_scale_value;
+ to->scale_decrement_interval = from->scale_decrement_interval;
+ to->scale_increment_interval = from->scale_increment_interval;
+ to->nfl_bpg_offset = from->nfl_bpg_offset;
+ to->nsl_bpg_offset = from->nsl_bpg_offset;
+ to->slice_bpg_offset = from->slice_bpg_offset;
+ to->final_offset = from->final_offset;
+ to->vbr_enable = from->vbr_enable;
+ to->slice_chunk_size = from->slice_chunk_size;
+ to->second_line_offset_adj = from->second_line_offset_adj;
+ to->dsc_version_minor = from->dsc_version_minor;
+}
+
+static void copy_rc_to_cfg(struct drm_dsc_config *dsc_cfg, const struct rc_params *rc)
+{
+ int i;
+
+ dsc_cfg->rc_quant_incr_limit0 = rc->rc_quant_incr_limit0;
+ dsc_cfg->rc_quant_incr_limit1 = rc->rc_quant_incr_limit1;
+ dsc_cfg->initial_offset = rc->initial_fullness_offset;
+ dsc_cfg->initial_xmit_delay = rc->initial_xmit_delay;
+ dsc_cfg->first_line_bpg_offset = rc->first_line_bpg_offset;
+ dsc_cfg->second_line_bpg_offset = rc->second_line_bpg_offset;
+ dsc_cfg->flatness_min_qp = rc->flatness_min_qp;
+ dsc_cfg->flatness_max_qp = rc->flatness_max_qp;
+ for (i = 0; i < QP_SET_SIZE; ++i) {
+ dsc_cfg->rc_range_params[i].range_min_qp = rc->qp_min[i];
+ dsc_cfg->rc_range_params[i].range_max_qp = rc->qp_max[i];
+ /* Truncate 8-bit signed value to 6-bit signed value */
+ dsc_cfg->rc_range_params[i].range_bpg_offset = 0x3f & rc->ofs[i];
+ }
+ dsc_cfg->rc_model_size = rc->rc_model_size;
+ dsc_cfg->rc_edge_factor = rc->rc_edge_factor;
+ dsc_cfg->rc_tgt_offset_high = rc->rc_tgt_offset_hi;
+ dsc_cfg->rc_tgt_offset_low = rc->rc_tgt_offset_lo;
+
+ for (i = 0; i < QP_SET_SIZE - 1; ++i)
+ dsc_cfg->rc_buf_thresh[i] = rc->rc_buf_thresh[i];
+}
+
+int dscc_compute_dsc_parameters(const struct drm_dsc_config *pps,
+ const struct rc_params *rc,
+ struct dsc_parameters *dsc_params)
+{
+ int ret;
+ struct drm_dsc_config dsc_cfg;
+ unsigned long long tmp;
+
+ dsc_params->pps = *pps;
+ dsc_params->pps.initial_scale_value = 8 * rc->rc_model_size / (rc->rc_model_size - rc->initial_fullness_offset);
+
+ copy_pps_fields(&dsc_cfg, &dsc_params->pps);
+ copy_rc_to_cfg(&dsc_cfg, rc);
+
+ dsc_cfg.mux_word_size = dsc_params->pps.bits_per_component <= 10 ? 48 : 64;
+
+ ret = drm_dsc_compute_rc_parameters(&dsc_cfg);
+ tmp = (unsigned long long)dsc_cfg.slice_chunk_size * 0x10000000 + (dsc_cfg.slice_width - 1);
+ do_div(tmp, (uint32_t)dsc_cfg.slice_width); //ROUND-UP
+ dsc_params->bytes_per_pixel = (uint32_t)tmp;
+
+ copy_pps_fields(&dsc_params->pps, &dsc_cfg);
+ dsc_params->rc_buffer_model_size = dsc_cfg.rc_bits;
+ return ret;
+}
+