2023-01-23 18:25:29

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 00/14] add display port DSC feature

This patch add DSC related supporting functions into to both dp controller and dpu enccoder

Kuogee Hsieh (14):
drm/msm/dp: add dpcd read of both dsc and fec capability
drm/msm/dp: add dsc factor into calculation of supported bpp
drm/msm/dp: add configure mainlink_levels base on lane number
drm/msm/dp: correct configure Colorimetry Indicator Field at MISC0
drm/msm/dp: upgrade tu calculation base on newest algorithm
drm/msm/dp: add display compression related struct
drm/msm/dp: add dsc helper functions
drm/msm/dp: add dsc supporting functions to DP controller
drm/msm/dsi: export struct msm_compression_info to dpu encoder
drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine
drm/msm/disp/dpu1: add supports of new flush mechanism
drm/msm/disp/dpu1: revise timing engine programming to work for DSC
drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder
drm/msm/disp/dpu1: add sc7280 dsc block and sub block

drivers/gpu/drm/msm/Makefile | 2 +
drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++
drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 +
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 341 +++++++--
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 7 +-
.../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 43 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 50 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 74 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 21 +
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 ++--
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h | 3 +
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 10 +-
drivers/gpu/drm/msm/dp/dp_catalog.c | 176 ++++-
drivers/gpu/drm/msm/dp/dp_catalog.h | 97 ++-
drivers/gpu/drm/msm/dp/dp_ctrl.c | 839 ++++++++++++++-------
drivers/gpu/drm/msm/dp/dp_display.c | 61 +-
drivers/gpu/drm/msm/dp/dp_link.c | 29 +-
drivers/gpu/drm/msm/dp/dp_panel.c | 749 +++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_panel.h | 67 +-
drivers/gpu/drm/msm/dp/dp_reg.h | 40 +-
drivers/gpu/drm/msm/dsi/dsi.c | 3 +-
drivers/gpu/drm/msm/dsi/dsi.h | 3 +-
drivers/gpu/drm/msm/dsi/dsi_host.c | 14 +-
drivers/gpu/drm/msm/msm_drv.h | 113 ++-
32 files changed, 3429 insertions(+), 497 deletions(-)
create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project



2023-01-23 18:25:33

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 07/14] drm/msm/dp: add dsc helper functions

Add DSC related supporting functions to calculate DSC related parameters.
In addition, DSC hardware encoder customized configuration parameters are
also included. Algorithms used to perform calculation are derived from
system engineer spreadsheet.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/Makefile | 1 +
drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++++++++++++++
drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 ++
drivers/gpu/drm/msm/msm_drv.h | 4 +
4 files changed, 567 insertions(+)
create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 7274c412..28cf52b 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
disp/dpu1/dpu_hw_catalog.o \
disp/dpu1/dpu_hw_ctl.o \
disp/dpu1/dpu_hw_dsc.o \
+ disp/dpu1/dpu_dsc_helper.o \
disp/dpu1/dpu_hw_interrupts.o \
disp/dpu1/dpu_hw_intf.o \
disp/dpu1/dpu_hw_lm.o \
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
new file mode 100644
index 00000000..48cef23
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2023 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
+ */
+
+#include "msm_drv.h"
+#include "dpu_kms.h"
+#include "dpu_hw_dsc.h"
+#include "dpu_dsc_helper.h"
+
+
+#define DPU_DSC_PPS_SIZE 128
+
+enum dpu_dsc_ratio_type {
+ DSC_V11_8BPC_8BPP,
+ DSC_V11_10BPC_8BPP,
+ DSC_V11_10BPC_10BPP,
+ DSC_V11_SCR1_8BPC_8BPP,
+ DSC_V11_SCR1_10BPC_8BPP,
+ DSC_V11_SCR1_10BPC_10BPP,
+ DSC_V12_444_8BPC_8BPP = DSC_V11_SCR1_8BPC_8BPP,
+ DSC_V12_444_10BPC_8BPP = DSC_V11_SCR1_10BPC_8BPP,
+ DSC_V12_444_10BPC_10BPP = DSC_V11_SCR1_10BPC_10BPP,
+ DSC_V12_422_8BPC_7BPP,
+ DSC_V12_422_8BPC_8BPP,
+ DSC_V12_422_10BPC_7BPP,
+ DSC_V12_422_10BPC_10BPP,
+ DSC_V12_420_8BPC_6BPP,
+ DSC_V12_420_10BPC_6BPP,
+ DSC_V12_420_10BPC_7_5BPP,
+ DSC_RATIO_TYPE_MAX
+};
+
+
+static u16 dpu_dsc_rc_buf_thresh[DSC_NUM_BUF_RANGES - 1] = {
+ 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54,
+ 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e
+};
+
+/*
+ * Rate control - Min QP values for each ratio type in dpu_dsc_ratio_type
+ */
+static char dpu_dsc_rc_range_min_qp[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
+ /* DSC v1.1 */
+ {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 13},
+ {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 17},
+ {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
+ /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
+ {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 9, 12},
+ {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 13, 16},
+ {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
+ /* DSC v1.2 YUV422 */
+ {0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 11},
+ {0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 10},
+ {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
+ {0, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 11, 12},
+ /* DSC v1.2 YUV420 */
+ {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 10},
+ {0, 2, 3, 4, 6, 7, 7, 7, 7, 7, 9, 9, 9, 11, 14},
+ {0, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 11, 12},
+};
+
+/*
+ * Rate control - Max QP values for each ratio type in dpu_dsc_ratio_type
+ */
+static char dpu_dsc_rc_range_max_qp[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
+ /* DSC v1.1 */
+ {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 11, 12, 13, 13, 15},
+ {4, 8, 9, 10, 11, 11, 11, 12, 13, 14, 15, 16, 17, 17, 19},
+ {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
+ /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
+ {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 10, 11, 11, 12, 13},
+ {8, 8, 9, 10, 11, 11, 11, 12, 13, 14, 14, 15, 15, 16, 17},
+ {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
+ /* DSC v1.2 YUV422 */
+ {3, 4, 5, 6, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12},
+ {2, 4, 5, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 10, 11},
+ {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
+ {2, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13},
+ /* DSC v1.2 YUV420 */
+ {2, 4, 5, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 10, 12},
+ {2, 5, 7, 8, 9, 10, 11, 12, 12, 13, 13, 13, 13, 14, 15},
+ {2, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13},
+ };
+
+/*
+ * Rate control - bpg offset values for each ratio type in dpu_dsc_ratio_type
+ */
+static char dpu_dsc_rc_range_bpg[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
+ /* DSC v1.1 */
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
+ /* DSC v1.1 SCR and DSC V1.2 RGB 444 */
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
+ /* DSC v1.2 YUV422 */
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
+ {10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12},
+ /* DSC v1.2 YUV420 */
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
+ {10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12},
+};
+
+static struct dpu_dsc_rc_init_params_lut {
+ u32 rc_quant_incr_limit0;
+ u32 rc_quant_incr_limit1;
+ u32 initial_fullness_offset;
+ u32 initial_xmit_delay;
+ u32 second_line_bpg_offset;
+ u32 second_line_offset_adj;
+ u32 flatness_min_qp;
+ u32 flatness_max_qp;
+} dpu_dsc_rc_init_param_lut[] = {
+ /* DSC v1.1 */
+ {11, 11, 6144, 512, 0, 0, 3, 12}, /* DSC_V11_8BPC_8BPP */
+ {15, 15, 6144, 512, 0, 0, 7, 16}, /* DSC_V11_10BPC_8BPP */
+ {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V11_10BPC_10BPP */
+ /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
+ {11, 11, 6144, 512, 0, 0, 3, 12}, /* DSC_V12_444_8BPC_8BPP or DSC_V11_SCR1_8BPC_8BPP */
+ {15, 15, 6144, 512, 0, 0, 7, 16}, /* DSC_V12_444_10BPC_8BPP or DSC_V11_SCR1_10BPC_8BPP */
+ {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V12_444_10BPC_10BPP or DSC_V11_SCR1_10BPC_10BPP */
+ /* DSC v1.2 YUV422 */
+ {11, 11, 5632, 410, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_7BPP */
+ {11, 11, 2048, 341, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_8BPP */
+ {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V12_422_10BPC_7BPP */
+ {15, 15, 2048, 273, 0, 0, 7, 16}, /* DSC_V12_422_10BPC_10BPP */
+ /* DSC v1.2 YUV420 */
+ {11, 11, 5632, 410, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_7BPP */
+ {11, 11, 2048, 341, 12, 512, 3, 12}, /* DSC_V12_420_8BPC_6BPP */
+ {15, 15, 2048, 341, 12, 512, 7, 16}, /* DSC_V12_420_10BPC_6BPP */
+ {15, 15, 2048, 256, 12, 512, 7, 16}, /* DSC_V12_420_10BPC_7_5BPP */
+};
+
+/**
+ * Maps to lookup the dpu_dsc_ratio_type index used in rate control tables
+ */
+static struct dpu_dsc_table_index_lut {
+ u32 fmt;
+ u32 scr_ver;
+ u32 minor_ver;
+ u32 bpc;
+ u32 bpp;
+ u32 type;
+} dpu_dsc_index_map[] = {
+ /* DSC 1.1 formats - scr version is considered */
+ {MSM_CHROMA_444, 0, 1, 8, 8, DSC_V11_8BPC_8BPP},
+ {MSM_CHROMA_444, 0, 1, 10, 8, DSC_V11_10BPC_8BPP},
+ {MSM_CHROMA_444, 0, 1, 10, 10, DSC_V11_10BPC_10BPP},
+
+ {MSM_CHROMA_444, 1, 1, 8, 8, DSC_V11_SCR1_8BPC_8BPP},
+ {MSM_CHROMA_444, 1, 1, 10, 8, DSC_V11_SCR1_10BPC_8BPP},
+ {MSM_CHROMA_444, 1, 1, 10, 10, DSC_V11_SCR1_10BPC_10BPP},
+
+ /* DSC 1.2 formats - scr version is no-op */
+ {MSM_CHROMA_444, -1, 2, 8, 8, DSC_V12_444_8BPC_8BPP},
+ {MSM_CHROMA_444, -1, 2, 10, 8, DSC_V12_444_10BPC_8BPP},
+ {MSM_CHROMA_444, -1, 2, 10, 10, DSC_V12_444_10BPC_10BPP},
+
+ {MSM_CHROMA_422, -1, 2, 8, 7, DSC_V12_422_8BPC_7BPP},
+ {MSM_CHROMA_422, -1, 2, 8, 8, DSC_V12_422_8BPC_8BPP},
+ {MSM_CHROMA_422, -1, 2, 10, 7, DSC_V12_422_10BPC_7BPP},
+ {MSM_CHROMA_422, -1, 2, 10, 10, DSC_V12_422_10BPC_10BPP},
+
+ {MSM_CHROMA_420, -1, 2, 8, 6, DSC_V12_420_8BPC_6BPP},
+ {MSM_CHROMA_420, -1, 2, 10, 6, DSC_V12_420_10BPC_6BPP},
+};
+
+static int _get_rc_table_index(struct drm_dsc_config *dsc, int scr_ver)
+{
+ u32 bpp, bpc, i, fmt = MSM_CHROMA_444;
+
+ if (dsc->dsc_version_major != 0x1) {
+ DPU_ERROR("unsupported major version %d\n",
+ dsc->dsc_version_major);
+ return -EINVAL;
+ }
+
+ bpc = dsc->bits_per_component;
+ bpp = DSC_BPP(*dsc);
+
+ if (dsc->native_422)
+ fmt = MSM_CHROMA_422;
+ else if (dsc->native_420)
+ fmt = MSM_CHROMA_420;
+
+
+ for (i = 0; i < ARRAY_SIZE(dpu_dsc_index_map); i++) {
+ if (dsc->dsc_version_minor == dpu_dsc_index_map[i].minor_ver &&
+ fmt == dpu_dsc_index_map[i].fmt &&
+ bpc == dpu_dsc_index_map[i].bpc &&
+ bpp == dpu_dsc_index_map[i].bpp &&
+ (dsc->dsc_version_minor != 0x1 ||
+ scr_ver == dpu_dsc_index_map[i].scr_ver))
+ return dpu_dsc_index_map[i].type;
+ }
+
+ DPU_ERROR("unsupported DSC v%d.%dr%d, bpc:%d, bpp:%d, fmt:0x%x\n",
+ dsc->dsc_version_major, dsc->dsc_version_minor,
+ scr_ver, bpc, bpp, fmt);
+ return -EINVAL;
+}
+
+u8 _get_dsc_v1_2_bpg_offset(struct drm_dsc_config *dsc)
+{
+ u8 bpg_offset = 0;
+ u8 uncompressed_bpg_rate;
+ u8 bpp = DSC_BPP(*dsc);
+
+ if (dsc->slice_height < 8)
+ bpg_offset = 2 * (dsc->slice_height - 1);
+ else if (dsc->slice_height < 20)
+ bpg_offset = 12;
+ else if (dsc->slice_height <= 30)
+ bpg_offset = 13;
+ else if (dsc->slice_height < 42)
+ bpg_offset = 14;
+ else
+ bpg_offset = 15;
+
+ if (dsc->native_422)
+ uncompressed_bpg_rate = 3 * bpp * 4;
+ else if (dsc->native_420)
+ uncompressed_bpg_rate = 3 * bpp;
+ else
+ uncompressed_bpg_rate = (3 * bpp + 2) * 3;
+
+ if (bpg_offset < (uncompressed_bpg_rate - (3 * bpp)))
+ return bpg_offset;
+ else
+ return (uncompressed_bpg_rate - (3 * bpp));
+}
+
+int dpu_dsc_populate_dsc_config(struct drm_dsc_config *dsc, int scr_ver)
+{
+ int bpp, bpc;
+ int groups_per_line, groups_total;
+ int min_rate_buffer_size;
+ int hrd_delay;
+ int pre_num_extra_mux_bits, num_extra_mux_bits;
+ int slice_bits;
+ int data;
+ int final_value, final_scale;
+ struct dpu_dsc_rc_init_params_lut *rc_param_lut;
+ u32 slice_width_mod;
+ int i, ratio_idx;
+
+ dsc->rc_model_size = 8192;
+
+ if ((dsc->dsc_version_major == 0x1) &&
+ (dsc->dsc_version_minor == 0x1)) {
+ if (scr_ver == 0x1)
+ dsc->first_line_bpg_offset = 15;
+ else
+ dsc->first_line_bpg_offset = 12;
+ } else if (dsc->dsc_version_minor == 0x2) {
+ dsc->first_line_bpg_offset = _get_dsc_v1_2_bpg_offset(dsc);
+ }
+
+ dsc->rc_edge_factor = 6;
+ dsc->rc_tgt_offset_high = 3;
+ dsc->rc_tgt_offset_low = 3;
+ dsc->simple_422 = 0;
+ dsc->convert_rgb = !(dsc->native_422 | dsc->native_420);
+ dsc->vbr_enable = 0;
+
+ bpp = DSC_BPP(*dsc);
+ bpc = dsc->bits_per_component;
+
+ ratio_idx = _get_rc_table_index(dsc, scr_ver);
+ if ((ratio_idx < 0) || (ratio_idx >= DSC_RATIO_TYPE_MAX))
+ return -EINVAL;
+
+
+ for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++)
+ dsc->rc_buf_thresh[i] = dpu_dsc_rc_buf_thresh[i];
+
+ for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
+ dsc->rc_range_params[i].range_min_qp =
+ dpu_dsc_rc_range_min_qp[ratio_idx][i];
+ dsc->rc_range_params[i].range_max_qp =
+ dpu_dsc_rc_range_max_qp[ratio_idx][i];
+ dsc->rc_range_params[i].range_bpg_offset =
+ dpu_dsc_rc_range_bpg[ratio_idx][i];
+ }
+
+ rc_param_lut = &dpu_dsc_rc_init_param_lut[ratio_idx];
+ dsc->rc_quant_incr_limit0 = rc_param_lut->rc_quant_incr_limit0;
+ dsc->rc_quant_incr_limit1 = rc_param_lut->rc_quant_incr_limit1;
+ dsc->initial_offset = rc_param_lut->initial_fullness_offset;
+ dsc->initial_xmit_delay = rc_param_lut->initial_xmit_delay;
+ dsc->second_line_bpg_offset = rc_param_lut->second_line_bpg_offset;
+ dsc->second_line_offset_adj = rc_param_lut->second_line_offset_adj;
+ dsc->flatness_min_qp = rc_param_lut->flatness_min_qp;
+ dsc->flatness_max_qp = rc_param_lut->flatness_max_qp;
+
+ slice_width_mod = dsc->slice_width;
+ if (dsc->native_422 || dsc->native_420) {
+ slice_width_mod = dsc->slice_width / 2;
+ bpp = bpp * 2;
+ }
+
+ dsc->line_buf_depth = bpc + 1;
+ dsc->mux_word_size = bpc > 10 ? DSC_MUX_WORD_SIZE_12_BPC : DSC_MUX_WORD_SIZE_8_10_BPC;
+
+ if ((dsc->dsc_version_minor == 0x2) && (dsc->native_420))
+ dsc->nsl_bpg_offset = (2048 * (DIV_ROUND_UP(dsc->second_line_bpg_offset,
+ (dsc->slice_height - 1))));
+
+ groups_per_line = DIV_ROUND_UP(slice_width_mod, 3);
+
+ dsc->slice_chunk_size = slice_width_mod * bpp / 8;
+ if ((slice_width_mod * bpp) % 8)
+ dsc->slice_chunk_size++;
+
+ /* rbs-min */
+ min_rate_buffer_size = dsc->rc_model_size - dsc->initial_offset +
+ dsc->initial_xmit_delay * bpp +
+ groups_per_line * dsc->first_line_bpg_offset;
+
+ hrd_delay = DIV_ROUND_UP(min_rate_buffer_size, bpp);
+
+ dsc->initial_dec_delay = hrd_delay - dsc->initial_xmit_delay;
+
+ dsc->initial_scale_value = 8 * dsc->rc_model_size /
+ (dsc->rc_model_size - dsc->initial_offset);
+
+ slice_bits = 8 * dsc->slice_chunk_size * dsc->slice_height;
+
+ groups_total = groups_per_line * dsc->slice_height;
+
+ data = dsc->first_line_bpg_offset * 2048;
+
+ dsc->nfl_bpg_offset = DIV_ROUND_UP(data, (dsc->slice_height - 1));
+
+ if (dsc->native_422)
+ pre_num_extra_mux_bits = 4 * dsc->mux_word_size + (4 * bpc + 4) + (3 * 4 * bpc) - 2;
+ else if (dsc->native_420)
+ pre_num_extra_mux_bits = 3 * dsc->mux_word_size + (4 * bpc + 4) + (2 * 4 * bpc) - 2;
+ else
+ pre_num_extra_mux_bits = 3 * (dsc->mux_word_size + (4 * bpc + 4) - 2);
+
+ num_extra_mux_bits = pre_num_extra_mux_bits - (dsc->mux_word_size -
+ ((slice_bits - pre_num_extra_mux_bits) % dsc->mux_word_size));
+
+ data = 2048 * (dsc->rc_model_size - dsc->initial_offset
+ + num_extra_mux_bits);
+ dsc->slice_bpg_offset = DIV_ROUND_UP(data, groups_total);
+
+ data = dsc->initial_xmit_delay * bpp;
+ final_value = dsc->rc_model_size - data + num_extra_mux_bits;
+
+ final_scale = 8 * dsc->rc_model_size /
+ (dsc->rc_model_size - final_value);
+
+ dsc->final_offset = final_value;
+
+ data = (final_scale - 9) * (dsc->nfl_bpg_offset +
+ dsc->slice_bpg_offset);
+ dsc->scale_increment_interval = (2048 * dsc->final_offset) / data;
+
+ dsc->scale_decrement_interval = groups_per_line /
+ (dsc->initial_scale_value - 8);
+
+ return 0;
+}
+
+bool dpu_dsc_ich_reset_override_needed(bool pu_en,
+ struct msm_display_dsc_info *dsc_info)
+{
+ /*
+ * As per the DSC spec, ICH_RESET can be either end of the slice line
+ * or at the end of the slice. HW internally generates ich_reset at
+ * end of the slice line if DSC_MERGE is used or encoder has two
+ * soft slices. However, if encoder has only 1 soft slice and DSC_MERGE
+ * is not used then it will generate ich_reset at the end of slice.
+ *
+ * Now as per the spec, during one PPS session, position where
+ * ich_reset is generated should not change. Now if full-screen frame
+ * has more than 1 soft slice then HW will automatically generate
+ * ich_reset at the end of slice_line. But for the same panel, if
+ * partial frame is enabled and only 1 encoder is used with 1 slice,
+ * then HW will generate ich_reset at end of the slice. This is a
+ * mismatch. Prevent this by overriding HW's decision.
+ */
+ return pu_en && dsc_info && (dsc_info->drm_dsc.slice_count > 1) &&
+ (dsc_info->drm_dsc.slice_width == dsc_info->drm_dsc.pic_width);
+}
+
+int dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc_info,
+ int enc_ip_width, int dsc_cmn_mode)
+{
+ int max_ssm_delay, max_se_size, max_muxword_size;
+ int compress_bpp_group, obuf_latency, input_ssm_out_latency;
+ int base_hs_latency, chunk_bits, ob_data_width;
+ int output_rate_extra_budget_bits, multi_hs_extra_budget_bits;
+ int multi_hs_extra_latency, mux_word_size;
+ int ob_data_width_4comps, ob_data_width_3comps;
+ int output_rate_ratio_complement, container_slice_width;
+ int rtl_num_components, multi_hs_c, multi_hs_d;
+
+ int bpc = dsc_info->drm_dsc.bits_per_component;
+ int bpp = DSC_BPP(dsc_info->drm_dsc);
+ bool native_422 = dsc_info->drm_dsc.native_422;
+ bool native_420 = dsc_info->drm_dsc.native_420;
+
+ /* Hardent core config */
+ int multiplex_mode_enable = 0, split_panel_enable = 0;
+ int rtl_max_bpc = 10, rtl_output_data_width = 64;
+ int pipeline_latency = 28;
+
+ if (dsc_cmn_mode & DSC_MODE_MULTIPLEX)
+ multiplex_mode_enable = 1;
+ if (dsc_cmn_mode & DSC_MODE_SPLIT_PANEL)
+ split_panel_enable = 1;
+ container_slice_width = (native_422 ?
+ dsc_info->drm_dsc.slice_width / 2 : dsc_info->drm_dsc.slice_width);
+ max_muxword_size = (rtl_max_bpc >= 12) ? 64 : 48;
+ max_se_size = 4 * (rtl_max_bpc + 1);
+ max_ssm_delay = max_se_size + max_muxword_size - 1;
+ mux_word_size = (bpc >= 12) ? 64 : 48;
+ compress_bpp_group = native_422 ? (2 * bpp) : bpp;
+ input_ssm_out_latency = pipeline_latency + 3 * (max_ssm_delay + 2)
+ * dsc_info->num_active_ss_per_enc;
+ rtl_num_components = (native_420 || native_422) ? 4 : 3;
+ ob_data_width_4comps = (rtl_output_data_width >= (2 *
+ max_muxword_size)) ?
+ rtl_output_data_width :
+ (2 * rtl_output_data_width);
+ ob_data_width_3comps = (rtl_output_data_width >= max_muxword_size) ?
+ rtl_output_data_width : 2 * rtl_output_data_width;
+ ob_data_width = (rtl_num_components == 4) ?
+ ob_data_width_4comps : ob_data_width_3comps;
+ obuf_latency = DIV_ROUND_UP((9 * ob_data_width + mux_word_size),
+ compress_bpp_group) + 1;
+ base_hs_latency = dsc_info->drm_dsc.initial_xmit_delay +
+ input_ssm_out_latency + obuf_latency;
+ chunk_bits = 8 * dsc_info->drm_dsc.slice_chunk_size;
+ output_rate_ratio_complement = ob_data_width - compress_bpp_group;
+ output_rate_extra_budget_bits =
+ (output_rate_ratio_complement * chunk_bits) >>
+ ((ob_data_width == 128) ? 7 : 6);
+ multi_hs_c = split_panel_enable * multiplex_mode_enable;
+ multi_hs_d = (dsc_info->num_active_ss_per_enc > 1) * (ob_data_width > compress_bpp_group);
+ multi_hs_extra_budget_bits = multi_hs_c ?
+ chunk_bits : (multi_hs_d ? chunk_bits :
+ output_rate_extra_budget_bits);
+ multi_hs_extra_latency = DIV_ROUND_UP(multi_hs_extra_budget_bits,
+ compress_bpp_group);
+ dsc_info->initial_lines = DIV_ROUND_UP((base_hs_latency +
+ multi_hs_extra_latency),
+ container_slice_width);
+
+ return 0;
+}
+
+int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
+ int intf_width)
+{
+ int mod_offset;
+ int slice_per_pkt, slice_per_intf;
+ int bytes_in_slice, total_bytes_per_intf;
+ u16 bpp;
+ u32 bytes_in_dsc_pair;
+ u32 total_bytes_in_dsc_pair;
+
+ if (!dsc_info || !dsc_info->drm_dsc.slice_width ||
+ !dsc_info->drm_dsc.slice_height ||
+ intf_width < dsc_info->drm_dsc.slice_width) {
+ DPU_ERROR("invalid input, intf_width=%d slice_width=%d\n",
+ intf_width, dsc_info ? dsc_info->drm_dsc.slice_width :
+ -1);
+ return -EINVAL;
+ }
+
+ mod_offset = dsc_info->drm_dsc.slice_width % 3;
+
+
+ switch (mod_offset) {
+ case 0:
+ dsc_info->slice_last_group_size = 2;
+ break;
+ case 1:
+ dsc_info->slice_last_group_size = 0;
+ break;
+ case 2:
+ dsc_info->slice_last_group_size = 1;
+ break;
+ default:
+ break;
+ }
+
+ dsc_info->det_thresh_flatness =
+ 2 << (dsc_info->drm_dsc.bits_per_component - 8);
+
+ slice_per_pkt = dsc_info->slice_per_pkt;
+ slice_per_intf = DIV_ROUND_UP(intf_width,
+ dsc_info->drm_dsc.slice_width);
+
+
+ /*
+ * If slice_per_pkt is greater than slice_per_intf then default to 1.
+ * This can happen during partial update.
+ */
+ if (slice_per_pkt > slice_per_intf)
+ slice_per_pkt = 1;
+
+ bpp = DSC_BPP(dsc_info->drm_dsc);
+ bytes_in_slice = DIV_ROUND_UP(dsc_info->drm_dsc.slice_width * bpp, 8);
+ total_bytes_per_intf = bytes_in_slice * slice_per_intf;
+
+
+ dsc_info->eol_byte_num = total_bytes_per_intf % 3;
+ dsc_info->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3);
+ dsc_info->bytes_in_slice = bytes_in_slice;
+ dsc_info->bytes_per_pkt = bytes_in_slice * slice_per_pkt;
+ dsc_info->pkt_per_line = slice_per_intf / slice_per_pkt;
+
+
+ bytes_in_dsc_pair = DIV_ROUND_UP(bytes_in_slice * 2, 3);
+ if (bytes_in_dsc_pair % 8) {
+ dsc_info->dsc_4hsmerge_padding = 8 - (bytes_in_dsc_pair % 8);
+ total_bytes_in_dsc_pair = bytes_in_dsc_pair +
+ dsc_info->dsc_4hsmerge_padding;
+ if (total_bytes_in_dsc_pair % 16)
+ dsc_info->dsc_4hsmerge_alignment = 16 -
+ (total_bytes_in_dsc_pair % 16);
+ }
+
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
new file mode 100644
index 00000000..9f26455
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 - 2023 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
+ */
+
+#ifndef __DPU_DSC_HELPER_H__
+#define __DPU_DSC_HELPER_H__
+
+#include "msm_drv.h"
+
+#define DSC_1_1_PPS_PARAMETER_SET_ELEMENTS 88
+
+int dpu_dsc_populate_dsc_config(struct drm_dsc_config *dsc, int scr_ver);
+
+int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
+ int intf_width);
+
+bool dpu_dsc_ich_reset_override_needed(bool pu_en, struct msm_display_dsc_info *dsc);
+
+int dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc,
+ int enc_ip_width, int dsc_cmn_mode);
+
+#endif /* __DPU_DSC_HELPER_H__ */
+
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index f155803..cf4eb8d 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -57,6 +57,10 @@ struct msm_disp_state;
#define MAX_CRTCS 8
#define MAX_BRIDGES 8

+#define MSM_CHROMA_444 0x0
+#define MSM_CHROMA_422 0x1
+#define MSM_CHROMA_420 0x2
+
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))

enum msm_dp_controller {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:25:51

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 09/14] drm/msm/dsi: export struct msm_compression_info to dpu encoder

struct msm_compression_info is used to support several different
compression mechanisms. It also contains customized info required
to configure DSC encoder engine. This patch also make changes DSI
module to have DSI exports struct msm_compreion_info to dpu encoder
instead of struct drm_dsc_config.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 7 +++++--
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 ++--
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 ++++++++--
drivers/gpu/drm/msm/dsi/dsi.c | 3 ++-
drivers/gpu/drm/msm/dsi/dsi.h | 3 ++-
drivers/gpu/drm/msm/dsi/dsi_host.c | 14 ++++++++++++--
drivers/gpu/drm/msm/msm_drv.h | 4 ++--
7 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 758261e..7f4a439 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2013 Red Hat
* Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
*
* Author: Rob Clark <[email protected]>
*/
@@ -210,6 +210,7 @@ struct dpu_encoder_virt {

/* DSC configuration */
struct drm_dsc_config *dsc;
+ struct msm_compression_info *comp_info;
};

#define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base)
@@ -2275,7 +2276,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
dpu_enc->idle_pc_supported =
dpu_kms->catalog->caps->has_idle_pc;

- dpu_enc->dsc = disp_info->dsc;
+ dpu_enc->comp_info = disp_info->comp_info;
+ if (dpu_enc->comp_info)
+ dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;

mutex_lock(&dpu_enc->enc_lock);
for (i = 0; i < disp_info->num_of_h_tiles && !ret; i++) {
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index 9e7236e..bd2da5e 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <[email protected]>
@@ -36,7 +36,7 @@ struct msm_display_info {
uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];
bool is_cmd_mode;
bool is_te_using_watchdog_timer;
- struct drm_dsc_config *dsc;
+ struct msm_compression_info *comp_info;
};

/**
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index d612419..70a74ed 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2013 Red Hat
* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
*
* Author: Rob Clark <[email protected]>
*/
@@ -570,7 +570,7 @@ static int _dpu_kms_initialize_dsi(struct drm_device *dev,
info.h_tile_instance[info.num_of_h_tiles++] = i;
info.is_cmd_mode = msm_dsi_is_cmd_mode(priv->dsi[i]);

- info.dsc = msm_dsi_get_dsc_config(priv->dsi[i]);
+ info.comp_info = msm_dsi_get_dsc_config(priv->dsi[i]);

if (msm_dsi_is_bonded_dsi(priv->dsi[i]) && priv->dsi[other]) {
rc = msm_dsi_modeset_init(priv->dsi[other], dev, encoder);
@@ -622,6 +622,8 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
info.num_of_h_tiles = 1;
info.h_tile_instance[0] = i;
info.intf_type = encoder->encoder_type;
+ info.is_cmd_mode = 0; /* dp always video mode */
+ info.comp_info = NULL;
rc = dpu_encoder_setup(dev, encoder, &info);
if (rc) {
DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
@@ -892,6 +894,10 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k

pm_runtime_get_sync(&dpu_kms->pdev->dev);

+ for (i = 0; i < cat->dsc_count; i++)
+ msm_disp_snapshot_add_block(disp_state, cat->dsc[i].len,
+ dpu_kms->mmio + cat->dsc[i].base, "dsc_%d", i);
+
/* dump CTL sub-blocks HW regs info */
for (i = 0; i < cat->ctl_count; i++)
msm_disp_snapshot_add_block(disp_state, cat->ctl[i].len,
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index 31fdee2..52b7e33 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
*/

#include "dsi.h"
@@ -13,7 +14,7 @@ bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi)
return !(host_flags & MIPI_DSI_MODE_VIDEO);
}

-struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
+struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
{
return msm_dsi_host_get_dsc_config(msm_dsi->host);
}
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index bd3763a..79ada54 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
*/

#ifndef __DSI_CONNECTOR_H__
@@ -133,7 +134,7 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host);
void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host);
-struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
+struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);

/* dsi phy */
struct msm_dsi_phy;
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 18fa30e..6188f4b 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
*/

#include <linux/clk.h>
@@ -163,6 +164,7 @@ struct msm_dsi_host {

struct drm_display_mode *mode;
struct drm_dsc_config *dsc;
+ struct msm_compression_info comp_info;

/* connected device info */
unsigned int channel;
@@ -2600,9 +2602,17 @@ void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host)
DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER);
}

-struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
+struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
{
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+ struct msm_compression_info *comp_info = NULL;

- return msm_host->dsc;
+ if (msm_host->dsc) {
+ comp_info = &msm_host->comp_info;
+ comp_info->msm_dsc_info.drm_dsc = *msm_host->dsc;
+ comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC;
+ comp_info->enabled = true;
+ }
+
+ return comp_info;
}
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 6a46ed7..eab0901 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -430,7 +430,7 @@ void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi
bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi);
bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi);
bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi);
-struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
+struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
#else
static inline void __init msm_dsi_register(void)
{
@@ -460,7 +460,7 @@ static inline bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi)
return false;
}

-static inline struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
+static inline struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
{
return NULL;
}
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:25:55

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 11/14] drm/msm/disp/dpu1: add supports of new flush mechanism

A new flushing mechanism is introduced to decouple peripheral metadata
flushing from timing engine related flush. This patch add peripheral
flushing functions.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 24 ++++++++++--
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 2 +
.../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 7 ++++
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 ++++++++++++++++++++--
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 21 +++++++++++
5 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 901e317..d2625b3 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1472,6 +1472,12 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc,
if (extra_flush_bits && ctl->ops.update_pending_flush)
ctl->ops.update_pending_flush(ctl, extra_flush_bits);

+ if (phys->hw_intf->cap->type == INTF_DP &&
+ phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
+ phys->comp_ratio) {
+ ctl->ops.update_pending_flush_periph(ctl, phys->hw_intf->idx);
+ }
+
ctl->ops.trigger_flush(ctl);

if (ctl->ops.get_pending_flush)
@@ -1814,12 +1820,18 @@ dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc,
return DIV_ROUND_UP(total_pixels, dsc->slice_width);
}

-static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
+static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
+ struct dpu_hw_dsc *hw_dsc,
struct dpu_hw_pingpong *hw_pp,
struct drm_dsc_config *dsc,
u32 common_mode,
u32 initial_lines)
{
+ struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
+ struct dpu_hw_ctl *ctl;
+
+ ctl = cur_master->hw_ctl;
+
if (hw_dsc->ops.dsc_config)
hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);

@@ -1834,6 +1846,10 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,

if (hw_pp->ops.enable_dsc)
hw_pp->ops.enable_dsc(hw_pp);
+
+ if (ctl->ops.update_pending_flush_dsc)
+ ctl->ops.update_pending_flush_dsc(ctl, hw_dsc->idx);
+
}

static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
@@ -1877,8 +1893,10 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
enc_ip_w = intf_ip_w / 2;
initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);

- for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
- dpu_encoder_dsc_pipe_cfg(hw_dsc[i], hw_pp[i], dsc, dsc_common_mode, initial_lines);
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc,
+ dsc_common_mode, initial_lines);
+ }
}

void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 1d434b2..0569b36 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -200,6 +200,8 @@ struct dpu_encoder_phys {
atomic_t pending_kickoff_cnt;
wait_queue_head_t pending_kickoff_wq;
int irq[INTR_IDX_MAX];
+ enum msm_display_compression_type comp_type;
+ u32 comp_ratio;
};

static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
index 48c4810..2d864f9 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/

#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
@@ -427,6 +428,12 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc)
if (ctl->ops.update_pending_flush_merge_3d && phys_enc->hw_pp->merge_3d)
ctl->ops.update_pending_flush_merge_3d(ctl, phys_enc->hw_pp->merge_3d->idx);

+ if (phys_enc->hw_intf->cap->type == INTF_DP &&
+ phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
+ phys_enc->comp_ratio) {
+ ctl->ops.update_pending_flush_periph(ctl, phys_enc->hw_intf->idx);
+ }
+
skip_flush:
DPU_DEBUG_VIDENC(phys_enc,
"update pending flush ctl %d intf %d\n",
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
index b88a2f3..1891c57 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
@@ -33,6 +33,7 @@
#define CTL_DSC_FLUSH 0x104
#define CTL_WB_FLUSH 0x108
#define CTL_INTF_FLUSH 0x110
+#define CTL_PERIPH_FLUSH 0x128
#define CTL_INTF_MASTER 0x134
#define CTL_FETCH_PIPE_ACTIVE 0x0FC

@@ -42,11 +43,13 @@
#define DPU_REG_RESET_TIMEOUT_US 2000
#define MERGE_3D_IDX 23
#define DSC_IDX 22
+#define PERIPH_IDX 30
#define INTF_IDX 31
#define WB_IDX 16
#define CTL_INVALID_BIT 0xffff
#define CTL_DEFAULT_GROUP_ID 0xf

+
static const u32 fetch_tbl[SSPP_MAX] = {CTL_INVALID_BIT, 16, 17, 18, 19,
CTL_INVALID_BIT, CTL_INVALID_BIT, CTL_INVALID_BIT, CTL_INVALID_BIT, 0,
1, 2, 3, CTL_INVALID_BIT, CTL_INVALID_BIT};
@@ -123,6 +126,7 @@ static inline void dpu_hw_ctl_update_pending_flush(struct dpu_hw_ctl *ctx,
trace_dpu_hw_ctl_update_pending_flush(flushbits,
ctx->pending_flush_mask);
ctx->pending_flush_mask |= flushbits;
+
}

static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx)
@@ -142,6 +146,15 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
ctx->pending_wb_flush_mask);

+ if (ctx->pending_flush_mask & BIT(PERIPH_IDX))
+ DPU_REG_WRITE(&ctx->hw, CTL_PERIPH_FLUSH,
+ ctx->pending_periph_flush_mask);
+
+ if (ctx->pending_flush_mask & BIT(DSC_IDX)) {
+ DPU_REG_WRITE(&ctx->hw, CTL_DSC_FLUSH,
+ ctx->pending_dsc_flush_mask);
+ }
+
DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
}

@@ -281,6 +294,13 @@ static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx,
ctx->pending_flush_mask |= BIT(INTF_IDX);
}

+static void dpu_hw_ctl_update_pending_flush_periph(struct dpu_hw_ctl *ctx,
+ enum dpu_intf intf)
+{
+ ctx->pending_periph_flush_mask |= BIT(intf - INTF_0);
+ ctx->pending_flush_mask |= BIT(PERIPH_IDX);
+}
+
static void dpu_hw_ctl_update_pending_flush_merge_3d_v1(struct dpu_hw_ctl *ctx,
enum dpu_merge_3d merge_3d)
{
@@ -288,6 +308,13 @@ static void dpu_hw_ctl_update_pending_flush_merge_3d_v1(struct dpu_hw_ctl *ctx,
ctx->pending_flush_mask |= BIT(MERGE_3D_IDX);
}

+static void dpu_hw_ctl_update_pending_flush_dsc_v1(struct dpu_hw_ctl *ctx,
+ enum dpu_dsc dsc_num)
+{
+ ctx->pending_dsc_flush_mask |= BIT(dsc_num - DSC_0);
+ ctx->pending_flush_mask |= BIT(DSC_IDX);
+}
+
static void dpu_hw_ctl_update_pending_flush_dspp(struct dpu_hw_ctl *ctx,
enum dpu_dspp dspp)
{
@@ -472,6 +499,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
u32 intf_active = 0;
u32 wb_active = 0;
u32 mode_sel = 0;
+ u32 dsc_active = 0;

/* CTL_TOP[31:28] carries group_id to collate CTL paths
* per VM. Explicitly disable it until VM support is
@@ -502,9 +530,11 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
if (cfg->merge_3d)
DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
BIT(cfg->merge_3d - MERGE_3D_0));
- if (cfg->dsc) {
- DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, DSC_IDX);
- DPU_REG_WRITE(c, CTL_DSC_ACTIVE, cfg->dsc);
+
+ if (cfg->dsc_num) {
+ dsc_active = DPU_REG_READ(c, CTL_DSC_ACTIVE);
+ dsc_active |= BIT(cfg->dsc_num - DSC_0);
+ DPU_REG_WRITE(c, CTL_DSC_ACTIVE, dsc_active);
}
}

@@ -605,9 +635,16 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
ops->reset_intf_cfg = dpu_hw_ctl_reset_intf_cfg_v1;
ops->update_pending_flush_intf =
dpu_hw_ctl_update_pending_flush_intf_v1;
+
+ ops->update_pending_flush_periph =
+ dpu_hw_ctl_update_pending_flush_periph;
+
ops->update_pending_flush_merge_3d =
dpu_hw_ctl_update_pending_flush_merge_3d_v1;
ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb_v1;
+
+ ops->update_pending_flush_dsc =
+ dpu_hw_ctl_update_pending_flush_dsc_v1;
} else {
ops->trigger_flush = dpu_hw_ctl_trigger_flush;
ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
index 96c012e..d3faa0b1 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
@@ -48,6 +48,7 @@ struct dpu_hw_intf_cfg {
enum dpu_3d_blend_mode mode_3d;
enum dpu_merge_3d merge_3d;
enum dpu_ctl_mode_sel intf_mode_sel;
+ enum dpu_dsc dsc_num;
int stream_sel;
unsigned int dsc;
};
@@ -121,6 +122,15 @@ struct dpu_hw_ctl_ops {
enum dpu_intf blk);

/**
+ * OR in the given flushbits to the cached pending_(periph_)flush_mask
+ * No effect on hardware
+ * @ctx : ctl path ctx pointer
+ * @blk : interface block index
+ */
+ void (*update_pending_flush_periph)(struct dpu_hw_ctl *ctx,
+ enum dpu_intf blk);
+
+ /**
* OR in the given flushbits to the cached pending_(merge_3d_)flush_mask
* No effect on hardware
* @ctx : ctl path ctx pointer
@@ -156,6 +166,15 @@ struct dpu_hw_ctl_ops {
void (*update_pending_flush_dspp)(struct dpu_hw_ctl *ctx,
enum dpu_dspp blk);
/**
+ * OR in the given flushbits to the cached pending_(dsc_)flush_mask
+ * No effect on hardware
+ * @ctx : ctl path ctx pointer
+ * @blk : interface block index
+ */
+ void (*update_pending_flush_dsc)(struct dpu_hw_ctl *ctx,
+ enum dpu_dsc blk);
+
+ /**
* Write the value of the pending_flush_mask to hardware
* @ctx : ctl path ctx pointer
*/
@@ -241,7 +260,9 @@ struct dpu_hw_ctl {
u32 pending_flush_mask;
u32 pending_intf_flush_mask;
u32 pending_wb_flush_mask;
+ u32 pending_periph_flush_mask;
u32 pending_merge_3d_flush_mask;
+ u32 pending_dsc_flush_mask;

/* ops */
struct dpu_hw_ctl_ops ops;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:25:58

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 08/14] drm/msm/dp: add dsc supporting functions to DP controller

This patch provides DSC required functions at DP controller to
complete DSC feature. those functions include enable fec, configure
dsc, configure dto, transmit pps and finally flush hardware registers.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/dp/dp_catalog.c | 139 ++++++++-
drivers/gpu/drm/msm/dp/dp_catalog.h | 93 ++++++
drivers/gpu/drm/msm/dp/dp_ctrl.c | 132 ++++++++-
drivers/gpu/drm/msm/dp/dp_display.c | 61 +++-
drivers/gpu/drm/msm/dp/dp_panel.c | 570 +++++++++++++++++++++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_panel.h | 4 +
drivers/gpu/drm/msm/dp/dp_reg.h | 40 ++-
drivers/gpu/drm/msm/msm_drv.h | 16 +
8 files changed, 1033 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 7ac37d8..20a86e7 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -48,6 +48,11 @@
#define DP_INTERRUPT_STATUS2_MASK \
(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_MASK_SHIFT)

+enum dp_flush_bit {
+ DP_PPS_FLUSH,
+ DP_DHDR_FLUSH,
+};
+
struct dp_catalog_private {
struct device *dev;
struct drm_device *drm_dev;
@@ -277,6 +282,30 @@ static void dump_regs(void __iomem *base, int len)
}
}

+void dp_catalog_fec_config(struct dp_catalog *dp_catalog, bool enable)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+ u32 reg;
+
+ reg = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
+
+ /*
+ * fec_en = BIT(12)
+ * fec_seq_mode = BIT(22)
+ * sde_flush = BIT(23) | BIT(24)
+ * fb_boundary_sel = BIT(25)
+ */
+ if (enable)
+ reg |= BIT(12) | BIT(22) | BIT(23) | BIT(24) | BIT(25);
+ else
+ reg &= ~BIT(12);
+
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, reg);
+ /* make sure mainlink configuration is updated with fec sequence */
+ wmb();
+}
+
void dp_catalog_dump_regs(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
@@ -344,6 +373,54 @@ void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 cfg)
dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL, cfg);
}

+void dp_catalog_config_dsc_dto(struct dp_catalog *dp_catalog)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+ struct dp_dsc_cfg_data *dsc_data = &dp_catalog->dsc_data;
+ u32 reg;
+
+ dp_write_p0(catalog, MMSS_DP_DSC_DTO_COUNT, dsc_data->dto_count);
+
+ reg = dp_read_p0(catalog, MMSS_DP_DSC_DTO);
+
+ if (dsc_data->dto_en) {
+ reg |= BIT(0);
+ reg |= BIT(3);
+ reg |= (dsc_data->dto_n << 8);
+ reg |= (dsc_data->dto_d << 16);
+ }
+
+ dp_write_p0(catalog, MMSS_DP_DSC_DTO, reg);
+
+ reg = 0;
+ if (dsc_data->dsc_en) {
+ reg = BIT(0);
+ reg |= (dsc_data->eol_byte_num << 3);
+ reg |= (dsc_data->slice_per_pkt << 5);
+ reg |= (dsc_data->bytes_per_pkt << 16);
+ reg |= (dsc_data->be_in_lane << 10);
+ }
+ dp_write_link(catalog, DP_COMPRESSION_MODE_CTRL, reg);
+
+ drm_dbg_dp(catalog->drm_dev, "compression:0x%x\n", reg);
+}
+
+void dp_catalog_override_ack_dto(struct dp_catalog *dp_catalog, bool not_ack)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+ u32 dsc_dto;
+
+ dsc_dto = dp_read_p0(catalog, MMSS_DP_DSC_DTO);
+ if (not_ack)
+ dsc_dto &= ~BIT(1);
+ else
+ dsc_dto = BIT(1);
+
+ dp_write_p0(catalog, MMSS_DP_DSC_DTO, dsc_dto);
+}
+
void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
@@ -429,6 +506,15 @@ void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
}
}

+static void dp_catalog_sdp_update( struct dp_catalog *dp_catalog)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+
+ dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x01);
+ dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x00);
+}
+
void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog,
u32 colorimetry_cfg,
u32 test_bits_depth)
@@ -504,7 +590,6 @@ void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog,
drm_dbg_dp(catalog->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
- dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
}

int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog,
@@ -918,6 +1003,58 @@ void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog)
dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN, 0x0);
}

+void dp_catalog_dsc_commit_pps(struct dp_catalog *dp_catalog)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+ struct dp_dsc_cfg_data *dsc_data = &dp_catalog->dsc_data;
+ int i;
+
+ dp_write_link(catalog, DP_PPS_HB_0_3, 0x7F1000);
+ dp_write_link(catalog, DP_PPS_PB_0_3, 0xA22300);
+
+ for (i = 0; i < dsc_data->parity_word_len; i++)
+ dp_write_link(catalog, DP_PPS_PB_4_7 + (i << 2),
+ dsc_data->parity_word[i]);
+
+ for (i = 0; i < dsc_data->pps_word_len; i++)
+ dp_write_link(catalog, DP_PPS_PPS_0_3 + (i << 2),
+ dsc_data->pps_word[i]);
+}
+
+static void dp_catalog_dp_flush(struct dp_catalog *dp_catalog,
+ enum dp_flush_bit flush_bit)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+ u32 dp_flush;
+ struct dp_dsc_cfg_data *dsc_data = &dp_catalog->dsc_data;
+
+ dp_flush = dp_read_link(catalog, MMSS_DP_FLUSH);
+
+ dsc_data->continuous_pps = true;
+
+ if ((flush_bit == DP_PPS_FLUSH) && dsc_data->continuous_pps)
+ dp_flush &= ~BIT(2);
+
+ dp_flush |= BIT(flush_bit);
+ dp_write_link(catalog, MMSS_DP_FLUSH, dp_flush);
+
+ /*
+ * TODO: no dp_config_sdp_update() required?
+ */
+ dp_catalog_sdp_update(dp_catalog);
+}
+
+void dp_catalog_pps_flush(struct dp_catalog *dp_catalog)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+
+ dp_catalog_dp_flush(dp_catalog, DP_PPS_FLUSH);
+ drm_dbg_dp(catalog->drm_dev, "pps flush\n");
+}
+
struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
{
struct dp_catalog_private *catalog;
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 990c162..537fb8d 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -62,6 +62,27 @@ enum dp_catalog_audio_header_type {
DP_AUDIO_SDP_HEADER_MAX,
};

+struct dp_dsc_cfg_data {
+ bool dsc_en;
+ bool continuous_pps;
+ char pps[128];
+ u32 pps_len;
+ u32 pps_word[32];
+ u32 pps_word_len;
+ u8 parity[32];
+ u8 parity_len;
+ u32 parity_word[8];
+ u32 parity_word_len;
+ u32 slice_per_pkt;
+ u32 bytes_per_pkt;
+ u32 eol_byte_num;
+ u32 be_in_lane;
+ u32 dto_en;
+ u32 dto_n;
+ u32 dto_d;
+ u32 dto_count;
+};
+
struct dp_catalog {
u32 aux_data;
u32 total;
@@ -72,8 +93,74 @@ struct dp_catalog {
enum dp_catalog_audio_header_type sdp_header;
u32 audio_data;
bool wide_bus_en;
+ struct dp_dsc_cfg_data dsc_data;
};

+static inline u8 dp_ecc_get_g0_value(u8 data)
+{
+ u8 c[4];
+ u8 g[4];
+ u8 ret_data = 0;
+ u8 i;
+
+ for (i = 0; i < 4; i++)
+ c[i] = (data >> i) & 0x01;
+
+ g[0] = c[3];
+ g[1] = c[0] ^ c[3];
+ g[2] = c[1];
+ g[3] = c[2];
+
+ for (i = 0; i < 4; i++)
+ ret_data = ((g[i] & 0x01) << i) | ret_data;
+
+ return ret_data;
+}
+
+static inline u8 dp_ecc_get_g1_value(u8 data)
+{
+ u8 c[4];
+ u8 g[4];
+ u8 ret_data = 0;
+ u8 i;
+
+ for (i = 0; i < 4; i++)
+ c[i] = (data >> i) & 0x01;
+
+ g[0] = c[0] ^ c[3];
+ g[1] = c[0] ^ c[1] ^ c[3];
+ g[2] = c[1] ^ c[2];
+ g[3] = c[2] ^ c[3];
+
+ for (i = 0; i < 4; i++)
+ ret_data = ((g[i] & 0x01) << i) | ret_data;
+
+ return ret_data;
+}
+
+static inline u8 dp_header_get_parity(u32 data)
+{
+ u8 x0 = 0;
+ u8 x1 = 0;
+ u8 ci = 0;
+ u8 iData = 0;
+ u8 i = 0;
+ u8 parity_byte;
+ u8 num_byte = (data > 0xFF) ? 8 : 2;
+
+ for (i = 0; i < num_byte; i++) {
+ iData = (data >> i*4) & 0xF;
+
+ ci = iData ^ x1;
+ x1 = x0 ^ dp_ecc_get_g1_value(ci);
+ x0 = dp_ecc_get_g0_value(ci);
+ }
+
+ parity_byte = x1 | (x0 << 4);
+
+ return parity_byte;
+}
+
/* Debug module */
void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *disp_state);

@@ -137,4 +224,10 @@ void dp_catalog_audio_config_sdp(struct dp_catalog *catalog);
void dp_catalog_audio_init(struct dp_catalog *catalog);
void dp_catalog_audio_sfe_level(struct dp_catalog *catalog);

+void dp_catalog_fec_config(struct dp_catalog *dp_catalog, bool enable);
+void dp_catalog_dsc_commit_pps(struct dp_catalog *catalog);
+void dp_catalog_config_dsc_dto(struct dp_catalog *catalog);
+void dp_catalog_override_ack_dto(struct dp_catalog *dp_catalog, bool not_ack);
+void dp_catalog_pps_flush(struct dp_catalog *catalog);
+
#endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index ae9c2b8..b315bf3 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -134,9 +134,13 @@ static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
tbd = dp_link_get_test_bits_depth(ctrl->link,
ctrl->panel->dp_mode.bpp);

- if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN) {
+ /*
+ * since dsc encoder output byte stream to dp controller,
+ * 8 bits bpc should be used as long as dsc eanabled
+ */
+ if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN || ctrl->panel->dsc_en) {
pr_debug("BIT_DEPTH not set. Configure default\n");
- tbd = DP_TEST_BIT_DEPTH_8;
+ tbd = DP_TEST_BIT_DEPTH_8 >> DP_TEST_BIT_DEPTH_SHIFT;
}

config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
@@ -366,8 +370,8 @@ static void dp_panel_update_tu_timings(struct dp_tu_calc_input *in,
tu->lwidth_fp = drm_fixp_from_fraction(in->hactive, 1);
tu->orig_lwidth = in->hactive;
tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1);
- tu->orig_hbp = in->hporch;
- tu->rb2 = (in->hporch <= 80) ? 1 : 0;
+ tu->orig_hbp = in->hporch;
+ tu->rb2 = (in->hporch < 160) ? 1 : 0;

if (tu->pixelEnc == 420) {
temp1_fp = drm_fixp_from_fraction(2, 1);
@@ -399,6 +403,8 @@ static void dp_panel_update_tu_timings(struct dp_tu_calc_input *in,
if (!in->dsc_en)
goto fec_check;

+ tu->bpp = 24; /* hardcode to 24 if DSC is enabled */
+
temp1_fp = drm_fixp_from_fraction(in->compress_ratio, 100);
temp2_fp = drm_fixp_from_fraction(in->bpp, 1);
temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
@@ -1076,6 +1082,11 @@ static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
{
struct dp_tu_calc_input in;
struct drm_display_mode *drm_mode;
+ struct dp_panel_info *timing;
+ struct msm_compression_info *comp_info;
+
+ timing = &ctrl->panel->dp_mode.timing;
+ comp_info = &timing->comp_info;

drm_mode = &ctrl->panel->dp_mode.drm_mode;

@@ -1086,12 +1097,22 @@ static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
in.nlanes = ctrl->link->link_params.num_lanes;
in.bpp = ctrl->panel->dp_mode.bpp;
in.pixel_enc = 444;
- in.dsc_en = 0;
+ in.dsc_en = ctrl->panel->dsc_en;
in.async_en = 0;
- in.fec_en = 0;
+ in.fec_en = ctrl->panel->fec_en;
in.num_of_dsc_slices = 0;
in.compress_ratio = 100;

+
+ /*
+ * TODO: only one dsc slice supported for now
+ */
+ if (ctrl->panel->dsc_en) {
+ in.num_of_dsc_slices = 1;
+ in.compress_ratio = mult_frac(100, comp_info->src_bpp,
+ comp_info->tgt_bpp);
+ }
+
_dp_ctrl_calc_tu(ctrl, &in, tu_table);
}

@@ -1424,6 +1445,74 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl,
return ret;
}

+static void dp_ctrl_sink_fec_enable(struct dp_ctrl_private *ctrl)
+{
+ int rlen;
+
+ rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_FEC_CONFIGURATION, 0x07);
+ if (rlen < 1)
+ DRM_ERROR("failed to enable sink fec\n");
+
+
+}
+
+static void dp_ctrl_host_fec_start(struct dp_ctrl_private *ctrl)
+{
+ u8 fec_sts = 0;
+ int i, max_retries = 3;
+ bool fec_en_detected = false;
+
+ if (!ctrl->panel->fec_en)
+ return;
+
+ /* Need to try to enable multiple times due to BS symbols collisions */
+ for (i = 0; i < max_retries; i++) {
+ dp_catalog_fec_config(ctrl->catalog, true);
+
+ /* wait for controller to start fec sequence */
+ usleep_range(900, 1000);
+
+ /* read back FEC status and check if it is enabled */
+ drm_dp_dpcd_readb(ctrl->aux, DP_FEC_STATUS, &fec_sts);
+ if (fec_sts & DP_FEC_DECODE_EN_DETECTED) {
+ fec_en_detected = true;
+ break;
+ }
+ }
+
+ drm_dbg_dp(ctrl->drm_dev, "retries %d, fec_en_detected %d\n",
+ i, fec_en_detected);
+
+ if (!fec_en_detected)
+ DRM_ERROR("failed to enable sink fec\n");
+}
+
+static void dp_ctrl_host_fec_stop(struct dp_ctrl_private *ctrl)
+{
+ dp_catalog_fec_config(ctrl->catalog, false);
+}
+
+static void dp_ctrl_sink_dsc_enable(struct dp_ctrl_private *ctrl)
+{
+ int rlen;
+ u32 dsc_enable;
+ u8 xx = 0;
+
+
+ if (!ctrl->panel->fec_en)
+ return;
+
+ dsc_enable = ctrl->panel->dsc_en ? 1 : 0;
+ rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_DSC_ENABLE, dsc_enable);
+ if (rlen < 1)
+ DRM_ERROR("failed to enable sink dsc\n");
+
+
+ dsc_enable = 0;
+ drm_dp_dpcd_readb(ctrl->aux, DP_DSC_ENABLE, &xx);
+
+}
+
static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl,
int *training_step)
{
@@ -1442,6 +1531,9 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl,
* a link training pattern, we have to first do soft reset.
*/

+ if (ctrl->panel->fec_en)
+ dp_ctrl_sink_fec_enable(ctrl);
+
ret = dp_ctrl_link_train(ctrl, training_step);

return ret;
@@ -1986,14 +2078,25 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train)
*/
reinit_completion(&ctrl->video_comp);

+ if (ctrl->panel->dsc_en)
+ dp_panel_config_dsc(ctrl->panel, true);
+
dp_ctrl_configure_source_params(ctrl);

dp_catalog_ctrl_config_msa(ctrl->catalog,
ctrl->link->link_params.rate,
pixel_rate_orig, dp_ctrl_use_fixed_nvid(ctrl));

+ if (ctrl->panel->dsc_en) {
+ dp_catalog_config_dsc_dto(ctrl->catalog);
+ dp_catalog_dsc_commit_pps(ctrl->catalog);
+ dp_catalog_pps_flush(ctrl->catalog);
+ }
+
dp_ctrl_setup_tr_unit(ctrl);

+ dp_catalog_override_ack_dto(ctrl->catalog, true);
+
dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);

ret = dp_ctrl_wait4video_ready(ctrl);
@@ -2004,6 +2107,12 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train)
drm_dbg_dp(ctrl->drm_dev,
"mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");

+ if (ctrl->panel->dsc_en) {
+ /* wait for link training completion before fec config as per spec */
+ dp_ctrl_host_fec_start(ctrl);
+ dp_ctrl_sink_dsc_enable(ctrl);
+ }
+
end:
return ret;
}
@@ -2019,6 +2128,9 @@ int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
dp_io = &ctrl->parser->io;
phy = dp_io->phy;

+ if (ctrl->panel->dsc_en)
+ dp_panel_config_dsc(ctrl->panel, false);
+
/* set dongle to D3 (power off) mode */
dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);

@@ -2093,6 +2205,14 @@ int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
dp_io = &ctrl->parser->io;
phy = dp_io->phy;

+ if (ctrl->panel->dsc_en) {
+ dp_ctrl_host_fec_stop(ctrl);
+ dp_panel_config_dsc(ctrl->panel, false);
+ dp_catalog_config_dsc_dto(ctrl->catalog);
+ }
+
+ dp_catalog_override_ack_dto(ctrl->catalog, false);
+
dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);

dp_catalog_ctrl_reset(ctrl->catalog);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index bde1a7c..da59d13 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
*/

#include <linux/module.h>
@@ -99,7 +100,7 @@ struct dp_display_private {
struct dp_debug *debug;

struct dp_usbpd_cb usbpd_cb;
- struct dp_display_mode dp_mode;
+ struct dp_display_mode *dp_mode;
struct msm_dp dp_display;

/* wait for audio signaling */
@@ -831,6 +832,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
goto error_link;
}

+ /* both dp_display and dp_panel shared same dp_mode */
+ dp->dp_mode = &dp->panel->dp_mode;
+
dp->ctrl = dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
dp->power, dp->catalog, dp->parser);
if (IS_ERR(dp->ctrl)) {
@@ -1662,7 +1666,7 @@ void dp_bridge_enable(struct drm_bridge *drm_bridge)
bool force_link_train = false;

dp_display = container_of(dp, struct dp_display_private, dp_display);
- if (!dp_display->dp_mode.drm_mode.clock) {
+ if (!dp_display->dp_mode->drm_mode.clock) {
DRM_ERROR("invalid params\n");
return;
}
@@ -1678,7 +1682,7 @@ void dp_bridge_enable(struct drm_bridge *drm_bridge)
return;
}

- rc = dp_display_set_mode(dp, &dp_display->dp_mode);
+ rc = dp_display_set_mode(dp, dp_display->dp_mode);
if (rc) {
DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
mutex_unlock(&dp_display->event_mutex);
@@ -1744,6 +1748,10 @@ void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
if (state == ST_DISCONNECT_PENDING) {
/* completed disconnection */
dp_display->hpd_state = ST_DISCONNECTED;
+ if (dp_display->panel->dsc_en) {
+ dp_display->dp_mode->timing.comp_info.enabled = false;
+ dp_display->panel->dsc_en = false;
+ }
} else {
dp_display->hpd_state = ST_DISPLAY_OFF;
}
@@ -1762,23 +1770,50 @@ void dp_bridge_mode_set(struct drm_bridge *drm_bridge,

dp_display = container_of(dp, struct dp_display_private, dp_display);

- memset(&dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode));
+ memset(dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode));

if (dp_display_check_video_test(dp))
- dp_display->dp_mode.bpp = dp_display_get_test_bpp(dp);
+ dp_display->dp_mode->bpp = dp_display_get_test_bpp(dp);
else /* Default num_components per pixel = 3 */
- dp_display->dp_mode.bpp = dp->connector->display_info.bpc * 3;
+ dp_display->dp_mode->bpp = dp->connector->display_info.bpc * 3;
+
+ if (!dp_display->dp_mode->bpp)
+ dp_display->dp_mode->bpp = 24; /* Default bpp */
+
+ drm_mode_copy(&dp_display->dp_mode->drm_mode, adjusted_mode);
+
+ dp_display->dp_mode->v_active_low =
+ !!(dp_display->dp_mode->drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
+
+ dp_display->dp_mode->h_active_low =
+ !!(dp_display->dp_mode->drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
+
+
+ if (dp_display->panel->dsc_en) {
+ dp_display->dp_mode->capabilities |= DP_PANEL_CAPS_DSC;
+ dp_panel_convert_to_dp_mode(dp_display->panel, dp_display->dp_mode);
+ }
+}

- if (!dp_display->dp_mode.bpp)
- dp_display->dp_mode.bpp = 24; /* Default bpp */
+void msm_dp_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ dp_bridge_mode_set(bridge, mode, adjusted_mode);
+}

- drm_mode_copy(&dp_display->dp_mode.drm_mode, adjusted_mode);
+struct msm_compression_info *msm_dp_bridge_get_compression(struct drm_bridge *drm_bridge)
+{
+ struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+ struct msm_dp *dp_display = dp_bridge->dp_display;
+ struct dp_display_private *dp;
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);

- dp_display->dp_mode.v_active_low =
- !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
+ if (!dp->panel->dsc_en)
+ return NULL;

- dp_display->dp_mode.h_active_low =
- !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
+ return &dp->dp_mode->timing.comp_info;
}

void dp_bridge_hpd_enable(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 55bb6b0..19e2f07 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -5,11 +5,12 @@
*/

#include "dp_panel.h"
-
+#include "dpu_dsc_helper.h"
#include <drm/drm_fixed.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_print.h>
+#include <drm/display/drm_dsc_helper.h>

#define DSC_TGT_BPP 10

@@ -612,6 +613,573 @@ u8 dp_panel_get_misc_colorimetry_val(struct dp_panel *dp_panel)
return colorimetry;
}

+static inline int fixp2int_ceil(s64 a)
+{
+ return (a ? drm_fixp2int_ceil(a) : 0);
+}
+
+struct dp_dsc_slices_per_line {
+ u32 min_ppr;
+ u32 max_ppr;
+ u8 num_slices;
+};
+
+struct dp_dsc_peak_throughput {
+ u32 index;
+ u32 peak_throughput;
+};
+
+struct dp_dsc_slice_caps_bit_map {
+ u32 num_slices;
+ u32 bit_index;
+};
+
+const struct dp_dsc_slices_per_line slice_per_line_tbl[] = {
+ {0, 340, 1 },
+ {340, 680, 2 },
+ {680, 1360, 4 },
+ {1360, 3200, 8 },
+ {3200, 4800, 12 },
+ {4800, 6400, 16 },
+ {6400, 8000, 20 },
+ {8000, 9600, 24 }
+};
+
+const struct dp_dsc_peak_throughput peak_throughput_mode_0_tbl[] = {
+ {0, 0},
+ {1, 340},
+ {2, 400},
+ {3, 450},
+ {4, 500},
+ {5, 550},
+ {6, 600},
+ {7, 650},
+ {8, 700},
+ {9, 750},
+ {10, 800},
+ {11, 850},
+ {12, 900},
+ {13, 950},
+ {14, 1000},
+};
+
+const struct dp_dsc_slice_caps_bit_map slice_caps_bit_map_tbl[] = {
+ {1, 0},
+ {2, 1},
+ {4, 3},
+ {6, 4},
+ {8, 5},
+ {10, 6},
+ {12, 7},
+ {16, 0},
+ {20, 1},
+ {24, 2},
+};
+
+static bool dp_panel_check_slice_support(u32 num_slices, u32 raw_data_1,
+ u32 raw_data_2)
+{
+ const struct dp_dsc_slice_caps_bit_map *bcap;
+ u32 raw_data;
+ int i;
+
+ if (num_slices <= 12)
+ raw_data = raw_data_1;
+ else
+ raw_data = raw_data_2;
+
+ for (i = 0; i < ARRAY_SIZE(slice_caps_bit_map_tbl); i++) {
+ bcap = &slice_caps_bit_map_tbl[i];
+
+ if (bcap->num_slices == num_slices) {
+ raw_data &= (1 << bcap->bit_index);
+
+ if (raw_data)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static int dp_panel_dsc_prepare_basic_params(
+ struct msm_compression_info *comp_info,
+ const struct dp_display_mode *dp_mode,
+ struct dp_panel *dp_panel)
+{
+ struct dp_panel_private *panel;
+ struct drm_dsc_config *dsc;
+ int i;
+ const struct dp_dsc_slices_per_line *rec;
+ const struct dp_dsc_peak_throughput *tput;
+ u32 slice_width;
+ u32 ppr = dp_mode->timing.pixel_clk_khz/1000;
+ u32 max_slice_width;
+ u32 ppr_max_index;
+ u32 peak_throughput;
+ u32 ppr_per_slice;
+ u32 slice_caps_1;
+ u32 slice_caps_2;
+ u32 dsc_version_major, dsc_version_minor;
+ bool dsc_version_supported = false;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ dsc_version_major = dp_panel->sink_dsc_caps.version & 0xF;
+ dsc_version_minor = (dp_panel->sink_dsc_caps.version >> 4) & 0xF;
+ dsc_version_supported = (dsc_version_major == 0x1 &&
+ (dsc_version_minor == 0x1 || dsc_version_minor == 0x2))
+ ? true : false;
+
+ drm_dbg_dp(panel->drm_dev, "DSC version: %d.%d, dpcd value: %x\n",
+ dsc_version_major, dsc_version_minor,
+ dp_panel->sink_dsc_caps.version);
+
+ if (!dsc_version_supported) {
+ dsc_version_major = 1;
+ dsc_version_minor = 1;
+ DRM_ERROR("invalid sink DSC version, fallback to %d.%d\n",
+ dsc_version_major, dsc_version_minor);
+ }
+
+ dsc = &comp_info->msm_dsc_info.drm_dsc;
+ dsc->dsc_version_major = dsc_version_major;
+ dsc->dsc_version_minor = dsc_version_minor;
+ comp_info->msm_dsc_info.scr_rev = 0x0;
+
+
+ comp_info->msm_dsc_info.slice_per_pkt = 0;
+ for (i = 0; i < ARRAY_SIZE(slice_per_line_tbl); i++) {
+ rec = &slice_per_line_tbl[i];
+ if ((ppr > rec->min_ppr) && (ppr <= rec->max_ppr)) {
+ comp_info->msm_dsc_info.slice_per_pkt = rec->num_slices;
+ i++;
+ break;
+ }
+ }
+
+ if (comp_info->msm_dsc_info.slice_per_pkt == 0)
+ return -EINVAL;
+
+ ppr_max_index = dp_panel->dsc_dpcd[11] &= 0xf;
+ if (!ppr_max_index || ppr_max_index >= 15) {
+ drm_dbg_dp(panel->drm_dev,
+ "Throughput mode 0 not supported");
+ return -EINVAL;
+ }
+
+ tput = &peak_throughput_mode_0_tbl[ppr_max_index];
+ peak_throughput = tput->peak_throughput;
+
+ max_slice_width = dp_panel->dsc_dpcd[12] * 320;
+ slice_width = (dp_mode->timing.h_active /
+ comp_info->msm_dsc_info.slice_per_pkt);
+
+ ppr_per_slice = ppr/comp_info->msm_dsc_info.slice_per_pkt;
+
+ slice_caps_1 = dp_panel->dsc_dpcd[4];
+ slice_caps_2 = dp_panel->dsc_dpcd[13] & 0x7;
+
+ /*
+ * There are 3 conditions to check for sink support:
+ * 1. The slice width cannot exceed the maximum.
+ * 2. The ppr per slice cannot exceed the maximum.
+ * 3. The number of slices must be explicitly supported.
+ */
+ while (slice_width >= max_slice_width ||
+ ppr_per_slice > peak_throughput ||
+ !dp_panel_check_slice_support(
+ comp_info->msm_dsc_info.slice_per_pkt, slice_caps_1,
+ slice_caps_2)) {
+ if (i == ARRAY_SIZE(slice_per_line_tbl))
+ return -EINVAL;
+
+ rec = &slice_per_line_tbl[i];
+ comp_info->msm_dsc_info.slice_per_pkt = rec->num_slices;
+ slice_width = (dp_mode->timing.h_active /
+ comp_info->msm_dsc_info.slice_per_pkt);
+ ppr_per_slice = ppr/comp_info->msm_dsc_info.slice_per_pkt;
+ i++;
+ }
+
+ dsc->block_pred_enable = dp_panel->sink_dsc_caps.block_pred_en;
+
+ dsc->pic_width = dp_mode->timing.h_active;
+ dsc->pic_height = dp_mode->timing.v_active;
+ dsc->slice_width = slice_width;
+
+ if (dsc->pic_height % 108 == 0)
+ dsc->slice_height = 108;
+ else if (dsc->pic_height % 16 == 0)
+ dsc->slice_height = 16;
+ else if (dsc->pic_height % 12 == 0)
+ dsc->slice_height = 12;
+ else
+ dsc->slice_height = 15;
+
+ dsc->bits_per_component = (dp_mode->timing.bpp / 3);
+ dsc->bits_per_pixel = DSC_TGT_BPP << 4;
+ dsc->slice_count = DIV_ROUND_UP(dp_mode->timing.h_active, slice_width);
+
+ comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC;
+ comp_info->tgt_bpp = DSC_TGT_BPP;
+ comp_info->src_bpp = dp_mode->timing.bpp;
+ comp_info->comp_ratio = dp_mode->timing.bpp / DSC_TGT_BPP;
+ comp_info->enabled = true;
+
+ return 0;
+}
+
+static void dp_panel_get_dto_params(u32 src_bpp, u32 tgt_bpp, u32 *num, u32 *denom)
+{
+ if ((tgt_bpp == 12) && (src_bpp == 24)) {
+ *num = 1;
+ *denom = 2;
+ } else if ((tgt_bpp == 15) && (src_bpp == 30)) {
+ *num = 5;
+ *denom = 8;
+ } else if ((tgt_bpp == 8) && ((src_bpp == 24) || (src_bpp == 30))) {
+ *num = 1;
+ *denom = 3;
+ } else if ((tgt_bpp == 10) && (src_bpp == 30)) {
+ *num = 5;
+ *denom = 12;
+ } else {
+ DRM_ERROR("dto params not found\n");
+ *num = 0;
+ *denom = 1;
+ }
+}
+
+static void dp_panel_dsc_prepare_pps_packet(struct dp_panel *dp_panel)
+{
+ struct dp_panel_private *panel;
+ struct dp_dsc_cfg_data *dsc_data;
+ u8 *pps, *parity;
+ u32 *pps_word, *parity_word;
+ int i, index_4;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ dsc_data = &panel->catalog->dsc_data;
+ pps = dsc_data->pps;
+ pps_word = dsc_data->pps_word;
+ parity = dsc_data->parity;
+ parity_word = dsc_data->parity_word;
+
+ memset(parity, 0, sizeof(dsc_data->parity));
+
+ dsc_data->pps_word_len = dsc_data->pps_len >> 2;
+ dsc_data->parity_len = dsc_data->pps_word_len;
+ dsc_data->parity_word_len = (dsc_data->parity_len >> 2) + 1;
+
+ for (i = 0; i < dsc_data->pps_word_len; i++) {
+ index_4 = i << 2;
+ pps_word[i] = pps[index_4 + 0] << 0 |
+ pps[index_4 + 1] << 8 |
+ pps[index_4 + 2] << 16 |
+ pps[index_4 + 3] << 24;
+
+ parity[i] = dp_header_get_parity(pps_word[i]);
+ }
+
+ for (i = 0; i < dsc_data->parity_word_len; i++) {
+ index_4 = i << 2;
+ parity_word[i] = parity[index_4 + 0] << 0 |
+ parity[index_4 + 1] << 8 |
+ parity[index_4 + 2] << 16 |
+ parity[index_4 + 3] << 24;
+ }
+}
+
+void dp_panel_config_dsc(struct dp_panel *dp_panel, bool enable)
+{
+ struct dp_panel_private *panel;
+ struct dp_panel_info *timing;
+ struct msm_compression_info *comp_info;
+ struct dp_dsc_cfg_data *dsc_data;
+ struct drm_dsc_picture_parameter_set *pps_payload;
+ struct drm_dsc_config *dsc;
+
+ if (!dp_panel->dsc_en)
+ return;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ dsc_data = &panel->catalog->dsc_data;
+ timing = &dp_panel->dp_mode.timing;
+ comp_info = &timing->comp_info;
+ dsc = &comp_info->msm_dsc_info.drm_dsc;
+
+ if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC && enable) {
+ memset(dsc_data->pps, 0, sizeof(dsc_data->pps));
+ pps_payload = (struct drm_dsc_picture_parameter_set *)dsc_data->pps;
+ drm_dsc_pps_payload_pack(pps_payload, dsc);
+
+ dsc_data->pps_len = DSC_1_1_PPS_PARAMETER_SET_ELEMENTS;
+ dp_panel_dsc_prepare_pps_packet(dp_panel);
+
+ dsc_data->slice_per_pkt = comp_info->msm_dsc_info.slice_per_pkt - 1;
+ dsc_data->bytes_per_pkt = comp_info->msm_dsc_info.bytes_per_pkt;
+ dsc_data->bytes_per_pkt /= comp_info->msm_dsc_info.slice_per_pkt;
+ dsc_data->eol_byte_num = comp_info->msm_dsc_info.eol_byte_num;
+ dsc_data->dto_count = comp_info->msm_dsc_info.pclk_per_line;
+ dsc_data->be_in_lane = 10;
+ dsc_data->dsc_en = true;
+ dsc_data->dto_en = true;
+ dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dsc_data->dto_n,
+ &dsc_data->dto_d);
+ } else {
+ dsc_data->dsc_en = false;
+ dsc_data->dto_en = false;
+ dsc_data->dto_n = 0;
+ dsc_data->dto_d = 0;
+ dsc_data->continuous_pps = false;
+ }
+}
+
+static void _dp_panel_dsc_get_num_extra_pclk(struct msm_compression_info *comp_info)
+{
+ unsigned int dto_n = 0, dto_d = 0, remainder;
+ int ack_required, last_few_ack_required, accum_ack;
+ int last_few_pclk, last_few_pclk_required;
+ struct msm_display_dsc_info *dsc_info = &comp_info->msm_dsc_info;
+ int start, temp, line_width = dsc_info->drm_dsc.pic_width/2;
+ s64 temp1_fp, temp2_fp;
+
+ dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dto_n, &dto_d);
+
+ ack_required = dsc_info->pclk_per_line;
+
+ /* number of pclk cycles left outside of the complete DTO set */
+ last_few_pclk = line_width % dto_d;
+
+ /* number of pclk cycles outside of the complete dto */
+ temp1_fp = drm_fixp_from_fraction(line_width, dto_d);
+ temp2_fp = drm_fixp_from_fraction(dto_n, 1);
+ temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ temp = drm_fixp2int(temp1_fp);
+ last_few_ack_required = ack_required - temp;
+
+ /*
+ * check how many more pclk is needed to
+ * accommodate the last few ack required
+ */
+ remainder = dto_n;
+ accum_ack = 0;
+ last_few_pclk_required = 0;
+ while (accum_ack < last_few_ack_required) {
+ last_few_pclk_required++;
+
+ if (remainder >= dto_n)
+ start = remainder;
+ else
+ start = remainder + dto_d;
+
+ remainder = start - dto_n;
+ if (remainder < dto_n)
+ accum_ack++;
+ }
+
+ /* if fewer pclk than required */
+ if (last_few_pclk < last_few_pclk_required)
+ dsc_info->extra_width = last_few_pclk_required - last_few_pclk;
+ else
+ dsc_info->extra_width = 0;
+}
+
+static void _dp_panel_dsc_bw_overhead_calc(struct dp_panel *dp_panel,
+ struct msm_display_dsc_info *dsc_info,
+ struct dp_display_mode *dp_mode, u32 dsc_byte_cnt)
+{
+ int num_slices, tot_num_eoc_symbols;
+ int tot_num_hor_bytes, tot_num_dummy_bytes;
+ int dwidth_dsc_bytes, eoc_bytes;
+ u32 num_lanes;
+ struct dp_panel_private *panel;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ num_lanes = panel->link->link_params.num_lanes;
+ num_slices = dsc_info->slice_per_pkt;
+
+ eoc_bytes = dsc_byte_cnt % num_lanes;
+ tot_num_eoc_symbols = num_lanes * num_slices;
+ tot_num_hor_bytes = dsc_byte_cnt * num_slices;
+ tot_num_dummy_bytes = (num_lanes - eoc_bytes) * num_slices;
+
+ if (!eoc_bytes)
+ tot_num_dummy_bytes = 0;
+
+ dwidth_dsc_bytes = tot_num_hor_bytes + tot_num_eoc_symbols +
+ tot_num_dummy_bytes;
+
+ drm_dbg_dp(panel->drm_dev, "dwidth_dsc_bytes:%d, tot_num_hor_bytes:%d\n",
+ dwidth_dsc_bytes, tot_num_hor_bytes);
+
+ dp_mode->dsc_overhead_fp = drm_fixp_from_fraction(dwidth_dsc_bytes,
+ tot_num_hor_bytes);
+
+ dp_mode->timing.dsc_overhead_fp = dp_mode->dsc_overhead_fp;
+}
+
+static void dp_panel_dsc_pclk_param_calc(struct dp_panel *dp_panel,
+ struct msm_compression_info *comp_info,
+ struct dp_display_mode *dp_mode)
+{
+ int comp_ratio = 100, intf_width;
+ int slice_per_pkt, slice_per_intf;
+ s64 temp1_fp, temp2_fp;
+ s64 numerator_fp, denominator_fp;
+ s64 dsc_byte_count_fp;
+ u32 dsc_byte_count, temp1, temp2;
+ struct msm_display_dsc_info *dsc_info = &comp_info->msm_dsc_info;
+
+ intf_width = dp_mode->timing.h_active;
+ if (!dsc_info || !dsc_info->drm_dsc.slice_width || !dsc_info->slice_per_pkt ||
+ (intf_width < dsc_info->drm_dsc.slice_width))
+ return;
+
+ slice_per_pkt = dsc_info->slice_per_pkt;
+ slice_per_intf = DIV_ROUND_UP(intf_width,
+ dsc_info->drm_dsc.slice_width);
+
+ comp_ratio = mult_frac(100, comp_info->src_bpp, comp_info->tgt_bpp);
+
+ temp1_fp = drm_fixp_from_fraction(comp_ratio, 100);
+ temp2_fp = drm_fixp_from_fraction(slice_per_pkt * 8, 1);
+ denominator_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ numerator_fp = drm_fixp_from_fraction(
+ intf_width * dsc_info->drm_dsc.bits_per_component * 3, 1);
+ dsc_byte_count_fp = drm_fixp_div(numerator_fp, denominator_fp);
+ dsc_byte_count = fixp2int_ceil(dsc_byte_count_fp);
+
+ temp1 = dsc_byte_count * slice_per_intf;
+ temp2 = temp1;
+ if (temp1 % 3 != 0)
+ temp1 += 3 - (temp1 % 3);
+
+ dsc_info->eol_byte_num = temp1 - temp2;
+
+ temp1_fp = drm_fixp_from_fraction(slice_per_intf, 6);
+ temp2_fp = drm_fixp_mul(dsc_byte_count_fp, temp1_fp);
+ dsc_info->pclk_per_line = fixp2int_ceil(temp2_fp);
+
+ _dp_panel_dsc_get_num_extra_pclk(comp_info);
+ dsc_info->pclk_per_line--;
+
+ _dp_panel_dsc_bw_overhead_calc(dp_panel, dsc_info, dp_mode, dsc_byte_count);
+}
+
+void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel,
+ struct dp_display_mode *dp_mode)
+{
+ struct dp_panel_private *panel;
+ const u32 num_components = 3, default_bpp = 24;
+ struct drm_display_mode *drm_mode;
+ struct dp_panel_info *timing;
+ struct msm_compression_info *comp_info;
+ bool dsc_cap = (dp_mode->capabilities & DP_PANEL_CAPS_DSC) ?
+ true : false;
+ int rc;
+
+ if (!dp_panel->dsc_en)
+ return;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ drm_mode = &dp_mode->drm_mode;
+ timing = &dp_mode->timing;
+
+ timing->h_active = drm_mode->hdisplay;
+ timing->h_back_porch = drm_mode->htotal - drm_mode->hsync_end;
+ timing->h_sync_width = drm_mode->htotal -
+ (drm_mode->hsync_start + dp_mode->timing.h_back_porch);
+
+ timing->h_front_porch = drm_mode->hsync_start -
+ drm_mode->hdisplay;
+ timing->h_skew = drm_mode->hskew;
+
+ timing->v_active = drm_mode->vdisplay;
+ timing->v_back_porch = drm_mode->vtotal - drm_mode->vsync_end;
+ timing->v_sync_width = drm_mode->vtotal -
+ (drm_mode->vsync_start + dp_mode->timing.v_back_porch);
+
+ timing->v_front_porch = drm_mode->vsync_start - drm_mode->vdisplay;
+
+ timing->refresh_rate = drm_mode_vrefresh(drm_mode);
+ timing->pixel_clk_khz = drm_mode->clock;
+
+ timing->v_active_low =
+ !!(drm_mode->flags & DRM_MODE_FLAG_NVSYNC);
+
+ timing->h_active_low =
+ !!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+ timing->bpp =
+ dp_panel->connector->display_info.bpc * num_components;
+ if (!timing->bpp)
+ timing->bpp = default_bpp;
+
+ timing->widebus_en = dp_panel->widebus_en;
+ timing->dsc_overhead_fp = 0;
+
+ comp_info = &timing->comp_info;
+ comp_info->src_bpp = default_bpp;
+ comp_info->tgt_bpp = default_bpp;
+ comp_info->comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+ comp_info->comp_ratio = 1;
+ comp_info->enabled = false;
+
+ /* As YUV was not supported now, so set the default format to RGB */
+ dp_mode->output_format = DP_OUTPUT_FORMAT_RGB;
+ /*
+ * If a given videomode can be only supported in YCBCR420, set
+ * the output format to YUV420. While now our driver did not
+ * support YUV display over DP, so just place this flag here.
+ * When we want to support YUV, we can use this flag to do
+ * a lot of settings, like CDM, CSC and pixel_clock.
+ */
+ if (drm_mode_is_420_only(&dp_panel->connector->display_info,
+ drm_mode)) {
+ dp_mode->output_format = DP_OUTPUT_FORMAT_YCBCR420;
+ drm_dbg_dp(panel->drm_dev, "YCBCR420 was not supported");
+ }
+
+ timing->bpp = dp_panel_get_mode_bpp(dp_panel,
+ timing->bpp, timing->pixel_clk_khz);
+
+
+ if (dp_panel->dsc_en && dsc_cap) {
+ if (dp_panel_dsc_prepare_basic_params(comp_info,
+ dp_mode, dp_panel)) {
+ drm_dbg_dp(panel->drm_dev,
+ "prepare DSC basic params failed\n");
+ return;
+ }
+
+ rc = dpu_dsc_populate_dsc_config(&comp_info->msm_dsc_info.drm_dsc, 0);
+ if (rc) {
+ drm_dbg_dp(panel->drm_dev,
+ "failed populating dsc params \n");
+ return;
+ }
+
+ rc = dpu_dsc_populate_dsc_private_params(&comp_info->msm_dsc_info,
+ dp_mode->timing.h_active);
+ if (rc) {
+ drm_dbg_dp(panel->drm_dev,
+ "failed populating other dsc params\n");
+ return;
+ }
+
+ dp_panel_dsc_pclk_param_calc(dp_panel, comp_info, dp_mode);
+ }
+ dp_mode->fec_overhead_fp = dp_panel->fec_overhead_fp;
+}
+
struct dp_panel *dp_panel_get(struct dp_panel_in *in)
{
struct dp_panel_private *panel;
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 4c45d51..576056c 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -112,6 +112,7 @@ struct dp_panel {
bool fec_feature_enable;
bool dsc_en;
bool fec_en;
+ bool widebus_en;
s64 fec_overhead_fp;
};

@@ -128,6 +129,9 @@ int dp_panel_get_modes(struct dp_panel *dp_panel,
void dp_panel_handle_sink_request(struct dp_panel *dp_panel);
void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable);
u8 dp_panel_get_misc_colorimetry_val(struct dp_panel *dp_panel);
+void dp_panel_config_dsc(struct dp_panel *dp_panel, bool enable);
+void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel,
+ struct dp_display_mode *dp_mode);

/**
* is_link_rate_valid() - validates the link rate
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 2686028..96d48d0c 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
*/

#ifndef _DP_REG_H_
@@ -167,7 +168,38 @@
#define MMSS_DP_PSR_CRC_RG (0x00000154)
#define MMSS_DP_PSR_CRC_B (0x00000158)

-#define REG_DP_COMPRESSION_MODE_CTRL (0x00000180)
+#define DP_COMPRESSION_MODE_CTRL (0x00000180)
+#define DP_PPS_HB_0_3 (0x00000184)
+#define DP_PPS_PB_0_3 (0x00000188)
+#define DP_PPS_PB_4_7 (0x0000018C)
+#define DP_PPS_PB_8_11 (0x00000190)
+#define DP_PPS_PB_12_15 (0x00000194)
+#define DP_PPS_PB_16_19 (0x00000198)
+#define DP_PPS_PB_20_23 (0x0000019C)
+#define DP_PPS_PB_24_27 (0x000001A0)
+#define DP_PPS_PB_28_31 (0x000001A4)
+#define DP_PPS_PPS_0_3 (0x000001A8)
+#define DP_PPS_PPS_4_7 (0x000001AC)
+#define DP_PPS_PPS_8_11 (0x000001B0)
+#define DP_PPS_PPS_12_15 (0x000001B4)
+#define DP_PPS_PPS_16_19 (0x000001B8)
+#define DP_PPS_PPS_20_23 (0x000001BC)
+#define DP_PPS_PPS_24_27 (0x000001C0)
+#define DP_PPS_PPS_28_31 (0x000001C4)
+#define DP_PPS_PPS_32_35 (0x000001C8)
+#define DP_PPS_PPS_36_39 (0x000001CC)
+#define DP_PPS_PPS_40_43 (0x000001D0)
+#define DP_PPS_PPS_44_47 (0x000001D4)
+#define DP_PPS_PPS_48_51 (0x000001D8)
+#define DP_PPS_PPS_52_55 (0x000001DC)
+#define DP_PPS_PPS_56_59 (0x000001E0)
+#define DP_PPS_PPS_60_63 (0x000001E4)
+#define DP_PPS_PPS_64_67 (0x000001E8)
+#define DP_PPS_PPS_68_71 (0x000001EC)
+#define DP_PPS_PPS_72_75 (0x000001F0)
+#define DP_PPS_PPS_76_79 (0x000001F4)
+#define DP_PPS_PPS_80_83 (0x000001F8)
+#define DP_PPS_PPS_84_87 (0x000001FC)

#define MMSS_DP_AUDIO_CFG (0x00000200)
#define MMSS_DP_AUDIO_STATUS (0x00000204)
@@ -178,6 +210,8 @@

#define MMSS_DP_SDP_CFG (0x00000228)
#define MMSS_DP_SDP_CFG2 (0x0000022C)
+#define MMSS_DP_SDP_CFG3 (0x0000024C)
+#define MMSS_DP_SDP_CFG4 (0x000004EC)
#define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
#define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)

@@ -210,6 +244,9 @@
#define MMSS_DP_AUDIO_INFOFRAME_1 (0x000002AC)
#define MMSS_DP_AUDIO_INFOFRAME_2 (0x000002B0)

+#define MMSS_DP_FLUSH (0x000002F8)
+#define MMSS_DP1_FLUSH (0x000002FC)
+
#define MMSS_DP_GENERIC0_0 (0x00000300)
#define MMSS_DP_GENERIC0_1 (0x00000304)
#define MMSS_DP_GENERIC0_2 (0x00000308)
@@ -268,6 +305,7 @@

#define MMSS_DP_TPG_MAIN_CONTROL (0x00000060)
#define MMSS_DP_DSC_DTO (0x0000007C)
+#define MMSS_DP_DSC_DTO_COUNT (0x00000084)
#define DP_TPG_CHECKERED_RECT_PATTERN (0x00000100)

#define MMSS_DP_TPG_VIDEO_CONFIG (0x00000064)
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index cf4eb8d..6a46ed7 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -476,6 +476,11 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_displa

void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor);
bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
+struct msm_compression_info *msm_dp_bridge_get_compression(struct drm_bridge *drm_bridge);
+
+void msm_dp_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);

#else
static inline int __init msm_dp_register(void)
@@ -510,6 +515,17 @@ static inline bool msm_dp_wide_bus_available(const struct msm_dp *dp_display)
return false;
}

+static inline struct msm_compression_info *msm_dp_bridge_get_compression(
+ struct drm_bridge *drm_bridge)
+{
+ return NULL;
+}
+static inline void msm_dp_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+
+}
#endif

#ifdef CONFIG_DRM_MSM_MDP4
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:26:01

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC

Current implementation timing engine programming does not consider
compression factors. This patch add consideration of DSC factors
while programming timing engine.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
.../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 2 +
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 14 ++-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 +++++++++++++--------
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
5 files changed, 110 insertions(+), 54 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
index 2d864f9..3330e185 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
@@ -279,6 +279,8 @@ static void dpu_encoder_phys_vid_setup_timing_engine(
if (phys_enc->hw_pp->merge_3d)
intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;

+ phys_enc->hw_intf->hw_rev = phys_enc->dpu_kms->core_rev;
+
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
&timing_params, fmt);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
index 7b0b092..c6ee789 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
@@ -43,16 +43,22 @@
#define DPU_HW_VER_500 DPU_HW_VER(5, 0, 0) /* sm8150 v1.0 */
#define DPU_HW_VER_501 DPU_HW_VER(5, 0, 1) /* sm8150 v2.0 */
#define DPU_HW_VER_510 DPU_HW_VER(5, 1, 1) /* sc8180 */
-#define DPU_HW_VER_600 DPU_HW_VER(6, 0, 0) /* sm8250 */
+#define DPU_HW_VER_600 DPU_HW_VER(6, 0, 0) /* sm8250, kona */
#define DPU_HW_VER_620 DPU_HW_VER(6, 2, 0) /* sc7180 v1.0 */
#define DPU_HW_VER_630 DPU_HW_VER(6, 3, 0) /* sm6115|sm4250 */
#define DPU_HW_VER_650 DPU_HW_VER(6, 5, 0) /* qcm2290|sm4125 */
-#define DPU_HW_VER_700 DPU_HW_VER(7, 0, 0) /* sm8350 */
+#define DPU_HW_VER_700 DPU_HW_VER(7, 0, 0) /* sm8350, lahaina */
#define DPU_HW_VER_720 DPU_HW_VER(7, 2, 0) /* sc7280 */
#define DPU_HW_VER_800 DPU_HW_VER(8, 0, 0) /* sc8280xp */
-#define DPU_HW_VER_810 DPU_HW_VER(8, 1, 0) /* sm8450 */
+#define DPU_HW_VER_810 DPU_HW_VER(8, 1, 0) /* sm8450, waipio */
#define DPU_HW_VER_900 DPU_HW_VER(9, 0, 0) /* sm8550 */

+/* Avoid using below IS_XXX macros outside catalog, use feature bit instead */
+#define IS_DPU_MAJOR_SAME(rev1, rev2) \
+ (DPU_HW_MAJOR((rev1)) == DPU_HW_MAJOR((rev2)))
+#define IS_DPU_MAJOR_MINOR_SAME(rev1, rev2) \
+ (DPU_HW_MAJOR_MINOR((rev1)) == DPU_HW_MAJOR_MINOR((rev2)))
+
#define IS_MSM8996_TARGET(rev) IS_DPU_MAJOR_MINOR_SAME((rev), DPU_HW_VER_170)
#define IS_MSM8998_TARGET(rev) IS_DPU_MAJOR_MINOR_SAME((rev), DPU_HW_VER_300)
#define IS_SDM845_TARGET(rev) IS_DPU_MAJOR_MINOR_SAME((rev), DPU_HW_VER_400)
@@ -240,6 +246,7 @@ enum {
* @DPU_INTF_INPUT_CTRL Supports the setting of pp block from which
* pixel data arrives to this INTF
* @DPU_INTF_TE INTF block has TE configuration support
+ * @DPU_INTF_TE_ALIGN_VSYNC INTF block has POMS Align vsync support
* @DPU_DATA_HCTL_EN Allows data to be transferred at different rate
than video timing
* @DPU_INTF_MAX
@@ -247,6 +254,7 @@ enum {
enum {
DPU_INTF_INPUT_CTRL = 0x1,
DPU_INTF_TE,
+ DPU_INTF_TE_ALIGN_VSYNC,
DPU_DATA_HCTL_EN,
DPU_INTF_MAX
};
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
index 7ce66bf..238efdb 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*/

@@ -44,6 +44,7 @@
#define INTF_DEFLICKER_STRNG_COEFF 0x0F4
#define INTF_DEFLICKER_WEAK_COEFF 0x0F8

+#define INTF_REG_SPLIT_LINK 0x080
#define INTF_DSI_CMD_MODE_TRIGGER_EN 0x084
#define INTF_PANEL_FORMAT 0x090
#define INTF_TPG_ENABLE 0x100
@@ -65,9 +66,9 @@

#define INTF_CFG_ACTIVE_H_EN BIT(29)
#define INTF_CFG_ACTIVE_V_EN BIT(30)
-
#define INTF_CFG2_DATABUS_WIDEN BIT(0)
#define INTF_CFG2_DATA_HCTL_EN BIT(4)
+#define INTF_CFG2_ALIGN_VSYNC_TO_TE BIT(16)

#define INTF_MISR_CTRL 0x180
#define INTF_MISR_SIGNATURE 0x184
@@ -91,6 +92,16 @@ static const struct dpu_intf_cfg *_intf_offset(enum dpu_intf intf,
return ERR_PTR(-EINVAL);
}

+static inline void _check_and_set_comp_bit(struct dpu_hw_intf *ctx,
+ bool dsc_4hs_merge, bool compression_en, u32 *intf_cfg2)
+{
+ if (((DPU_HW_MAJOR(ctx->hw_rev) >= DPU_HW_MAJOR(DPU_HW_VER_700)) && compression_en)
+ || (IS_DPU_MAJOR_SAME(ctx->hw_rev, DPU_HW_VER_600) && dsc_4hs_merge))
+ (*intf_cfg2) |= BIT(12);
+ else if (!compression_en)
+ (*intf_cfg2) &= ~BIT(12);
+}
+
static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
const struct intf_timing_params *p,
const struct dpu_format *fmt)
@@ -113,82 +124,96 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
/* read interface_cfg */
intf_cfg = DPU_REG_READ(c, INTF_CONFIG);

- if (ctx->cap->type == INTF_DP)
+ if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP)
dp_intf = true;

hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width +
- p->h_front_porch;
+ p->h_front_porch;
vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height +
- p->v_front_porch;
+ p->v_front_porch;

display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
- hsync_period) + p->hsync_skew;
+ hsync_period) + p->hsync_skew;
display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) +
- p->hsync_skew - 1;
+ p->hsync_skew - 1;
+
+ hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;

hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
hsync_end_x = hsync_period - p->h_front_porch - 1;

- if (p->width != p->xres) { /* border fill added */
- active_h_start = hsync_start_x;
- active_h_end = active_h_start + p->xres - 1;
- } else {
- active_h_start = 0;
- active_h_end = 0;
- }
-
- if (p->height != p->yres) { /* border fill added */
- active_v_start = display_v_start;
- active_v_end = active_v_start + (p->yres * hsync_period) - 1;
- } else {
- active_v_start = 0;
- active_v_end = 0;
- }
-
- if (active_h_end) {
- active_hctl = (active_h_end << 16) | active_h_start;
- intf_cfg |= INTF_CFG_ACTIVE_H_EN;
- } else {
- active_hctl = 0;
- }
-
- if (active_v_end)
- intf_cfg |= INTF_CFG_ACTIVE_V_EN;
-
- hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
- display_hctl = (hsync_end_x << 16) | hsync_start_x;
-
/*
* DATA_HCTL_EN controls data timing which can be different from
* video timing. It is recommended to enable it for all cases, except
* if compression is enabled in 1 pixel per clock mode
*/
+ if (!p->compression_en || p->wide_bus_en)
+ intf_cfg2 |= INTF_CFG2_DATA_HCTL_EN;
+
if (p->wide_bus_en)
- intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN | INTF_CFG2_DATA_HCTL_EN;
+ intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;

+ /*
+ * If widebus is disabled:
+ * For uncompressed stream, the data is valid for the entire active
+ * window period.
+ * For compressed stream, data is valid for a shorter time period
+ * inside the active window depending on the compression ratio.
+ *
+ * If widebus is enabled:
+ * For uncompressed stream, data is valid for only half the active
+ * window, since the data rate is doubled in this mode.
+ * p->width holds the adjusted width for DP but unadjusted width for DSI
+ * For compressed stream, data validity window needs to be adjusted for
+ * compression ratio and then further halved.
+ */
data_width = p->width;

+ if (p->compression_en) {
+ if (p->wide_bus_en)
+ data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6);
+ else
+ data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3);
+ } else if (!dp_intf && p->wide_bus_en) {
+ data_width = p->width >> 1;
+ } else {
+ data_width = p->width;
+ }
+
hsync_data_start_x = hsync_start_x;
hsync_data_end_x = hsync_start_x + data_width - 1;

+ display_hctl = (hsync_end_x << 16) | hsync_start_x;
display_data_hctl = (hsync_data_end_x << 16) | hsync_data_start_x;

if (dp_intf) {
/* DP timing adjustment */
display_v_start += p->hsync_pulse_width + p->h_back_porch;
display_v_end -= p->h_front_porch;
+ }
+
+ intf_cfg |= INTF_CFG_ACTIVE_H_EN;
+ intf_cfg |= INTF_CFG_ACTIVE_V_EN;
+ active_h_start = hsync_start_x;
+ active_h_end = active_h_start + p->xres - 1;
+ active_v_start = display_v_start;
+ active_v_end = active_v_start + (p->yres * hsync_period) - 1;

- active_h_start = hsync_start_x;
- active_h_end = active_h_start + p->xres - 1;
- active_v_start = display_v_start;
- active_v_end = active_v_start + (p->yres * hsync_period) - 1;
+ active_hctl = (active_h_end << 16) | active_h_start;

- active_hctl = (active_h_end << 16) | active_h_start;
+ if (dp_intf) {
display_hctl = active_hctl;

- intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN;
+ if (p->compression_en) {
+ active_data_hctl = (hsync_start_x + p->extra_dto_cycles) << 16;
+ active_data_hctl += hsync_start_x;
+
+ display_data_hctl = active_data_hctl;
+ }
}

+ _check_and_set_comp_bit(ctx, p->dsc_4hs_merge, p->compression_en, &intf_cfg2);
+
den_polarity = 0;
if (ctx->cap->type == INTF_HDMI) {
hsync_polarity = p->yres >= 720 ? 0 : 1;
@@ -202,7 +227,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
}
polarity_ctl = (den_polarity << 2) | /* DEN Polarity */
(vsync_polarity << 1) | /* VSYNC Polarity */
- (hsync_polarity << 0); /* HSYNC Polarity */
+ (hsync_polarity << 0); /* HSYNC Polarity */

if (!DPU_FORMAT_IS_YUV(fmt))
panel_format = (fmt->bits[C0_G_Y] |
@@ -216,6 +241,17 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
(COLOR_8BIT << 4) |
(0x21 << 8));

+ if (p->wide_bus_en)
+ intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
+
+ /* Synchronize timing engine enable to TE */
+ if ((ctx->cap->features & BIT(DPU_INTF_TE_ALIGN_VSYNC))
+ && p->poms_align_vsync)
+ intf_cfg2 |= INTF_CFG2_ALIGN_VSYNC_TO_TE;
+
+ if (ctx->cfg.split_link_en)
+ DPU_REG_WRITE(c, INTF_REG_SPLIT_LINK, 0x3);
+
DPU_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl);
DPU_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period);
DPU_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0,
@@ -233,11 +269,9 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
DPU_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3);
DPU_REG_WRITE(c, INTF_CONFIG, intf_cfg);
DPU_REG_WRITE(c, INTF_PANEL_FORMAT, panel_format);
- if (ctx->cap->features & BIT(DPU_DATA_HCTL_EN)) {
- DPU_REG_WRITE(c, INTF_CONFIG2, intf_cfg2);
- DPU_REG_WRITE(c, INTF_DISPLAY_DATA_HCTL, display_data_hctl);
- DPU_REG_WRITE(c, INTF_ACTIVE_DATA_HCTL, active_data_hctl);
- }
+ DPU_REG_WRITE(c, INTF_CONFIG2, intf_cfg2);
+ DPU_REG_WRITE(c, INTF_DISPLAY_DATA_HCTL, display_data_hctl);
+ DPU_REG_WRITE(c, INTF_ACTIVE_DATA_HCTL, active_data_hctl);
}

static void dpu_hw_intf_enable_timing_engine(
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
index 643dd10..57be86d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*/

@@ -10,6 +10,7 @@
#include "dpu_hw_catalog.h"
#include "dpu_hw_mdss.h"
#include "dpu_hw_util.h"
+#include "dpu_hw_top.h"

struct dpu_hw_intf;

@@ -33,6 +34,11 @@ struct intf_timing_params {
u32 hsync_skew;

bool wide_bus_en;
+ bool compression_en;
+ u32 extra_dto_cycles; /* for DP only */
+ bool dsc_4hs_merge; /* DSC 4HS merge */
+ bool poms_align_vsync; /* poms with vsync aligned */
+ u32 dce_bytes_per_line;
};

struct intf_prog_fetch {
@@ -86,11 +92,13 @@ struct dpu_hw_intf_ops {

struct dpu_hw_intf {
struct dpu_hw_blk_reg_map hw;
+ u32 hw_rev; /* mdss hw_rev */

/* intf */
enum dpu_intf idx;
const struct dpu_intf_cfg *cap;
const struct dpu_mdss_cfg *mdss;
+ struct split_pipe_cfg cfg;

/* ops */
struct dpu_hw_intf_ops ops;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
index a1a9e44..1212fa2 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/

#ifndef _DPU_HW_TOP_H
@@ -34,12 +36,14 @@ struct traffic_shaper_cfg {
* @intf : Interface id for main control path
* @split_flush_en: Allows both the paths to be flushed when master path is
* flushed
+ * @split_link_en: Check if split link is enabled
*/
struct split_pipe_cfg {
bool en;
enum dpu_intf_mode mode;
enum dpu_intf intf;
bool split_flush_en;
+ bool split_link_en;
};

/**
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:26:04

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

DSC V1.2 encoder engine is newly added hardware module. This patch
add support functions to configure and enable DSC V1.2 encoder engine.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/Makefile | 1 +
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 2 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 60 +++-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++++++++++++++++++
6 files changed, 463 insertions(+), 17 deletions(-)
create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 28cf52b..271c29a15 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
disp/dpu1/dpu_hw_catalog.o \
disp/dpu1/dpu_hw_ctl.o \
disp/dpu1/dpu_hw_dsc.o \
+ disp/dpu1/dpu_hw_dsc_1_2.o \
disp/dpu1/dpu_dsc_helper.o \
disp/dpu1/dpu_hw_interrupts.o \
disp/dpu1/dpu_hw_intf.o \
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 7f4a439..901e317 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1821,7 +1821,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
u32 initial_lines)
{
if (hw_dsc->ops.dsc_config)
- hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines);
+ hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);

if (hw_dsc->ops.dsc_config_thresh)
hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
index 978e3bd..7b0b092 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
*/

@@ -11,6 +11,7 @@
#include <linux/bug.h>
#include <linux/bitmap.h>
#include <linux/err.h>
+#include "dpu_hw_mdss.h"

/**
* Max hardware block count: For ex: max 12 SSPP pipes or
@@ -182,6 +183,7 @@ enum {
* @DPU_PINGPONG_TE2 Additional tear check block for split pipes
* @DPU_PINGPONG_SPLIT PP block supports split fifo
* @DPU_PINGPONG_SLAVE PP block is a suitable slave for split fifo
+ * @DPU_PINGPONG_DSC, Display stream compression blocks
* @DPU_PINGPONG_DITHER, Dither blocks
* @DPU_PINGPONG_MAX
*/
@@ -190,10 +192,32 @@ enum {
DPU_PINGPONG_TE2,
DPU_PINGPONG_SPLIT,
DPU_PINGPONG_SLAVE,
+ DPU_PINGPONG_DSC,
DPU_PINGPONG_DITHER,
DPU_PINGPONG_MAX
};

+
+/** DSC sub-blocks/features
+ * @DPU_DSC_OUTPUT_CTRL Supports the control of the pp id which gets
+ * the pixel output from this DSC.
+ * @DPU_DSC_HW_REV_1_1 dsc block supports dsc 1.1 only
+ * @DPU_DSC_HW_REV_1_2 dsc block supports dsc 1.1 and 1.2
+ * @DPU_DSC_NATIVE_422_EN, Supports native422 and native420 encoding
+ * @DPU_DSC_ENC, DSC encoder sub block
+ * @DPU_DSC_CTL, DSC ctl sub block
+ * @DPU_DSC_MAX
+ */
+enum {
+ DPU_DSC_OUTPUT_CTRL = 0x1,
+ DPU_DSC_HW_REV_1_1,
+ DPU_DSC_HW_REV_1_2,
+ DPU_DSC_NATIVE_422_EN,
+ DPU_DSC_ENC,
+ DPU_DSC_CTL,
+ DPU_DSC_MAX
+};
+
/**
* CTL sub-blocks
* @DPU_CTL_SPLIT_DISPLAY: CTL supports video mode split display
@@ -276,15 +300,6 @@ enum {
};

/**
- * DSC features
- * @DPU_DSC_OUTPUT_CTRL Configure which PINGPONG block gets
- * the pixel output from this DSC.
- */
-enum {
- DPU_DSC_OUTPUT_CTRL = 0x1,
-};
-
-/**
* MACRO DPU_HW_BLK_INFO - information of HW blocks inside DPU
* @name: string name for debug purposes
* @id: enum identifying this block
@@ -346,6 +361,14 @@ struct dpu_pp_blk {
};

/**
+ * struct dpu_dsc_blk : DSC Encoder sub-blk information
+ * @info: HW register and features supported by this sub-blk
+ */
+struct dpu_dsc_blk {
+ DPU_HW_SUBBLK_INFO;
+};
+
+/**
* enum dpu_qos_lut_usage - define QoS LUT use cases
*/
enum dpu_qos_lut_usage {
@@ -403,6 +426,7 @@ struct dpu_rotation_cfg {
* @pixel_ram_size size of latency hiding and de-tiling buffer in bytes
* @max_hdeci_exp max horizontal decimation supported (max is 2^value)
* @max_vdeci_exp max vertical decimation supported (max is 2^value)
+ * @max_dsc_width max dsc line width support.
*/
struct dpu_caps {
u32 max_mixer_width;
@@ -419,6 +443,7 @@ struct dpu_caps {
u32 pixel_ram_size;
u32 max_hdeci_exp;
u32 max_vdeci_exp;
+ u32 max_dsc_width;
};

/**
@@ -494,9 +519,20 @@ struct dpu_dspp_sub_blks {
struct dpu_pingpong_sub_blks {
struct dpu_pp_blk te;
struct dpu_pp_blk te2;
+ struct dpu_pp_blk dsc;
struct dpu_pp_blk dither;
};

+
+/**
+ * struct dpu_dsc_sub_blks : DSC sub-blks
+ *
+ */
+struct dpu_dsc_sub_blks {
+ struct dpu_dsc_blk enc;
+ struct dpu_dsc_blk ctl;
+};
+
/**
* dpu_clk_ctrl_type - Defines top level clock control signals
*/
@@ -641,10 +677,14 @@ struct dpu_merge_3d_cfg {
* struct dpu_dsc_cfg - information of DSC blocks
* @id enum identifying this block
* @base register offset of this block
+ * @len: length of hardware block
* @features bit mask identifying sub-blocks/features
+ * @dsc_pair_mask: Bitmask of DSCs that can be controlled by same CTL
*/
struct dpu_dsc_cfg {
DPU_HW_BLK_INFO;
+ DECLARE_BITMAP(dsc_pair_mask, DSC_MAX);
+ const struct dpu_dsc_sub_blks *sblk;
};

/**
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
index 619926d..51e8890 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2022, Linaro Limited
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
*/

#include "dpu_kms.h"
@@ -41,10 +42,11 @@ static void dpu_hw_dsc_disable(struct dpu_hw_dsc *dsc)
static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
struct drm_dsc_config *dsc,
u32 mode,
- u32 initial_lines)
+ u32 initial_lines,
+ bool ich_reset_override)
{
struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
- u32 data;
+ u32 data, lsb, bpp;
u32 slice_last_group_size;
u32 det_thresh_flatness;
bool is_cmd_mode = !(mode & DSC_MODE_VIDEO);
@@ -58,7 +60,14 @@ static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
data = (initial_lines << 20);
data |= ((slice_last_group_size - 1) << 18);
/* bpp is 6.4 format, 4 LSBs bits are for fractional part */
- data |= (dsc->bits_per_pixel << 8);
+ data |= dsc->bits_per_pixel << 12;
+ lsb = dsc->bits_per_pixel % 4;
+ bpp = dsc->bits_per_pixel / 4;
+ bpp *= 4;
+ bpp <<= 4;
+ bpp |= lsb;
+
+ data |= bpp << 8;
data |= (dsc->block_pred_enable << 7);
data |= (dsc->line_buf_depth << 3);
data |= (dsc->simple_422 << 2);
@@ -221,7 +230,13 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,

c->idx = idx;
c->caps = cfg;
- _setup_dsc_ops(&c->ops, c->caps->features);
+
+ if (test_bit(DPU_DSC_HW_REV_1_1, &c->caps->features))
+ _setup_dsc_ops(&c->ops, c->caps->features);
+ else if (test_bit(DPU_DSC_HW_REV_1_2, &c->caps->features))
+ dpu_dsc_1_2_setup_ops(&c->ops, c->caps->features);
+ else
+ _setup_dsc_ops(&c->ops, c->caps->features);

return c;
}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
index ae9b5db..a48f572 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
@@ -1,5 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2020-2022, Linaro Limited */
+/*
+ * Copyright (c) 2020-2022, Linaro Limited
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
+ */

#ifndef _DPU_HW_DSC_H
#define _DPU_HW_DSC_H
@@ -33,7 +36,8 @@ struct dpu_hw_dsc_ops {
void (*dsc_config)(struct dpu_hw_dsc *hw_dsc,
struct drm_dsc_config *dsc,
u32 mode,
- u32 initial_lines);
+ u32 initial_lines,
+ bool ich_reset_override);

/**
* dsc_config_thresh - programs panel thresholds
@@ -43,6 +47,12 @@ struct dpu_hw_dsc_ops {
void (*dsc_config_thresh)(struct dpu_hw_dsc *hw_dsc,
struct drm_dsc_config *dsc);

+ /**
+ * bind_pingpong_blk - enable/disable the connection with pp
+ * @hw_dsc: Pointer to dsc context
+ * @enable: enable/disable connection
+ * @pp: pingpong blk id
+ */
void (*dsc_bind_pingpong_blk)(struct dpu_hw_dsc *hw_dsc,
bool enable,
enum dpu_pingpong pp);
@@ -51,6 +61,7 @@ struct dpu_hw_dsc_ops {
struct dpu_hw_dsc {
struct dpu_hw_blk base;
struct dpu_hw_blk_reg_map hw;
+ struct dpu_hw_ctl *hw_ctl;

/* dsc */
enum dpu_dsc idx;
@@ -76,9 +87,17 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
*/
void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);

+/**
+ * dpu_hw_dsc - convert base object dpu_hw_base to container
+ * @hw: Pointer to base hardware block
+ * return: Pointer to hardware block container
+ */
static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
{
return container_of(hw, struct dpu_hw_dsc, base);
}

+void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
+ const unsigned long features);
+
#endif /* _DPU_HW_DSC_H */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
new file mode 100644
index 00000000..2be74ae
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
+ */
+
+#include "dpu_kms.h"
+#include "dpu_hw_catalog.h"
+#include "dpu_hwio.h"
+#include "dpu_hw_mdss.h"
+#include "dpu_hw_dsc.h"
+
+
+#define DSC_CMN_MAIN_CNF 0x00
+
+/* DPU_DSC_ENC register offsets */
+#define ENC_DF_CTRL 0x00
+#define ENC_GENERAL_STATUS 0x04
+#define ENC_HSLICE_STATUS 0x08
+#define ENC_OUT_STATUS 0x0C
+#define ENC_INT_STAT 0x10
+#define ENC_INT_CLR 0x14
+#define ENC_INT_MASK 0x18
+#define DSC_MAIN_CONF 0x30
+#define DSC_PICTURE_SIZE 0x34
+#define DSC_SLICE_SIZE 0x38
+#define DSC_MISC_SIZE 0x3C
+#define DSC_HRD_DELAYS 0x40
+#define DSC_RC_SCALE 0x44
+#define DSC_RC_SCALE_INC_DEC 0x48
+#define DSC_RC_OFFSETS_1 0x4C
+#define DSC_RC_OFFSETS_2 0x50
+#define DSC_RC_OFFSETS_3 0x54
+#define DSC_RC_OFFSETS_4 0x58
+#define DSC_FLATNESS_QP 0x5C
+#define DSC_RC_MODEL_SIZE 0x60
+#define DSC_RC_CONFIG 0x64
+#define DSC_RC_BUF_THRESH_0 0x68
+#define DSC_RC_BUF_THRESH_1 0x6C
+#define DSC_RC_BUF_THRESH_2 0x70
+#define DSC_RC_BUF_THRESH_3 0x74
+#define DSC_RC_MIN_QP_0 0x78
+#define DSC_RC_MIN_QP_1 0x7C
+#define DSC_RC_MIN_QP_2 0x80
+#define DSC_RC_MAX_QP_0 0x84
+#define DSC_RC_MAX_QP_1 0x88
+#define DSC_RC_MAX_QP_2 0x8C
+#define DSC_RC_RANGE_BPG_OFFSETS_0 0x90
+#define DSC_RC_RANGE_BPG_OFFSETS_1 0x94
+#define DSC_RC_RANGE_BPG_OFFSETS_2 0x98
+
+/* DPU_DSC_CTL register offsets */
+#define DSC_CTL 0x00
+#define DSC_CFG 0x04
+#define DSC_DATA_IN_SWAP 0x08
+#define DSC_CLK_CTRL 0x0C
+
+
+static int _dsc_calc_ob_max_addr(struct dpu_hw_dsc *hw_dsc, int num_ss)
+{
+ enum dpu_dsc idx;
+
+ idx = hw_dsc->idx;
+
+ if (!(hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_422_EN))) {
+ if (num_ss == 1)
+ return 2399;
+ else if (num_ss == 2)
+ return 1199;
+ } else {
+ if (num_ss == 1)
+ return 1199;
+ else if (num_ss == 2)
+ return 599;
+ }
+ return 0;
+}
+
+static inline int _dsc_subblk_offset(struct dpu_hw_dsc *hw_dsc, int s_id,
+ u32 *idx)
+{
+ const struct dpu_dsc_sub_blks *sblk;
+
+ if (!hw_dsc)
+ return -EINVAL;
+
+ *idx = 0;
+
+ sblk = hw_dsc->caps->sblk;
+
+ switch (s_id) {
+
+ case DPU_DSC_ENC:
+ *idx = sblk->enc.base;
+ break;
+ case DPU_DSC_CTL:
+ *idx = sblk->ctl.base;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void dpu_hw_dsc_disable_1_2(struct dpu_hw_dsc *hw_dsc)
+{
+ struct dpu_hw_blk_reg_map *hw;
+ u32 idx;
+
+ if (!hw_dsc)
+ return;
+
+ if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
+ return;
+
+ hw = &hw_dsc->hw;
+ DPU_REG_WRITE(hw, DSC_CFG + idx, 0);
+
+ if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
+ return;
+
+ DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, 0);
+ DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, 0);
+}
+
+static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
+ struct drm_dsc_config *dsc, u32 mode,
+ u32 initial_lines, bool ich_reset_override)
+{
+ struct dpu_hw_blk_reg_map *hw;
+ struct msm_display_dsc_info *dsc_info;
+ u32 idx;
+ u32 data = 0;
+ u32 bpp;
+ void __iomem *off;
+
+ if (!hw_dsc || !dsc)
+ return;
+
+ hw = &hw_dsc->hw;
+
+ dsc_info = to_msm_dsc_info(dsc);
+
+ if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
+ return;
+
+ if (mode & DSC_MODE_SPLIT_PANEL)
+ data |= BIT(0);
+
+ if (mode & DSC_MODE_MULTIPLEX)
+ data |= BIT(1);
+
+ data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7;
+
+ DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);
+
+ data = (dsc_info->initial_lines & 0xff);
+ data |= ((mode & DSC_MODE_VIDEO) ? 1 : 0) << 9;
+ if (ich_reset_override)
+ data |= 0xC00; // set bit 10 and 11
+ data |= (_dsc_calc_ob_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18);
+
+ DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, data);
+
+ data = (dsc->dsc_version_minor & 0xf) << 28;
+ if (dsc->dsc_version_minor == 0x2) {
+ if (dsc->native_422)
+ data |= BIT(22);
+ if (dsc->native_420)
+ data |= BIT(21);
+ }
+
+ bpp = dsc->bits_per_pixel;
+ /* as per hw requirement bpp should be programmed
+ * twice the actual value in case of 420 or 422 encoding
+ */
+ if (dsc->native_422 || dsc->native_420)
+ bpp = 2 * bpp;
+ data |= (dsc->block_pred_enable ? 1 : 0) << 20;
+ data |= (bpp << 10);
+ data |= (dsc->line_buf_depth & 0xf) << 6;
+ data |= dsc->convert_rgb << 4;
+ data |= dsc->bits_per_component & 0xf;
+
+ DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, data);
+
+ data = (dsc->pic_width & 0xffff) |
+ ((dsc->pic_height & 0xffff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_PICTURE_SIZE + idx, data);
+
+ data = (dsc->slice_width & 0xffff) |
+ ((dsc->slice_height & 0xffff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_SLICE_SIZE + idx, data);
+
+ DPU_REG_WRITE(hw, DSC_MISC_SIZE + idx,
+ (dsc->slice_chunk_size) & 0xffff);
+
+ data = (dsc->initial_xmit_delay & 0xffff) |
+ ((dsc->initial_dec_delay & 0xffff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_HRD_DELAYS + idx, data);
+
+ DPU_REG_WRITE(hw, DSC_RC_SCALE + idx,
+ dsc->initial_scale_value & 0x3f);
+
+ data = (dsc->scale_increment_interval & 0xffff) |
+ ((dsc->scale_decrement_interval & 0x7ff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_RC_SCALE_INC_DEC + idx, data);
+
+ data = (dsc->first_line_bpg_offset & 0x1f) |
+ ((dsc->second_line_bpg_offset & 0x1f) << 5);
+
+ DPU_REG_WRITE(hw, DSC_RC_OFFSETS_1 + idx, data);
+
+ data = (dsc->nfl_bpg_offset & 0xffff) |
+ ((dsc->slice_bpg_offset & 0xffff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_RC_OFFSETS_2 + idx, data);
+
+ data = (dsc->initial_offset & 0xffff) |
+ ((dsc->final_offset & 0xffff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_RC_OFFSETS_3 + idx, data);
+
+ data = (dsc->nsl_bpg_offset & 0xffff) |
+ ((dsc->second_line_offset_adj & 0xffff) << 16);
+
+ DPU_REG_WRITE(hw, DSC_RC_OFFSETS_4 + idx, data);
+
+ data = (dsc->flatness_min_qp & 0x1f);
+ data |= (dsc->flatness_max_qp & 0x1f) << 5;
+ data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
+
+ DPU_REG_WRITE(hw, DSC_FLATNESS_QP + idx, data);
+
+ DPU_REG_WRITE(hw, DSC_RC_MODEL_SIZE + idx,
+ (dsc->rc_model_size) & 0xffff);
+
+ data = dsc->rc_edge_factor & 0xf;
+ data |= (dsc->rc_quant_incr_limit0 & 0x1f) << 8;
+ data |= (dsc->rc_quant_incr_limit1 & 0x1f) << 13;
+ data |= (dsc->rc_tgt_offset_high & 0xf) << 20;
+ data |= (dsc->rc_tgt_offset_low & 0xf) << 24;
+
+ DPU_REG_WRITE(hw, DSC_RC_CONFIG + idx, data);
+
+ /* program the dsc wrapper */
+ if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
+ return;
+
+ off = hw->blk_addr + idx;
+
+ data = BIT(0); /* encoder enable */
+ if (dsc->native_422)
+ data |= BIT(8);
+ else if (dsc->native_420)
+ data |= BIT(9);
+ if (!dsc->convert_rgb)
+ data |= BIT(10);
+ if (dsc->bits_per_component == 8)
+ data |= BIT(11);
+ if (mode & DSC_MODE_SPLIT_PANEL)
+ data |= BIT(12);
+ if (mode & DSC_MODE_MULTIPLEX)
+ data |= BIT(13);
+ if (!(mode & DSC_MODE_VIDEO))
+ data |= BIT(17);
+
+ if (dsc_info->dsc_4hsmerge_en) {
+ data |= dsc_info->dsc_4hsmerge_padding << 18;
+ data |= dsc_info->dsc_4hsmerge_alignment << 22;
+ data |= BIT(16);
+ }
+
+ DPU_REG_WRITE(hw, DSC_CFG + idx, data);
+
+// DPU_REG_WRITE(hw, DSC_DATA_IN_SWAP + idx, 0x14e5);
+}
+
+static void dpu_hw_dsc_config_thresh_1_2(struct dpu_hw_dsc *hw_dsc,
+ struct drm_dsc_config *dsc)
+{
+ struct dpu_hw_blk_reg_map *hw;
+ struct msm_display_dsc_info *dsc_info;
+ u32 idx, off;
+ int i, j = 0;
+ struct drm_dsc_rc_range_parameters *rc;
+ u32 data = 0, min_qp = 0, max_qp = 0, bpg_off = 0;
+
+ if (!hw_dsc || !dsc)
+ return;
+
+ if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
+ return;
+
+ hw = &hw_dsc->hw;
+
+ dsc_info = to_msm_dsc_info(dsc);
+
+ rc = dsc->rc_range_params;
+
+ off = 0;
+ for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
+ data |= dsc->rc_buf_thresh[i] << (8*j);
+ j++;
+ if ((j == 4) || (i == DSC_NUM_BUF_RANGES - 2)) {
+ DPU_REG_WRITE(hw, DSC_RC_BUF_THRESH_0 + idx + off,
+ data);
+ off += 4;
+ j = 0;
+ data = 0;
+ }
+ }
+
+ off = 0;
+ for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
+ min_qp |= (rc[i].range_min_qp & 0x1f) << 5*j;
+ max_qp |= (rc[i].range_max_qp & 0x1f) << 5*j;
+ bpg_off |= (rc[i].range_bpg_offset & 0x3f) << 6*j;
+ j++;
+ if (j == 5) {
+ DPU_REG_WRITE(hw, DSC_RC_MIN_QP_0 + idx + off,
+ min_qp);
+ DPU_REG_WRITE(hw, DSC_RC_MAX_QP_0 + idx + off,
+ max_qp);
+ DPU_REG_WRITE(hw,
+ DSC_RC_RANGE_BPG_OFFSETS_0 + idx + off,
+ bpg_off);
+ off += 4;
+ j = 0;
+ min_qp = 0;
+ max_qp = 0;
+ bpg_off = 0;
+ }
+ }
+}
+
+static void dpu_hw_dsc_bind_pingpong_blk_1_2(
+ struct dpu_hw_dsc *hw_dsc,
+ bool enable,
+ const enum dpu_pingpong pp)
+{
+ struct dpu_hw_blk_reg_map *hw;
+ int idx;
+ int mux_cfg = 0xF; /* Disabled */
+
+ if (!hw_dsc)
+ return;
+
+ if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
+ return;
+
+ hw = &hw_dsc->hw;
+ if (enable)
+ mux_cfg = (pp - PINGPONG_0) & 0x7;
+
+ DPU_REG_WRITE(hw, DSC_CTL + idx, mux_cfg);
+}
+
+void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
+ const unsigned long features)
+{
+ ops->dsc_disable = dpu_hw_dsc_disable_1_2;
+ ops->dsc_config = dpu_hw_dsc_config_1_2;
+ ops->dsc_config_thresh = dpu_hw_dsc_config_thresh_1_2;
+ ops->dsc_bind_pingpong_blk = dpu_hw_dsc_bind_pingpong_blk_1_2;
+}
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:26:19

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 13/14] drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder

Since display Port is an external peripheral, runtime compression
detection is added to handle plug in and unplugged events. Currently
only DSC compression supported. Once DSC compression detected, topology
is static added and used to allocate system resources to accommodate
DSC requirement. DSC related parameters are calculated and committed to
DSC encoder. Also compression information are propagated to phy and
committed to timing engine at video mode. This patch completes DSC
implementation.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 314 ++++++++++++++++-----
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 5 +-
.../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 34 ++-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h | 3 +
drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 10 +-
5 files changed, 292 insertions(+), 74 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index d2625b3..d7f5f93 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -15,6 +15,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_file.h>
#include <drm/drm_probe_helper.h>
+#include <drm/drm_bridge.h>

#include "msm_drv.h"
#include "dpu_kms.h"
@@ -30,6 +31,7 @@
#include "dpu_crtc.h"
#include "dpu_trace.h"
#include "dpu_core_irq.h"
+#include "dpu_dsc_helper.h"
#include "disp/msm_disp_snapshot.h"

#define DPU_DEBUG_ENC(e, fmt, ...) DRM_DEBUG_ATOMIC("enc%d " fmt,\
@@ -542,12 +544,12 @@ bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
return (num_dsc > 0) && (num_dsc > intf_count);
}

-static struct msm_display_topology dpu_encoder_get_topology(
+static void dpu_encoder_get_topology(
struct dpu_encoder_virt *dpu_enc,
struct dpu_kms *dpu_kms,
- struct drm_display_mode *mode)
+ struct drm_display_mode *mode,
+ struct msm_display_topology *topology)
{
- struct msm_display_topology topology = {0};
int i, intf_count = 0;

for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
@@ -567,19 +569,19 @@ static struct msm_display_topology dpu_encoder_get_topology(
* sufficient number
*/
if (intf_count == 2)
- topology.num_lm = 2;
+ topology->num_lm = 2;
else if (!dpu_kms->catalog->caps->has_3d_merge)
- topology.num_lm = 1;
+ topology->num_lm = 1;
else
- topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
+ topology->num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;

if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_DSI) {
if (dpu_kms->catalog->dspp &&
- (dpu_kms->catalog->dspp_count >= topology.num_lm))
- topology.num_dspp = topology.num_lm;
+ (dpu_kms->catalog->dspp_count >= topology->num_lm))
+ topology->num_dspp = topology->num_lm;
}

- topology.num_intf = intf_count;
+ topology->num_intf = intf_count;

if (dpu_enc->dsc) {
/*
@@ -588,12 +590,31 @@ static struct msm_display_topology dpu_encoder_get_topology(
* this is power optimal and can drive up to (including) 4k
* screens
*/
- topology.num_dsc = 2;
- topology.num_lm = 2;
- topology.num_intf = 1;
+ topology->num_dsc = 2;
+ topology->num_intf = 1;
+ topology->num_lm = 2;
}

- return topology;
+ /*
+ * default topology for display port DSC implementation.
+ * TODO:
+ * change to runtime resource calculation
+ */
+ if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
+ topology->num_dsc = 0;
+ topology->num_intf = intf_count;
+
+ if (dpu_enc->comp_info) {
+ /* In case of Display Stream Compression (DSC), we would use
+ * 2 encoders, 2 layer mixers and 1 interface
+ * this is power optimal and can drive up to (including) 4k
+ * screens
+ */
+ topology->num_dsc = 1;
+ topology->num_intf = 1;
+ topology->num_lm = 1;
+ }
+ }
}

static int dpu_encoder_virt_atomic_check(
@@ -605,7 +626,7 @@ static int dpu_encoder_virt_atomic_check(
struct msm_drm_private *priv;
struct dpu_kms *dpu_kms;
struct drm_display_mode *adj_mode;
- struct msm_display_topology topology;
+ struct msm_display_topology *topology;
struct dpu_global_state *global_state;
int i = 0;
int ret = 0;
@@ -642,7 +663,27 @@ static int dpu_encoder_virt_atomic_check(
}
}

- topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode);
+ /*
+ * For display port, at this moment we know panel had been plugged in
+ * and dsc supported is detected.
+ * however we do not know the details of resolution will be used
+ * until mode_set had been done.
+ *
+ * use default topology to reserve system resource for dsc
+ *
+ * TODO: run time calculation of topology instead of hardcode it now
+ */
+ if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
+ struct drm_bridge *bridge;
+
+ if (!dpu_enc->comp_info) {
+ bridge = drm_bridge_chain_get_first_bridge(drm_enc);
+ dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
+ }
+ }
+
+ topology = &dpu_enc->topology;
+ dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, topology);

/* Reserve dynamic resources now. */
if (!ret) {
@@ -655,7 +696,7 @@ static int dpu_encoder_virt_atomic_check(

if (!crtc_state->active_changed || crtc_state->active)
ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
- drm_enc, crtc_state, topology);
+ drm_enc, crtc_state, *topology);
}
}

@@ -1009,7 +1050,37 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,

if (phys->ops.cleanup_wb_job)
phys->ops.cleanup_wb_job(phys, job);
+ }
+}
+
+static void dpu_encoder_populate_encoder_phys(struct drm_encoder *drm_enc,
+ struct dpu_encoder_virt *dpu_enc)
+{
+ struct msm_compression_info *comp_info;
+ struct msm_display_dsc_info *dsc_info;
+ int i;
+
+ if (!dpu_enc->comp_info)
+ return;
+
+ comp_info = dpu_enc->comp_info;
+ dsc_info = &comp_info->msm_dsc_info;
+
+ for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+ struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
+
+ if (!phys)
+ continue;
+
+ phys->comp_type = comp_info->comp_type;
+ phys->comp_ratio = comp_info->comp_ratio;

+ if (phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC) {
+ phys->dsc_extra_pclk_cycle_cnt = dsc_info->pclk_per_line;
+ phys->dsc_extra_disp_width = dsc_info->extra_width;
+ phys->dce_bytes_per_line =
+ dsc_info->bytes_per_pkt * dsc_info->pkt_per_line;
+ }
}
}

@@ -1050,6 +1121,24 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,

trace_dpu_enc_mode_set(DRMID(drm_enc));

+ /*
+ * For display port, msm_dp_bridge_mode_set() will conver panel info
+ * into dp_mode. This including detail dsc information if it is enabled.
+ * after that, msm_dp_bridge_get_compression() will return detail
+ * dsc compression info
+ */
+ if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
+ struct drm_display_mode *mode, *adjusted_mode;
+ struct drm_bridge *bridge;
+
+ mode = &crtc_state->mode;
+ adjusted_mode = &crtc_state->adjusted_mode;
+ bridge = drm_bridge_chain_get_first_bridge(drm_enc);
+ msm_dp_bridge_mode_set(bridge, mode, adjusted_mode);
+
+ dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
+ }
+
/* Query resource that have been reserved in atomic check step. */
num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
drm_enc->base.id, DPU_HW_BLK_PINGPONG, hw_pp,
@@ -1061,19 +1150,18 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
drm_enc->base.id, DPU_HW_BLK_DSPP, hw_dspp,
ARRAY_SIZE(hw_dspp));
+ num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
+ drm_enc->base.id, DPU_HW_BLK_DSC,
+ hw_dsc, ARRAY_SIZE(hw_dsc));

for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i])
: NULL;

- if (dpu_enc->dsc) {
- num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
- drm_enc->base.id, DPU_HW_BLK_DSC,
- hw_dsc, ARRAY_SIZE(hw_dsc));
- for (i = 0; i < num_dsc; i++) {
- dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
- dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
- }
+ for (i = 0; i < num_dsc; i++) {
+ dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
+ dpu_enc->hw_pp[i]->dsc = dpu_enc->hw_dsc[i]; /* bind dsc to pp */
+ dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
}

dpu_enc->dsc_mask = dsc_mask;
@@ -1110,10 +1198,22 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
phys->hw_pp = dpu_enc->hw_pp[i];
phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);

+ if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
+ phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
+
+ /* phys->hw_intf populated at dpu_encoder_setup_display() */
+ if (!phys->hw_intf) {
+ DPU_ERROR_ENC(dpu_enc,
+ "no intf block assigned at idx: %d\n", i);
+ return;
+ }
+
phys->cached_mode = crtc_state->adjusted_mode;
if (phys->ops.atomic_mode_set)
phys->ops.atomic_mode_set(phys, crtc_state, conn_state);
}
+
+ dpu_encoder_populate_encoder_phys(drm_enc, dpu_enc);
}

static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
@@ -1208,6 +1308,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
mutex_unlock(&dpu_enc->enc_lock);
}

+static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc);
+
static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
{
struct dpu_encoder_virt *dpu_enc = NULL;
@@ -1233,6 +1335,10 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
phys->ops.disable(phys);
}

+ if (dpu_enc->comp_info && (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS)) {
+ dpu_encoder_unprep_dsc(dpu_enc);
+ dpu_enc->comp_info = NULL;
+ }

/* after phys waits for frame-done, should be no more frames pending */
if (atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
@@ -1795,40 +1901,16 @@ static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
}

-static u32
-dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc,
- u32 enc_ip_width)
-{
- int ssm_delay, total_pixels, soft_slice_per_enc;
-
- soft_slice_per_enc = enc_ip_width / dsc->slice_width;
-
- /*
- * minimum number of initial line pixels is a sum of:
- * 1. sub-stream multiplexer delay (83 groups for 8bpc,
- * 91 for 10 bpc) * 3
- * 2. for two soft slice cases, add extra sub-stream multiplexer * 3
- * 3. the initial xmit delay
- * 4. total pipeline delay through the "lock step" of encoder (47)
- * 5. 6 additional pixels as the output of the rate buffer is
- * 48 bits wide
- */
- ssm_delay = ((dsc->bits_per_component < 10) ? 84 : 92);
- total_pixels = ssm_delay * 3 + dsc->initial_xmit_delay + 47;
- if (soft_slice_per_enc > 1)
- total_pixels += (ssm_delay * 3);
- return DIV_ROUND_UP(total_pixels, dsc->slice_width);
-}
-
static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
struct dpu_hw_dsc *hw_dsc,
struct dpu_hw_pingpong *hw_pp,
- struct drm_dsc_config *dsc,
+ struct msm_display_dsc_info *dsc_info,
u32 common_mode,
u32 initial_lines)
{
struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
struct dpu_hw_ctl *ctl;
+ struct drm_dsc_config *dsc = &dsc_info->drm_dsc;

ctl = cur_master->hw_ctl;

@@ -1852,51 +1934,137 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,

}

+static void dpu_encoder_dsc_disable(struct dpu_encoder_virt *dpu_enc,
+ struct dpu_hw_dsc *hw_dsc,
+ struct dpu_hw_pingpong *hw_pp)
+{
+ struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
+ struct dpu_hw_ctl *ctl;
+
+ ctl = cur_master->hw_ctl;
+
+ if (hw_dsc->ops.dsc_disable)
+ hw_dsc->ops.dsc_disable(hw_dsc);
+
+ if (hw_pp->ops.disable_dsc)
+ hw_pp->ops.disable_dsc(hw_pp);
+
+}
+
static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
- struct drm_dsc_config *dsc)
+ struct msm_display_dsc_info *dsc_info)
{
/* coding only for 2LM, 2enc, 1 dsc config */
struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+ struct msm_display_topology *topology = &dpu_enc->topology;
+ enum dpu_3d_blend_mode mode_3d;
int this_frame_slices;
int intf_ip_w, enc_ip_w;
- int dsc_common_mode;
- int pic_width;
- u32 initial_lines;
+ int dsc_common_mode = 0;
+ int dsc_pic_width;
+ int num_lm, num_dsc, num_intf;
+ bool dsc_merge, merge_3d, dsc_4hsmerge;
+ bool disable_merge_3d = false;
+ int ich_res;
int i;

for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
hw_pp[i] = dpu_enc->hw_pp[i];
hw_dsc[i] = dpu_enc->hw_dsc[i];
+ }

- if (!hw_pp[i] || !hw_dsc[i]) {
- DPU_ERROR_ENC(dpu_enc, "invalid params for DSC\n");
- return;
- }
+ num_lm = topology->num_lm;
+ num_dsc = topology->num_dsc;
+ num_intf = topology->num_intf;
+
+
+ mode_3d = (num_lm > num_dsc) ? BLEND_3D_H_ROW_INT : BLEND_3D_NONE;
+ merge_3d = ((mode_3d != BLEND_3D_NONE) && !(enc_master->hw_intf->cfg.split_link_en)) ?
+ true : false;
+
+ dsc_merge = ((num_dsc > num_intf) && !dsc_info->half_panel_pu &&
+ !(enc_master->hw_intf->cfg.split_link_en)) ? true : false;
+ disable_merge_3d = (merge_3d && dsc_info->half_panel_pu) ? false : true;
+ dsc_4hsmerge = (dsc_merge && num_dsc == 4 && num_intf == 1) ? true : false;
+
+ /*
+ * If this encoder is driving more than one DSC encoder, they
+ * operate in tandem, same pic dimension needs to be used by
+ * each of them.(pp-split is assumed to be not supported)
+ *
+ * If encoder is driving more than 2 DSCs, each DSC pair will operate
+ * on half of the picture in tandem.
+ */
+ dsc_pic_width = dsc_info->drm_dsc.pic_width;
+
+ if (num_dsc > 2) {
+ dsc_pic_width /= 2;
+ dsc_info->dsc_4hsmerge_en = dsc_4hsmerge;
}

- dsc_common_mode = 0;
- pic_width = dsc->pic_width;
+ this_frame_slices = dsc_pic_width / dsc_info->drm_dsc.slice_width;
+ intf_ip_w = this_frame_slices * dsc_info->drm_dsc.slice_width;
+ enc_ip_w = intf_ip_w;
+
+ if (!dsc_info->half_panel_pu)
+ intf_ip_w /= num_intf;
+ if (!dsc_info->half_panel_pu && (num_dsc > 1))
+ dsc_common_mode |= DSC_MODE_SPLIT_PANEL;
+ if (dsc_merge) {
+ dsc_common_mode |= DSC_MODE_MULTIPLEX;
+ /*
+ * in dsc merge case: when using 2 encoders for the same
+ * stream, no. of slices need to be same on both the
+ * encoders.
+ */
+ enc_ip_w = intf_ip_w / 2;
+ }

- dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
if (enc_master->intf_mode == INTF_MODE_VIDEO)
dsc_common_mode |= DSC_MODE_VIDEO;

- this_frame_slices = pic_width / dsc->slice_width;
- intf_ip_w = this_frame_slices * dsc->slice_width;
+ dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count;
+
+ if (dsc_info->dsc_4hsmerge_en)
+ dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 2;
+ else if ((dsc_common_mode & DSC_MODE_MULTIPLEX) || (dsc_info->half_panel_pu))
+ dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 1;
+
+ dpu_dsc_populate_dsc_private_params(dsc_info, intf_ip_w);
+
+ dpu_dsc_initial_line_calc(dsc_info, enc_ip_w, dsc_common_mode);

/*
* dsc merge case: when using 2 encoders for the same stream,
* no. of slices need to be same on both the encoders.
*/
- enc_ip_w = intf_ip_w / 2;
- initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
+ ich_res = dpu_dsc_ich_reset_override_needed(dsc_info->half_panel_pu, dsc_info);
+
+ for (i = 0; i < num_dsc; i++) {
+ dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc_info,
+ dsc_common_mode, 0);
+ }
+}
+
+static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc)
+{
+ /* coding only for 2LM, 2enc, 1 dsc config */
+ struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+ struct msm_display_topology *topology = &dpu_enc->topology;
+ int i, num_dsc;

for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
- dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc,
- dsc_common_mode, initial_lines);
+ hw_pp[i] = dpu_enc->hw_pp[i];
+ hw_dsc[i] = dpu_enc->hw_dsc[i];
}
+
+ num_dsc = topology->num_dsc;
+
+ for (i = 0; i < num_dsc; i++)
+ dpu_encoder_dsc_disable(dpu_enc, hw_dsc[i], hw_pp[i]);
}

void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
@@ -1904,6 +2072,7 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
struct dpu_encoder_virt *dpu_enc;
struct dpu_encoder_phys *phys;
bool needs_hw_reset = false;
+ bool needs_phy_enable = false;
unsigned int i;

dpu_enc = to_dpu_encoder_virt(drm_enc);
@@ -1918,6 +2087,9 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
phys->ops.prepare_for_kickoff(phys);
if (phys->enable_state == DPU_ENC_ERR_NEEDS_HW_RESET)
needs_hw_reset = true;
+
+ if (phys->enable_state == DPU_ENC_ENABLING)
+ needs_phy_enable = true;
}
DPU_ATRACE_END("enc_prepare_for_kickoff");

@@ -1931,8 +2103,10 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
}
}

- if (dpu_enc->dsc)
- dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
+ if (needs_phy_enable && dpu_enc->comp_info)
+ dpu_encoder_prep_dsc(dpu_enc, &dpu_enc->comp_info->msm_dsc_info);
+
+
}

bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
@@ -2295,7 +2469,7 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
dpu_kms->catalog->caps->has_idle_pc;

dpu_enc->comp_info = disp_info->comp_info;
- if (dpu_enc->comp_info)
+ if (dpu_enc->comp_info && dpu_enc->comp_info->enabled)
dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;

mutex_lock(&dpu_enc->enc_lock);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 0569b36..ae4f6a8 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
*/

@@ -202,6 +202,9 @@ struct dpu_encoder_phys {
int irq[INTR_IDX_MAX];
enum msm_display_compression_type comp_type;
u32 comp_ratio;
+ u32 dsc_extra_pclk_cycle_cnt;
+ u32 dsc_extra_disp_width;
+ u32 dce_bytes_per_line;
};

static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
index 3330e185..6c7d791 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
@@ -86,6 +86,11 @@ static void drm_mode_to_intf_timing_params(
timing->underflow_clr = 0xff;
timing->hsync_skew = mode->hskew;

+ if (phys_enc->comp_type != MSM_DISPLAY_COMPRESSION_NONE) {
+ timing->compression_en = true;
+ timing->dce_bytes_per_line = phys_enc->dce_bytes_per_line;
+ }
+
/* DSI controller cannot handle active-low sync signals. */
if (phys_enc->hw_intf->cap->type == INTF_DSI) {
timing->hsync_polarity = 0;
@@ -104,14 +109,36 @@ static void drm_mode_to_intf_timing_params(

/*
* for DP, divide the horizonal parameters by 2 when
- * widebus is enabled
+ * widebus or compression is enabled, irrespective of
+ * compression ratio
*/
- if (phys_enc->hw_intf->cap->type == INTF_DP && timing->wide_bus_en) {
+ if (phys_enc->hw_intf->cap->type == INTF_DP &&
+ (timing->wide_bus_en || (phys_enc->comp_ratio > 1))) {
timing->width = timing->width >> 1;
timing->xres = timing->xres >> 1;
timing->h_back_porch = timing->h_back_porch >> 1;
timing->h_front_porch = timing->h_front_porch >> 1;
timing->hsync_pulse_width = timing->hsync_pulse_width >> 1;
+
+ if (phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
+ (phys_enc->comp_ratio > 1)) {
+ timing->extra_dto_cycles =
+ phys_enc->dsc_extra_pclk_cycle_cnt;
+ timing->width += phys_enc->dsc_extra_disp_width;
+ timing->h_back_porch +=
+ phys_enc->dsc_extra_disp_width;
+ }
+ }
+
+ /*
+ * for DSI, if compression is enabled, then divide the horizonal active
+ * timing parameters by compression ratio.
+ */
+ if ((phys_enc->hw_intf->cap->type != INTF_DP) &&
+ ((phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC))) {
+ // adjust active dimensions
+ timing->width = DIV_ROUND_UP(timing->width, phys_enc->comp_ratio);
+ timing->xres = DIV_ROUND_UP(timing->xres, phys_enc->comp_ratio);
}
}

@@ -281,6 +308,9 @@ static void dpu_encoder_phys_vid_setup_timing_engine(

phys_enc->hw_intf->hw_rev = phys_enc->dpu_kms->core_rev;

+ if (phys_enc->hw_pp->dsc)
+ intf_cfg.dsc_num = phys_enc->hw_pp->dsc->idx;
+
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
&timing_params, fmt);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
index c002234..ee71cee 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/

#ifndef _DPU_HW_PINGPONG_H
@@ -8,6 +9,7 @@
#include "dpu_hw_catalog.h"
#include "dpu_hw_mdss.h"
#include "dpu_hw_util.h"
+#include "dpu_hw_dsc.h"

#define DITHER_MATRIX_SZ 16

@@ -149,6 +151,7 @@ struct dpu_hw_pingpong {
enum dpu_pingpong idx;
const struct dpu_pingpong_cfg *caps;
struct dpu_hw_merge_3d *merge_3d;
+ struct dpu_hw_dsc *dsc;

/* ops */
struct dpu_hw_pingpong_ops ops;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
index 396429e..bb22ec8 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/

#define pr_fmt(fmt) "[drm:%s] " fmt, __func__
@@ -246,6 +247,11 @@ int dpu_rm_init(struct dpu_rm *rm,
struct dpu_hw_dsc *hw;
const struct dpu_dsc_cfg *dsc = &cat->dsc[i];

+ if (dsc->id < DSC_0 || dsc->id >= DSC_MAX) {
+ DPU_ERROR("skip dsc %d with invalid id\n", dsc->id);
+ continue;
+ }
+
hw = dpu_hw_dsc_init(dsc->id, mmio, cat);
if (IS_ERR_OR_NULL(hw)) {
rc = PTR_ERR(hw);
@@ -535,8 +541,10 @@ static int _dpu_rm_make_reservation(
}

ret = _dpu_rm_reserve_dsc(rm, global_state, enc, &reqs->topology);
- if (ret)
+ if (ret) {
+ DPU_ERROR("unable to find appropriate DSC\n");
return ret;
+ }

return ret;
}
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:26:21

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v1 14/14] drm/msm/disp/dpu1: add sc7280 dsc block and sub block

This patch add DSC block and sub block to support new DSC v1.2 hardware
encoder. Also sc7280 DSC related hardware information are added to allow
sc7280 DSC feature be enabled at sc7280 platform.

Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 50 +++++++++++++++++++-------
1 file changed, 38 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
index 7deffc9f9..2c78a46 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/

#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
@@ -476,6 +476,7 @@ static const struct dpu_caps sc7280_dpu_caps = {
.has_idle_pc = true,
.max_linewidth = 2400,
.pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
+ .max_dsc_width = 2560,
};

static const struct dpu_mdp_cfg msm8998_mdp[] = {
@@ -1707,7 +1708,7 @@ static const struct dpu_pingpong_cfg sm8350_pp[] = {
};

static const struct dpu_pingpong_cfg sc7280_pp[] = {
- PP_BLK("pingpong_0", PINGPONG_0, 0x59000, 0, sc7280_pp_sblk, -1, -1),
+ PP_BLK("pingpong_0", PINGPONG_0, 0x69000, 0, sc7280_pp_sblk, -1, -1),
PP_BLK("pingpong_1", PINGPONG_1, 0x6a000, 0, sc7280_pp_sblk, -1, -1),
PP_BLK("pingpong_2", PINGPONG_2, 0x6b000, 0, sc7280_pp_sblk, -1, -1),
PP_BLK("pingpong_3", PINGPONG_3, 0x6c000, 0, sc7280_pp_sblk, -1, -1),
@@ -1814,25 +1815,48 @@ static const struct dpu_merge_3d_cfg sm8550_merge_3d[] = {
/*************************************************************
* DSC sub blocks config
*************************************************************/
-#define DSC_BLK(_name, _id, _base, _features) \
+#define DSC_BLK_HW_1_1(_name, _id, _base, _features) \
{\
.name = _name, .id = _id, \
.base = _base, .len = 0x140, \
- .features = _features, \
+ .features = BIT(DPU_DSC_HW_REV_1_1) | _features, \
+ }
+
+#define DSC_BLK_HW_1_2(_name, _id, _base, _features, _sblk) \
+ {\
+ .name = _name, .id = _id, \
+ .base = _base, .len = 0x140, \
+ .features = BIT(DPU_DSC_HW_REV_1_2) | _features, \
+ .sblk = &_sblk, \
}

static struct dpu_dsc_cfg sdm845_dsc[] = {
- DSC_BLK("dsc_0", DSC_0, 0x80000, 0),
- DSC_BLK("dsc_1", DSC_1, 0x80400, 0),
- DSC_BLK("dsc_2", DSC_2, 0x80800, 0),
- DSC_BLK("dsc_3", DSC_3, 0x80c00, 0),
+ DSC_BLK_HW_1_1("dsc_0", DSC_0, 0x80000, 0),
+ DSC_BLK_HW_1_1("dsc_1", DSC_1, 0x80400, 0),
+ DSC_BLK_HW_1_1("dsc_2", DSC_2, 0x80800, 0),
+ DSC_BLK_HW_1_1("dsc_3", DSC_3, 0x80c00, 0),
};

static struct dpu_dsc_cfg sm8150_dsc[] = {
- DSC_BLK("dsc_0", DSC_0, 0x80000, BIT(DPU_DSC_OUTPUT_CTRL)),
- DSC_BLK("dsc_1", DSC_1, 0x80400, BIT(DPU_DSC_OUTPUT_CTRL)),
- DSC_BLK("dsc_2", DSC_2, 0x80800, BIT(DPU_DSC_OUTPUT_CTRL)),
- DSC_BLK("dsc_3", DSC_3, 0x80c00, BIT(DPU_DSC_OUTPUT_CTRL)),
+ DSC_BLK_HW_1_1("dsc_0", DSC_0, 0x80000, BIT(DPU_DSC_OUTPUT_CTRL)),
+ DSC_BLK_HW_1_1("dsc_1", DSC_1, 0x80400, BIT(DPU_DSC_OUTPUT_CTRL)),
+ DSC_BLK_HW_1_1("dsc_2", DSC_2, 0x80800, BIT(DPU_DSC_OUTPUT_CTRL)),
+ DSC_BLK_HW_1_1("dsc_3", DSC_3, 0x80c00, BIT(DPU_DSC_OUTPUT_CTRL)),
+};
+
+static struct dpu_dsc_sub_blks sc7280_dsc_sblk_0 = {
+ .enc = {.base = 0x100, .len = 0x100},
+ .ctl = {.base = 0xF00, .len = 0x10},
+};
+
+static struct dpu_dsc_sub_blks sc7280_dsc_sblk_1 = {
+ .enc = {.base = 0x200, .len = 0x100},
+ .ctl = {.base = 0xF80, .len = 0x10},
+};
+
+static struct dpu_dsc_cfg sc7280_dsc[] = {
+ DSC_BLK_HW_1_2("dsc_0", DSC_0, 0x80000, BIT(DPU_DSC_NATIVE_422_EN), sc7280_dsc_sblk_0),
+ DSC_BLK_HW_1_2("dsc_1", DSC_1, 0x80000, BIT(DPU_DSC_NATIVE_422_EN), sc7280_dsc_sblk_1),
};

/*************************************************************
@@ -2809,6 +2833,8 @@ static const struct dpu_mdss_cfg sc7280_dpu_cfg = {
.pingpong_count = ARRAY_SIZE(sc7280_pp),
.pingpong = sc7280_pp,
.intf_count = ARRAY_SIZE(sc7280_intf),
+ .dsc_count = ARRAY_SIZE(sc7280_dsc),
+ .dsc = sc7280_dsc,
.intf = sc7280_intf,
.vbif_count = ARRAY_SIZE(sdm845_vbif),
.vbif = sdm845_vbif,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2023-01-23 18:35:11

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 00/14] add display port DSC feature

DisplayPort is a name, and I think you should spell it as such in both
the cover letter title and individual patch descriptions (capital D and
P, no space in between).

On 2023-01-23 10:24:20, Kuogee Hsieh wrote:
> This patch add DSC related supporting functions into to both dp controller and dpu enccoder
>
> Kuogee Hsieh (14):
> drm/msm/dp: add dpcd read of both dsc and fec capability
> drm/msm/dp: add dsc factor into calculation of supported bpp
> drm/msm/dp: add configure mainlink_levels base on lane number
> drm/msm/dp: correct configure Colorimetry Indicator Field at MISC0
> drm/msm/dp: upgrade tu calculation base on newest algorithm
> drm/msm/dp: add display compression related struct
> drm/msm/dp: add dsc helper functions
> drm/msm/dp: add dsc supporting functions to DP controller
> drm/msm/dsi: export struct msm_compression_info to dpu encoder
> drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine
> drm/msm/disp/dpu1: add supports of new flush mechanism
> drm/msm/disp/dpu1: revise timing engine programming to work for DSC
> drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder
> drm/msm/disp/dpu1: add sc7280 dsc block and sub block

For DSC, capitalize it everywhere instead of the current free-form lower
and uppercase mixup in patch titles.

Still asking around for the subsystem tag, I've seen:

drm/msm/dpu
drm/msm/dpu1
drm/msm/disp/dpu
drm/msm/disp/dpu1

And you're already mixing two of them.

Aside that, thanks for sending this series! Been looking forward to DSC
1.2 for a while, but for DSI!

- Marijn

> drivers/gpu/drm/msm/Makefile | 2 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 341 +++++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 7 +-
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 43 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 50 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 74 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 21 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 ++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h | 3 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 10 +-
> drivers/gpu/drm/msm/dp/dp_catalog.c | 176 ++++-
> drivers/gpu/drm/msm/dp/dp_catalog.h | 97 ++-
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 839 ++++++++++++++-------
> drivers/gpu/drm/msm/dp/dp_display.c | 61 +-
> drivers/gpu/drm/msm/dp/dp_link.c | 29 +-
> drivers/gpu/drm/msm/dp/dp_panel.c | 749 +++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_panel.h | 67 +-
> drivers/gpu/drm/msm/dp/dp_reg.h | 40 +-
> drivers/gpu/drm/msm/dsi/dsi.c | 3 +-
> drivers/gpu/drm/msm/dsi/dsi.h | 3 +-
> drivers/gpu/drm/msm/dsi/dsi_host.c | 14 +-
> drivers/gpu/drm/msm/msm_drv.h | 113 ++-
> 32 files changed, 3429 insertions(+), 497 deletions(-)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

2023-01-23 20:11:43

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

add support for*

drm/msm/dpu*

On 2023-01-23 10:24:30, Kuogee Hsieh wrote:
> DSC V1.2 encoder engine is newly added hardware module. This patch
> add support functions to configure and enable DSC V1.2 encoder engine.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/Makefile | 1 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 2 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 60 +++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++++++++++++++++++
> 6 files changed, 463 insertions(+), 17 deletions(-)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 28cf52b..271c29a15 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> disp/dpu1/dpu_hw_catalog.o \
> disp/dpu1/dpu_hw_ctl.o \
> disp/dpu1/dpu_hw_dsc.o \
> + disp/dpu1/dpu_hw_dsc_1_2.o \
> disp/dpu1/dpu_dsc_helper.o \
> disp/dpu1/dpu_hw_interrupts.o \
> disp/dpu1/dpu_hw_intf.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 7f4a439..901e317 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1821,7 +1821,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
> u32 initial_lines)
> {
> if (hw_dsc->ops.dsc_config)
> - hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines);
> + hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);

As usual, an enum is better: readers have no idea what a free-floating
bool means.

>
> if (hw_dsc->ops.dsc_config_thresh)
> hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> index 978e3bd..7b0b092 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
> */
>
> @@ -11,6 +11,7 @@
> #include <linux/bug.h>
> #include <linux/bitmap.h>
> #include <linux/err.h>
> +#include "dpu_hw_mdss.h"

Unused if you remove the unused DECLARE_BITMAP(dsc_pair_mask, DSC_MAX).

>
> /**
> * Max hardware block count: For ex: max 12 SSPP pipes or
> @@ -182,6 +183,7 @@ enum {
> * @DPU_PINGPONG_TE2 Additional tear check block for split pipes
> * @DPU_PINGPONG_SPLIT PP block supports split fifo
> * @DPU_PINGPONG_SLAVE PP block is a suitable slave for split fifo
> + * @DPU_PINGPONG_DSC, Display stream compression blocks
> * @DPU_PINGPONG_DITHER, Dither blocks
> * @DPU_PINGPONG_MAX
> */
> @@ -190,10 +192,32 @@ enum {
> DPU_PINGPONG_TE2,
> DPU_PINGPONG_SPLIT,
> DPU_PINGPONG_SLAVE,
> + DPU_PINGPONG_DSC,

This is not used.

> DPU_PINGPONG_DITHER,
> DPU_PINGPONG_MAX
> };
>
> +
> +/** DSC sub-blocks/features

Newline between /** and the text.

> + * @DPU_DSC_OUTPUT_CTRL Supports the control of the pp id which gets
> + * the pixel output from this DSC.

The original comment is much more concise, can we keep it?

> + * @DPU_DSC_HW_REV_1_1 dsc block supports dsc 1.1 only
> + * @DPU_DSC_HW_REV_1_2 dsc block supports dsc 1.1 and 1.2

Capitalize DSC just like elsewhere.

> + * @DPU_DSC_NATIVE_422_EN, Supports native422 and native420 encoding
> + * @DPU_DSC_ENC, DSC encoder sub block
> + * @DPU_DSC_CTL, DSC ctl sub block

No need for trailing commas in doc comments; if anything replace them
with colons?

> + * @DPU_DSC_MAX
> + */
> +enum {
> + DPU_DSC_OUTPUT_CTRL = 0x1,
> + DPU_DSC_HW_REV_1_1,
> + DPU_DSC_HW_REV_1_2,
> + DPU_DSC_NATIVE_422_EN,
> + DPU_DSC_ENC,
> + DPU_DSC_CTL,

These two enum values only have a meaning within the dpu_hw_dsc_1_2.c
file, and have nothing to do with the other feature flags/block
description. Please move them there (and give _dsc_subblk_offset a
proper enum type).

> + DPU_DSC_MAX
> +};
> +
> /**
> * CTL sub-blocks
> * @DPU_CTL_SPLIT_DISPLAY: CTL supports video mode split display
> @@ -276,15 +300,6 @@ enum {
> };
>
> /**
> - * DSC features
> - * @DPU_DSC_OUTPUT_CTRL Configure which PINGPONG block gets
> - * the pixel output from this DSC.
> - */
> -enum {
> - DPU_DSC_OUTPUT_CTRL = 0x1,

Did this have to move?

> -};
> -
> -/**
> * MACRO DPU_HW_BLK_INFO - information of HW blocks inside DPU
> * @name: string name for debug purposes
> * @id: enum identifying this block
> @@ -346,6 +361,14 @@ struct dpu_pp_blk {
> };
>
> /**
> + * struct dpu_dsc_blk : DSC Encoder sub-blk information

Use a hyphen here and everywhere else:
https://docs.kernel.org/doc-guide/kernel-doc.html

> + * @info: HW register and features supported by this sub-blk
> + */
> +struct dpu_dsc_blk {
> + DPU_HW_SUBBLK_INFO;
> +};
> +
> +/**
> * enum dpu_qos_lut_usage - define QoS LUT use cases
> */
> enum dpu_qos_lut_usage {
> @@ -403,6 +426,7 @@ struct dpu_rotation_cfg {
> * @pixel_ram_size size of latency hiding and de-tiling buffer in bytes
> * @max_hdeci_exp max horizontal decimation supported (max is 2^value)
> * @max_vdeci_exp max vertical decimation supported (max is 2^value)
> + * @max_dsc_width max dsc line width support.

DSC*

> */
> struct dpu_caps {
> u32 max_mixer_width;
> @@ -419,6 +443,7 @@ struct dpu_caps {
> u32 pixel_ram_size;
> u32 max_hdeci_exp;
> u32 max_vdeci_exp;
> + u32 max_dsc_width;

This is never read.

> };
>
> /**
> @@ -494,9 +519,20 @@ struct dpu_dspp_sub_blks {
> struct dpu_pingpong_sub_blks {
> struct dpu_pp_blk te;
> struct dpu_pp_blk te2;
> + struct dpu_pp_blk dsc;

Unused.

> struct dpu_pp_blk dither;
> };
>
> +
> +/**
> + * struct dpu_dsc_sub_blks : DSC sub-blks
> + *

A sub-block of sub-blocks? Use the documentation to explain what this
is for, describe @enc and @ctl.

> + */
> +struct dpu_dsc_sub_blks {
> + struct dpu_dsc_blk enc;
> + struct dpu_dsc_blk ctl;
> +};
> +
> /**
> * dpu_clk_ctrl_type - Defines top level clock control signals
> */
> @@ -641,10 +677,14 @@ struct dpu_merge_3d_cfg {
> * struct dpu_dsc_cfg - information of DSC blocks
> * @id enum identifying this block
> * @base register offset of this block
> + * @len: length of hardware block
> * @features bit mask identifying sub-blocks/features
> + * @dsc_pair_mask: Bitmask of DSCs that can be controlled by same CTL
> */
> struct dpu_dsc_cfg {
> DPU_HW_BLK_INFO;
> + DECLARE_BITMAP(dsc_pair_mask, DSC_MAX);

This bitmask is unused.

> + const struct dpu_dsc_sub_blks *sblk;
> };
>
> /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> index 619926d..51e8890 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2020-2022, Linaro Limited
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include "dpu_kms.h"
> @@ -41,10 +42,11 @@ static void dpu_hw_dsc_disable(struct dpu_hw_dsc *dsc)
> static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc,
> u32 mode,
> - u32 initial_lines)
> + u32 initial_lines,
> + bool ich_reset_override)
> {
> struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
> - u32 data;
> + u32 data, lsb, bpp;
> u32 slice_last_group_size;
> u32 det_thresh_flatness;
> bool is_cmd_mode = !(mode & DSC_MODE_VIDEO);
> @@ -58,7 +60,14 @@ static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
> data = (initial_lines << 20);
> data |= ((slice_last_group_size - 1) << 18);
> /* bpp is 6.4 format, 4 LSBs bits are for fractional part */
> - data |= (dsc->bits_per_pixel << 8);
> + data |= dsc->bits_per_pixel << 12;
> + lsb = dsc->bits_per_pixel % 4;
> + bpp = dsc->bits_per_pixel / 4;
> + bpp *= 4;
> + bpp <<= 4;
> + bpp |= lsb;
> +
> + data |= bpp << 8;

Why are you re-adding this nonsense? It was removed in [1] _and_ does
not account for bits_per_pixel _already being in x.4 format_. This will
regress existing hardware.

[1]: https://lore.kernel.org/linux-arm-msm/[email protected]/

> data |= (dsc->block_pred_enable << 7);
> data |= (dsc->line_buf_depth << 3);
> data |= (dsc->simple_422 << 2);
> @@ -221,7 +230,13 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
>
> c->idx = idx;
> c->caps = cfg;
> - _setup_dsc_ops(&c->ops, c->caps->features);
> +
> + if (test_bit(DPU_DSC_HW_REV_1_1, &c->caps->features))
> + _setup_dsc_ops(&c->ops, c->caps->features);
> + else if (test_bit(DPU_DSC_HW_REV_1_2, &c->caps->features))
> + dpu_dsc_1_2_setup_ops(&c->ops, c->caps->features);
> + else
> + _setup_dsc_ops(&c->ops, c->caps->features);
>
> return c;
> }
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> index ae9b5db..a48f572 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> @@ -1,5 +1,8 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> -/* Copyright (c) 2020-2022, Linaro Limited */
> +/*
> + * Copyright (c) 2020-2022, Linaro Limited
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> + */
>
> #ifndef _DPU_HW_DSC_H
> #define _DPU_HW_DSC_H
> @@ -33,7 +36,8 @@ struct dpu_hw_dsc_ops {
> void (*dsc_config)(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc,
> u32 mode,
> - u32 initial_lines);
> + u32 initial_lines,
> + bool ich_reset_override);
>
> /**
> * dsc_config_thresh - programs panel thresholds
> @@ -43,6 +47,12 @@ struct dpu_hw_dsc_ops {
> void (*dsc_config_thresh)(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc);
>
> + /**
> + * bind_pingpong_blk - enable/disable the connection with pp

Inherit docs from the enum.

> + * @hw_dsc: Pointer to dsc context

DSC*

> + * @enable: enable/disable connection
> + * @pp: pingpong blk id

It's documentation, write out block fully.

> + */
> void (*dsc_bind_pingpong_blk)(struct dpu_hw_dsc *hw_dsc,
> bool enable,
> enum dpu_pingpong pp);
> @@ -51,6 +61,7 @@ struct dpu_hw_dsc_ops {
> struct dpu_hw_dsc {
> struct dpu_hw_blk base;
> struct dpu_hw_blk_reg_map hw;
> + struct dpu_hw_ctl *hw_ctl;

Unused.

>
> /* dsc */
> enum dpu_dsc idx;
> @@ -76,9 +87,17 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
> */
> void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);
>
> +/**
> + * dpu_hw_dsc - convert base object dpu_hw_base to container
> + * @hw: Pointer to base hardware block
> + * return: Pointer to hardware block container
> + */
> static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
> {
> return container_of(hw, struct dpu_hw_dsc, base);
> }
>
> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
> + const unsigned long features);
> +
> #endif /* _DPU_HW_DSC_H */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
> new file mode 100644
> index 00000000..2be74ae
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
> @@ -0,0 +1,371 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#include "dpu_kms.h"
> +#include "dpu_hw_catalog.h"
> +#include "dpu_hwio.h"
> +#include "dpu_hw_mdss.h"
> +#include "dpu_hw_dsc.h"
> +
> +
> +#define DSC_CMN_MAIN_CNF 0x00
> +
> +/* DPU_DSC_ENC register offsets */
> +#define ENC_DF_CTRL 0x00
> +#define ENC_GENERAL_STATUS 0x04
> +#define ENC_HSLICE_STATUS 0x08
> +#define ENC_OUT_STATUS 0x0C
> +#define ENC_INT_STAT 0x10
> +#define ENC_INT_CLR 0x14
> +#define ENC_INT_MASK 0x18
> +#define DSC_MAIN_CONF 0x30
> +#define DSC_PICTURE_SIZE 0x34
> +#define DSC_SLICE_SIZE 0x38
> +#define DSC_MISC_SIZE 0x3C
> +#define DSC_HRD_DELAYS 0x40
> +#define DSC_RC_SCALE 0x44
> +#define DSC_RC_SCALE_INC_DEC 0x48
> +#define DSC_RC_OFFSETS_1 0x4C
> +#define DSC_RC_OFFSETS_2 0x50
> +#define DSC_RC_OFFSETS_3 0x54
> +#define DSC_RC_OFFSETS_4 0x58
> +#define DSC_FLATNESS_QP 0x5C
> +#define DSC_RC_MODEL_SIZE 0x60
> +#define DSC_RC_CONFIG 0x64
> +#define DSC_RC_BUF_THRESH_0 0x68
> +#define DSC_RC_BUF_THRESH_1 0x6C
> +#define DSC_RC_BUF_THRESH_2 0x70
> +#define DSC_RC_BUF_THRESH_3 0x74
> +#define DSC_RC_MIN_QP_0 0x78
> +#define DSC_RC_MIN_QP_1 0x7C
> +#define DSC_RC_MIN_QP_2 0x80
> +#define DSC_RC_MAX_QP_0 0x84
> +#define DSC_RC_MAX_QP_1 0x88
> +#define DSC_RC_MAX_QP_2 0x8C
> +#define DSC_RC_RANGE_BPG_OFFSETS_0 0x90
> +#define DSC_RC_RANGE_BPG_OFFSETS_1 0x94
> +#define DSC_RC_RANGE_BPG_OFFSETS_2 0x98

Reindent to line this back up.

> +
> +/* DPU_DSC_CTL register offsets */
> +#define DSC_CTL 0x00
> +#define DSC_CFG 0x04
> +#define DSC_DATA_IN_SWAP 0x08
> +#define DSC_CLK_CTRL 0x0C
> +
> +
> +static int _dsc_calc_ob_max_addr(struct dpu_hw_dsc *hw_dsc, int num_ss)
> +{
> + enum dpu_dsc idx;
> +
> + idx = hw_dsc->idx;
> +
> + if (!(hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_422_EN))) {

Why not swap the bodies instead of inverting this.

> + if (num_ss == 1)
> + return 2399;
> + else if (num_ss == 2)
> + return 1199;
> + } else {
> + if (num_ss == 1)
> + return 1199;
> + else if (num_ss == 2)
> + return 599;
> + }
> + return 0;
> +}
> +
> +static inline int _dsc_subblk_offset(struct dpu_hw_dsc *hw_dsc, int s_id,
> + u32 *idx)
> +{
> + const struct dpu_dsc_sub_blks *sblk;
> +
> + if (!hw_dsc)
> + return -EINVAL;
> +
> + *idx = 0;
> +
> + sblk = hw_dsc->caps->sblk;
> +
> + switch (s_id) {
> +
> + case DPU_DSC_ENC:
> + *idx = sblk->enc.base;
> + break;
> + case DPU_DSC_CTL:
> + *idx = sblk->ctl.base;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void dpu_hw_dsc_disable_1_2(struct dpu_hw_dsc *hw_dsc)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + u32 idx;

Can we rename these to offset or subblk_offset or something more clear?

> +
> + if (!hw_dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;

These error checks are excessive: you pass in a non-null hw_dsc and
known enum constant - _dsc_subblk_offset should perhaps not return
errors at all.

> +
> + hw = &hw_dsc->hw;
> + DPU_REG_WRITE(hw, DSC_CFG + idx, 0);

Swap the arguments to + so that it's clear that DSC_CFG is a register on
the subblock offset denoted by "idx", not the other way around.

> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, 0);
> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, 0);
> +}
> +
> +static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
> + struct drm_dsc_config *dsc, u32 mode,
> + u32 initial_lines, bool ich_reset_override)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + struct msm_display_dsc_info *dsc_info;
> + u32 idx;
> + u32 data = 0;
> + u32 bpp;
> + void __iomem *off;
> +
> + if (!hw_dsc || !dsc)
> + return;
> +
> + hw = &hw_dsc->hw;
> +
> + dsc_info = to_msm_dsc_info(dsc);
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + if (mode & DSC_MODE_SPLIT_PANEL)
> + data |= BIT(0);
> +
> + if (mode & DSC_MODE_MULTIPLEX)
> + data |= BIT(1);

These are well known bitwise definitions for a reason, data |= mode will
do (or out DSC_MODE_VIDEO since you have to shift that one at BIT(9).

> +
> + data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7;
> +
> + DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);
> +
> + data = (dsc_info->initial_lines & 0xff);

You already get initial_lines passed as function argument, but ignore
it?

> + data |= ((mode & DSC_MODE_VIDEO) ? 1 : 0) << 9;

Yuck. if (mode & DSC_MODE_VIDEO) data |= BIT(9);.

> + if (ich_reset_override)
> + data |= 0xC00; // set bit 10 and 11

Instead of a comment, make this self-describing BIT(10) | BIT(11) code.

> + data |= (_dsc_calc_ob_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18);
> +
> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, data);
> +
> + data = (dsc->dsc_version_minor & 0xf) << 28;
> + if (dsc->dsc_version_minor == 0x2) {
> + if (dsc->native_422)
> + data |= BIT(22);
> + if (dsc->native_420)
> + data |= BIT(21);
> + }
> +
> + bpp = dsc->bits_per_pixel;

As above, don't forget to read the documentation on this field:

Target bits per pixel with 4 fractional bits, bits_per_pixel << 4

> + /* as per hw requirement bpp should be programmed
> + * twice the actual value in case of 420 or 422 encoding
> + */
> + if (dsc->native_422 || dsc->native_420)
> + bpp = 2 * bpp;
> + data |= (dsc->block_pred_enable ? 1 : 0) << 20;
> + data |= (bpp << 10);

Either wrap everything or nothing in ().

> + data |= (dsc->line_buf_depth & 0xf) << 6;
> + data |= dsc->convert_rgb << 4;
> + data |= dsc->bits_per_component & 0xf;
> +
> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, data);
> +
> + data = (dsc->pic_width & 0xffff) |
> + ((dsc->pic_height & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_PICTURE_SIZE + idx, data);
> +
> + data = (dsc->slice_width & 0xffff) |
> + ((dsc->slice_height & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_SLICE_SIZE + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_MISC_SIZE + idx,
> + (dsc->slice_chunk_size) & 0xffff);
> +
> + data = (dsc->initial_xmit_delay & 0xffff) |
> + ((dsc->initial_dec_delay & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_HRD_DELAYS + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_RC_SCALE + idx,
> + dsc->initial_scale_value & 0x3f);
> +
> + data = (dsc->scale_increment_interval & 0xffff) |
> + ((dsc->scale_decrement_interval & 0x7ff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_SCALE_INC_DEC + idx, data);
> +
> + data = (dsc->first_line_bpg_offset & 0x1f) |
> + ((dsc->second_line_bpg_offset & 0x1f) << 5);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_1 + idx, data);
> +
> + data = (dsc->nfl_bpg_offset & 0xffff) |
> + ((dsc->slice_bpg_offset & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_2 + idx, data);
> +
> + data = (dsc->initial_offset & 0xffff) |
> + ((dsc->final_offset & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_3 + idx, data);
> +
> + data = (dsc->nsl_bpg_offset & 0xffff) |
> + ((dsc->second_line_offset_adj & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_4 + idx, data);
> +
> + data = (dsc->flatness_min_qp & 0x1f);
> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;

dpu_hw_dsc.c computes this on the fly. After removing that, and
using initial_lines from the function parameters, only
dsc_info->num_active_ss_per_enc remains. Do you really need that
msm_display_dsc_info struct here, do you need it at all?

> +
> + DPU_REG_WRITE(hw, DSC_FLATNESS_QP + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_RC_MODEL_SIZE + idx,
> + (dsc->rc_model_size) & 0xffff);
> +
> + data = dsc->rc_edge_factor & 0xf;
> + data |= (dsc->rc_quant_incr_limit0 & 0x1f) << 8;
> + data |= (dsc->rc_quant_incr_limit1 & 0x1f) << 13;
> + data |= (dsc->rc_tgt_offset_high & 0xf) << 20;
> + data |= (dsc->rc_tgt_offset_low & 0xf) << 24;
> +
> + DPU_REG_WRITE(hw, DSC_RC_CONFIG + idx, data);
> +
> + /* program the dsc wrapper */
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + off = hw->blk_addr + idx;
> +
> + data = BIT(0); /* encoder enable */
> + if (dsc->native_422)
> + data |= BIT(8);
> + else if (dsc->native_420)
> + data |= BIT(9);
> + if (!dsc->convert_rgb)
> + data |= BIT(10);
> + if (dsc->bits_per_component == 8)
> + data |= BIT(11);
> + if (mode & DSC_MODE_SPLIT_PANEL)
> + data |= BIT(12);
> + if (mode & DSC_MODE_MULTIPLEX)
> + data |= BIT(13);
> + if (!(mode & DSC_MODE_VIDEO))
> + data |= BIT(17);
> +
> + if (dsc_info->dsc_4hsmerge_en) {
> + data |= dsc_info->dsc_4hsmerge_padding << 18;
> + data |= dsc_info->dsc_4hsmerge_alignment << 22;
> + data |= BIT(16);
> + }
> +
> + DPU_REG_WRITE(hw, DSC_CFG + idx, data);
> +
> +// DPU_REG_WRITE(hw, DSC_DATA_IN_SWAP + idx, 0x14e5);

No commented-out code please, especially not with //

> +}
> +
> +static void dpu_hw_dsc_config_thresh_1_2(struct dpu_hw_dsc *hw_dsc,
> + struct drm_dsc_config *dsc)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + struct msm_display_dsc_info *dsc_info;
> + u32 idx, off;
> + int i, j = 0;
> + struct drm_dsc_rc_range_parameters *rc;
> + u32 data = 0, min_qp = 0, max_qp = 0, bpg_off = 0;
> +
> + if (!hw_dsc || !dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> +
> + dsc_info = to_msm_dsc_info(dsc);
> +
> + rc = dsc->rc_range_params;
> +
> + off = 0;
> + for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
> + data |= dsc->rc_buf_thresh[i] << (8*j);

Lack of spaces does not make this multiplication any prettier to read.

* has precedence over << but it's better to replicate the () below as
well.

> + j++;
> + if ((j == 4) || (i == DSC_NUM_BUF_RANGES - 2)) {
> + DPU_REG_WRITE(hw, DSC_RC_BUF_THRESH_0 + idx + off,
> + data);
> + off += 4;
> + j = 0;
> + data = 0;
> + }
> + }
> +
> + off = 0;
> + for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
> + min_qp |= (rc[i].range_min_qp & 0x1f) << 5*j;
> + max_qp |= (rc[i].range_max_qp & 0x1f) << 5*j;
> + bpg_off |= (rc[i].range_bpg_offset & 0x3f) << 6*j;

These values _must_ already be masked to be useful in
drm_dsc_compute_rc_parameters(), no need to mask them again just like
the v1.1 block implementation.

> + j++;
> + if (j == 5) {
> + DPU_REG_WRITE(hw, DSC_RC_MIN_QP_0 + idx + off,
> + min_qp);
> + DPU_REG_WRITE(hw, DSC_RC_MAX_QP_0 + idx + off,
> + max_qp);
> + DPU_REG_WRITE(hw,
> + DSC_RC_RANGE_BPG_OFFSETS_0 + idx + off,
> + bpg_off);
> + off += 4;
> + j = 0;
> + min_qp = 0;
> + max_qp = 0;
> + bpg_off = 0;
> + }
> + }
> +}
> +
> +static void dpu_hw_dsc_bind_pingpong_blk_1_2(
> + struct dpu_hw_dsc *hw_dsc,
> + bool enable,
> + const enum dpu_pingpong pp)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + int idx;
> + int mux_cfg = 0xF; /* Disabled */

Lowercase hex (and anywhere else if I skipped any).

> +
> + if (!hw_dsc)
> + return;

As with the v1.1 implementation, we don't check this, and your function
below also checks it (but it does not need to).

> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> + if (enable)
> + mux_cfg = (pp - PINGPONG_0) & 0x7;
> +
> + DPU_REG_WRITE(hw, DSC_CTL + idx, mux_cfg);
> +}
> +
> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
> + const unsigned long features)
> +{
> + ops->dsc_disable = dpu_hw_dsc_disable_1_2;
> + ops->dsc_config = dpu_hw_dsc_config_1_2;
> + ops->dsc_config_thresh = dpu_hw_dsc_config_thresh_1_2;
> + ops->dsc_bind_pingpong_blk = dpu_hw_dsc_bind_pingpong_blk_1_2;
> +}
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

All in all you really need to revise and clean your patches before
sending them to the lists; these are already far too many comments and
nits, and massively take away from reviewing code behaviour which I have
not even started with after looking at 1 out of 14 patches :(

- Marijn

2023-01-23 20:20:01

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 14/14] drm/msm/disp/dpu1: add sc7280 dsc block and sub block

On 2023-01-23 10:24:34, Kuogee Hsieh wrote:
> This patch add DSC block and sub block to support new DSC v1.2 hardware
> encoder. Also sc7280 DSC related hardware information are added to allow
> sc7280 DSC feature be enabled at sc7280 platform.

You're not adding support (that happened in previous patches), you're
only describing SC7280 DSC blocks. Drop the first sentence.

> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 50 +++++++++++++++++++-------
> 1 file changed, 38 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
> index 7deffc9f9..2c78a46 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> - * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
> @@ -476,6 +476,7 @@ static const struct dpu_caps sc7280_dpu_caps = {
> .has_idle_pc = true,
> .max_linewidth = 2400,
> .pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
> + .max_dsc_width = 2560,
> };
>
> static const struct dpu_mdp_cfg msm8998_mdp[] = {
> @@ -1707,7 +1708,7 @@ static const struct dpu_pingpong_cfg sm8350_pp[] = {
> };
>
> static const struct dpu_pingpong_cfg sc7280_pp[] = {
> - PP_BLK("pingpong_0", PINGPONG_0, 0x59000, 0, sc7280_pp_sblk, -1, -1),
> + PP_BLK("pingpong_0", PINGPONG_0, 0x69000, 0, sc7280_pp_sblk, -1, -1),

This should go in a separate Fixes: patch.

> PP_BLK("pingpong_1", PINGPONG_1, 0x6a000, 0, sc7280_pp_sblk, -1, -1),
> PP_BLK("pingpong_2", PINGPONG_2, 0x6b000, 0, sc7280_pp_sblk, -1, -1),
> PP_BLK("pingpong_3", PINGPONG_3, 0x6c000, 0, sc7280_pp_sblk, -1, -1),
> @@ -1814,25 +1815,48 @@ static const struct dpu_merge_3d_cfg sm8550_merge_3d[] = {
> /*************************************************************
> * DSC sub blocks config
> *************************************************************/
> -#define DSC_BLK(_name, _id, _base, _features) \
> +#define DSC_BLK_HW_1_1(_name, _id, _base, _features) \

Not sure if HW_ is necessary here, DSC_BLK_1_1 seems cleaner.

> {\
> .name = _name, .id = _id, \
> .base = _base, .len = 0x140, \
> - .features = _features, \
> + .features = BIT(DPU_DSC_HW_REV_1_1) | _features, \
> + }
> +
> +#define DSC_BLK_HW_1_2(_name, _id, _base, _features, _sblk) \
> + {\
> + .name = _name, .id = _id, \
> + .base = _base, .len = 0x140, \
> + .features = BIT(DPU_DSC_HW_REV_1_2) | _features, \
> + .sblk = &_sblk, \
> }
>
> static struct dpu_dsc_cfg sdm845_dsc[] = {
> - DSC_BLK("dsc_0", DSC_0, 0x80000, 0),
> - DSC_BLK("dsc_1", DSC_1, 0x80400, 0),
> - DSC_BLK("dsc_2", DSC_2, 0x80800, 0),
> - DSC_BLK("dsc_3", DSC_3, 0x80c00, 0),
> + DSC_BLK_HW_1_1("dsc_0", DSC_0, 0x80000, 0),
> + DSC_BLK_HW_1_1("dsc_1", DSC_1, 0x80400, 0),
> + DSC_BLK_HW_1_1("dsc_2", DSC_2, 0x80800, 0),
> + DSC_BLK_HW_1_1("dsc_3", DSC_3, 0x80c00, 0),
> };
>
> static struct dpu_dsc_cfg sm8150_dsc[] = {
> - DSC_BLK("dsc_0", DSC_0, 0x80000, BIT(DPU_DSC_OUTPUT_CTRL)),
> - DSC_BLK("dsc_1", DSC_1, 0x80400, BIT(DPU_DSC_OUTPUT_CTRL)),
> - DSC_BLK("dsc_2", DSC_2, 0x80800, BIT(DPU_DSC_OUTPUT_CTRL)),
> - DSC_BLK("dsc_3", DSC_3, 0x80c00, BIT(DPU_DSC_OUTPUT_CTRL)),
> + DSC_BLK_HW_1_1("dsc_0", DSC_0, 0x80000, BIT(DPU_DSC_OUTPUT_CTRL)),
> + DSC_BLK_HW_1_1("dsc_1", DSC_1, 0x80400, BIT(DPU_DSC_OUTPUT_CTRL)),
> + DSC_BLK_HW_1_1("dsc_2", DSC_2, 0x80800, BIT(DPU_DSC_OUTPUT_CTRL)),
> + DSC_BLK_HW_1_1("dsc_3", DSC_3, 0x80c00, BIT(DPU_DSC_OUTPUT_CTRL)),
> +};
> +
> +static struct dpu_dsc_sub_blks sc7280_dsc_sblk_0 = {
> + .enc = {.base = 0x100, .len = 0x100},
> + .ctl = {.base = 0xF00, .len = 0x10},
> +};
> +
> +static struct dpu_dsc_sub_blks sc7280_dsc_sblk_1 = {
> + .enc = {.base = 0x200, .len = 0x100},
> + .ctl = {.base = 0xF80, .len = 0x10},
> +};
> +
> +static struct dpu_dsc_cfg sc7280_dsc[] = {
> + DSC_BLK_HW_1_2("dsc_0", DSC_0, 0x80000, BIT(DPU_DSC_NATIVE_422_EN), sc7280_dsc_sblk_0),
> + DSC_BLK_HW_1_2("dsc_1", DSC_1, 0x80000, BIT(DPU_DSC_NATIVE_422_EN), sc7280_dsc_sblk_1),

Bit scary that the blocks have the same address, because it is purely
driven by the sub-blocks with non-overlapping offsets/ranges. Should we
clarify that with a comment?

While at it, in v1.1 we use a hacky DSC_CTL() macro to bind a pingpong
block via a register in this magical "CTL" range at (0x1800 - 0x3FC * (m
- DSC_0)), can and/or should we represent that CTL sub-block explicitly
in the catalog for v1.1 hardware as well?

- Marijn

> };
>
> /*************************************************************
> @@ -2809,6 +2833,8 @@ static const struct dpu_mdss_cfg sc7280_dpu_cfg = {
> .pingpong_count = ARRAY_SIZE(sc7280_pp),
> .pingpong = sc7280_pp,
> .intf_count = ARRAY_SIZE(sc7280_intf),
> + .dsc_count = ARRAY_SIZE(sc7280_dsc),
> + .dsc = sc7280_dsc,
> .intf = sc7280_intf,
> .vbif_count = ARRAY_SIZE(sdm845_vbif),
> .vbif = sdm845_vbif,
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

2023-01-23 21:40:01

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> Current implementation timing engine programming does not consider
> compression factors. This patch add consideration of DSC factors
> while programming timing engine.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 2 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 14 ++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 +++++++++++++--------
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
> 5 files changed, 110 insertions(+), 54 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> index 2d864f9..3330e185 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> @@ -279,6 +279,8 @@ static void dpu_encoder_phys_vid_setup_timing_engine(
> if (phys_enc->hw_pp->merge_3d)
> intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;
>
> + phys_enc->hw_intf->hw_rev = phys_enc->dpu_kms->core_rev;
> +
> spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
> phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
> &timing_params, fmt);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> index 7b0b092..c6ee789 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> @@ -43,16 +43,22 @@
> #define DPU_HW_VER_500 DPU_HW_VER(5, 0, 0) /* sm8150 v1.0 */
> #define DPU_HW_VER_501 DPU_HW_VER(5, 0, 1) /* sm8150 v2.0 */
> #define DPU_HW_VER_510 DPU_HW_VER(5, 1, 1) /* sc8180 */
> -#define DPU_HW_VER_600 DPU_HW_VER(6, 0, 0) /* sm8250 */
> +#define DPU_HW_VER_600 DPU_HW_VER(6, 0, 0) /* sm8250, kona */
> #define DPU_HW_VER_620 DPU_HW_VER(6, 2, 0) /* sc7180 v1.0 */
> #define DPU_HW_VER_630 DPU_HW_VER(6, 3, 0) /* sm6115|sm4250 */
> #define DPU_HW_VER_650 DPU_HW_VER(6, 5, 0) /* qcm2290|sm4125 */
> -#define DPU_HW_VER_700 DPU_HW_VER(7, 0, 0) /* sm8350 */
> +#define DPU_HW_VER_700 DPU_HW_VER(7, 0, 0) /* sm8350, lahaina */
> #define DPU_HW_VER_720 DPU_HW_VER(7, 2, 0) /* sc7280 */
> #define DPU_HW_VER_800 DPU_HW_VER(8, 0, 0) /* sc8280xp */
> -#define DPU_HW_VER_810 DPU_HW_VER(8, 1, 0) /* sm8450 */
> +#define DPU_HW_VER_810 DPU_HW_VER(8, 1, 0) /* sm8450, waipio */
> #define DPU_HW_VER_900 DPU_HW_VER(9, 0, 0) /* sm8550 */

No.

>
> +/* Avoid using below IS_XXX macros outside catalog, use feature bit instead */
> +#define IS_DPU_MAJOR_SAME(rev1, rev2) \
> + (DPU_HW_MAJOR((rev1)) == DPU_HW_MAJOR((rev2)))
> +#define IS_DPU_MAJOR_MINOR_SAME(rev1, rev2) \
> + (DPU_HW_MAJOR_MINOR((rev1)) == DPU_HW_MAJOR_MINOR((rev2)))
> +
> #define IS_MSM8996_TARGET(rev) IS_DPU_MAJOR_MINOR_SAME((rev), DPU_HW_VER_170)
> #define IS_MSM8998_TARGET(rev) IS_DPU_MAJOR_MINOR_SAME((rev), DPU_HW_VER_300)
> #define IS_SDM845_TARGET(rev) IS_DPU_MAJOR_MINOR_SAME((rev), DPU_HW_VER_400)
> @@ -240,6 +246,7 @@ enum {
> * @DPU_INTF_INPUT_CTRL Supports the setting of pp block from which
> * pixel data arrives to this INTF
> * @DPU_INTF_TE INTF block has TE configuration support
> + * @DPU_INTF_TE_ALIGN_VSYNC INTF block has POMS Align vsync support
> * @DPU_DATA_HCTL_EN Allows data to be transferred at different rate
> than video timing
> * @DPU_INTF_MAX
> @@ -247,6 +254,7 @@ enum {
> enum {
> DPU_INTF_INPUT_CTRL = 0x1,
> DPU_INTF_TE,
> + DPU_INTF_TE_ALIGN_VSYNC,
> DPU_DATA_HCTL_EN,
> DPU_INTF_MAX
> };
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> index 7ce66bf..238efdb 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> */
>
> @@ -44,6 +44,7 @@
> #define INTF_DEFLICKER_STRNG_COEFF 0x0F4
> #define INTF_DEFLICKER_WEAK_COEFF 0x0F8
>
> +#define INTF_REG_SPLIT_LINK 0x080
> #define INTF_DSI_CMD_MODE_TRIGGER_EN 0x084
> #define INTF_PANEL_FORMAT 0x090
> #define INTF_TPG_ENABLE 0x100
> @@ -65,9 +66,9 @@
>
> #define INTF_CFG_ACTIVE_H_EN BIT(29)
> #define INTF_CFG_ACTIVE_V_EN BIT(30)
> -
> #define INTF_CFG2_DATABUS_WIDEN BIT(0)
> #define INTF_CFG2_DATA_HCTL_EN BIT(4)
> +#define INTF_CFG2_ALIGN_VSYNC_TO_TE BIT(16)
>
> #define INTF_MISR_CTRL 0x180
> #define INTF_MISR_SIGNATURE 0x184
> @@ -91,6 +92,16 @@ static const struct dpu_intf_cfg *_intf_offset(enum dpu_intf intf,
> return ERR_PTR(-EINVAL);
> }
>
> +static inline void _check_and_set_comp_bit(struct dpu_hw_intf *ctx,
> + bool dsc_4hs_merge, bool compression_en, u32 *intf_cfg2)
> +{
> + if (((DPU_HW_MAJOR(ctx->hw_rev) >= DPU_HW_MAJOR(DPU_HW_VER_700)) && compression_en)
> + || (IS_DPU_MAJOR_SAME(ctx->hw_rev, DPU_HW_VER_600) && dsc_4hs_merge))
> + (*intf_cfg2) |= BIT(12);
> + else if (!compression_en)
> + (*intf_cfg2) &= ~BIT(12);

NAK. Make this into a sensible API rather than poking at DPU revision.
Not to mention that this BIT(12) should be defined somewhere.

> +}
> +
> static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
> const struct intf_timing_params *p,
> const struct dpu_format *fmt)
> @@ -113,82 +124,96 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
> /* read interface_cfg */
> intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
>
> - if (ctx->cap->type == INTF_DP)
> + if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP)

You remember, we have been here. INTF_EDP is for old (msm8974, apq8084)
eDP.

> dp_intf = true;
>
> hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width +
> - p->h_front_porch;
> + p->h_front_porch;
> vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height +
> - p->v_front_porch;
> + p->v_front_porch;
>
> display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
> - hsync_period) + p->hsync_skew;
> + hsync_period) + p->hsync_skew;
> display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) +
> - p->hsync_skew - 1;
> + p->hsync_skew - 1;

As usual. A mixture of formatting changes and data changes. Please turn
this into reviewable changes. Split them into atomic commits. Rewriting
the whole function is not revieweable and has no way to go in.

> +
> + hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
>
> hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
> hsync_end_x = hsync_period - p->h_front_porch - 1;
>
> - if (p->width != p->xres) { /* border fill added */
> - active_h_start = hsync_start_x;
> - active_h_end = active_h_start + p->xres - 1;
> - } else {
> - active_h_start = 0;
> - active_h_end = 0;
> - }
> -
> - if (p->height != p->yres) { /* border fill added */
> - active_v_start = display_v_start;
> - active_v_end = active_v_start + (p->yres * hsync_period) - 1;
> - } else {
> - active_v_start = 0;
> - active_v_end = 0;
> - }
> -
> - if (active_h_end) {
> - active_hctl = (active_h_end << 16) | active_h_start;
> - intf_cfg |= INTF_CFG_ACTIVE_H_EN;
> - } else {
> - active_hctl = 0;
> - }
> -
> - if (active_v_end)
> - intf_cfg |= INTF_CFG_ACTIVE_V_EN;
> -
> - hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
> - display_hctl = (hsync_end_x << 16) | hsync_start_x;
> -
> /*
> * DATA_HCTL_EN controls data timing which can be different from
> * video timing. It is recommended to enable it for all cases, except
> * if compression is enabled in 1 pixel per clock mode
> */
> + if (!p->compression_en || p->wide_bus_en)
> + intf_cfg2 |= INTF_CFG2_DATA_HCTL_EN;
> +
> if (p->wide_bus_en)
> - intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN | INTF_CFG2_DATA_HCTL_EN;
> + intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
>
> + /*
> + * If widebus is disabled:
> + * For uncompressed stream, the data is valid for the entire active
> + * window period.
> + * For compressed stream, data is valid for a shorter time period
> + * inside the active window depending on the compression ratio.
> + *
> + * If widebus is enabled:
> + * For uncompressed stream, data is valid for only half the active
> + * window, since the data rate is doubled in this mode.
> + * p->width holds the adjusted width for DP but unadjusted width for DSI
> + * For compressed stream, data validity window needs to be adjusted for
> + * compression ratio and then further halved.
> + */
> data_width = p->width;
>
> + if (p->compression_en) {
> + if (p->wide_bus_en)
> + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6);
> + else
> + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3);
> + } else if (!dp_intf && p->wide_bus_en) {
> + data_width = p->width >> 1;
> + } else {
> + data_width = p->width;
> + }
> +
> hsync_data_start_x = hsync_start_x;
> hsync_data_end_x = hsync_start_x + data_width - 1;
>
> + display_hctl = (hsync_end_x << 16) | hsync_start_x;
> display_data_hctl = (hsync_data_end_x << 16) | hsync_data_start_x;
>
> if (dp_intf) {
> /* DP timing adjustment */
> display_v_start += p->hsync_pulse_width + p->h_back_porch;
> display_v_end -= p->h_front_porch;
> + }
> +
> + intf_cfg |= INTF_CFG_ACTIVE_H_EN;
> + intf_cfg |= INTF_CFG_ACTIVE_V_EN;
> + active_h_start = hsync_start_x;
> + active_h_end = active_h_start + p->xres - 1;
> + active_v_start = display_v_start;
> + active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>
> - active_h_start = hsync_start_x;
> - active_h_end = active_h_start + p->xres - 1;
> - active_v_start = display_v_start;
> - active_v_end = active_v_start + (p->yres * hsync_period) - 1;
> + active_hctl = (active_h_end << 16) | active_h_start;
>
> - active_hctl = (active_h_end << 16) | active_h_start;
> + if (dp_intf) {
> display_hctl = active_hctl;
>
> - intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN;
> + if (p->compression_en) {
> + active_data_hctl = (hsync_start_x + p->extra_dto_cycles) << 16;
> + active_data_hctl += hsync_start_x;
> +
> + display_data_hctl = active_data_hctl;
> + }
> }
>
> + _check_and_set_comp_bit(ctx, p->dsc_4hs_merge, p->compression_en, &intf_cfg2);
> +
> den_polarity = 0;
> if (ctx->cap->type == INTF_HDMI) {
> hsync_polarity = p->yres >= 720 ? 0 : 1;
> @@ -202,7 +227,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
> }
> polarity_ctl = (den_polarity << 2) | /* DEN Polarity */
> (vsync_polarity << 1) | /* VSYNC Polarity */
> - (hsync_polarity << 0); /* HSYNC Polarity */
> + (hsync_polarity << 0); /* HSYNC Polarity */
>
> if (!DPU_FORMAT_IS_YUV(fmt))
> panel_format = (fmt->bits[C0_G_Y] |
> @@ -216,6 +241,17 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
> (COLOR_8BIT << 4) |
> (0x21 << 8));
>
> + if (p->wide_bus_en)
> + intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
> +
> + /* Synchronize timing engine enable to TE */
> + if ((ctx->cap->features & BIT(DPU_INTF_TE_ALIGN_VSYNC))
> + && p->poms_align_vsync)
> + intf_cfg2 |= INTF_CFG2_ALIGN_VSYNC_TO_TE;
> +
> + if (ctx->cfg.split_link_en)
> + DPU_REG_WRITE(c, INTF_REG_SPLIT_LINK, 0x3);
> +
> DPU_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl);
> DPU_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period);
> DPU_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0,
> @@ -233,11 +269,9 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
> DPU_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3);
> DPU_REG_WRITE(c, INTF_CONFIG, intf_cfg);
> DPU_REG_WRITE(c, INTF_PANEL_FORMAT, panel_format);
> - if (ctx->cap->features & BIT(DPU_DATA_HCTL_EN)) {
> - DPU_REG_WRITE(c, INTF_CONFIG2, intf_cfg2);
> - DPU_REG_WRITE(c, INTF_DISPLAY_DATA_HCTL, display_data_hctl);
> - DPU_REG_WRITE(c, INTF_ACTIVE_DATA_HCTL, active_data_hctl);
> - }
> + DPU_REG_WRITE(c, INTF_CONFIG2, intf_cfg2);
> + DPU_REG_WRITE(c, INTF_DISPLAY_DATA_HCTL, display_data_hctl);
> + DPU_REG_WRITE(c, INTF_ACTIVE_DATA_HCTL, active_data_hctl);
> }
>
> static void dpu_hw_intf_enable_timing_engine(
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> index 643dd10..57be86d 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> */
>
> @@ -10,6 +10,7 @@
> #include "dpu_hw_catalog.h"
> #include "dpu_hw_mdss.h"
> #include "dpu_hw_util.h"
> +#include "dpu_hw_top.h"
>
> struct dpu_hw_intf;
>
> @@ -33,6 +34,11 @@ struct intf_timing_params {
> u32 hsync_skew;
>
> bool wide_bus_en;
> + bool compression_en;
> + u32 extra_dto_cycles; /* for DP only */
> + bool dsc_4hs_merge; /* DSC 4HS merge */
> + bool poms_align_vsync; /* poms with vsync aligned */
> + u32 dce_bytes_per_line;
> };
>
> struct intf_prog_fetch {
> @@ -86,11 +92,13 @@ struct dpu_hw_intf_ops {
>
> struct dpu_hw_intf {
> struct dpu_hw_blk_reg_map hw;
> + u32 hw_rev; /* mdss hw_rev */
>
> /* intf */
> enum dpu_intf idx;
> const struct dpu_intf_cfg *cap;
> const struct dpu_mdss_cfg *mdss;
> + struct split_pipe_cfg cfg;
>
> /* ops */
> struct dpu_hw_intf_ops ops;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
> index a1a9e44..1212fa2 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
> @@ -1,5 +1,7 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> +/*
> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> #ifndef _DPU_HW_TOP_H
> @@ -34,12 +36,14 @@ struct traffic_shaper_cfg {
> * @intf : Interface id for main control path
> * @split_flush_en: Allows both the paths to be flushed when master path is
> * flushed
> + * @split_link_en: Check if split link is enabled
> */
> struct split_pipe_cfg {
> bool en;
> enum dpu_intf_mode mode;
> enum dpu_intf intf;
> bool split_flush_en;
> + bool split_link_en;
> };
>
> /**

--
With best wishes
Dmitry


2023-01-23 21:43:38

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 11/14] drm/msm/disp/dpu1: add supports of new flush mechanism

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> A new flushing mechanism is introduced to decouple peripheral metadata
> flushing from timing engine related flush. This patch add peripheral
> flushing functions.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 24 ++++++++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 2 +
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 7 ++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 ++++++++++++++++++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 21 +++++++++++
> 5 files changed, 91 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 901e317..d2625b3 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1472,6 +1472,12 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc,
> if (extra_flush_bits && ctl->ops.update_pending_flush)
> ctl->ops.update_pending_flush(ctl, extra_flush_bits);
>
> + if (phys->hw_intf->cap->type == INTF_DP &&
> + phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
> + phys->comp_ratio) {

Do you really need to know comp_ratio here? And the comp_type?

> + ctl->ops.update_pending_flush_periph(ctl, phys->hw_intf->idx);
> + }
> +
> ctl->ops.trigger_flush(ctl);
>
> if (ctl->ops.get_pending_flush)
> @@ -1814,12 +1820,18 @@ dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc,
> return DIV_ROUND_UP(total_pixels, dsc->slice_width);
> }
>
> -static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
> +static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
> + struct dpu_hw_dsc *hw_dsc,
> struct dpu_hw_pingpong *hw_pp,
> struct drm_dsc_config *dsc,
> u32 common_mode,
> u32 initial_lines)
> {
> + struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
> + struct dpu_hw_ctl *ctl;
> +
> + ctl = cur_master->hw_ctl;
> +
> if (hw_dsc->ops.dsc_config)
> hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);
>
> @@ -1834,6 +1846,10 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
>
> if (hw_pp->ops.enable_dsc)
> hw_pp->ops.enable_dsc(hw_pp);
> +
> + if (ctl->ops.update_pending_flush_dsc)
> + ctl->ops.update_pending_flush_dsc(ctl, hw_dsc->idx);
> +
> }
>
> static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
> @@ -1877,8 +1893,10 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
> enc_ip_w = intf_ip_w / 2;
> initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
>
> - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
> - dpu_encoder_dsc_pipe_cfg(hw_dsc[i], hw_pp[i], dsc, dsc_common_mode, initial_lines);
> + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
> + dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc,
> + dsc_common_mode, initial_lines);
> + }
> }
>
> void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 1d434b2..0569b36 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -200,6 +200,8 @@ struct dpu_encoder_phys {
> atomic_t pending_kickoff_cnt;
> wait_queue_head_t pending_kickoff_wq;
> int irq[INTR_IDX_MAX];
> + enum msm_display_compression_type comp_type;
> + u32 comp_ratio;
> };
>
> static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> index 48c4810..2d864f9 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> @@ -1,5 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /* Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
> @@ -427,6 +428,12 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc)
> if (ctl->ops.update_pending_flush_merge_3d && phys_enc->hw_pp->merge_3d)
> ctl->ops.update_pending_flush_merge_3d(ctl, phys_enc->hw_pp->merge_3d->idx);
>
> + if (phys_enc->hw_intf->cap->type == INTF_DP &&
> + phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
> + phys_enc->comp_ratio) {
> + ctl->ops.update_pending_flush_periph(ctl, phys_enc->hw_intf->idx);
> + }
> +
> skip_flush:
> DPU_DEBUG_VIDENC(phys_enc,
> "update pending flush ctl %d intf %d\n",
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> index b88a2f3..1891c57 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> @@ -33,6 +33,7 @@
> #define CTL_DSC_FLUSH 0x104
> #define CTL_WB_FLUSH 0x108
> #define CTL_INTF_FLUSH 0x110
> +#define CTL_PERIPH_FLUSH 0x128
> #define CTL_INTF_MASTER 0x134
> #define CTL_FETCH_PIPE_ACTIVE 0x0FC
>
> @@ -42,11 +43,13 @@
> #define DPU_REG_RESET_TIMEOUT_US 2000
> #define MERGE_3D_IDX 23
> #define DSC_IDX 22
> +#define PERIPH_IDX 30
> #define INTF_IDX 31
> #define WB_IDX 16
> #define CTL_INVALID_BIT 0xffff
> #define CTL_DEFAULT_GROUP_ID 0xf
>
> +

No extra empty lines please.

> static const u32 fetch_tbl[SSPP_MAX] = {CTL_INVALID_BIT, 16, 17, 18, 19,
> CTL_INVALID_BIT, CTL_INVALID_BIT, CTL_INVALID_BIT, CTL_INVALID_BIT, 0,
> 1, 2, 3, CTL_INVALID_BIT, CTL_INVALID_BIT};
> @@ -123,6 +126,7 @@ static inline void dpu_hw_ctl_update_pending_flush(struct dpu_hw_ctl *ctx,
> trace_dpu_hw_ctl_update_pending_flush(flushbits,
> ctx->pending_flush_mask);
> ctx->pending_flush_mask |= flushbits;
> +
> }
>
> static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx)
> @@ -142,6 +146,15 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
> DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
> ctx->pending_wb_flush_mask);
>
> + if (ctx->pending_flush_mask & BIT(PERIPH_IDX))
> + DPU_REG_WRITE(&ctx->hw, CTL_PERIPH_FLUSH,
> + ctx->pending_periph_flush_mask);
> +
> + if (ctx->pending_flush_mask & BIT(DSC_IDX)) {
> + DPU_REG_WRITE(&ctx->hw, CTL_DSC_FLUSH,
> + ctx->pending_dsc_flush_mask);
> + }
> +
> DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
> }
>
> @@ -281,6 +294,13 @@ static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx,
> ctx->pending_flush_mask |= BIT(INTF_IDX);
> }
>
> +static void dpu_hw_ctl_update_pending_flush_periph(struct dpu_hw_ctl *ctx,
> + enum dpu_intf intf)
> +{
> + ctx->pending_periph_flush_mask |= BIT(intf - INTF_0);
> + ctx->pending_flush_mask |= BIT(PERIPH_IDX);
> +}
> +
> static void dpu_hw_ctl_update_pending_flush_merge_3d_v1(struct dpu_hw_ctl *ctx,
> enum dpu_merge_3d merge_3d)
> {
> @@ -288,6 +308,13 @@ static void dpu_hw_ctl_update_pending_flush_merge_3d_v1(struct dpu_hw_ctl *ctx,
> ctx->pending_flush_mask |= BIT(MERGE_3D_IDX);
> }
>
> +static void dpu_hw_ctl_update_pending_flush_dsc_v1(struct dpu_hw_ctl *ctx,
> + enum dpu_dsc dsc_num)
> +{
> + ctx->pending_dsc_flush_mask |= BIT(dsc_num - DSC_0);
> + ctx->pending_flush_mask |= BIT(DSC_IDX);
> +}
> +
> static void dpu_hw_ctl_update_pending_flush_dspp(struct dpu_hw_ctl *ctx,
> enum dpu_dspp dspp)
> {
> @@ -472,6 +499,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
> u32 intf_active = 0;
> u32 wb_active = 0;
> u32 mode_sel = 0;
> + u32 dsc_active = 0;
>
> /* CTL_TOP[31:28] carries group_id to collate CTL paths
> * per VM. Explicitly disable it until VM support is
> @@ -502,9 +530,11 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
> if (cfg->merge_3d)
> DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
> BIT(cfg->merge_3d - MERGE_3D_0));
> - if (cfg->dsc) {
> - DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, DSC_IDX);
> - DPU_REG_WRITE(c, CTL_DSC_ACTIVE, cfg->dsc);
> +
> + if (cfg->dsc_num) {
> + dsc_active = DPU_REG_READ(c, CTL_DSC_ACTIVE);
> + dsc_active |= BIT(cfg->dsc_num - DSC_0);
> + DPU_REG_WRITE(c, CTL_DSC_ACTIVE, dsc_active);

This deserves a separate patch with Fixed tag.

> }
> }
>
> @@ -605,9 +635,16 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
> ops->reset_intf_cfg = dpu_hw_ctl_reset_intf_cfg_v1;
> ops->update_pending_flush_intf =
> dpu_hw_ctl_update_pending_flush_intf_v1;
> +
> + ops->update_pending_flush_periph =
> + dpu_hw_ctl_update_pending_flush_periph;
> +
> ops->update_pending_flush_merge_3d =
> dpu_hw_ctl_update_pending_flush_merge_3d_v1;
> ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb_v1;
> +
> + ops->update_pending_flush_dsc =
> + dpu_hw_ctl_update_pending_flush_dsc_v1;
> } else {
> ops->trigger_flush = dpu_hw_ctl_trigger_flush;
> ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> index 96c012e..d3faa0b1 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> @@ -48,6 +48,7 @@ struct dpu_hw_intf_cfg {
> enum dpu_3d_blend_mode mode_3d;
> enum dpu_merge_3d merge_3d;
> enum dpu_ctl_mode_sel intf_mode_sel;
> + enum dpu_dsc dsc_num;
> int stream_sel;
> unsigned int dsc;
> };
> @@ -121,6 +122,15 @@ struct dpu_hw_ctl_ops {
> enum dpu_intf blk);
>
> /**
> + * OR in the given flushbits to the cached pending_(periph_)flush_mask
> + * No effect on hardware
> + * @ctx : ctl path ctx pointer
> + * @blk : interface block index
> + */
> + void (*update_pending_flush_periph)(struct dpu_hw_ctl *ctx,
> + enum dpu_intf blk);
> +
> + /**
> * OR in the given flushbits to the cached pending_(merge_3d_)flush_mask
> * No effect on hardware
> * @ctx : ctl path ctx pointer
> @@ -156,6 +166,15 @@ struct dpu_hw_ctl_ops {
> void (*update_pending_flush_dspp)(struct dpu_hw_ctl *ctx,
> enum dpu_dspp blk);
> /**
> + * OR in the given flushbits to the cached pending_(dsc_)flush_mask
> + * No effect on hardware
> + * @ctx : ctl path ctx pointer
> + * @blk : interface block index
> + */
> + void (*update_pending_flush_dsc)(struct dpu_hw_ctl *ctx,
> + enum dpu_dsc blk);
> +
> + /**
> * Write the value of the pending_flush_mask to hardware
> * @ctx : ctl path ctx pointer
> */
> @@ -241,7 +260,9 @@ struct dpu_hw_ctl {
> u32 pending_flush_mask;
> u32 pending_intf_flush_mask;
> u32 pending_wb_flush_mask;
> + u32 pending_periph_flush_mask;
> u32 pending_merge_3d_flush_mask;
> + u32 pending_dsc_flush_mask;
>
> /* ops */
> struct dpu_hw_ctl_ops ops;

--
With best wishes
Dmitry


2023-01-23 22:02:37

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 07/14] drm/msm/dp: add dsc helper functions

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> Add DSC related supporting functions to calculate DSC related parameters.
> In addition, DSC hardware encoder customized configuration parameters are
> also included. Algorithms used to perform calculation are derived from
> system engineer spreadsheet.

Overall impression. First rewrite this patch to use existing data
structures and helpers. Then move the remnants to the
drm/display/drm_dsc_helper.c.

>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/Makefile | 1 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++++++++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 ++
> drivers/gpu/drm/msm/msm_drv.h | 4 +
> 4 files changed, 567 insertions(+)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 7274c412..28cf52b 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> disp/dpu1/dpu_hw_catalog.o \
> disp/dpu1/dpu_hw_ctl.o \
> disp/dpu1/dpu_hw_dsc.o \
> + disp/dpu1/dpu_dsc_helper.o \
> disp/dpu1/dpu_hw_interrupts.o \
> disp/dpu1/dpu_hw_intf.o \
> disp/dpu1/dpu_hw_lm.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> new file mode 100644
> index 00000000..48cef23
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> @@ -0,0 +1,537 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2012-2023 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#include "msm_drv.h"
> +#include "dpu_kms.h"
> +#include "dpu_hw_dsc.h"
> +#include "dpu_dsc_helper.h"
> +
> +
> +#define DPU_DSC_PPS_SIZE 128

doesn't sizeof(drm_dsc_picture_parameter_set) work instead?

> +
> +enum dpu_dsc_ratio_type {
> + DSC_V11_8BPC_8BPP,
> + DSC_V11_10BPC_8BPP,
> + DSC_V11_10BPC_10BPP,
> + DSC_V11_SCR1_8BPC_8BPP,
> + DSC_V11_SCR1_10BPC_8BPP,
> + DSC_V11_SCR1_10BPC_10BPP,
> + DSC_V12_444_8BPC_8BPP = DSC_V11_SCR1_8BPC_8BPP,
> + DSC_V12_444_10BPC_8BPP = DSC_V11_SCR1_10BPC_8BPP,
> + DSC_V12_444_10BPC_10BPP = DSC_V11_SCR1_10BPC_10BPP,
> + DSC_V12_422_8BPC_7BPP,
> + DSC_V12_422_8BPC_8BPP,
> + DSC_V12_422_10BPC_7BPP,
> + DSC_V12_422_10BPC_10BPP,
> + DSC_V12_420_8BPC_6BPP,
> + DSC_V12_420_10BPC_6BPP,
> + DSC_V12_420_10BPC_7_5BPP,
> + DSC_RATIO_TYPE_MAX
> +};
> +
> +
> +static u16 dpu_dsc_rc_buf_thresh[DSC_NUM_BUF_RANGES - 1] = {
> + 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54,
> + 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e
> +};
> +
> +/*
> + * Rate control - Min QP values for each ratio type in dpu_dsc_ratio_type
> + */
> +static char dpu_dsc_rc_range_min_qp[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
> + /* DSC v1.1 */
> + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 13},
> + {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 17},
> + {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
> + /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
> + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 9, 12},
> + {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 13, 16},
> + {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
> + /* DSC v1.2 YUV422 */
> + {0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 11},
> + {0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 10},
> + {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
> + {0, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 11, 12},
> + /* DSC v1.2 YUV420 */
> + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 10},
> + {0, 2, 3, 4, 6, 7, 7, 7, 7, 7, 9, 9, 9, 11, 14},
> + {0, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 11, 12},
> +};
> +
> +/*
> + * Rate control - Max QP values for each ratio type in dpu_dsc_ratio_type
> + */
> +static char dpu_dsc_rc_range_max_qp[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
> + /* DSC v1.1 */
> + {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 11, 12, 13, 13, 15},
> + {4, 8, 9, 10, 11, 11, 11, 12, 13, 14, 15, 16, 17, 17, 19},
> + {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
> + /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
> + {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 10, 11, 11, 12, 13},
> + {8, 8, 9, 10, 11, 11, 11, 12, 13, 14, 14, 15, 15, 16, 17},
> + {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
> + /* DSC v1.2 YUV422 */
> + {3, 4, 5, 6, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12},
> + {2, 4, 5, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 10, 11},
> + {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
> + {2, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13},
> + /* DSC v1.2 YUV420 */
> + {2, 4, 5, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 10, 12},
> + {2, 5, 7, 8, 9, 10, 11, 12, 12, 13, 13, 13, 13, 14, 15},
> + {2, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13},
> + };
> +
> +/*
> + * Rate control - bpg offset values for each ratio type in dpu_dsc_ratio_type
> + */
> +static char dpu_dsc_rc_range_bpg[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
> + /* DSC v1.1 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + /* DSC v1.1 SCR and DSC V1.2 RGB 444 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + /* DSC v1.2 YUV422 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + {10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12},
> + /* DSC v1.2 YUV420 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12},
> +};
> +
> +static struct dpu_dsc_rc_init_params_lut {
> + u32 rc_quant_incr_limit0;
> + u32 rc_quant_incr_limit1;
> + u32 initial_fullness_offset;
> + u32 initial_xmit_delay;
> + u32 second_line_bpg_offset;
> + u32 second_line_offset_adj;
> + u32 flatness_min_qp;
> + u32 flatness_max_qp;
> +} dpu_dsc_rc_init_param_lut[] = {
> + /* DSC v1.1 */
> + {11, 11, 6144, 512, 0, 0, 3, 12}, /* DSC_V11_8BPC_8BPP */
> + {15, 15, 6144, 512, 0, 0, 7, 16}, /* DSC_V11_10BPC_8BPP */
> + {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V11_10BPC_10BPP */
> + /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
> + {11, 11, 6144, 512, 0, 0, 3, 12}, /* DSC_V12_444_8BPC_8BPP or DSC_V11_SCR1_8BPC_8BPP */
> + {15, 15, 6144, 512, 0, 0, 7, 16}, /* DSC_V12_444_10BPC_8BPP or DSC_V11_SCR1_10BPC_8BPP */
> + {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V12_444_10BPC_10BPP or DSC_V11_SCR1_10BPC_10BPP */
> + /* DSC v1.2 YUV422 */
> + {11, 11, 5632, 410, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_7BPP */
> + {11, 11, 2048, 341, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_8BPP */
> + {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V12_422_10BPC_7BPP */
> + {15, 15, 2048, 273, 0, 0, 7, 16}, /* DSC_V12_422_10BPC_10BPP */
> + /* DSC v1.2 YUV420 */
> + {11, 11, 5632, 410, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_7BPP */
> + {11, 11, 2048, 341, 12, 512, 3, 12}, /* DSC_V12_420_8BPC_6BPP */
> + {15, 15, 2048, 341, 12, 512, 7, 16}, /* DSC_V12_420_10BPC_6BPP */
> + {15, 15, 2048, 256, 12, 512, 7, 16}, /* DSC_V12_420_10BPC_7_5BPP */
> +};
> +
> +/**
> + * Maps to lookup the dpu_dsc_ratio_type index used in rate control tables
> + */
> +static struct dpu_dsc_table_index_lut {
> + u32 fmt;
> + u32 scr_ver;
> + u32 minor_ver;
> + u32 bpc;
> + u32 bpp;
> + u32 type;
> +} dpu_dsc_index_map[] = {
> + /* DSC 1.1 formats - scr version is considered */
> + {MSM_CHROMA_444, 0, 1, 8, 8, DSC_V11_8BPC_8BPP},
> + {MSM_CHROMA_444, 0, 1, 10, 8, DSC_V11_10BPC_8BPP},
> + {MSM_CHROMA_444, 0, 1, 10, 10, DSC_V11_10BPC_10BPP},
> +
> + {MSM_CHROMA_444, 1, 1, 8, 8, DSC_V11_SCR1_8BPC_8BPP},
> + {MSM_CHROMA_444, 1, 1, 10, 8, DSC_V11_SCR1_10BPC_8BPP},
> + {MSM_CHROMA_444, 1, 1, 10, 10, DSC_V11_SCR1_10BPC_10BPP},
> +
> + /* DSC 1.2 formats - scr version is no-op */

Such distinctions begs to split this table into a pair or three of them.

> + {MSM_CHROMA_444, -1, 2, 8, 8, DSC_V12_444_8BPC_8BPP},
> + {MSM_CHROMA_444, -1, 2, 10, 8, DSC_V12_444_10BPC_8BPP},
> + {MSM_CHROMA_444, -1, 2, 10, 10, DSC_V12_444_10BPC_10BPP},
> +
> + {MSM_CHROMA_422, -1, 2, 8, 7, DSC_V12_422_8BPC_7BPP},
> + {MSM_CHROMA_422, -1, 2, 8, 8, DSC_V12_422_8BPC_8BPP},
> + {MSM_CHROMA_422, -1, 2, 10, 7, DSC_V12_422_10BPC_7BPP},
> + {MSM_CHROMA_422, -1, 2, 10, 10, DSC_V12_422_10BPC_10BPP},
> +
> + {MSM_CHROMA_420, -1, 2, 8, 6, DSC_V12_420_8BPC_6BPP},
> + {MSM_CHROMA_420, -1, 2, 10, 6, DSC_V12_420_10BPC_6BPP},
> +};
> +
> +static int _get_rc_table_index(struct drm_dsc_config *dsc, int scr_ver)
> +{
> + u32 bpp, bpc, i, fmt = MSM_CHROMA_444;
> +
> + if (dsc->dsc_version_major != 0x1) {
> + DPU_ERROR("unsupported major version %d\n",
> + dsc->dsc_version_major);
> + return -EINVAL;
> + }
> +
> + bpc = dsc->bits_per_component;
> + bpp = DSC_BPP(*dsc);
> +
> + if (dsc->native_422)
> + fmt = MSM_CHROMA_422;
> + else if (dsc->native_420)
> + fmt = MSM_CHROMA_420;
> +
> +
> + for (i = 0; i < ARRAY_SIZE(dpu_dsc_index_map); i++) {
> + if (dsc->dsc_version_minor == dpu_dsc_index_map[i].minor_ver &&
> + fmt == dpu_dsc_index_map[i].fmt &&
> + bpc == dpu_dsc_index_map[i].bpc &&
> + bpp == dpu_dsc_index_map[i].bpp &&
> + (dsc->dsc_version_minor != 0x1 ||
> + scr_ver == dpu_dsc_index_map[i].scr_ver))
> + return dpu_dsc_index_map[i].type;
> + }
> +
> + DPU_ERROR("unsupported DSC v%d.%dr%d, bpc:%d, bpp:%d, fmt:0x%x\n",
> + dsc->dsc_version_major, dsc->dsc_version_minor,
> + scr_ver, bpc, bpp, fmt);
> + return -EINVAL;
> +}
> +
> +u8 _get_dsc_v1_2_bpg_offset(struct drm_dsc_config *dsc)
> +{
> + u8 bpg_offset = 0;
> + u8 uncompressed_bpg_rate;
> + u8 bpp = DSC_BPP(*dsc);
> +
> + if (dsc->slice_height < 8)
> + bpg_offset = 2 * (dsc->slice_height - 1);
> + else if (dsc->slice_height < 20)
> + bpg_offset = 12;
> + else if (dsc->slice_height <= 30)
> + bpg_offset = 13;
> + else if (dsc->slice_height < 42)
> + bpg_offset = 14;
> + else
> + bpg_offset = 15;
> +
> + if (dsc->native_422)
> + uncompressed_bpg_rate = 3 * bpp * 4;
> + else if (dsc->native_420)
> + uncompressed_bpg_rate = 3 * bpp;
> + else
> + uncompressed_bpg_rate = (3 * bpp + 2) * 3;
> +
> + if (bpg_offset < (uncompressed_bpg_rate - (3 * bpp)))
> + return bpg_offset;
> + else
> + return (uncompressed_bpg_rate - (3 * bpp));
> +}
> +
> +int dpu_dsc_populate_dsc_config(struct drm_dsc_config *dsc, int scr_ver)
> +{
> + int bpp, bpc;
> + int groups_per_line, groups_total;
> + int min_rate_buffer_size;
> + int hrd_delay;
> + int pre_num_extra_mux_bits, num_extra_mux_bits;
> + int slice_bits;
> + int data;
> + int final_value, final_scale;
> + struct dpu_dsc_rc_init_params_lut *rc_param_lut;
> + u32 slice_width_mod;
> + int i, ratio_idx;

We just got rid of a duplicate of drm_dsc_compute_rc_parameters(), so we
are not going to introduce another one. Please use that function to
compute relevant parameters.

> +
> + dsc->rc_model_size = 8192;
> +
> + if ((dsc->dsc_version_major == 0x1) &&
> + (dsc->dsc_version_minor == 0x1)) {
> + if (scr_ver == 0x1)
> + dsc->first_line_bpg_offset = 15;
> + else
> + dsc->first_line_bpg_offset = 12;
> + } else if (dsc->dsc_version_minor == 0x2) {
> + dsc->first_line_bpg_offset = _get_dsc_v1_2_bpg_offset(dsc);
> + }
> +
> + dsc->rc_edge_factor = 6;
> + dsc->rc_tgt_offset_high = 3;
> + dsc->rc_tgt_offset_low = 3;
> + dsc->simple_422 = 0;
> + dsc->convert_rgb = !(dsc->native_422 | dsc->native_420);
> + dsc->vbr_enable = 0;
> +
> + bpp = DSC_BPP(*dsc);
> + bpc = dsc->bits_per_component;
> +
> + ratio_idx = _get_rc_table_index(dsc, scr_ver);
> + if ((ratio_idx < 0) || (ratio_idx >= DSC_RATIO_TYPE_MAX))
> + return -EINVAL;
> +
> +
> + for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++)
> + dsc->rc_buf_thresh[i] = dpu_dsc_rc_buf_thresh[i];
> +
> + for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
> + dsc->rc_range_params[i].range_min_qp =
> + dpu_dsc_rc_range_min_qp[ratio_idx][i];
> + dsc->rc_range_params[i].range_max_qp =
> + dpu_dsc_rc_range_max_qp[ratio_idx][i];
> + dsc->rc_range_params[i].range_bpg_offset =
> + dpu_dsc_rc_range_bpg[ratio_idx][i];
> + }
> +
> + rc_param_lut = &dpu_dsc_rc_init_param_lut[ratio_idx];
> + dsc->rc_quant_incr_limit0 = rc_param_lut->rc_quant_incr_limit0;
> + dsc->rc_quant_incr_limit1 = rc_param_lut->rc_quant_incr_limit1;
> + dsc->initial_offset = rc_param_lut->initial_fullness_offset;
> + dsc->initial_xmit_delay = rc_param_lut->initial_xmit_delay;
> + dsc->second_line_bpg_offset = rc_param_lut->second_line_bpg_offset;
> + dsc->second_line_offset_adj = rc_param_lut->second_line_offset_adj;
> + dsc->flatness_min_qp = rc_param_lut->flatness_min_qp;
> + dsc->flatness_max_qp = rc_param_lut->flatness_max_qp;
> +
> + slice_width_mod = dsc->slice_width;
> + if (dsc->native_422 || dsc->native_420) {
> + slice_width_mod = dsc->slice_width / 2;
> + bpp = bpp * 2;
> + }
> +
> + dsc->line_buf_depth = bpc + 1;
> + dsc->mux_word_size = bpc > 10 ? DSC_MUX_WORD_SIZE_12_BPC : DSC_MUX_WORD_SIZE_8_10_BPC;
> +
> + if ((dsc->dsc_version_minor == 0x2) && (dsc->native_420))
> + dsc->nsl_bpg_offset = (2048 * (DIV_ROUND_UP(dsc->second_line_bpg_offset,
> + (dsc->slice_height - 1))));
> +
> + groups_per_line = DIV_ROUND_UP(slice_width_mod, 3);
> +
> + dsc->slice_chunk_size = slice_width_mod * bpp / 8;
> + if ((slice_width_mod * bpp) % 8)
> + dsc->slice_chunk_size++;
> +
> + /* rbs-min */
> + min_rate_buffer_size = dsc->rc_model_size - dsc->initial_offset +
> + dsc->initial_xmit_delay * bpp +
> + groups_per_line * dsc->first_line_bpg_offset;
> +
> + hrd_delay = DIV_ROUND_UP(min_rate_buffer_size, bpp);
> +
> + dsc->initial_dec_delay = hrd_delay - dsc->initial_xmit_delay;
> +
> + dsc->initial_scale_value = 8 * dsc->rc_model_size /
> + (dsc->rc_model_size - dsc->initial_offset);
> +
> + slice_bits = 8 * dsc->slice_chunk_size * dsc->slice_height;
> +
> + groups_total = groups_per_line * dsc->slice_height;
> +
> + data = dsc->first_line_bpg_offset * 2048;
> +
> + dsc->nfl_bpg_offset = DIV_ROUND_UP(data, (dsc->slice_height - 1));
> +
> + if (dsc->native_422)
> + pre_num_extra_mux_bits = 4 * dsc->mux_word_size + (4 * bpc + 4) + (3 * 4 * bpc) - 2;
> + else if (dsc->native_420)
> + pre_num_extra_mux_bits = 3 * dsc->mux_word_size + (4 * bpc + 4) + (2 * 4 * bpc) - 2;
> + else
> + pre_num_extra_mux_bits = 3 * (dsc->mux_word_size + (4 * bpc + 4) - 2);
> +
> + num_extra_mux_bits = pre_num_extra_mux_bits - (dsc->mux_word_size -
> + ((slice_bits - pre_num_extra_mux_bits) % dsc->mux_word_size));
> +
> + data = 2048 * (dsc->rc_model_size - dsc->initial_offset
> + + num_extra_mux_bits);
> + dsc->slice_bpg_offset = DIV_ROUND_UP(data, groups_total);
> +
> + data = dsc->initial_xmit_delay * bpp;
> + final_value = dsc->rc_model_size - data + num_extra_mux_bits;
> +
> + final_scale = 8 * dsc->rc_model_size /
> + (dsc->rc_model_size - final_value);
> +
> + dsc->final_offset = final_value;
> +
> + data = (final_scale - 9) * (dsc->nfl_bpg_offset +
> + dsc->slice_bpg_offset);
> + dsc->scale_increment_interval = (2048 * dsc->final_offset) / data;
> +
> + dsc->scale_decrement_interval = groups_per_line /
> + (dsc->initial_scale_value - 8);
> +
> + return 0;
> +}
> +
> +bool dpu_dsc_ich_reset_override_needed(bool pu_en,
> + struct msm_display_dsc_info *dsc_info)
> +{
> + /*
> + * As per the DSC spec, ICH_RESET can be either end of the slice line
> + * or at the end of the slice. HW internally generates ich_reset at
> + * end of the slice line if DSC_MERGE is used or encoder has two
> + * soft slices. However, if encoder has only 1 soft slice and DSC_MERGE
> + * is not used then it will generate ich_reset at the end of slice.
> + *
> + * Now as per the spec, during one PPS session, position where
> + * ich_reset is generated should not change. Now if full-screen frame
> + * has more than 1 soft slice then HW will automatically generate
> + * ich_reset at the end of slice_line. But for the same panel, if
> + * partial frame is enabled and only 1 encoder is used with 1 slice,
> + * then HW will generate ich_reset at end of the slice. This is a
> + * mismatch. Prevent this by overriding HW's decision.
> + */
> + return pu_en && dsc_info && (dsc_info->drm_dsc.slice_count > 1) &&
> + (dsc_info->drm_dsc.slice_width == dsc_info->drm_dsc.pic_width);
> +}
> +
> +int dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc_info,
> + int enc_ip_width, int dsc_cmn_mode)
> +{
> + int max_ssm_delay, max_se_size, max_muxword_size;
> + int compress_bpp_group, obuf_latency, input_ssm_out_latency;
> + int base_hs_latency, chunk_bits, ob_data_width;
> + int output_rate_extra_budget_bits, multi_hs_extra_budget_bits;
> + int multi_hs_extra_latency, mux_word_size;
> + int ob_data_width_4comps, ob_data_width_3comps;
> + int output_rate_ratio_complement, container_slice_width;
> + int rtl_num_components, multi_hs_c, multi_hs_d;
> +
> + int bpc = dsc_info->drm_dsc.bits_per_component;
> + int bpp = DSC_BPP(dsc_info->drm_dsc);
> + bool native_422 = dsc_info->drm_dsc.native_422;
> + bool native_420 = dsc_info->drm_dsc.native_420;
> +
> + /* Hardent core config */
> + int multiplex_mode_enable = 0, split_panel_enable = 0;
> + int rtl_max_bpc = 10, rtl_output_data_width = 64;
> + int pipeline_latency = 28;
> +
> + if (dsc_cmn_mode & DSC_MODE_MULTIPLEX)
> + multiplex_mode_enable = 1;
> + if (dsc_cmn_mode & DSC_MODE_SPLIT_PANEL)
> + split_panel_enable = 1;
> + container_slice_width = (native_422 ?
> + dsc_info->drm_dsc.slice_width / 2 : dsc_info->drm_dsc.slice_width);
> + max_muxword_size = (rtl_max_bpc >= 12) ? 64 : 48;
> + max_se_size = 4 * (rtl_max_bpc + 1);
> + max_ssm_delay = max_se_size + max_muxword_size - 1;
> + mux_word_size = (bpc >= 12) ? 64 : 48;
> + compress_bpp_group = native_422 ? (2 * bpp) : bpp;
> + input_ssm_out_latency = pipeline_latency + 3 * (max_ssm_delay + 2)
> + * dsc_info->num_active_ss_per_enc;
> + rtl_num_components = (native_420 || native_422) ? 4 : 3;
> + ob_data_width_4comps = (rtl_output_data_width >= (2 *
> + max_muxword_size)) ?
> + rtl_output_data_width :
> + (2 * rtl_output_data_width);
> + ob_data_width_3comps = (rtl_output_data_width >= max_muxword_size) ?
> + rtl_output_data_width : 2 * rtl_output_data_width;
> + ob_data_width = (rtl_num_components == 4) ?
> + ob_data_width_4comps : ob_data_width_3comps;
> + obuf_latency = DIV_ROUND_UP((9 * ob_data_width + mux_word_size),
> + compress_bpp_group) + 1;
> + base_hs_latency = dsc_info->drm_dsc.initial_xmit_delay +
> + input_ssm_out_latency + obuf_latency;
> + chunk_bits = 8 * dsc_info->drm_dsc.slice_chunk_size;
> + output_rate_ratio_complement = ob_data_width - compress_bpp_group;
> + output_rate_extra_budget_bits =
> + (output_rate_ratio_complement * chunk_bits) >>
> + ((ob_data_width == 128) ? 7 : 6);
> + multi_hs_c = split_panel_enable * multiplex_mode_enable;
> + multi_hs_d = (dsc_info->num_active_ss_per_enc > 1) * (ob_data_width > compress_bpp_group);
> + multi_hs_extra_budget_bits = multi_hs_c ?
> + chunk_bits : (multi_hs_d ? chunk_bits :
> + output_rate_extra_budget_bits);
> + multi_hs_extra_latency = DIV_ROUND_UP(multi_hs_extra_budget_bits,
> + compress_bpp_group);
> + dsc_info->initial_lines = DIV_ROUND_UP((base_hs_latency +
> + multi_hs_extra_latency),
> + container_slice_width);
> +
> + return 0;
> +}
> +
> +int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
> + int intf_width)
> +{
> + int mod_offset;
> + int slice_per_pkt, slice_per_intf;
> + int bytes_in_slice, total_bytes_per_intf;
> + u16 bpp;
> + u32 bytes_in_dsc_pair;
> + u32 total_bytes_in_dsc_pair;
> +
> + if (!dsc_info || !dsc_info->drm_dsc.slice_width ||
> + !dsc_info->drm_dsc.slice_height ||
> + intf_width < dsc_info->drm_dsc.slice_width) {
> + DPU_ERROR("invalid input, intf_width=%d slice_width=%d\n",
> + intf_width, dsc_info ? dsc_info->drm_dsc.slice_width :
> + -1);
> + return -EINVAL;
> + }
> +
> + mod_offset = dsc_info->drm_dsc.slice_width % 3;
> +
> +
> + switch (mod_offset) {
> + case 0:
> + dsc_info->slice_last_group_size = 2;
> + break;
> + case 1:
> + dsc_info->slice_last_group_size = 0;
> + break;
> + case 2:
> + dsc_info->slice_last_group_size = 1;
> + break;
> + default:
> + break;
> + }
> +
> + dsc_info->det_thresh_flatness =
> + 2 << (dsc_info->drm_dsc.bits_per_component - 8);
> +
> + slice_per_pkt = dsc_info->slice_per_pkt;
> + slice_per_intf = DIV_ROUND_UP(intf_width,
> + dsc_info->drm_dsc.slice_width);
> +
> +
> + /*
> + * If slice_per_pkt is greater than slice_per_intf then default to 1.
> + * This can happen during partial update.
> + */
> + if (slice_per_pkt > slice_per_intf)
> + slice_per_pkt = 1;
> +
> + bpp = DSC_BPP(dsc_info->drm_dsc);
> + bytes_in_slice = DIV_ROUND_UP(dsc_info->drm_dsc.slice_width * bpp, 8);
> + total_bytes_per_intf = bytes_in_slice * slice_per_intf;
> +
> +
> + dsc_info->eol_byte_num = total_bytes_per_intf % 3;
> + dsc_info->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3);
> + dsc_info->bytes_in_slice = bytes_in_slice;
> + dsc_info->bytes_per_pkt = bytes_in_slice * slice_per_pkt;
> + dsc_info->pkt_per_line = slice_per_intf / slice_per_pkt;
> +
> +
> + bytes_in_dsc_pair = DIV_ROUND_UP(bytes_in_slice * 2, 3);
> + if (bytes_in_dsc_pair % 8) {
> + dsc_info->dsc_4hsmerge_padding = 8 - (bytes_in_dsc_pair % 8);
> + total_bytes_in_dsc_pair = bytes_in_dsc_pair +
> + dsc_info->dsc_4hsmerge_padding;
> + if (total_bytes_in_dsc_pair % 16)
> + dsc_info->dsc_4hsmerge_alignment = 16 -
> + (total_bytes_in_dsc_pair % 16);
> + }
> +
> + return 0;
> +}
> +
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> new file mode 100644
> index 00000000..9f26455
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2020 - 2023 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#ifndef __DPU_DSC_HELPER_H__
> +#define __DPU_DSC_HELPER_H__
> +
> +#include "msm_drv.h"
> +
> +#define DSC_1_1_PPS_PARAMETER_SET_ELEMENTS 88

Where is this used?

> +
> +int dpu_dsc_populate_dsc_config(struct drm_dsc_config *dsc, int scr_ver);
> +
> +int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
> + int intf_width);
> +
> +bool dpu_dsc_ich_reset_override_needed(bool pu_en, struct msm_display_dsc_info *dsc);
> +
> +int dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc,
> + int enc_ip_width, int dsc_cmn_mode);
> +
> +#endif /* __DPU_DSC_HELPER_H__ */
> +
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index f155803..cf4eb8d 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -57,6 +57,10 @@ struct msm_disp_state;
> #define MAX_CRTCS 8
> #define MAX_BRIDGES 8
>
> +#define MSM_CHROMA_444 0x0
> +#define MSM_CHROMA_422 0x1
> +#define MSM_CHROMA_420 0x2

enum?

> +
> #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
>
> enum msm_dp_controller {

--
With best wishes
Dmitry


2023-01-23 22:07:24

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 09/14] drm/msm/dsi: export struct msm_compression_info to dpu encoder

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> struct msm_compression_info is used to support several different
> compression mechanisms. It also contains customized info required
> to configure DSC encoder engine. This patch also make changes DSI
> module to have DSI exports struct msm_compreion_info to dpu encoder
> instead of struct drm_dsc_config.

Let's see how patch 07 evolves first. But generally I don't like the
idea of passing around another wrapper structure.

>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 7 +++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 ++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 ++++++++--
> drivers/gpu/drm/msm/dsi/dsi.c | 3 ++-
> drivers/gpu/drm/msm/dsi/dsi.h | 3 ++-
> drivers/gpu/drm/msm/dsi/dsi_host.c | 14 ++++++++++++--
> drivers/gpu/drm/msm/msm_drv.h | 4 ++--
> 7 files changed, 33 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 758261e..7f4a439 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -2,7 +2,7 @@
> /*
> * Copyright (C) 2013 Red Hat
> * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> *
> * Author: Rob Clark <[email protected]>
> */
> @@ -210,6 +210,7 @@ struct dpu_encoder_virt {
>
> /* DSC configuration */
> struct drm_dsc_config *dsc;
> + struct msm_compression_info *comp_info;
> };
>
> #define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base)
> @@ -2275,7 +2276,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
> dpu_enc->idle_pc_supported =
> dpu_kms->catalog->caps->has_idle_pc;
>
> - dpu_enc->dsc = disp_info->dsc;
> + dpu_enc->comp_info = disp_info->comp_info;
> + if (dpu_enc->comp_info)
> + dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;
>
> mutex_lock(&dpu_enc->enc_lock);
> for (i = 0; i < disp_info->num_of_h_tiles && !ret; i++) {
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> index 9e7236e..bd2da5e 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> * Copyright (C) 2013 Red Hat
> * Author: Rob Clark <[email protected]>
> @@ -36,7 +36,7 @@ struct msm_display_info {
> uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];
> bool is_cmd_mode;
> bool is_te_using_watchdog_timer;
> - struct drm_dsc_config *dsc;
> + struct msm_compression_info *comp_info;
> };
>
> /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index d612419..70a74ed 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -2,7 +2,7 @@
> /*
> * Copyright (C) 2013 Red Hat
> * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> *
> * Author: Rob Clark <[email protected]>
> */
> @@ -570,7 +570,7 @@ static int _dpu_kms_initialize_dsi(struct drm_device *dev,
> info.h_tile_instance[info.num_of_h_tiles++] = i;
> info.is_cmd_mode = msm_dsi_is_cmd_mode(priv->dsi[i]);
>
> - info.dsc = msm_dsi_get_dsc_config(priv->dsi[i]);
> + info.comp_info = msm_dsi_get_dsc_config(priv->dsi[i]);
>
> if (msm_dsi_is_bonded_dsi(priv->dsi[i]) && priv->dsi[other]) {
> rc = msm_dsi_modeset_init(priv->dsi[other], dev, encoder);
> @@ -622,6 +622,8 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
> info.num_of_h_tiles = 1;
> info.h_tile_instance[0] = i;
> info.intf_type = encoder->encoder_type;
> + info.is_cmd_mode = 0; /* dp always video mode */
> + info.comp_info = NULL;
> rc = dpu_encoder_setup(dev, encoder, &info);
> if (rc) {
> DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
> @@ -892,6 +894,10 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
>
> pm_runtime_get_sync(&dpu_kms->pdev->dev);
>
> + for (i = 0; i < cat->dsc_count; i++)
> + msm_disp_snapshot_add_block(disp_state, cat->dsc[i].len,
> + dpu_kms->mmio + cat->dsc[i].base, "dsc_%d", i);
> +

unrelated, isn't it?

> /* dump CTL sub-blocks HW regs info */
> for (i = 0; i < cat->ctl_count; i++)
> msm_disp_snapshot_add_block(disp_state, cat->ctl[i].len,
> diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
> index 31fdee2..52b7e33 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi.c
> +++ b/drivers/gpu/drm/msm/dsi/dsi.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include "dsi.h"
> @@ -13,7 +14,7 @@ bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi)
> return !(host_flags & MIPI_DSI_MODE_VIDEO);
> }
>
> -struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> +struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> {
> return msm_dsi_host_get_dsc_config(msm_dsi->host);
> }
> diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
> index bd3763a..79ada54 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi.h
> +++ b/drivers/gpu/drm/msm/dsi/dsi.h
> @@ -1,6 +1,7 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #ifndef __DSI_CONNECTOR_H__
> @@ -133,7 +134,7 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
> int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
> void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host);
> void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host);
> -struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
> +struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
>
> /* dsi phy */
> struct msm_dsi_phy;
> diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
> index 18fa30e..6188f4b 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi_host.c
> +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include <linux/clk.h>
> @@ -163,6 +164,7 @@ struct msm_dsi_host {
>
> struct drm_display_mode *mode;
> struct drm_dsc_config *dsc;
> + struct msm_compression_info comp_info;
>
> /* connected device info */
> unsigned int channel;
> @@ -2600,9 +2602,17 @@ void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host)
> DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER);
> }
>
> -struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
> +struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
> {
> struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
> + struct msm_compression_info *comp_info = NULL;
>
> - return msm_host->dsc;
> + if (msm_host->dsc) {
> + comp_info = &msm_host->comp_info;
> + comp_info->msm_dsc_info.drm_dsc = *msm_host->dsc;
> + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC;
> + comp_info->enabled = true;
> + }
> +
> + return comp_info;
> }
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 6a46ed7..eab0901 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -430,7 +430,7 @@ void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi
> bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi);
> bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi);
> bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi);
> -struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
> +struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
> #else
> static inline void __init msm_dsi_register(void)
> {
> @@ -460,7 +460,7 @@ static inline bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi)
> return false;
> }
>
> -static inline struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> +static inline struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> {
> return NULL;
> }

--
With best wishes
Dmitry


2023-01-23 22:09:51

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 07/14] drm/msm/dp: add dsc helper functions

This has nothing to do with /dp, make it /dpu


On 2023-01-23 10:24:27, Kuogee Hsieh wrote:
> Add DSC related supporting functions to calculate DSC related parameters.
> In addition, DSC hardware encoder customized configuration parameters are
> also included. Algorithms used to perform calculation are derived from
> system engineer spreadsheet.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/Makefile | 1 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++++++++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 ++
> drivers/gpu/drm/msm/msm_drv.h | 4 +
> 4 files changed, 567 insertions(+)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 7274c412..28cf52b 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> disp/dpu1/dpu_hw_catalog.o \
> disp/dpu1/dpu_hw_ctl.o \
> disp/dpu1/dpu_hw_dsc.o \
> + disp/dpu1/dpu_dsc_helper.o \
> disp/dpu1/dpu_hw_interrupts.o \
> disp/dpu1/dpu_hw_intf.o \
> disp/dpu1/dpu_hw_lm.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> new file mode 100644
> index 00000000..48cef23
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> @@ -0,0 +1,537 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2012-2023 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#include "msm_drv.h"
> +#include "dpu_kms.h"
> +#include "dpu_hw_dsc.h"
> +#include "dpu_dsc_helper.h"
> +
> +
> +#define DPU_DSC_PPS_SIZE 128
> +
> +enum dpu_dsc_ratio_type {
> + DSC_V11_8BPC_8BPP,
> + DSC_V11_10BPC_8BPP,
> + DSC_V11_10BPC_10BPP,
> + DSC_V11_SCR1_8BPC_8BPP,
> + DSC_V11_SCR1_10BPC_8BPP,
> + DSC_V11_SCR1_10BPC_10BPP,
> + DSC_V12_444_8BPC_8BPP = DSC_V11_SCR1_8BPC_8BPP,
> + DSC_V12_444_10BPC_8BPP = DSC_V11_SCR1_10BPC_8BPP,
> + DSC_V12_444_10BPC_10BPP = DSC_V11_SCR1_10BPC_10BPP,
> + DSC_V12_422_8BPC_7BPP,
> + DSC_V12_422_8BPC_8BPP,
> + DSC_V12_422_10BPC_7BPP,
> + DSC_V12_422_10BPC_10BPP,
> + DSC_V12_420_8BPC_6BPP,
> + DSC_V12_420_10BPC_6BPP,
> + DSC_V12_420_10BPC_7_5BPP,
> + DSC_RATIO_TYPE_MAX
> +};
> +
> +
> +static u16 dpu_dsc_rc_buf_thresh[DSC_NUM_BUF_RANGES - 1] = {
> + 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54,
> + 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e
> +};
> +
> +/*
> + * Rate control - Min QP values for each ratio type in dpu_dsc_ratio_type
> + */
> +static char dpu_dsc_rc_range_min_qp[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
> + /* DSC v1.1 */
> + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 13},
> + {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 17},
> + {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
> + /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
> + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 9, 12},
> + {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 13, 16},
> + {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
> + /* DSC v1.2 YUV422 */
> + {0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 11},
> + {0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 10},
> + {0, 4, 5, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 15},
> + {0, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 11, 12},
> + /* DSC v1.2 YUV420 */
> + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 10},
> + {0, 2, 3, 4, 6, 7, 7, 7, 7, 7, 9, 9, 9, 11, 14},
> + {0, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 11, 12},
> +};
> +
> +/*
> + * Rate control - Max QP values for each ratio type in dpu_dsc_ratio_type
> + */
> +static char dpu_dsc_rc_range_max_qp[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
> + /* DSC v1.1 */
> + {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 11, 12, 13, 13, 15},
> + {4, 8, 9, 10, 11, 11, 11, 12, 13, 14, 15, 16, 17, 17, 19},
> + {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
> + /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
> + {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 10, 11, 11, 12, 13},
> + {8, 8, 9, 10, 11, 11, 11, 12, 13, 14, 14, 15, 15, 16, 17},
> + {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
> + /* DSC v1.2 YUV422 */
> + {3, 4, 5, 6, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12},
> + {2, 4, 5, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 10, 11},
> + {7, 8, 9, 10, 11, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16},
> + {2, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13},
> + /* DSC v1.2 YUV420 */
> + {2, 4, 5, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 10, 12},
> + {2, 5, 7, 8, 9, 10, 11, 12, 12, 13, 13, 13, 13, 14, 15},
> + {2, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13},
> + };
> +
> +/*
> + * Rate control - bpg offset values for each ratio type in dpu_dsc_ratio_type
> + */
> +static char dpu_dsc_rc_range_bpg[DSC_RATIO_TYPE_MAX][DSC_NUM_BUF_RANGES] = {
> + /* DSC v1.1 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + /* DSC v1.1 SCR and DSC V1.2 RGB 444 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + /* DSC v1.2 YUV422 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12},
> + {10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12},
> + /* DSC v1.2 YUV420 */
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12},
> + {10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12},
> +};
> +
> +static struct dpu_dsc_rc_init_params_lut {
> + u32 rc_quant_incr_limit0;
> + u32 rc_quant_incr_limit1;
> + u32 initial_fullness_offset;
> + u32 initial_xmit_delay;
> + u32 second_line_bpg_offset;
> + u32 second_line_offset_adj;
> + u32 flatness_min_qp;
> + u32 flatness_max_qp;
> +} dpu_dsc_rc_init_param_lut[] = {
> + /* DSC v1.1 */
> + {11, 11, 6144, 512, 0, 0, 3, 12}, /* DSC_V11_8BPC_8BPP */
> + {15, 15, 6144, 512, 0, 0, 7, 16}, /* DSC_V11_10BPC_8BPP */
> + {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V11_10BPC_10BPP */
> + /* DSC v1.1 SCR and DSC v1.2 RGB 444 */
> + {11, 11, 6144, 512, 0, 0, 3, 12}, /* DSC_V12_444_8BPC_8BPP or DSC_V11_SCR1_8BPC_8BPP */
> + {15, 15, 6144, 512, 0, 0, 7, 16}, /* DSC_V12_444_10BPC_8BPP or DSC_V11_SCR1_10BPC_8BPP */
> + {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V12_444_10BPC_10BPP or DSC_V11_SCR1_10BPC_10BPP */
> + /* DSC v1.2 YUV422 */
> + {11, 11, 5632, 410, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_7BPP */
> + {11, 11, 2048, 341, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_8BPP */
> + {15, 15, 5632, 410, 0, 0, 7, 16}, /* DSC_V12_422_10BPC_7BPP */
> + {15, 15, 2048, 273, 0, 0, 7, 16}, /* DSC_V12_422_10BPC_10BPP */
> + /* DSC v1.2 YUV420 */
> + {11, 11, 5632, 410, 0, 0, 3, 12}, /* DSC_V12_422_8BPC_7BPP */
> + {11, 11, 2048, 341, 12, 512, 3, 12}, /* DSC_V12_420_8BPC_6BPP */
> + {15, 15, 2048, 341, 12, 512, 7, 16}, /* DSC_V12_420_10BPC_6BPP */
> + {15, 15, 2048, 256, 12, 512, 7, 16}, /* DSC_V12_420_10BPC_7_5BPP */
> +};
> +
> +/**
> + * Maps to lookup the dpu_dsc_ratio_type index used in rate control tables
> + */
> +static struct dpu_dsc_table_index_lut {
> + u32 fmt;
> + u32 scr_ver;
> + u32 minor_ver;
> + u32 bpc;
> + u32 bpp;
> + u32 type;
> +} dpu_dsc_index_map[] = {
> + /* DSC 1.1 formats - scr version is considered */
> + {MSM_CHROMA_444, 0, 1, 8, 8, DSC_V11_8BPC_8BPP},
> + {MSM_CHROMA_444, 0, 1, 10, 8, DSC_V11_10BPC_8BPP},
> + {MSM_CHROMA_444, 0, 1, 10, 10, DSC_V11_10BPC_10BPP},
> +
> + {MSM_CHROMA_444, 1, 1, 8, 8, DSC_V11_SCR1_8BPC_8BPP},
> + {MSM_CHROMA_444, 1, 1, 10, 8, DSC_V11_SCR1_10BPC_8BPP},
> + {MSM_CHROMA_444, 1, 1, 10, 10, DSC_V11_SCR1_10BPC_10BPP},
> +
> + /* DSC 1.2 formats - scr version is no-op */
> + {MSM_CHROMA_444, -1, 2, 8, 8, DSC_V12_444_8BPC_8BPP},
> + {MSM_CHROMA_444, -1, 2, 10, 8, DSC_V12_444_10BPC_8BPP},
> + {MSM_CHROMA_444, -1, 2, 10, 10, DSC_V12_444_10BPC_10BPP},
> +
> + {MSM_CHROMA_422, -1, 2, 8, 7, DSC_V12_422_8BPC_7BPP},
> + {MSM_CHROMA_422, -1, 2, 8, 8, DSC_V12_422_8BPC_8BPP},
> + {MSM_CHROMA_422, -1, 2, 10, 7, DSC_V12_422_10BPC_7BPP},
> + {MSM_CHROMA_422, -1, 2, 10, 10, DSC_V12_422_10BPC_10BPP},
> +
> + {MSM_CHROMA_420, -1, 2, 8, 6, DSC_V12_420_8BPC_6BPP},
> + {MSM_CHROMA_420, -1, 2, 10, 6, DSC_V12_420_10BPC_6BPP},
> +};
> +
> +static int _get_rc_table_index(struct drm_dsc_config *dsc, int scr_ver)
> +{
> + u32 bpp, bpc, i, fmt = MSM_CHROMA_444;
> +
> + if (dsc->dsc_version_major != 0x1) {
> + DPU_ERROR("unsupported major version %d\n",
> + dsc->dsc_version_major);
> + return -EINVAL;
> + }
> +
> + bpc = dsc->bits_per_component;
> + bpp = DSC_BPP(*dsc);
> +
> + if (dsc->native_422)
> + fmt = MSM_CHROMA_422;
> + else if (dsc->native_420)
> + fmt = MSM_CHROMA_420;
> +
> +
> + for (i = 0; i < ARRAY_SIZE(dpu_dsc_index_map); i++) {
> + if (dsc->dsc_version_minor == dpu_dsc_index_map[i].minor_ver &&
> + fmt == dpu_dsc_index_map[i].fmt &&
> + bpc == dpu_dsc_index_map[i].bpc &&
> + bpp == dpu_dsc_index_map[i].bpp &&
> + (dsc->dsc_version_minor != 0x1 ||
> + scr_ver == dpu_dsc_index_map[i].scr_ver))
> + return dpu_dsc_index_map[i].type;
> + }
> +
> + DPU_ERROR("unsupported DSC v%d.%dr%d, bpc:%d, bpp:%d, fmt:0x%x\n",
> + dsc->dsc_version_major, dsc->dsc_version_minor,
> + scr_ver, bpc, bpp, fmt);
> + return -EINVAL;
> +}
> +
> +u8 _get_dsc_v1_2_bpg_offset(struct drm_dsc_config *dsc)
> +{
> + u8 bpg_offset = 0;
> + u8 uncompressed_bpg_rate;
> + u8 bpp = DSC_BPP(*dsc);
> +
> + if (dsc->slice_height < 8)
> + bpg_offset = 2 * (dsc->slice_height - 1);
> + else if (dsc->slice_height < 20)
> + bpg_offset = 12;
> + else if (dsc->slice_height <= 30)
> + bpg_offset = 13;
> + else if (dsc->slice_height < 42)
> + bpg_offset = 14;
> + else
> + bpg_offset = 15;
> +
> + if (dsc->native_422)
> + uncompressed_bpg_rate = 3 * bpp * 4;
> + else if (dsc->native_420)
> + uncompressed_bpg_rate = 3 * bpp;
> + else
> + uncompressed_bpg_rate = (3 * bpp + 2) * 3;
> +
> + if (bpg_offset < (uncompressed_bpg_rate - (3 * bpp)))
> + return bpg_offset;
> + else
> + return (uncompressed_bpg_rate - (3 * bpp));
> +}
> +
> +int dpu_dsc_populate_dsc_config(struct drm_dsc_config *dsc, int scr_ver)

We just got rid of this /wrong/ downstream gunk in [1] in favour of the
upstreamed drm_dsc_compute_rc_parameters(), don't add it back.

[1]: https://lore.kernel.org/linux-arm-msm/[email protected]/

> +{
> + int bpp, bpc;
> + int groups_per_line, groups_total;
> + int min_rate_buffer_size;
> + int hrd_delay;
> + int pre_num_extra_mux_bits, num_extra_mux_bits;
> + int slice_bits;
> + int data;
> + int final_value, final_scale;
> + struct dpu_dsc_rc_init_params_lut *rc_param_lut;
> + u32 slice_width_mod;
> + int i, ratio_idx;
> +
> + dsc->rc_model_size = 8192;
> +
> + if ((dsc->dsc_version_major == 0x1) &&
> + (dsc->dsc_version_minor == 0x1)) {
> + if (scr_ver == 0x1)
> + dsc->first_line_bpg_offset = 15;
> + else
> + dsc->first_line_bpg_offset = 12;
> + } else if (dsc->dsc_version_minor == 0x2) {
> + dsc->first_line_bpg_offset = _get_dsc_v1_2_bpg_offset(dsc);
> + }
> +
> + dsc->rc_edge_factor = 6;
> + dsc->rc_tgt_offset_high = 3;
> + dsc->rc_tgt_offset_low = 3;
> + dsc->simple_422 = 0;
> + dsc->convert_rgb = !(dsc->native_422 | dsc->native_420);
> + dsc->vbr_enable = 0;
> +
> + bpp = DSC_BPP(*dsc);
> + bpc = dsc->bits_per_component;
> +
> + ratio_idx = _get_rc_table_index(dsc, scr_ver);
> + if ((ratio_idx < 0) || (ratio_idx >= DSC_RATIO_TYPE_MAX))
> + return -EINVAL;
> +
> +
> + for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++)
> + dsc->rc_buf_thresh[i] = dpu_dsc_rc_buf_thresh[i];
> +
> + for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
> + dsc->rc_range_params[i].range_min_qp =
> + dpu_dsc_rc_range_min_qp[ratio_idx][i];
> + dsc->rc_range_params[i].range_max_qp =
> + dpu_dsc_rc_range_max_qp[ratio_idx][i];
> + dsc->rc_range_params[i].range_bpg_offset =
> + dpu_dsc_rc_range_bpg[ratio_idx][i];
> + }
> +
> + rc_param_lut = &dpu_dsc_rc_init_param_lut[ratio_idx];
> + dsc->rc_quant_incr_limit0 = rc_param_lut->rc_quant_incr_limit0;
> + dsc->rc_quant_incr_limit1 = rc_param_lut->rc_quant_incr_limit1;
> + dsc->initial_offset = rc_param_lut->initial_fullness_offset;
> + dsc->initial_xmit_delay = rc_param_lut->initial_xmit_delay;
> + dsc->second_line_bpg_offset = rc_param_lut->second_line_bpg_offset;
> + dsc->second_line_offset_adj = rc_param_lut->second_line_offset_adj;
> + dsc->flatness_min_qp = rc_param_lut->flatness_min_qp;
> + dsc->flatness_max_qp = rc_param_lut->flatness_max_qp;
> +
> + slice_width_mod = dsc->slice_width;
> + if (dsc->native_422 || dsc->native_420) {
> + slice_width_mod = dsc->slice_width / 2;
> + bpp = bpp * 2;
> + }
> +
> + dsc->line_buf_depth = bpc + 1;
> + dsc->mux_word_size = bpc > 10 ? DSC_MUX_WORD_SIZE_12_BPC : DSC_MUX_WORD_SIZE_8_10_BPC;
> +
> + if ((dsc->dsc_version_minor == 0x2) && (dsc->native_420))
> + dsc->nsl_bpg_offset = (2048 * (DIV_ROUND_UP(dsc->second_line_bpg_offset,
> + (dsc->slice_height - 1))));
> +
> + groups_per_line = DIV_ROUND_UP(slice_width_mod, 3);
> +
> + dsc->slice_chunk_size = slice_width_mod * bpp / 8;
> + if ((slice_width_mod * bpp) % 8)
> + dsc->slice_chunk_size++;

Besides this code being completely superfluous, please familiarize
yourself with prior DSC contributions and review, and don't add (back)
patterns that were rejected or cleaned up.

- Marijn

> +
> + /* rbs-min */
> + min_rate_buffer_size = dsc->rc_model_size - dsc->initial_offset +
> + dsc->initial_xmit_delay * bpp +
> + groups_per_line * dsc->first_line_bpg_offset;
> +
> + hrd_delay = DIV_ROUND_UP(min_rate_buffer_size, bpp);
> +
> + dsc->initial_dec_delay = hrd_delay - dsc->initial_xmit_delay;
> +
> + dsc->initial_scale_value = 8 * dsc->rc_model_size /
> + (dsc->rc_model_size - dsc->initial_offset);
> +
> + slice_bits = 8 * dsc->slice_chunk_size * dsc->slice_height;
> +
> + groups_total = groups_per_line * dsc->slice_height;
> +
> + data = dsc->first_line_bpg_offset * 2048;
> +
> + dsc->nfl_bpg_offset = DIV_ROUND_UP(data, (dsc->slice_height - 1));
> +
> + if (dsc->native_422)
> + pre_num_extra_mux_bits = 4 * dsc->mux_word_size + (4 * bpc + 4) + (3 * 4 * bpc) - 2;
> + else if (dsc->native_420)
> + pre_num_extra_mux_bits = 3 * dsc->mux_word_size + (4 * bpc + 4) + (2 * 4 * bpc) - 2;
> + else
> + pre_num_extra_mux_bits = 3 * (dsc->mux_word_size + (4 * bpc + 4) - 2);
> +
> + num_extra_mux_bits = pre_num_extra_mux_bits - (dsc->mux_word_size -
> + ((slice_bits - pre_num_extra_mux_bits) % dsc->mux_word_size));
> +
> + data = 2048 * (dsc->rc_model_size - dsc->initial_offset
> + + num_extra_mux_bits);
> + dsc->slice_bpg_offset = DIV_ROUND_UP(data, groups_total);
> +
> + data = dsc->initial_xmit_delay * bpp;
> + final_value = dsc->rc_model_size - data + num_extra_mux_bits;
> +
> + final_scale = 8 * dsc->rc_model_size /
> + (dsc->rc_model_size - final_value);
> +
> + dsc->final_offset = final_value;
> +
> + data = (final_scale - 9) * (dsc->nfl_bpg_offset +
> + dsc->slice_bpg_offset);
> + dsc->scale_increment_interval = (2048 * dsc->final_offset) / data;
> +
> + dsc->scale_decrement_interval = groups_per_line /
> + (dsc->initial_scale_value - 8);
> +
> + return 0;
> +}
> +
> +bool dpu_dsc_ich_reset_override_needed(bool pu_en,
> + struct msm_display_dsc_info *dsc_info)
> +{
> + /*
> + * As per the DSC spec, ICH_RESET can be either end of the slice line
> + * or at the end of the slice. HW internally generates ich_reset at
> + * end of the slice line if DSC_MERGE is used or encoder has two
> + * soft slices. However, if encoder has only 1 soft slice and DSC_MERGE
> + * is not used then it will generate ich_reset at the end of slice.
> + *
> + * Now as per the spec, during one PPS session, position where
> + * ich_reset is generated should not change. Now if full-screen frame
> + * has more than 1 soft slice then HW will automatically generate
> + * ich_reset at the end of slice_line. But for the same panel, if
> + * partial frame is enabled and only 1 encoder is used with 1 slice,
> + * then HW will generate ich_reset at end of the slice. This is a
> + * mismatch. Prevent this by overriding HW's decision.
> + */
> + return pu_en && dsc_info && (dsc_info->drm_dsc.slice_count > 1) &&
> + (dsc_info->drm_dsc.slice_width == dsc_info->drm_dsc.pic_width);
> +}
> +
> +int dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc_info,
> + int enc_ip_width, int dsc_cmn_mode)
> +{
> + int max_ssm_delay, max_se_size, max_muxword_size;
> + int compress_bpp_group, obuf_latency, input_ssm_out_latency;
> + int base_hs_latency, chunk_bits, ob_data_width;
> + int output_rate_extra_budget_bits, multi_hs_extra_budget_bits;
> + int multi_hs_extra_latency, mux_word_size;
> + int ob_data_width_4comps, ob_data_width_3comps;
> + int output_rate_ratio_complement, container_slice_width;
> + int rtl_num_components, multi_hs_c, multi_hs_d;
> +
> + int bpc = dsc_info->drm_dsc.bits_per_component;
> + int bpp = DSC_BPP(dsc_info->drm_dsc);
> + bool native_422 = dsc_info->drm_dsc.native_422;
> + bool native_420 = dsc_info->drm_dsc.native_420;
> +
> + /* Hardent core config */
> + int multiplex_mode_enable = 0, split_panel_enable = 0;
> + int rtl_max_bpc = 10, rtl_output_data_width = 64;
> + int pipeline_latency = 28;
> +
> + if (dsc_cmn_mode & DSC_MODE_MULTIPLEX)
> + multiplex_mode_enable = 1;
> + if (dsc_cmn_mode & DSC_MODE_SPLIT_PANEL)
> + split_panel_enable = 1;
> + container_slice_width = (native_422 ?
> + dsc_info->drm_dsc.slice_width / 2 : dsc_info->drm_dsc.slice_width);
> + max_muxword_size = (rtl_max_bpc >= 12) ? 64 : 48;
> + max_se_size = 4 * (rtl_max_bpc + 1);
> + max_ssm_delay = max_se_size + max_muxword_size - 1;
> + mux_word_size = (bpc >= 12) ? 64 : 48;
> + compress_bpp_group = native_422 ? (2 * bpp) : bpp;
> + input_ssm_out_latency = pipeline_latency + 3 * (max_ssm_delay + 2)
> + * dsc_info->num_active_ss_per_enc;
> + rtl_num_components = (native_420 || native_422) ? 4 : 3;
> + ob_data_width_4comps = (rtl_output_data_width >= (2 *
> + max_muxword_size)) ?
> + rtl_output_data_width :
> + (2 * rtl_output_data_width);
> + ob_data_width_3comps = (rtl_output_data_width >= max_muxword_size) ?
> + rtl_output_data_width : 2 * rtl_output_data_width;
> + ob_data_width = (rtl_num_components == 4) ?
> + ob_data_width_4comps : ob_data_width_3comps;
> + obuf_latency = DIV_ROUND_UP((9 * ob_data_width + mux_word_size),
> + compress_bpp_group) + 1;
> + base_hs_latency = dsc_info->drm_dsc.initial_xmit_delay +
> + input_ssm_out_latency + obuf_latency;
> + chunk_bits = 8 * dsc_info->drm_dsc.slice_chunk_size;
> + output_rate_ratio_complement = ob_data_width - compress_bpp_group;
> + output_rate_extra_budget_bits =
> + (output_rate_ratio_complement * chunk_bits) >>
> + ((ob_data_width == 128) ? 7 : 6);
> + multi_hs_c = split_panel_enable * multiplex_mode_enable;
> + multi_hs_d = (dsc_info->num_active_ss_per_enc > 1) * (ob_data_width > compress_bpp_group);
> + multi_hs_extra_budget_bits = multi_hs_c ?
> + chunk_bits : (multi_hs_d ? chunk_bits :
> + output_rate_extra_budget_bits);
> + multi_hs_extra_latency = DIV_ROUND_UP(multi_hs_extra_budget_bits,
> + compress_bpp_group);
> + dsc_info->initial_lines = DIV_ROUND_UP((base_hs_latency +
> + multi_hs_extra_latency),
> + container_slice_width);
> +
> + return 0;
> +}
> +
> +int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
> + int intf_width)
> +{
> + int mod_offset;
> + int slice_per_pkt, slice_per_intf;
> + int bytes_in_slice, total_bytes_per_intf;
> + u16 bpp;
> + u32 bytes_in_dsc_pair;
> + u32 total_bytes_in_dsc_pair;
> +
> + if (!dsc_info || !dsc_info->drm_dsc.slice_width ||
> + !dsc_info->drm_dsc.slice_height ||
> + intf_width < dsc_info->drm_dsc.slice_width) {
> + DPU_ERROR("invalid input, intf_width=%d slice_width=%d\n",
> + intf_width, dsc_info ? dsc_info->drm_dsc.slice_width :
> + -1);
> + return -EINVAL;
> + }
> +
> + mod_offset = dsc_info->drm_dsc.slice_width % 3;
> +
> +
> + switch (mod_offset) {
> + case 0:
> + dsc_info->slice_last_group_size = 2;
> + break;
> + case 1:
> + dsc_info->slice_last_group_size = 0;
> + break;
> + case 2:
> + dsc_info->slice_last_group_size = 1;
> + break;
> + default:
> + break;
> + }
> +
> + dsc_info->det_thresh_flatness =
> + 2 << (dsc_info->drm_dsc.bits_per_component - 8);
> +
> + slice_per_pkt = dsc_info->slice_per_pkt;
> + slice_per_intf = DIV_ROUND_UP(intf_width,
> + dsc_info->drm_dsc.slice_width);
> +
> +
> + /*
> + * If slice_per_pkt is greater than slice_per_intf then default to 1.
> + * This can happen during partial update.
> + */
> + if (slice_per_pkt > slice_per_intf)
> + slice_per_pkt = 1;
> +
> + bpp = DSC_BPP(dsc_info->drm_dsc);
> + bytes_in_slice = DIV_ROUND_UP(dsc_info->drm_dsc.slice_width * bpp, 8);
> + total_bytes_per_intf = bytes_in_slice * slice_per_intf;
> +
> +
> + dsc_info->eol_byte_num = total_bytes_per_intf % 3;
> + dsc_info->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3);
> + dsc_info->bytes_in_slice = bytes_in_slice;
> + dsc_info->bytes_per_pkt = bytes_in_slice * slice_per_pkt;
> + dsc_info->pkt_per_line = slice_per_intf / slice_per_pkt;
> +
> +
> + bytes_in_dsc_pair = DIV_ROUND_UP(bytes_in_slice * 2, 3);
> + if (bytes_in_dsc_pair % 8) {
> + dsc_info->dsc_4hsmerge_padding = 8 - (bytes_in_dsc_pair % 8);
> + total_bytes_in_dsc_pair = bytes_in_dsc_pair +
> + dsc_info->dsc_4hsmerge_padding;
> + if (total_bytes_in_dsc_pair % 16)
> + dsc_info->dsc_4hsmerge_alignment = 16 -
> + (total_bytes_in_dsc_pair % 16);
> + }
> +
> + return 0;
> +}
> +
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> new file mode 100644
> index 00000000..9f26455
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2020 - 2023 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#ifndef __DPU_DSC_HELPER_H__
> +#define __DPU_DSC_HELPER_H__
> +
> +#include "msm_drv.h"
> +
> +#define DSC_1_1_PPS_PARAMETER_SET_ELEMENTS 88
> +
> +int dpu_dsc_populate_dsc_config(struct drm_dsc_config *dsc, int scr_ver);
> +
> +int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
> + int intf_width);
> +
> +bool dpu_dsc_ich_reset_override_needed(bool pu_en, struct msm_display_dsc_info *dsc);
> +
> +int dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc,
> + int enc_ip_width, int dsc_cmn_mode);
> +
> +#endif /* __DPU_DSC_HELPER_H__ */
> +
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index f155803..cf4eb8d 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -57,6 +57,10 @@ struct msm_disp_state;
> #define MAX_CRTCS 8
> #define MAX_BRIDGES 8
>
> +#define MSM_CHROMA_444 0x0
> +#define MSM_CHROMA_422 0x1
> +#define MSM_CHROMA_420 0x2
> +
> #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
>
> enum msm_dp_controller {
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

2023-01-23 22:30:22

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 08/14] drm/msm/dp: add dsc supporting functions to DP controller

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> This patch provides DSC required functions at DP controller to
> complete DSC feature. those functions include enable fec, configure
> dsc, configure dto, transmit pps and finally flush hardware registers.

Too many items for a single patch in my opinion.

>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/dp/dp_catalog.c | 139 ++++++++-
> drivers/gpu/drm/msm/dp/dp_catalog.h | 93 ++++++
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 132 ++++++++-
> drivers/gpu/drm/msm/dp/dp_display.c | 61 +++-
> drivers/gpu/drm/msm/dp/dp_panel.c | 570 +++++++++++++++++++++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_panel.h | 4 +
> drivers/gpu/drm/msm/dp/dp_reg.h | 40 ++-
> drivers/gpu/drm/msm/msm_drv.h | 16 +
> 8 files changed, 1033 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
> index 7ac37d8..20a86e7 100644
> --- a/drivers/gpu/drm/msm/dp/dp_catalog.c
> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
> @@ -48,6 +48,11 @@
> #define DP_INTERRUPT_STATUS2_MASK \
> (DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_MASK_SHIFT)
>
> +enum dp_flush_bit {
> + DP_PPS_FLUSH,
> + DP_DHDR_FLUSH,
> +};
> +
> struct dp_catalog_private {
> struct device *dev;
> struct drm_device *drm_dev;
> @@ -277,6 +282,30 @@ static void dump_regs(void __iomem *base, int len)
> }
> }
>
> +void dp_catalog_fec_config(struct dp_catalog *dp_catalog, bool enable)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> + u32 reg;
> +
> + reg = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
> +
> + /*
> + * fec_en = BIT(12)
> + * fec_seq_mode = BIT(22)
> + * sde_flush = BIT(23) | BIT(24)
> + * fb_boundary_sel = BIT(25)

This should go to #define's instead.

> + */
> + if (enable)
> + reg |= BIT(12) | BIT(22) | BIT(23) | BIT(24) | BIT(25);
> + else
> + reg &= ~BIT(12);
> +
> + dp_write_link(catalog, REG_DP_MAINLINK_CTRL, reg);
> + /* make sure mainlink configuration is updated with fec sequence */
> + wmb();
> +}
> +
> void dp_catalog_dump_regs(struct dp_catalog *dp_catalog)
> {
> struct dp_catalog_private *catalog = container_of(dp_catalog,
> @@ -344,6 +373,54 @@ void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 cfg)
> dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL, cfg);
> }
>
> +void dp_catalog_config_dsc_dto(struct dp_catalog *dp_catalog)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> + struct dp_dsc_cfg_data *dsc_data = &dp_catalog->dsc_data;
> + u32 reg;
> +
> + dp_write_p0(catalog, MMSS_DP_DSC_DTO_COUNT, dsc_data->dto_count);
> +
> + reg = dp_read_p0(catalog, MMSS_DP_DSC_DTO);
> +
> + if (dsc_data->dto_en) {
> + reg |= BIT(0);
> + reg |= BIT(3);
> + reg |= (dsc_data->dto_n << 8);
> + reg |= (dsc_data->dto_d << 16);
> + }
> +
> + dp_write_p0(catalog, MMSS_DP_DSC_DTO, reg);
> +
> + reg = 0;
> + if (dsc_data->dsc_en) {
> + reg = BIT(0);
> + reg |= (dsc_data->eol_byte_num << 3);
> + reg |= (dsc_data->slice_per_pkt << 5);
> + reg |= (dsc_data->bytes_per_pkt << 16);
> + reg |= (dsc_data->be_in_lane << 10);
> + }
> + dp_write_link(catalog, DP_COMPRESSION_MODE_CTRL, reg);
> +
> + drm_dbg_dp(catalog->drm_dev, "compression:0x%x\n", reg);
> +}
> +
> +void dp_catalog_override_ack_dto(struct dp_catalog *dp_catalog, bool not_ack)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> + u32 dsc_dto;
> +
> + dsc_dto = dp_read_p0(catalog, MMSS_DP_DSC_DTO);
> + if (not_ack)
> + dsc_dto &= ~BIT(1);
> + else
> + dsc_dto = BIT(1);
> +
> + dp_write_p0(catalog, MMSS_DP_DSC_DTO, dsc_dto);

The indentation looks weird. It might be my email client. Or may be not.

> +}
> +
> void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog)
> {
> struct dp_catalog_private *catalog = container_of(dp_catalog,
> @@ -429,6 +506,15 @@ void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
> }
> }
>
> +static void dp_catalog_sdp_update( struct dp_catalog *dp_catalog)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> +
> + dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x01);
> + dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x00);
> +}
> +
> void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog,
> u32 colorimetry_cfg,
> u32 test_bits_depth)
> @@ -504,7 +590,6 @@ void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog,
> drm_dbg_dp(catalog->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
> dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
> dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
> - dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
> }
>
> int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog,
> @@ -918,6 +1003,58 @@ void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog)
> dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN, 0x0);
> }
>
> +void dp_catalog_dsc_commit_pps(struct dp_catalog *dp_catalog)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> + struct dp_dsc_cfg_data *dsc_data = &dp_catalog->dsc_data;
> + int i;
> +
> + dp_write_link(catalog, DP_PPS_HB_0_3, 0x7F1000);
> + dp_write_link(catalog, DP_PPS_PB_0_3, 0xA22300);
> +
> + for (i = 0; i < dsc_data->parity_word_len; i++)
> + dp_write_link(catalog, DP_PPS_PB_4_7 + (i << 2),
> + dsc_data->parity_word[i]);
> +
> + for (i = 0; i < dsc_data->pps_word_len; i++)
> + dp_write_link(catalog, DP_PPS_PPS_0_3 + (i << 2),
> + dsc_data->pps_word[i]);
> +}
> +
> +static void dp_catalog_dp_flush(struct dp_catalog *dp_catalog,
> + enum dp_flush_bit flush_bit)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> + u32 dp_flush;
> + struct dp_dsc_cfg_data *dsc_data = &dp_catalog->dsc_data;
> +
> + dp_flush = dp_read_link(catalog, MMSS_DP_FLUSH);
> +
> + dsc_data->continuous_pps = true;

This doesn't look like a part of the flush. Especially since you check
this boolean on the next line.

> +
> + if ((flush_bit == DP_PPS_FLUSH) && dsc_data->continuous_pps)
> + dp_flush &= ~BIT(2);
> +
> + dp_flush |= BIT(flush_bit);

I'd prefer to see BIT(n) as a defines rather than doing a BIT(enum_value).

> + dp_write_link(catalog, MMSS_DP_FLUSH, dp_flush);
> +
> + /*
> + * TODO: no dp_config_sdp_update() required?
> + */

Is it going to be resolved?

> + dp_catalog_sdp_update(dp_catalog);
> +}
> +
> +void dp_catalog_pps_flush(struct dp_catalog *dp_catalog)
> +{
> + struct dp_catalog_private *catalog = container_of(dp_catalog,
> + struct dp_catalog_private, dp_catalog);
> +
> + dp_catalog_dp_flush(dp_catalog, DP_PPS_FLUSH);
> + drm_dbg_dp(catalog->drm_dev, "pps flush\n");
> +}
> +
> struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
> {
> struct dp_catalog_private *catalog;
> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
> index 990c162..537fb8d 100644
> --- a/drivers/gpu/drm/msm/dp/dp_catalog.h
> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
> @@ -62,6 +62,27 @@ enum dp_catalog_audio_header_type {
> DP_AUDIO_SDP_HEADER_MAX,
> };
>
> +struct dp_dsc_cfg_data {
> + bool dsc_en;
> + bool continuous_pps;
> + char pps[128];
> + u32 pps_len;
> + u32 pps_word[32];
> + u32 pps_word_len;
> + u8 parity[32];
> + u8 parity_len;
> + u32 parity_word[8];
> + u32 parity_word_len;
> + u32 slice_per_pkt;
> + u32 bytes_per_pkt;
> + u32 eol_byte_num;
> + u32 be_in_lane;
> + u32 dto_en;
> + u32 dto_n;
> + u32 dto_d;
> + u32 dto_count;
> +};
> +
> struct dp_catalog {
> u32 aux_data;
> u32 total;
> @@ -72,8 +93,74 @@ struct dp_catalog {
> enum dp_catalog_audio_header_type sdp_header;
> u32 audio_data;
> bool wide_bus_en;
> + struct dp_dsc_cfg_data dsc_data;
> };
>
> +static inline u8 dp_ecc_get_g0_value(u8 data)
> +{
> + u8 c[4];
> + u8 g[4];
> + u8 ret_data = 0;
> + u8 i;
> +
> + for (i = 0; i < 4; i++)
> + c[i] = (data >> i) & 0x01;
> +
> + g[0] = c[3];
> + g[1] = c[0] ^ c[3];
> + g[2] = c[1];
> + g[3] = c[2];
> +
> + for (i = 0; i < 4; i++)
> + ret_data = ((g[i] & 0x01) << i) | ret_data;
> +
> + return ret_data;
> +}
> +
> +static inline u8 dp_ecc_get_g1_value(u8 data)
> +{
> + u8 c[4];
> + u8 g[4];
> + u8 ret_data = 0;
> + u8 i;
> +
> + for (i = 0; i < 4; i++)
> + c[i] = (data >> i) & 0x01;
> +
> + g[0] = c[0] ^ c[3];
> + g[1] = c[0] ^ c[1] ^ c[3];
> + g[2] = c[1] ^ c[2];
> + g[3] = c[2] ^ c[3];
> +
> + for (i = 0; i < 4; i++)
> + ret_data = ((g[i] & 0x01) << i) | ret_data;
> +
> + return ret_data;
> +}
> +
> +static inline u8 dp_header_get_parity(u32 data)
> +{
> + u8 x0 = 0;
> + u8 x1 = 0;
> + u8 ci = 0;
> + u8 iData = 0;
> + u8 i = 0;
> + u8 parity_byte;
> + u8 num_byte = (data > 0xFF) ? 8 : 2;
> +
> + for (i = 0; i < num_byte; i++) {
> + iData = (data >> i*4) & 0xF;
> +
> + ci = iData ^ x1;
> + x1 = x0 ^ dp_ecc_get_g1_value(ci);
> + x0 = dp_ecc_get_g0_value(ci);
> + }
> +
> + parity_byte = x1 | (x0 << 4);
> +
> + return parity_byte;
> +}
> +

Why do we have them in the header? Are they used by more than one module?

> /* Debug module */
> void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *disp_state);
>
> @@ -137,4 +224,10 @@ void dp_catalog_audio_config_sdp(struct dp_catalog *catalog);
> void dp_catalog_audio_init(struct dp_catalog *catalog);
> void dp_catalog_audio_sfe_level(struct dp_catalog *catalog);
>
> +void dp_catalog_fec_config(struct dp_catalog *dp_catalog, bool enable);
> +void dp_catalog_dsc_commit_pps(struct dp_catalog *catalog);
> +void dp_catalog_config_dsc_dto(struct dp_catalog *catalog);
> +void dp_catalog_override_ack_dto(struct dp_catalog *dp_catalog, bool not_ack);
> +void dp_catalog_pps_flush(struct dp_catalog *catalog);
> +
> #endif /* _DP_CATALOG_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index ae9c2b8..b315bf3 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -134,9 +134,13 @@ static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
> tbd = dp_link_get_test_bits_depth(ctrl->link,
> ctrl->panel->dp_mode.bpp);
>
> - if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN) {
> + /*
> + * since dsc encoder output byte stream to dp controller,

outputs

> + * 8 bits bpc should be used as long as dsc eanabled
> + */
> + if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN || ctrl->panel->dsc_en) {
> pr_debug("BIT_DEPTH not set. Configure default\n");
> - tbd = DP_TEST_BIT_DEPTH_8;
> + tbd = DP_TEST_BIT_DEPTH_8 >> DP_TEST_BIT_DEPTH_SHIFT;
> }
>
> config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
> @@ -366,8 +370,8 @@ static void dp_panel_update_tu_timings(struct dp_tu_calc_input *in,
> tu->lwidth_fp = drm_fixp_from_fraction(in->hactive, 1);
> tu->orig_lwidth = in->hactive;
> tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1);
> - tu->orig_hbp = in->hporch;
> - tu->rb2 = (in->hporch <= 80) ? 1 : 0;
> + tu->orig_hbp = in->hporch;

No unnecessary indentation changes, please.

> + tu->rb2 = (in->hporch < 160) ? 1 : 0;


>
> if (tu->pixelEnc == 420) {
> temp1_fp = drm_fixp_from_fraction(2, 1);
> @@ -399,6 +403,8 @@ static void dp_panel_update_tu_timings(struct dp_tu_calc_input *in,
> if (!in->dsc_en)
> goto fec_check;
>
> + tu->bpp = 24; /* hardcode to 24 if DSC is enabled */
> +
> temp1_fp = drm_fixp_from_fraction(in->compress_ratio, 100);
> temp2_fp = drm_fixp_from_fraction(in->bpp, 1);
> temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
> @@ -1076,6 +1082,11 @@ static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
> {
> struct dp_tu_calc_input in;
> struct drm_display_mode *drm_mode;
> + struct dp_panel_info *timing;
> + struct msm_compression_info *comp_info;
> +
> + timing = &ctrl->panel->dp_mode.timing;
> + comp_info = &timing->comp_info;
>
> drm_mode = &ctrl->panel->dp_mode.drm_mode;
>
> @@ -1086,12 +1097,22 @@ static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
> in.nlanes = ctrl->link->link_params.num_lanes;
> in.bpp = ctrl->panel->dp_mode.bpp;
> in.pixel_enc = 444;
> - in.dsc_en = 0;
> + in.dsc_en = ctrl->panel->dsc_en;
> in.async_en = 0;
> - in.fec_en = 0;
> + in.fec_en = ctrl->panel->fec_en;
> in.num_of_dsc_slices = 0;
> in.compress_ratio = 100;
>
> +
> + /*
> + * TODO: only one dsc slice supported for now
> + */
> + if (ctrl->panel->dsc_en) {
> + in.num_of_dsc_slices = 1;
> + in.compress_ratio = mult_frac(100, comp_info->src_bpp,
> + comp_info->tgt_bpp);
> + }
> +
> _dp_ctrl_calc_tu(ctrl, &in, tu_table);
> }
>
> @@ -1424,6 +1445,74 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl,
> return ret;
> }
>
> +static void dp_ctrl_sink_fec_enable(struct dp_ctrl_private *ctrl)
> +{
> + int rlen;
> +
> + rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_FEC_CONFIGURATION, 0x07);
> + if (rlen < 1)
> + DRM_ERROR("failed to enable sink fec\n");
> +
> +
> +}
> +
> +static void dp_ctrl_host_fec_start(struct dp_ctrl_private *ctrl)
> +{
> + u8 fec_sts = 0;
> + int i, max_retries = 3;
> + bool fec_en_detected = false;
> +
> + if (!ctrl->panel->fec_en)
> + return;
> +
> + /* Need to try to enable multiple times due to BS symbols collisions */
> + for (i = 0; i < max_retries; i++) {
> + dp_catalog_fec_config(ctrl->catalog, true);
> +
> + /* wait for controller to start fec sequence */
> + usleep_range(900, 1000);
> +
> + /* read back FEC status and check if it is enabled */
> + drm_dp_dpcd_readb(ctrl->aux, DP_FEC_STATUS, &fec_sts);
> + if (fec_sts & DP_FEC_DECODE_EN_DETECTED) {
> + fec_en_detected = true;
> + break;
> + }
> + }
> +
> + drm_dbg_dp(ctrl->drm_dev, "retries %d, fec_en_detected %d\n",
> + i, fec_en_detected);
> +
> + if (!fec_en_detected)
> + DRM_ERROR("failed to enable sink fec\n");
> +}
> +
> +static void dp_ctrl_host_fec_stop(struct dp_ctrl_private *ctrl)
> +{
> + dp_catalog_fec_config(ctrl->catalog, false);
> +}
> +
> +static void dp_ctrl_sink_dsc_enable(struct dp_ctrl_private *ctrl)
> +{
> + int rlen;
> + u32 dsc_enable;
> + u8 xx = 0;
> +
> +
> + if (!ctrl->panel->fec_en)
> + return;
> +
> + dsc_enable = ctrl->panel->dsc_en ? 1 : 0;
> + rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_DSC_ENABLE, dsc_enable);
> + if (rlen < 1)
> + DRM_ERROR("failed to enable sink dsc\n");
> +
> +
> + dsc_enable = 0;

Why?

> + drm_dp_dpcd_readb(ctrl->aux, DP_DSC_ENABLE, &xx);

Again, why do you need to read it back? And 'xx' doesn't sound like a
good variable name.

> +
> +}
> +
> static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl,
> int *training_step)
> {
> @@ -1442,6 +1531,9 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl,
> * a link training pattern, we have to first do soft reset.
> */
>
> + if (ctrl->panel->fec_en)
> + dp_ctrl_sink_fec_enable(ctrl);
> +
> ret = dp_ctrl_link_train(ctrl, training_step);
>
> return ret;
> @@ -1986,14 +2078,25 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train)
> */
> reinit_completion(&ctrl->video_comp);
>
> + if (ctrl->panel->dsc_en)
> + dp_panel_config_dsc(ctrl->panel, true);
> +
> dp_ctrl_configure_source_params(ctrl);
>
> dp_catalog_ctrl_config_msa(ctrl->catalog,
> ctrl->link->link_params.rate,
> pixel_rate_orig, dp_ctrl_use_fixed_nvid(ctrl));
>
> + if (ctrl->panel->dsc_en) {
> + dp_catalog_config_dsc_dto(ctrl->catalog);
> + dp_catalog_dsc_commit_pps(ctrl->catalog);
> + dp_catalog_pps_flush(ctrl->catalog);
> + }
> +
> dp_ctrl_setup_tr_unit(ctrl);
>
> + dp_catalog_override_ack_dto(ctrl->catalog, true);
> +
> dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
>
> ret = dp_ctrl_wait4video_ready(ctrl);
> @@ -2004,6 +2107,12 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train)
> drm_dbg_dp(ctrl->drm_dev,
> "mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
>
> + if (ctrl->panel->dsc_en) {
> + /* wait for link training completion before fec config as per spec */
> + dp_ctrl_host_fec_start(ctrl);
> + dp_ctrl_sink_dsc_enable(ctrl);
> + }
> +
> end:
> return ret;
> }
> @@ -2019,6 +2128,9 @@ int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
> dp_io = &ctrl->parser->io;
> phy = dp_io->phy;
>
> + if (ctrl->panel->dsc_en)
> + dp_panel_config_dsc(ctrl->panel, false);
> +
> /* set dongle to D3 (power off) mode */
> dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
>
> @@ -2093,6 +2205,14 @@ int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
> dp_io = &ctrl->parser->io;
> phy = dp_io->phy;
>
> + if (ctrl->panel->dsc_en) {
> + dp_ctrl_host_fec_stop(ctrl);
> + dp_panel_config_dsc(ctrl->panel, false);
> + dp_catalog_config_dsc_dto(ctrl->catalog);
> + }
> +
> + dp_catalog_override_ack_dto(ctrl->catalog, false);
> +
> dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
>
> dp_catalog_ctrl_reset(ctrl->catalog);
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index bde1a7c..da59d13 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include <linux/module.h>
> @@ -99,7 +100,7 @@ struct dp_display_private {
> struct dp_debug *debug;
>
> struct dp_usbpd_cb usbpd_cb;
> - struct dp_display_mode dp_mode;
> + struct dp_display_mode *dp_mode;
> struct msm_dp dp_display;
>
> /* wait for audio signaling */
> @@ -831,6 +832,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
> goto error_link;
> }
>
> + /* both dp_display and dp_panel shared same dp_mode */
> + dp->dp_mode = &dp->panel->dp_mode;
> +
> dp->ctrl = dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
> dp->power, dp->catalog, dp->parser);
> if (IS_ERR(dp->ctrl)) {
> @@ -1662,7 +1666,7 @@ void dp_bridge_enable(struct drm_bridge *drm_bridge)
> bool force_link_train = false;
>
> dp_display = container_of(dp, struct dp_display_private, dp_display);
> - if (!dp_display->dp_mode.drm_mode.clock) {
> + if (!dp_display->dp_mode->drm_mode.clock) {
> DRM_ERROR("invalid params\n");
> return;
> }
> @@ -1678,7 +1682,7 @@ void dp_bridge_enable(struct drm_bridge *drm_bridge)
> return;
> }
>
> - rc = dp_display_set_mode(dp, &dp_display->dp_mode);
> + rc = dp_display_set_mode(dp, dp_display->dp_mode);
> if (rc) {
> DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
> mutex_unlock(&dp_display->event_mutex);
> @@ -1744,6 +1748,10 @@ void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
> if (state == ST_DISCONNECT_PENDING) {
> /* completed disconnection */
> dp_display->hpd_state = ST_DISCONNECTED;
> + if (dp_display->panel->dsc_en) {
> + dp_display->dp_mode->timing.comp_info.enabled = false;
> + dp_display->panel->dsc_en = false;
> + }
> } else {
> dp_display->hpd_state = ST_DISPLAY_OFF;
> }
> @@ -1762,23 +1770,50 @@ void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
>
> dp_display = container_of(dp, struct dp_display_private, dp_display);
>
> - memset(&dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode));
> + memset(dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode));
>
> if (dp_display_check_video_test(dp))
> - dp_display->dp_mode.bpp = dp_display_get_test_bpp(dp);
> + dp_display->dp_mode->bpp = dp_display_get_test_bpp(dp);
> else /* Default num_components per pixel = 3 */
> - dp_display->dp_mode.bpp = dp->connector->display_info.bpc * 3;
> + dp_display->dp_mode->bpp = dp->connector->display_info.bpc * 3;
> +
> + if (!dp_display->dp_mode->bpp)
> + dp_display->dp_mode->bpp = 24; /* Default bpp */
> +
> + drm_mode_copy(&dp_display->dp_mode->drm_mode, adjusted_mode);
> +
> + dp_display->dp_mode->v_active_low =
> + !!(dp_display->dp_mode->drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
> +
> + dp_display->dp_mode->h_active_low =
> + !!(dp_display->dp_mode->drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
> +
> +
> + if (dp_display->panel->dsc_en) {
> + dp_display->dp_mode->capabilities |= DP_PANEL_CAPS_DSC;
> + dp_panel_convert_to_dp_mode(dp_display->panel, dp_display->dp_mode);
> + }
> +}
>
> - if (!dp_display->dp_mode.bpp)
> - dp_display->dp_mode.bpp = 24; /* Default bpp */
> +void msm_dp_bridge_mode_set(struct drm_bridge *bridge,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + dp_bridge_mode_set(bridge, mode, adjusted_mode);
> +}
>
> - drm_mode_copy(&dp_display->dp_mode.drm_mode, adjusted_mode);
> +struct msm_compression_info *msm_dp_bridge_get_compression(struct drm_bridge *drm_bridge)
> +{
> + struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> + struct msm_dp *dp_display = dp_bridge->dp_display;
> + struct dp_display_private *dp;
> +
> + dp = container_of(dp_display, struct dp_display_private, dp_display);
>
> - dp_display->dp_mode.v_active_low =
> - !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
> + if (!dp->panel->dsc_en)
> + return NULL;
>
> - dp_display->dp_mode.h_active_low =
> - !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
> + return &dp->dp_mode->timing.comp_info;
> }
>
> void dp_bridge_hpd_enable(struct drm_bridge *bridge)
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> index 55bb6b0..19e2f07 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> @@ -5,11 +5,12 @@
> */
>
> #include "dp_panel.h"
> -
> +#include "dpu_dsc_helper.h"

DP driver can not depend on DPU.

> #include <drm/drm_fixed.h>
> #include <drm/drm_connector.h>
> #include <drm/drm_edid.h>
> #include <drm/drm_print.h>
> +#include <drm/display/drm_dsc_helper.h>
>
> #define DSC_TGT_BPP 10
>
> @@ -612,6 +613,573 @@ u8 dp_panel_get_misc_colorimetry_val(struct dp_panel *dp_panel)
> return colorimetry;
> }
>
> +static inline int fixp2int_ceil(s64 a)
> +{
> + return (a ? drm_fixp2int_ceil(a) : 0);
> +}

??

> +
> +struct dp_dsc_slices_per_line {
> + u32 min_ppr;
> + u32 max_ppr;
> + u8 num_slices;
> +};
> +
> +struct dp_dsc_peak_throughput {
> + u32 index;
> + u32 peak_throughput;
> +};
> +
> +struct dp_dsc_slice_caps_bit_map {
> + u32 num_slices;
> + u32 bit_index;
> +};
> +
> +const struct dp_dsc_slices_per_line slice_per_line_tbl[] = {
> + {0, 340, 1 },
> + {340, 680, 2 },
> + {680, 1360, 4 },
> + {1360, 3200, 8 },
> + {3200, 4800, 12 },
> + {4800, 6400, 16 },
> + {6400, 8000, 20 },
> + {8000, 9600, 24 }
> +};
> +
> +const struct dp_dsc_peak_throughput peak_throughput_mode_0_tbl[] = {
> + {0, 0},
> + {1, 340},
> + {2, 400},
> + {3, 450},
> + {4, 500},
> + {5, 550},
> + {6, 600},
> + {7, 650},
> + {8, 700},
> + {9, 750},
> + {10, 800},
> + {11, 850},
> + {12, 900},
> + {13, 950},
> + {14, 1000},
> +};
> +
> +const struct dp_dsc_slice_caps_bit_map slice_caps_bit_map_tbl[] = {
> + {1, 0},
> + {2, 1},
> + {4, 3},
> + {6, 4},
> + {8, 5},
> + {10, 6},
> + {12, 7},
> + {16, 0},
> + {20, 1},
> + {24, 2},
> +};
> +
> +static bool dp_panel_check_slice_support(u32 num_slices, u32 raw_data_1,
> + u32 raw_data_2)
> +{
> + const struct dp_dsc_slice_caps_bit_map *bcap;
> + u32 raw_data;
> + int i;
> +
> + if (num_slices <= 12)
> + raw_data = raw_data_1;
> + else
> + raw_data = raw_data_2;
> +
> + for (i = 0; i < ARRAY_SIZE(slice_caps_bit_map_tbl); i++) {
> + bcap = &slice_caps_bit_map_tbl[i];
> +
> + if (bcap->num_slices == num_slices) {
> + raw_data &= (1 << bcap->bit_index);
> +
> + if (raw_data)
> + return true;
> + else
> + return false;
> + }
> + }
> +
> + return false;
> +}
> +
> +static int dp_panel_dsc_prepare_basic_params(
> + struct msm_compression_info *comp_info,
> + const struct dp_display_mode *dp_mode,
> + struct dp_panel *dp_panel)
> +{
> + struct dp_panel_private *panel;
> + struct drm_dsc_config *dsc;
> + int i;
> + const struct dp_dsc_slices_per_line *rec;
> + const struct dp_dsc_peak_throughput *tput;
> + u32 slice_width;
> + u32 ppr = dp_mode->timing.pixel_clk_khz/1000;
> + u32 max_slice_width;
> + u32 ppr_max_index;
> + u32 peak_throughput;
> + u32 ppr_per_slice;
> + u32 slice_caps_1;
> + u32 slice_caps_2;
> + u32 dsc_version_major, dsc_version_minor;
> + bool dsc_version_supported = false;
> +
> + panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
> +
> + dsc_version_major = dp_panel->sink_dsc_caps.version & 0xF;
> + dsc_version_minor = (dp_panel->sink_dsc_caps.version >> 4) & 0xF;
> + dsc_version_supported = (dsc_version_major == 0x1 &&
> + (dsc_version_minor == 0x1 || dsc_version_minor == 0x2))
> + ? true : false;
> +
> + drm_dbg_dp(panel->drm_dev, "DSC version: %d.%d, dpcd value: %x\n",
> + dsc_version_major, dsc_version_minor,
> + dp_panel->sink_dsc_caps.version);
> +
> + if (!dsc_version_supported) {
> + dsc_version_major = 1;
> + dsc_version_minor = 1;
> + DRM_ERROR("invalid sink DSC version, fallback to %d.%d\n",
> + dsc_version_major, dsc_version_minor);
> + }
> +
> + dsc = &comp_info->msm_dsc_info.drm_dsc;
> + dsc->dsc_version_major = dsc_version_major;
> + dsc->dsc_version_minor = dsc_version_minor;
> + comp_info->msm_dsc_info.scr_rev = 0x0;
> +
> +
> + comp_info->msm_dsc_info.slice_per_pkt = 0;
> + for (i = 0; i < ARRAY_SIZE(slice_per_line_tbl); i++) {
> + rec = &slice_per_line_tbl[i];
> + if ((ppr > rec->min_ppr) && (ppr <= rec->max_ppr)) {
> + comp_info->msm_dsc_info.slice_per_pkt = rec->num_slices;
> + i++;
> + break;
> + }
> + }
> +
> + if (comp_info->msm_dsc_info.slice_per_pkt == 0)
> + return -EINVAL;
> +
> + ppr_max_index = dp_panel->dsc_dpcd[11] &= 0xf;
> + if (!ppr_max_index || ppr_max_index >= 15) {
> + drm_dbg_dp(panel->drm_dev,
> + "Throughput mode 0 not supported");
> + return -EINVAL;
> + }
> +
> + tput = &peak_throughput_mode_0_tbl[ppr_max_index];
> + peak_throughput = tput->peak_throughput;
> +
> + max_slice_width = dp_panel->dsc_dpcd[12] * 320;
> + slice_width = (dp_mode->timing.h_active /
> + comp_info->msm_dsc_info.slice_per_pkt);
> +
> + ppr_per_slice = ppr/comp_info->msm_dsc_info.slice_per_pkt;
> +
> + slice_caps_1 = dp_panel->dsc_dpcd[4];
> + slice_caps_2 = dp_panel->dsc_dpcd[13] & 0x7;
> +
> + /*
> + * There are 3 conditions to check for sink support:
> + * 1. The slice width cannot exceed the maximum.
> + * 2. The ppr per slice cannot exceed the maximum.
> + * 3. The number of slices must be explicitly supported.
> + */
> + while (slice_width >= max_slice_width ||
> + ppr_per_slice > peak_throughput ||
> + !dp_panel_check_slice_support(
> + comp_info->msm_dsc_info.slice_per_pkt, slice_caps_1,
> + slice_caps_2)) {
> + if (i == ARRAY_SIZE(slice_per_line_tbl))
> + return -EINVAL;
> +
> + rec = &slice_per_line_tbl[i];
> + comp_info->msm_dsc_info.slice_per_pkt = rec->num_slices;
> + slice_width = (dp_mode->timing.h_active /
> + comp_info->msm_dsc_info.slice_per_pkt);
> + ppr_per_slice = ppr/comp_info->msm_dsc_info.slice_per_pkt;
> + i++;
> + }
> +
> + dsc->block_pred_enable = dp_panel->sink_dsc_caps.block_pred_en;
> +
> + dsc->pic_width = dp_mode->timing.h_active;
> + dsc->pic_height = dp_mode->timing.v_active;
> + dsc->slice_width = slice_width;
> +
> + if (dsc->pic_height % 108 == 0)
> + dsc->slice_height = 108;
> + else if (dsc->pic_height % 16 == 0)
> + dsc->slice_height = 16;
> + else if (dsc->pic_height % 12 == 0)
> + dsc->slice_height = 12;
> + else
> + dsc->slice_height = 15;
> +
> + dsc->bits_per_component = (dp_mode->timing.bpp / 3);
> + dsc->bits_per_pixel = DSC_TGT_BPP << 4;
> + dsc->slice_count = DIV_ROUND_UP(dp_mode->timing.h_active, slice_width);
> +
> + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC;
> + comp_info->tgt_bpp = DSC_TGT_BPP;
> + comp_info->src_bpp = dp_mode->timing.bpp;
> + comp_info->comp_ratio = dp_mode->timing.bpp / DSC_TGT_BPP;
> + comp_info->enabled = true;
> +
> + return 0;
> +}
> +
> +static void dp_panel_get_dto_params(u32 src_bpp, u32 tgt_bpp, u32 *num, u32 *denom)
> +{
> + if ((tgt_bpp == 12) && (src_bpp == 24)) {
> + *num = 1;
> + *denom = 2;
> + } else if ((tgt_bpp == 15) && (src_bpp == 30)) {
> + *num = 5;
> + *denom = 8;
> + } else if ((tgt_bpp == 8) && ((src_bpp == 24) || (src_bpp == 30))) {
> + *num = 1;
> + *denom = 3;
> + } else if ((tgt_bpp == 10) && (src_bpp == 30)) {
> + *num = 5;
> + *denom = 12;
> + } else {
> + DRM_ERROR("dto params not found\n");
> + *num = 0;
> + *denom = 1;
> + }
> +}
> +
> +static void dp_panel_dsc_prepare_pps_packet(struct dp_panel *dp_panel)
> +{
> + struct dp_panel_private *panel;
> + struct dp_dsc_cfg_data *dsc_data;
> + u8 *pps, *parity;
> + u32 *pps_word, *parity_word;
> + int i, index_4;
> +
> + panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
> +
> + dsc_data = &panel->catalog->dsc_data;
> + pps = dsc_data->pps;
> + pps_word = dsc_data->pps_word;
> + parity = dsc_data->parity;
> + parity_word = dsc_data->parity_word;
> +
> + memset(parity, 0, sizeof(dsc_data->parity));
> +
> + dsc_data->pps_word_len = dsc_data->pps_len >> 2;
> + dsc_data->parity_len = dsc_data->pps_word_len;
> + dsc_data->parity_word_len = (dsc_data->parity_len >> 2) + 1;
> +
> + for (i = 0; i < dsc_data->pps_word_len; i++) {
> + index_4 = i << 2;
> + pps_word[i] = pps[index_4 + 0] << 0 |
> + pps[index_4 + 1] << 8 |
> + pps[index_4 + 2] << 16 |
> + pps[index_4 + 3] << 24;
> +
> + parity[i] = dp_header_get_parity(pps_word[i]);
> + }
> +
> + for (i = 0; i < dsc_data->parity_word_len; i++) {
> + index_4 = i << 2;
> + parity_word[i] = parity[index_4 + 0] << 0 |
> + parity[index_4 + 1] << 8 |
> + parity[index_4 + 2] << 16 |
> + parity[index_4 + 3] << 24;
> + }
> +}
> +
> +void dp_panel_config_dsc(struct dp_panel *dp_panel, bool enable)
> +{
> + struct dp_panel_private *panel;
> + struct dp_panel_info *timing;
> + struct msm_compression_info *comp_info;
> + struct dp_dsc_cfg_data *dsc_data;
> + struct drm_dsc_picture_parameter_set *pps_payload;
> + struct drm_dsc_config *dsc;
> +
> + if (!dp_panel->dsc_en)
> + return;
> +
> + panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
> +
> + dsc_data = &panel->catalog->dsc_data;
> + timing = &dp_panel->dp_mode.timing;
> + comp_info = &timing->comp_info;
> + dsc = &comp_info->msm_dsc_info.drm_dsc;
> +
> + if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC && enable) {
> + memset(dsc_data->pps, 0, sizeof(dsc_data->pps));
> + pps_payload = (struct drm_dsc_picture_parameter_set *)dsc_data->pps;
> + drm_dsc_pps_payload_pack(pps_payload, dsc);
> +
> + dsc_data->pps_len = DSC_1_1_PPS_PARAMETER_SET_ELEMENTS;
> + dp_panel_dsc_prepare_pps_packet(dp_panel);
> +
> + dsc_data->slice_per_pkt = comp_info->msm_dsc_info.slice_per_pkt - 1;
> + dsc_data->bytes_per_pkt = comp_info->msm_dsc_info.bytes_per_pkt;
> + dsc_data->bytes_per_pkt /= comp_info->msm_dsc_info.slice_per_pkt;
> + dsc_data->eol_byte_num = comp_info->msm_dsc_info.eol_byte_num;
> + dsc_data->dto_count = comp_info->msm_dsc_info.pclk_per_line;
> + dsc_data->be_in_lane = 10;
> + dsc_data->dsc_en = true;
> + dsc_data->dto_en = true;
> + dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dsc_data->dto_n,
> + &dsc_data->dto_d);
> + } else {
> + dsc_data->dsc_en = false;
> + dsc_data->dto_en = false;
> + dsc_data->dto_n = 0;
> + dsc_data->dto_d = 0;
> + dsc_data->continuous_pps = false;
> + }
> +}
> +
> +static void _dp_panel_dsc_get_num_extra_pclk(struct msm_compression_info *comp_info)
> +{
> + unsigned int dto_n = 0, dto_d = 0, remainder;
> + int ack_required, last_few_ack_required, accum_ack;
> + int last_few_pclk, last_few_pclk_required;
> + struct msm_display_dsc_info *dsc_info = &comp_info->msm_dsc_info;
> + int start, temp, line_width = dsc_info->drm_dsc.pic_width/2;
> + s64 temp1_fp, temp2_fp;
> +
> + dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dto_n, &dto_d);
> +
> + ack_required = dsc_info->pclk_per_line;
> +
> + /* number of pclk cycles left outside of the complete DTO set */
> + last_few_pclk = line_width % dto_d;
> +
> + /* number of pclk cycles outside of the complete dto */
> + temp1_fp = drm_fixp_from_fraction(line_width, dto_d);
> + temp2_fp = drm_fixp_from_fraction(dto_n, 1);
> + temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
> + temp = drm_fixp2int(temp1_fp);
> + last_few_ack_required = ack_required - temp;
> +
> + /*
> + * check how many more pclk is needed to
> + * accommodate the last few ack required
> + */
> + remainder = dto_n;
> + accum_ack = 0;
> + last_few_pclk_required = 0;
> + while (accum_ack < last_few_ack_required) {
> + last_few_pclk_required++;
> +
> + if (remainder >= dto_n)
> + start = remainder;
> + else
> + start = remainder + dto_d;
> +
> + remainder = start - dto_n;
> + if (remainder < dto_n)
> + accum_ack++;
> + }
> +
> + /* if fewer pclk than required */
> + if (last_few_pclk < last_few_pclk_required)
> + dsc_info->extra_width = last_few_pclk_required - last_few_pclk;
> + else
> + dsc_info->extra_width = 0;
> +}
> +
> +static void _dp_panel_dsc_bw_overhead_calc(struct dp_panel *dp_panel,
> + struct msm_display_dsc_info *dsc_info,
> + struct dp_display_mode *dp_mode, u32 dsc_byte_cnt)
> +{
> + int num_slices, tot_num_eoc_symbols;
> + int tot_num_hor_bytes, tot_num_dummy_bytes;
> + int dwidth_dsc_bytes, eoc_bytes;
> + u32 num_lanes;
> + struct dp_panel_private *panel;
> +
> + panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
> +
> + num_lanes = panel->link->link_params.num_lanes;
> + num_slices = dsc_info->slice_per_pkt;
> +
> + eoc_bytes = dsc_byte_cnt % num_lanes;
> + tot_num_eoc_symbols = num_lanes * num_slices;
> + tot_num_hor_bytes = dsc_byte_cnt * num_slices;
> + tot_num_dummy_bytes = (num_lanes - eoc_bytes) * num_slices;
> +
> + if (!eoc_bytes)
> + tot_num_dummy_bytes = 0;
> +
> + dwidth_dsc_bytes = tot_num_hor_bytes + tot_num_eoc_symbols +
> + tot_num_dummy_bytes;
> +
> + drm_dbg_dp(panel->drm_dev, "dwidth_dsc_bytes:%d, tot_num_hor_bytes:%d\n",
> + dwidth_dsc_bytes, tot_num_hor_bytes);
> +
> + dp_mode->dsc_overhead_fp = drm_fixp_from_fraction(dwidth_dsc_bytes,
> + tot_num_hor_bytes);
> +
> + dp_mode->timing.dsc_overhead_fp = dp_mode->dsc_overhead_fp;
> +}
> +
> +static void dp_panel_dsc_pclk_param_calc(struct dp_panel *dp_panel,
> + struct msm_compression_info *comp_info,
> + struct dp_display_mode *dp_mode)
> +{
> + int comp_ratio = 100, intf_width;
> + int slice_per_pkt, slice_per_intf;
> + s64 temp1_fp, temp2_fp;
> + s64 numerator_fp, denominator_fp;
> + s64 dsc_byte_count_fp;
> + u32 dsc_byte_count, temp1, temp2;
> + struct msm_display_dsc_info *dsc_info = &comp_info->msm_dsc_info;
> +
> + intf_width = dp_mode->timing.h_active;
> + if (!dsc_info || !dsc_info->drm_dsc.slice_width || !dsc_info->slice_per_pkt ||
> + (intf_width < dsc_info->drm_dsc.slice_width))
> + return;
> +
> + slice_per_pkt = dsc_info->slice_per_pkt;
> + slice_per_intf = DIV_ROUND_UP(intf_width,
> + dsc_info->drm_dsc.slice_width);
> +
> + comp_ratio = mult_frac(100, comp_info->src_bpp, comp_info->tgt_bpp);
> +
> + temp1_fp = drm_fixp_from_fraction(comp_ratio, 100);
> + temp2_fp = drm_fixp_from_fraction(slice_per_pkt * 8, 1);
> + denominator_fp = drm_fixp_mul(temp1_fp, temp2_fp);
> + numerator_fp = drm_fixp_from_fraction(
> + intf_width * dsc_info->drm_dsc.bits_per_component * 3, 1);
> + dsc_byte_count_fp = drm_fixp_div(numerator_fp, denominator_fp);
> + dsc_byte_count = fixp2int_ceil(dsc_byte_count_fp);
> +
> + temp1 = dsc_byte_count * slice_per_intf;
> + temp2 = temp1;
> + if (temp1 % 3 != 0)
> + temp1 += 3 - (temp1 % 3);
> +
> + dsc_info->eol_byte_num = temp1 - temp2;
> +
> + temp1_fp = drm_fixp_from_fraction(slice_per_intf, 6);
> + temp2_fp = drm_fixp_mul(dsc_byte_count_fp, temp1_fp);
> + dsc_info->pclk_per_line = fixp2int_ceil(temp2_fp);
> +
> + _dp_panel_dsc_get_num_extra_pclk(comp_info);
> + dsc_info->pclk_per_line--;
> +
> + _dp_panel_dsc_bw_overhead_calc(dp_panel, dsc_info, dp_mode, dsc_byte_count);
> +}
> +
> +void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel,
> + struct dp_display_mode *dp_mode)
> +{
> + struct dp_panel_private *panel;
> + const u32 num_components = 3, default_bpp = 24;
> + struct drm_display_mode *drm_mode;
> + struct dp_panel_info *timing;
> + struct msm_compression_info *comp_info;
> + bool dsc_cap = (dp_mode->capabilities & DP_PANEL_CAPS_DSC) ?
> + true : false;
> + int rc;
> +
> + if (!dp_panel->dsc_en)
> + return;
> +
> + panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
> +
> + drm_mode = &dp_mode->drm_mode;
> + timing = &dp_mode->timing;
> +
> + timing->h_active = drm_mode->hdisplay;
> + timing->h_back_porch = drm_mode->htotal - drm_mode->hsync_end;
> + timing->h_sync_width = drm_mode->htotal -
> + (drm_mode->hsync_start + dp_mode->timing.h_back_porch);
> +
> + timing->h_front_porch = drm_mode->hsync_start -
> + drm_mode->hdisplay;
> + timing->h_skew = drm_mode->hskew;
> +
> + timing->v_active = drm_mode->vdisplay;
> + timing->v_back_porch = drm_mode->vtotal - drm_mode->vsync_end;
> + timing->v_sync_width = drm_mode->vtotal -
> + (drm_mode->vsync_start + dp_mode->timing.v_back_porch);
> +
> + timing->v_front_porch = drm_mode->vsync_start - drm_mode->vdisplay;
> +
> + timing->refresh_rate = drm_mode_vrefresh(drm_mode);
> + timing->pixel_clk_khz = drm_mode->clock;
> +
> + timing->v_active_low =
> + !!(drm_mode->flags & DRM_MODE_FLAG_NVSYNC);
> +
> + timing->h_active_low =
> + !!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
> +
> + timing->bpp =
> + dp_panel->connector->display_info.bpc * num_components;
> + if (!timing->bpp)
> + timing->bpp = default_bpp;
> +
> + timing->widebus_en = dp_panel->widebus_en;
> + timing->dsc_overhead_fp = 0;
> +
> + comp_info = &timing->comp_info;
> + comp_info->src_bpp = default_bpp;
> + comp_info->tgt_bpp = default_bpp;
> + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_NONE;
> + comp_info->comp_ratio = 1;
> + comp_info->enabled = false;
> +
> + /* As YUV was not supported now, so set the default format to RGB */
> + dp_mode->output_format = DP_OUTPUT_FORMAT_RGB;
> + /*
> + * If a given videomode can be only supported in YCBCR420, set
> + * the output format to YUV420. While now our driver did not
> + * support YUV display over DP, so just place this flag here.
> + * When we want to support YUV, we can use this flag to do
> + * a lot of settings, like CDM, CSC and pixel_clock.
> + */
> + if (drm_mode_is_420_only(&dp_panel->connector->display_info,
> + drm_mode)) {
> + dp_mode->output_format = DP_OUTPUT_FORMAT_YCBCR420;
> + drm_dbg_dp(panel->drm_dev, "YCBCR420 was not supported");
> + }
> +
> + timing->bpp = dp_panel_get_mode_bpp(dp_panel,
> + timing->bpp, timing->pixel_clk_khz);
> +
> +
> + if (dp_panel->dsc_en && dsc_cap) {
> + if (dp_panel_dsc_prepare_basic_params(comp_info,
> + dp_mode, dp_panel)) {
> + drm_dbg_dp(panel->drm_dev,
> + "prepare DSC basic params failed\n");
> + return;
> + }
> +
> + rc = dpu_dsc_populate_dsc_config(&comp_info->msm_dsc_info.drm_dsc, 0);
> + if (rc) {
> + drm_dbg_dp(panel->drm_dev,
> + "failed populating dsc params \n");
> + return;
> + }
> +
> + rc = dpu_dsc_populate_dsc_private_params(&comp_info->msm_dsc_info,
> + dp_mode->timing.h_active);
> + if (rc) {
> + drm_dbg_dp(panel->drm_dev,
> + "failed populating other dsc params\n");
> + return;
> + }
> +
> + dp_panel_dsc_pclk_param_calc(dp_panel, comp_info, dp_mode);
> + }
> + dp_mode->fec_overhead_fp = dp_panel->fec_overhead_fp;
> +}
> +
> struct dp_panel *dp_panel_get(struct dp_panel_in *in)
> {
> struct dp_panel_private *panel;
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
> index 4c45d51..576056c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
> @@ -112,6 +112,7 @@ struct dp_panel {
> bool fec_feature_enable;
> bool dsc_en;
> bool fec_en;
> + bool widebus_en;
> s64 fec_overhead_fp;
> };
>
> @@ -128,6 +129,9 @@ int dp_panel_get_modes(struct dp_panel *dp_panel,
> void dp_panel_handle_sink_request(struct dp_panel *dp_panel);
> void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable);
> u8 dp_panel_get_misc_colorimetry_val(struct dp_panel *dp_panel);
> +void dp_panel_config_dsc(struct dp_panel *dp_panel, bool enable);
> +void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel,
> + struct dp_display_mode *dp_mode);
>
> /**
> * is_link_rate_valid() - validates the link rate
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index 2686028..96d48d0c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -1,6 +1,7 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #ifndef _DP_REG_H_
> @@ -167,7 +168,38 @@
> #define MMSS_DP_PSR_CRC_RG (0x00000154)
> #define MMSS_DP_PSR_CRC_B (0x00000158)
>
> -#define REG_DP_COMPRESSION_MODE_CTRL (0x00000180)
> +#define DP_COMPRESSION_MODE_CTRL (0x00000180)
> +#define DP_PPS_HB_0_3 (0x00000184)
> +#define DP_PPS_PB_0_3 (0x00000188)
> +#define DP_PPS_PB_4_7 (0x0000018C)
> +#define DP_PPS_PB_8_11 (0x00000190)
> +#define DP_PPS_PB_12_15 (0x00000194)
> +#define DP_PPS_PB_16_19 (0x00000198)
> +#define DP_PPS_PB_20_23 (0x0000019C)
> +#define DP_PPS_PB_24_27 (0x000001A0)
> +#define DP_PPS_PB_28_31 (0x000001A4)
> +#define DP_PPS_PPS_0_3 (0x000001A8)
> +#define DP_PPS_PPS_4_7 (0x000001AC)
> +#define DP_PPS_PPS_8_11 (0x000001B0)
> +#define DP_PPS_PPS_12_15 (0x000001B4)
> +#define DP_PPS_PPS_16_19 (0x000001B8)
> +#define DP_PPS_PPS_20_23 (0x000001BC)
> +#define DP_PPS_PPS_24_27 (0x000001C0)
> +#define DP_PPS_PPS_28_31 (0x000001C4)
> +#define DP_PPS_PPS_32_35 (0x000001C8)
> +#define DP_PPS_PPS_36_39 (0x000001CC)
> +#define DP_PPS_PPS_40_43 (0x000001D0)
> +#define DP_PPS_PPS_44_47 (0x000001D4)
> +#define DP_PPS_PPS_48_51 (0x000001D8)
> +#define DP_PPS_PPS_52_55 (0x000001DC)
> +#define DP_PPS_PPS_56_59 (0x000001E0)
> +#define DP_PPS_PPS_60_63 (0x000001E4)
> +#define DP_PPS_PPS_64_67 (0x000001E8)
> +#define DP_PPS_PPS_68_71 (0x000001EC)
> +#define DP_PPS_PPS_72_75 (0x000001F0)
> +#define DP_PPS_PPS_76_79 (0x000001F4)
> +#define DP_PPS_PPS_80_83 (0x000001F8)
> +#define DP_PPS_PPS_84_87 (0x000001FC)
>
> #define MMSS_DP_AUDIO_CFG (0x00000200)
> #define MMSS_DP_AUDIO_STATUS (0x00000204)
> @@ -178,6 +210,8 @@
>
> #define MMSS_DP_SDP_CFG (0x00000228)
> #define MMSS_DP_SDP_CFG2 (0x0000022C)
> +#define MMSS_DP_SDP_CFG3 (0x0000024C)
> +#define MMSS_DP_SDP_CFG4 (0x000004EC)
> #define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
> #define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
>
> @@ -210,6 +244,9 @@
> #define MMSS_DP_AUDIO_INFOFRAME_1 (0x000002AC)
> #define MMSS_DP_AUDIO_INFOFRAME_2 (0x000002B0)
>
> +#define MMSS_DP_FLUSH (0x000002F8)
> +#define MMSS_DP1_FLUSH (0x000002FC)
> +
> #define MMSS_DP_GENERIC0_0 (0x00000300)
> #define MMSS_DP_GENERIC0_1 (0x00000304)
> #define MMSS_DP_GENERIC0_2 (0x00000308)
> @@ -268,6 +305,7 @@
>
> #define MMSS_DP_TPG_MAIN_CONTROL (0x00000060)
> #define MMSS_DP_DSC_DTO (0x0000007C)
> +#define MMSS_DP_DSC_DTO_COUNT (0x00000084)
> #define DP_TPG_CHECKERED_RECT_PATTERN (0x00000100)
>
> #define MMSS_DP_TPG_VIDEO_CONFIG (0x00000064)
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index cf4eb8d..6a46ed7 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -476,6 +476,11 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_displa
>
> void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor);
> bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
> +struct msm_compression_info *msm_dp_bridge_get_compression(struct drm_bridge *drm_bridge);
> +
> +void msm_dp_bridge_mode_set(struct drm_bridge *bridge,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode);

Why do you need to export this function?

>
> #else
> static inline int __init msm_dp_register(void)
> @@ -510,6 +515,17 @@ static inline bool msm_dp_wide_bus_available(const struct msm_dp *dp_display)
> return false;
> }
>
> +static inline struct msm_compression_info *msm_dp_bridge_get_compression(
> + struct drm_bridge *drm_bridge)
> +{
> + return NULL;
> +}
> +static inline void msm_dp_bridge_mode_set(struct drm_bridge *bridge,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> +
> +}
> #endif
>
> #ifdef CONFIG_DRM_MSM_MDP4

--
With best wishes
Dmitry


2023-01-24 00:10:46

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 00/14] add display port DSC feature

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> This patch add DSC related supporting functions into to both dp controller and dpu enccoder
>
> Kuogee Hsieh (14):
> drm/msm/dp: add dpcd read of both dsc and fec capability
> drm/msm/dp: add dsc factor into calculation of supported bpp
> drm/msm/dp: add configure mainlink_levels base on lane number
> drm/msm/dp: correct configure Colorimetry Indicator Field at MISC0
> drm/msm/dp: upgrade tu calculation base on newest algorithm
> drm/msm/dp: add display compression related struct
> drm/msm/dp: add dsc helper functions
> drm/msm/dp: add dsc supporting functions to DP controller
> drm/msm/dsi: export struct msm_compression_info to dpu encoder
> drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine
> drm/msm/disp/dpu1: add supports of new flush mechanism
> drm/msm/disp/dpu1: revise timing engine programming to work for DSC
> drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder
> drm/msm/disp/dpu1: add sc7280 dsc block and sub block

Some generic notes regarding the series. I understand that the the
series is complex, but following points might ease both your work and
the review proces.

First, atomicity. If your commit message says 'do this and that', it is
highly likely that the patch should be split into smaller parts.

Second, please pay attention to the history. If some part of the code or
the data structure was removed, you have to justify bringing it back.
This is extremely important in your case, as significant parts of the
code come from the vendor code, thut it is easy to step on the same rake
again. And if the previous removal was incorrect, please describe why.

If we went through 10 revisions of a patch a year ago, it's not worth
sending again a patch that closely remedies one of early iterations. It
doesn't stand a chance of getting through.

Next. Obvious item. ./scripts/checkpatch.pl should be your friend. It is
not.

Last, but not least. Please follow the mailing list. Less than a week
ago one of reviews pointed out that commit messages like 'this patch
does this and that' are not really welcomed. By sending the same kind of
commit messages, you stand a high chance of receiveing the same
response. Please go through the recommendations in
Documentation/process/submitting-patches.rst.

>
> drivers/gpu/drm/msm/Makefile | 2 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 341 +++++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 7 +-
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 43 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 50 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 74 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 21 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 ++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h | 3 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 10 +-
> drivers/gpu/drm/msm/dp/dp_catalog.c | 176 ++++-
> drivers/gpu/drm/msm/dp/dp_catalog.h | 97 ++-
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 839 ++++++++++++++-------
> drivers/gpu/drm/msm/dp/dp_display.c | 61 +-
> drivers/gpu/drm/msm/dp/dp_link.c | 29 +-
> drivers/gpu/drm/msm/dp/dp_panel.c | 749 +++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_panel.h | 67 +-
> drivers/gpu/drm/msm/dp/dp_reg.h | 40 +-
> drivers/gpu/drm/msm/dsi/dsi.c | 3 +-
> drivers/gpu/drm/msm/dsi/dsi.h | 3 +-
> drivers/gpu/drm/msm/dsi/dsi_host.c | 14 +-
> drivers/gpu/drm/msm/msm_drv.h | 113 ++-
> 32 files changed, 3429 insertions(+), 497 deletions(-)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>

--
With best wishes
Dmitry


2023-01-24 07:44:31

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> DSC V1.2 encoder engine is newly added hardware module. This patch
> add support functions to configure and enable DSC V1.2 encoder engine.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/Makefile | 1 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 2 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 60 +++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++++++++++++++++++
> 6 files changed, 463 insertions(+), 17 deletions(-)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 28cf52b..271c29a15 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> disp/dpu1/dpu_hw_catalog.o \
> disp/dpu1/dpu_hw_ctl.o \
> disp/dpu1/dpu_hw_dsc.o \
> + disp/dpu1/dpu_hw_dsc_1_2.o \
> disp/dpu1/dpu_dsc_helper.o \
> disp/dpu1/dpu_hw_interrupts.o \
> disp/dpu1/dpu_hw_intf.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 7f4a439..901e317 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1821,7 +1821,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
> u32 initial_lines)
> {
> if (hw_dsc->ops.dsc_config)
> - hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines);
> + hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);
>
> if (hw_dsc->ops.dsc_config_thresh)
> hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> index 978e3bd..7b0b092 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
> */
>
> @@ -11,6 +11,7 @@
> #include <linux/bug.h>
> #include <linux/bitmap.h>
> #include <linux/err.h>
> +#include "dpu_hw_mdss.h"
>
> /**
> * Max hardware block count: For ex: max 12 SSPP pipes or
> @@ -182,6 +183,7 @@ enum {
> * @DPU_PINGPONG_TE2 Additional tear check block for split pipes
> * @DPU_PINGPONG_SPLIT PP block supports split fifo
> * @DPU_PINGPONG_SLAVE PP block is a suitable slave for split fifo
> + * @DPU_PINGPONG_DSC, Display stream compression blocks
> * @DPU_PINGPONG_DITHER, Dither blocks
> * @DPU_PINGPONG_MAX
> */
> @@ -190,10 +192,32 @@ enum {
> DPU_PINGPONG_TE2,
> DPU_PINGPONG_SPLIT,
> DPU_PINGPONG_SLAVE,
> + DPU_PINGPONG_DSC,
> DPU_PINGPONG_DITHER,
> DPU_PINGPONG_MAX
> };
>
> +
> +/** DSC sub-blocks/features
> + * @DPU_DSC_OUTPUT_CTRL Supports the control of the pp id which gets
> + * the pixel output from this DSC.
> + * @DPU_DSC_HW_REV_1_1 dsc block supports dsc 1.1 only
> + * @DPU_DSC_HW_REV_1_2 dsc block supports dsc 1.1 and 1.2
> + * @DPU_DSC_NATIVE_422_EN, Supports native422 and native420 encoding
> + * @DPU_DSC_ENC, DSC encoder sub block
> + * @DPU_DSC_CTL, DSC ctl sub block
> + * @DPU_DSC_MAX
> + */
> +enum {
> + DPU_DSC_OUTPUT_CTRL = 0x1,
> + DPU_DSC_HW_REV_1_1,
> + DPU_DSC_HW_REV_1_2,
> + DPU_DSC_NATIVE_422_EN,
> + DPU_DSC_ENC,
> + DPU_DSC_CTL,
> + DPU_DSC_MAX
> +};
> +
> /**
> * CTL sub-blocks
> * @DPU_CTL_SPLIT_DISPLAY: CTL supports video mode split display
> @@ -276,15 +300,6 @@ enum {
> };
>
> /**
> - * DSC features
> - * @DPU_DSC_OUTPUT_CTRL Configure which PINGPONG block gets
> - * the pixel output from this DSC.
> - */
> -enum {
> - DPU_DSC_OUTPUT_CTRL = 0x1,
> -};
> -

Any reason for this move?

> -/**
> * MACRO DPU_HW_BLK_INFO - information of HW blocks inside DPU
> * @name: string name for debug purposes
> * @id: enum identifying this block
> @@ -346,6 +361,14 @@ struct dpu_pp_blk {
> };
>
> /**
> + * struct dpu_dsc_blk : DSC Encoder sub-blk information
> + * @info: HW register and features supported by this sub-blk
> + */
> +struct dpu_dsc_blk {
> + DPU_HW_SUBBLK_INFO;
> +};
> +
> +/**
> * enum dpu_qos_lut_usage - define QoS LUT use cases
> */
> enum dpu_qos_lut_usage {
> @@ -403,6 +426,7 @@ struct dpu_rotation_cfg {
> * @pixel_ram_size size of latency hiding and de-tiling buffer in bytes
> * @max_hdeci_exp max horizontal decimation supported (max is 2^value)
> * @max_vdeci_exp max vertical decimation supported (max is 2^value)
> + * @max_dsc_width max dsc line width support.
> */
> struct dpu_caps {
> u32 max_mixer_width;
> @@ -419,6 +443,7 @@ struct dpu_caps {
> u32 pixel_ram_size;
> u32 max_hdeci_exp;
> u32 max_vdeci_exp;
> + u32 max_dsc_width;
> };
>
> /**
> @@ -494,9 +519,20 @@ struct dpu_dspp_sub_blks {
> struct dpu_pingpong_sub_blks {
> struct dpu_pp_blk te;
> struct dpu_pp_blk te2;
> + struct dpu_pp_blk dsc;
> struct dpu_pp_blk dither;
> };
>
> +
> +/**
> + * struct dpu_dsc_sub_blks : DSC sub-blks
> + *
> + */
> +struct dpu_dsc_sub_blks {
> + struct dpu_dsc_blk enc;
> + struct dpu_dsc_blk ctl;
> +};
> +
> /**
> * dpu_clk_ctrl_type - Defines top level clock control signals
> */
> @@ -641,10 +677,14 @@ struct dpu_merge_3d_cfg {
> * struct dpu_dsc_cfg - information of DSC blocks
> * @id enum identifying this block
> * @base register offset of this block
> + * @len: length of hardware block
> * @features bit mask identifying sub-blocks/features
> + * @dsc_pair_mask: Bitmask of DSCs that can be controlled by same CTL
> */
> struct dpu_dsc_cfg {
> DPU_HW_BLK_INFO;
> + DECLARE_BITMAP(dsc_pair_mask, DSC_MAX);

Please change this from the bitmap to enum dpu_dsc instance

> + const struct dpu_dsc_sub_blks *sblk;
> };
>
> /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> index 619926d..51e8890 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2020-2022, Linaro Limited
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include "dpu_kms.h"
> @@ -41,10 +42,11 @@ static void dpu_hw_dsc_disable(struct dpu_hw_dsc *dsc)
> static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc,
> u32 mode,
> - u32 initial_lines)
> + u32 initial_lines,
> + bool ich_reset_override)
> {
> struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
> - u32 data;
> + u32 data, lsb, bpp;
> u32 slice_last_group_size;
> u32 det_thresh_flatness;
> bool is_cmd_mode = !(mode & DSC_MODE_VIDEO);
> @@ -58,7 +60,14 @@ static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
> data = (initial_lines << 20);
> data |= ((slice_last_group_size - 1) << 18);
> /* bpp is 6.4 format, 4 LSBs bits are for fractional part */
> - data |= (dsc->bits_per_pixel << 8);
> + data |= dsc->bits_per_pixel << 12;
> + lsb = dsc->bits_per_pixel % 4;
> + bpp = dsc->bits_per_pixel / 4;
> + bpp *= 4;
> + bpp <<= 4;
> + bpp |= lsb;
> +

NAK. This was changed by the commit d3c1a8663d0d ("drm/msm/dpu1: Account
for DSC's bits_per_pixel having 4 fractional bits"). It removed the code
that you are trying to bring back.

> + data |= bpp << 8;
> data |= (dsc->block_pred_enable << 7);
> data |= (dsc->line_buf_depth << 3);
> data |= (dsc->simple_422 << 2);
> @@ -221,7 +230,13 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
>
> c->idx = idx;
> c->caps = cfg;
> - _setup_dsc_ops(&c->ops, c->caps->features);
> +
> + if (test_bit(DPU_DSC_HW_REV_1_1, &c->caps->features))
> + _setup_dsc_ops(&c->ops, c->caps->features);
> + else if (test_bit(DPU_DSC_HW_REV_1_2, &c->caps->features))
> + dpu_dsc_1_2_setup_ops(&c->ops, c->caps->features);
> + else
> + _setup_dsc_ops(&c->ops, c->caps->features);

Can we handle this in a more sensible way, please. E.g. let RM check the
flags and then call either dpu_hw_dsc_1_1_init() or dpu_hw_dsc_1_2_init()?

Granted that to generations of DSC blocks are _that_ different it might
even make sense to handle them separately in the HW catalog too, but I
wouldn't insist on that.

>
> return c;
> }
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> index ae9b5db..a48f572 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> @@ -1,5 +1,8 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> -/* Copyright (c) 2020-2022, Linaro Limited */
> +/*
> + * Copyright (c) 2020-2022, Linaro Limited
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> + */
>
> #ifndef _DPU_HW_DSC_H
> #define _DPU_HW_DSC_H
> @@ -33,7 +36,8 @@ struct dpu_hw_dsc_ops {
> void (*dsc_config)(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc,
> u32 mode,
> - u32 initial_lines);
> + u32 initial_lines,
> + bool ich_reset_override);

Documentation?

>
> /**
> * dsc_config_thresh - programs panel thresholds
> @@ -43,6 +47,12 @@ struct dpu_hw_dsc_ops {
> void (*dsc_config_thresh)(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc);
>
> + /**
> + * bind_pingpong_blk - enable/disable the connection with pp
> + * @hw_dsc: Pointer to dsc context
> + * @enable: enable/disable connection
> + * @pp: pingpong blk id
> + */
> void (*dsc_bind_pingpong_blk)(struct dpu_hw_dsc *hw_dsc,
> bool enable,
> enum dpu_pingpong pp);
> @@ -51,6 +61,7 @@ struct dpu_hw_dsc_ops {
> struct dpu_hw_dsc {
> struct dpu_hw_blk base;
> struct dpu_hw_blk_reg_map hw;
> + struct dpu_hw_ctl *hw_ctl;

Why? This is not used by the rest of the patch.

>
> /* dsc */
> enum dpu_dsc idx;
> @@ -76,9 +87,17 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
> */
> void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);
>
> +/**
> + * dpu_hw_dsc - convert base object dpu_hw_base to container
> + * @hw: Pointer to base hardware block
> + * return: Pointer to hardware block container
> + */

You are adding docs for the obvious items (which is unnecessary and just
clobbers the code), but leaving out important items (for which docs
might be necessary).

> static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
> {
> return container_of(hw, struct dpu_hw_dsc, base);
> }
>
> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
> + const unsigned long features);
> +
> #endif /* _DPU_HW_DSC_H */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
> new file mode 100644
> index 00000000..2be74ae
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
> @@ -0,0 +1,371 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#include "dpu_kms.h"
> +#include "dpu_hw_catalog.h"
> +#include "dpu_hwio.h"
> +#include "dpu_hw_mdss.h"
> +#include "dpu_hw_dsc.h"
> +
> +
> +#define DSC_CMN_MAIN_CNF 0x00
> +
> +/* DPU_DSC_ENC register offsets */
> +#define ENC_DF_CTRL 0x00
> +#define ENC_GENERAL_STATUS 0x04
> +#define ENC_HSLICE_STATUS 0x08
> +#define ENC_OUT_STATUS 0x0C
> +#define ENC_INT_STAT 0x10
> +#define ENC_INT_CLR 0x14
> +#define ENC_INT_MASK 0x18
> +#define DSC_MAIN_CONF 0x30
> +#define DSC_PICTURE_SIZE 0x34
> +#define DSC_SLICE_SIZE 0x38
> +#define DSC_MISC_SIZE 0x3C
> +#define DSC_HRD_DELAYS 0x40
> +#define DSC_RC_SCALE 0x44
> +#define DSC_RC_SCALE_INC_DEC 0x48
> +#define DSC_RC_OFFSETS_1 0x4C
> +#define DSC_RC_OFFSETS_2 0x50
> +#define DSC_RC_OFFSETS_3 0x54
> +#define DSC_RC_OFFSETS_4 0x58
> +#define DSC_FLATNESS_QP 0x5C
> +#define DSC_RC_MODEL_SIZE 0x60
> +#define DSC_RC_CONFIG 0x64
> +#define DSC_RC_BUF_THRESH_0 0x68
> +#define DSC_RC_BUF_THRESH_1 0x6C
> +#define DSC_RC_BUF_THRESH_2 0x70
> +#define DSC_RC_BUF_THRESH_3 0x74
> +#define DSC_RC_MIN_QP_0 0x78
> +#define DSC_RC_MIN_QP_1 0x7C
> +#define DSC_RC_MIN_QP_2 0x80
> +#define DSC_RC_MAX_QP_0 0x84
> +#define DSC_RC_MAX_QP_1 0x88
> +#define DSC_RC_MAX_QP_2 0x8C
> +#define DSC_RC_RANGE_BPG_OFFSETS_0 0x90
> +#define DSC_RC_RANGE_BPG_OFFSETS_1 0x94
> +#define DSC_RC_RANGE_BPG_OFFSETS_2 0x98
> +
> +/* DPU_DSC_CTL register offsets */
> +#define DSC_CTL 0x00
> +#define DSC_CFG 0x04
> +#define DSC_DATA_IN_SWAP 0x08
> +#define DSC_CLK_CTRL 0x0C
> +
> +
> +static int _dsc_calc_ob_max_addr(struct dpu_hw_dsc *hw_dsc, int num_ss)
> +{
> + enum dpu_dsc idx;
> +
> + idx = hw_dsc->idx;
> +
> + if (!(hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_422_EN))) {
> + if (num_ss == 1)
> + return 2399;
> + else if (num_ss == 2)
> + return 1199;
> + } else {
> + if (num_ss == 1)
> + return 1199;
> + else if (num_ss == 2)
> + return 599;
> + }
> + return 0;

int max_addr = 2400 / num_ss;
if (hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_422_EN))
max_addr /= 2;

return max_addr -1;

Isn't this nicer?

> +}
> +
> +static inline int _dsc_subblk_offset(struct dpu_hw_dsc *hw_dsc, int s_id,
> + u32 *idx)
> +{
> + const struct dpu_dsc_sub_blks *sblk;
> +
> + if (!hw_dsc)
> + return -EINVAL;
> +
> + *idx = 0;
> +
> + sblk = hw_dsc->caps->sblk;
> +
> + switch (s_id) {
> +
> + case DPU_DSC_ENC:
> + *idx = sblk->enc.base;
> + break;
> + case DPU_DSC_CTL:
> + *idx = sblk->ctl.base;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void dpu_hw_dsc_disable_1_2(struct dpu_hw_dsc *hw_dsc)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + u32 idx;
> +
> + if (!hw_dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> + DPU_REG_WRITE(hw, DSC_CFG + idx, 0);
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, 0);
> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, 0);
> +}
> +
> +static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
> + struct drm_dsc_config *dsc, u32 mode,
> + u32 initial_lines, bool ich_reset_override)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + struct msm_display_dsc_info *dsc_info;
> + u32 idx;
> + u32 data = 0;
> + u32 bpp;
> + void __iomem *off;
> +
> + if (!hw_dsc || !dsc)
> + return;
> +
> + hw = &hw_dsc->hw;
> +
> + dsc_info = to_msm_dsc_info(dsc);
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + if (mode & DSC_MODE_SPLIT_PANEL)
> + data |= BIT(0);
> +
> + if (mode & DSC_MODE_MULTIPLEX)
> + data |= BIT(1);
> +
> + data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7;
> +
> + DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);
> +
> + data = (dsc_info->initial_lines & 0xff);
> + data |= ((mode & DSC_MODE_VIDEO) ? 1 : 0) << 9;
> + if (ich_reset_override)
> + data |= 0xC00; // set bit 10 and 11

Comment style is wrong. The comment is useless: from 0xc00 we see which
bits are being set. We'd better know why. Please add corresponding
defines to the bits and bitfields in these function.

> + data |= (_dsc_calc_ob_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18);
> +
> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, data);
> +
> + data = (dsc->dsc_version_minor & 0xf) << 28;
> + if (dsc->dsc_version_minor == 0x2) {
> + if (dsc->native_422)
> + data |= BIT(22);
> + if (dsc->native_420)
> + data |= BIT(21);
> + }
> +
> + bpp = dsc->bits_per_pixel;
> + /* as per hw requirement bpp should be programmed
> + * twice the actual value in case of 420 or 422 encoding
> + */
> + if (dsc->native_422 || dsc->native_420)
> + bpp = 2 * bpp;
> + data |= (dsc->block_pred_enable ? 1 : 0) << 20;
> + data |= (bpp << 10);
> + data |= (dsc->line_buf_depth & 0xf) << 6;
> + data |= dsc->convert_rgb << 4;
> + data |= dsc->bits_per_component & 0xf;
> +
> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, data);
> +
> + data = (dsc->pic_width & 0xffff) |
> + ((dsc->pic_height & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_PICTURE_SIZE + idx, data);
> +
> + data = (dsc->slice_width & 0xffff) |
> + ((dsc->slice_height & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_SLICE_SIZE + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_MISC_SIZE + idx,
> + (dsc->slice_chunk_size) & 0xffff);
> +
> + data = (dsc->initial_xmit_delay & 0xffff) |
> + ((dsc->initial_dec_delay & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_HRD_DELAYS + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_RC_SCALE + idx,
> + dsc->initial_scale_value & 0x3f);
> +
> + data = (dsc->scale_increment_interval & 0xffff) |
> + ((dsc->scale_decrement_interval & 0x7ff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_SCALE_INC_DEC + idx, data);
> +
> + data = (dsc->first_line_bpg_offset & 0x1f) |
> + ((dsc->second_line_bpg_offset & 0x1f) << 5);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_1 + idx, data);
> +
> + data = (dsc->nfl_bpg_offset & 0xffff) |
> + ((dsc->slice_bpg_offset & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_2 + idx, data);
> +
> + data = (dsc->initial_offset & 0xffff) |
> + ((dsc->final_offset & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_3 + idx, data);
> +
> + data = (dsc->nsl_bpg_offset & 0xffff) |
> + ((dsc->second_line_offset_adj & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_4 + idx, data);
> +
> + data = (dsc->flatness_min_qp & 0x1f);
> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
> +
> + DPU_REG_WRITE(hw, DSC_FLATNESS_QP + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_RC_MODEL_SIZE + idx,
> + (dsc->rc_model_size) & 0xffff);
> +
> + data = dsc->rc_edge_factor & 0xf;
> + data |= (dsc->rc_quant_incr_limit0 & 0x1f) << 8;
> + data |= (dsc->rc_quant_incr_limit1 & 0x1f) << 13;
> + data |= (dsc->rc_tgt_offset_high & 0xf) << 20;
> + data |= (dsc->rc_tgt_offset_low & 0xf) << 24;
> +
> + DPU_REG_WRITE(hw, DSC_RC_CONFIG + idx, data);
> +
> + /* program the dsc wrapper */
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + off = hw->blk_addr + idx;
> +
> + data = BIT(0); /* encoder enable */
> + if (dsc->native_422)
> + data |= BIT(8);
> + else if (dsc->native_420)
> + data |= BIT(9);
> + if (!dsc->convert_rgb)
> + data |= BIT(10);
> + if (dsc->bits_per_component == 8)
> + data |= BIT(11);
> + if (mode & DSC_MODE_SPLIT_PANEL)
> + data |= BIT(12);
> + if (mode & DSC_MODE_MULTIPLEX)
> + data |= BIT(13);
> + if (!(mode & DSC_MODE_VIDEO))
> + data |= BIT(17);
> +
> + if (dsc_info->dsc_4hsmerge_en) {
> + data |= dsc_info->dsc_4hsmerge_padding << 18;
> + data |= dsc_info->dsc_4hsmerge_alignment << 22;
> + data |= BIT(16);
> + }
> +
> + DPU_REG_WRITE(hw, DSC_CFG + idx, data);
> +
> +// DPU_REG_WRITE(hw, DSC_DATA_IN_SWAP + idx, 0x14e5);

Is this necessary or not? If not, please drop it.

> +}
> +
> +static void dpu_hw_dsc_config_thresh_1_2(struct dpu_hw_dsc *hw_dsc,
> + struct drm_dsc_config *dsc)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + struct msm_display_dsc_info *dsc_info;
> + u32 idx, off;
> + int i, j = 0;
> + struct drm_dsc_rc_range_parameters *rc;
> + u32 data = 0, min_qp = 0, max_qp = 0, bpg_off = 0;
> +
> + if (!hw_dsc || !dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> +
> + dsc_info = to_msm_dsc_info(dsc);
> +
> + rc = dsc->rc_range_params;
> +
> + off = 0;
> + for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
> + data |= dsc->rc_buf_thresh[i] << (8*j);
> + j++;
> + if ((j == 4) || (i == DSC_NUM_BUF_RANGES - 2)) {
> + DPU_REG_WRITE(hw, DSC_RC_BUF_THRESH_0 + idx + off,
> + data);
> + off += 4;
> + j = 0;
> + data = 0;
> + }
> + }
> +
> + off = 0;
> + for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
> + min_qp |= (rc[i].range_min_qp & 0x1f) << 5*j;
> + max_qp |= (rc[i].range_max_qp & 0x1f) << 5*j;
> + bpg_off |= (rc[i].range_bpg_offset & 0x3f) << 6*j;
> + j++;
> + if (j == 5) {
> + DPU_REG_WRITE(hw, DSC_RC_MIN_QP_0 + idx + off,
> + min_qp);
> + DPU_REG_WRITE(hw, DSC_RC_MAX_QP_0 + idx + off,
> + max_qp);
> + DPU_REG_WRITE(hw,
> + DSC_RC_RANGE_BPG_OFFSETS_0 + idx + off,
> + bpg_off);
> + off += 4;
> + j = 0;
> + min_qp = 0;
> + max_qp = 0;
> + bpg_off = 0;
> + }
> + }
> +}
> +
> +static void dpu_hw_dsc_bind_pingpong_blk_1_2(
> + struct dpu_hw_dsc *hw_dsc,
> + bool enable,
> + const enum dpu_pingpong pp)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + int idx;
> + int mux_cfg = 0xF; /* Disabled */
> +
> + if (!hw_dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> + if (enable)
> + mux_cfg = (pp - PINGPONG_0) & 0x7;
> +
> + DPU_REG_WRITE(hw, DSC_CTL + idx, mux_cfg);
> +}
> +
> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
> + const unsigned long features)
> +{
> + ops->dsc_disable = dpu_hw_dsc_disable_1_2;
> + ops->dsc_config = dpu_hw_dsc_config_1_2;
> + ops->dsc_config_thresh = dpu_hw_dsc_config_thresh_1_2;
> + ops->dsc_bind_pingpong_blk = dpu_hw_dsc_bind_pingpong_blk_1_2;
> +}

--
With best wishes
Dmitry


2023-01-24 09:11:34

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC

On 23/01/2023 20:24, Kuogee Hsieh wrote:
> Current implementation timing engine programming does not consider
> compression factors. This patch add consideration of DSC factors
> while programming timing engine.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 2 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 14 ++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 +++++++++++++--------
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
> 5 files changed, 110 insertions(+), 54 deletions(-)
>

[skipped]

> @@ -113,82 +124,96 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
> /* read interface_cfg */
> intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
>
> - if (ctx->cap->type == INTF_DP)
> + if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP)
> dp_intf = true;
>
> hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width +
> - p->h_front_porch;
> + p->h_front_porch;
> vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height +
> - p->v_front_porch;
> + p->v_front_porch;

Actually I went on through the history and found the previous
submission, https://patchwork.freedesktop.org/patch/471505/.
Exactly the same piece of code. Did you expect that the comments will be
different this time?

I really hoped that at that time we already went through this. But it
seems I was wrong. That series went through v10 or v12 before being
accepted. And it was just adding wide_bus_en. Back at that time we
lightly discussed that the code will receive compression support. But I
never expected to see the original submission again.

It might sound bad, but could you please find somebody who can do
internal review for you? Good internal review.

That said, I really do not expect to see v2 before the whole series is
reworked, restructured and prepared for the review on your side.

>
> display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
> - hsync_period) + p->hsync_skew;
> + hsync_period) + p->hsync_skew;
> display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) +
> - p->hsync_skew - 1;
> + p->hsync_skew - 1;
> +
> + hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
>
> hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
> hsync_end_x = hsync_period - p->h_front_porch - 1;
>
> - if (p->width != p->xres) { /* border fill added */
> - active_h_start = hsync_start_x;
> - active_h_end = active_h_start + p->xres - 1;
> - } else {
> - active_h_start = 0;
> - active_h_end = 0;
> - }
> -
> - if (p->height != p->yres) { /* border fill added */
> - active_v_start = display_v_start;
> - active_v_end = active_v_start + (p->yres * hsync_period) - 1;
> - } else {
> - active_v_start = 0;
> - active_v_end = 0;
> - }
> -
> - if (active_h_end) {
> - active_hctl = (active_h_end << 16) | active_h_start;
> - intf_cfg |= INTF_CFG_ACTIVE_H_EN;
> - } else {
> - active_hctl = 0;
> - }
> -
> - if (active_v_end)
> - intf_cfg |= INTF_CFG_ACTIVE_V_EN;
> -
> - hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
> - display_hctl = (hsync_end_x << 16) | hsync_start_x;
> -
> /*
> * DATA_HCTL_EN controls data timing which can be different from
> * video timing. It is recommended to enable it for all cases, except
> * if compression is enabled in 1 pixel per clock mode
> */
> + if (!p->compression_en || p->wide_bus_en)
> + intf_cfg2 |= INTF_CFG2_DATA_HCTL_EN;
> +
> if (p->wide_bus_en)
> - intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN | INTF_CFG2_DATA_HCTL_EN;
> + intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
>
> + /*
> + * If widebus is disabled:
> + * For uncompressed stream, the data is valid for the entire active
> + * window period.
> + * For compressed stream, data is valid for a shorter time period
> + * inside the active window depending on the compression ratio.
> + *
> + * If widebus is enabled:
> + * For uncompressed stream, data is valid for only half the active
> + * window, since the data rate is doubled in this mode.
> + * p->width holds the adjusted width for DP but unadjusted width for DSI
> + * For compressed stream, data validity window needs to be adjusted for
> + * compression ratio and then further halved.
> + */
> data_width = p->width;
>
> + if (p->compression_en) {
> + if (p->wide_bus_en)
> + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6);
> + else
> + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3);
> + } else if (!dp_intf && p->wide_bus_en) {
> + data_width = p->width >> 1;
> + } else {
> + data_width = p->width;
> + }
> +
> hsync_data_start_x = hsync_start_x;
> hsync_data_end_x = hsync_start_x + data_width - 1;
>
> + display_hctl = (hsync_end_x << 16) | hsync_start_x;
> display_data_hctl = (hsync_data_end_x << 16) | hsync_data_start_x;
>
> if (dp_intf) {
> /* DP timing adjustment */
> display_v_start += p->hsync_pulse_width + p->h_back_porch;
> display_v_end -= p->h_front_porch;
> + }
> +
> + intf_cfg |= INTF_CFG_ACTIVE_H_EN;
> + intf_cfg |= INTF_CFG_ACTIVE_V_EN;
> + active_h_start = hsync_start_x;
> + active_h_end = active_h_start + p->xres - 1;
> + active_v_start = display_v_start;
> + active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>
> - active_h_start = hsync_start_x;
> - active_h_end = active_h_start + p->xres - 1;
> - active_v_start = display_v_start;
> - active_v_end = active_v_start + (p->yres * hsync_period) - 1;
> + active_hctl = (active_h_end << 16) | active_h_start;
>
> - active_hctl = (active_h_end << 16) | active_h_start;
> + if (dp_intf) {
> display_hctl = active_hctl;
>
> - intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN;
> + if (p->compression_en) {
> + active_data_hctl = (hsync_start_x + p->extra_dto_cycles) << 16;
> + active_data_hctl += hsync_start_x;
> +
> + display_data_hctl = active_data_hctl;
> + }
> }
>
> + _check_and_set_comp_bit(ctx, p->dsc_4hs_merge, p->compression_en, &intf_cfg2);
> +
> den_polarity = 0;
> if (ctx->cap->type == INTF_HDMI) {
> hsync_polarity = p->yres >= 720 ? 0 : 1;
> @@ -202,7 +227,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,

--
With best wishes
Dmitry


2023-01-24 15:07:07

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v1 09/14] drm/msm/dsi: export struct msm_compression_info to dpu encoder

On 23/01/2023 19:24, Kuogee Hsieh wrote:
> struct msm_compression_info is used to support several different
> compression mechanisms. It also contains customized info required
> to configure DSC encoder engine. This patch also make changes DSI
> module to have DSI exports struct msm_compreion_info to dpu encoder
> instead of struct drm_dsc_config.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 7 +++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 ++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 ++++++++--
> drivers/gpu/drm/msm/dsi/dsi.c | 3 ++-
> drivers/gpu/drm/msm/dsi/dsi.h | 3 ++-
> drivers/gpu/drm/msm/dsi/dsi_host.c | 14 ++++++++++++--
> drivers/gpu/drm/msm/msm_drv.h | 4 ++--
> 7 files changed, 33 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 758261e..7f4a439 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -2,7 +2,7 @@
> /*
> * Copyright (C) 2013 Red Hat
> * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> *
> * Author: Rob Clark <[email protected]>
> */
> @@ -210,6 +210,7 @@ struct dpu_encoder_virt {
>
> /* DSC configuration */
> struct drm_dsc_config *dsc;
> + struct msm_compression_info *comp_info;
> };
>
> #define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base)
> @@ -2275,7 +2276,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
> dpu_enc->idle_pc_supported =
> dpu_kms->catalog->caps->has_idle_pc;
>
> - dpu_enc->dsc = disp_info->dsc;
> + dpu_enc->comp_info = disp_info->comp_info;
> + if (dpu_enc->comp_info)
> + dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;
>
> mutex_lock(&dpu_enc->enc_lock);
> for (i = 0; i < disp_info->num_of_h_tiles && !ret; i++) {
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> index 9e7236e..bd2da5e 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> * Copyright (C) 2013 Red Hat
> * Author: Rob Clark <[email protected]>
> @@ -36,7 +36,7 @@ struct msm_display_info {
> uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];
> bool is_cmd_mode;
> bool is_te_using_watchdog_timer;
> - struct drm_dsc_config *dsc;
> + struct msm_compression_info *comp_info;
> };
>
> /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index d612419..70a74ed 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -2,7 +2,7 @@
> /*
> * Copyright (C) 2013 Red Hat
> * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> *
> * Author: Rob Clark <[email protected]>
> */
> @@ -570,7 +570,7 @@ static int _dpu_kms_initialize_dsi(struct drm_device *dev,
> info.h_tile_instance[info.num_of_h_tiles++] = i;
> info.is_cmd_mode = msm_dsi_is_cmd_mode(priv->dsi[i]);
>
> - info.dsc = msm_dsi_get_dsc_config(priv->dsi[i]);
> + info.comp_info = msm_dsi_get_dsc_config(priv->dsi[i]);

This breaks DSC on DSI since before we had a pointer of the DSC struct that would be filled
by the DSI driver later in pre_enable(), and now we have a copy of it before it is filled.

Instead we should keep a pointer of the DSC struct in the comp_info, store the DP dsc struct in the
dp panel private and pass the dsc pointer back in dp_panel_dsc_prepare_basic_params().

>
> if (msm_dsi_is_bonded_dsi(priv->dsi[i]) && priv->dsi[other]) {
> rc = msm_dsi_modeset_init(priv->dsi[other], dev, encoder);
> @@ -622,6 +622,8 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
> info.num_of_h_tiles = 1;
> info.h_tile_instance[0] = i;
> info.intf_type = encoder->encoder_type;
> + info.is_cmd_mode = 0; /* dp always video mode */
> + info.comp_info = NULL;
> rc = dpu_encoder_setup(dev, encoder, &info);
> if (rc) {
> DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
> @@ -892,6 +894,10 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
>
> pm_runtime_get_sync(&dpu_kms->pdev->dev);
>
> + for (i = 0; i < cat->dsc_count; i++)
> + msm_disp_snapshot_add_block(disp_state, cat->dsc[i].len,
> + dpu_kms->mmio + cat->dsc[i].base, "dsc_%d", i);
> +
> /* dump CTL sub-blocks HW regs info */
> for (i = 0; i < cat->ctl_count; i++)
> msm_disp_snapshot_add_block(disp_state, cat->ctl[i].len,
> diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
> index 31fdee2..52b7e33 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi.c
> +++ b/drivers/gpu/drm/msm/dsi/dsi.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include "dsi.h"
> @@ -13,7 +14,7 @@ bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi)
> return !(host_flags & MIPI_DSI_MODE_VIDEO);
> }
>
> -struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> +struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> {
> return msm_dsi_host_get_dsc_config(msm_dsi->host);
> }
> diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
> index bd3763a..79ada54 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi.h
> +++ b/drivers/gpu/drm/msm/dsi/dsi.h
> @@ -1,6 +1,7 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #ifndef __DSI_CONNECTOR_H__
> @@ -133,7 +134,7 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
> int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
> void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host);
> void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host);
> -struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
> +struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
>
> /* dsi phy */
> struct msm_dsi_phy;
> diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
> index 18fa30e..6188f4b 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi_host.c
> +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include <linux/clk.h>
> @@ -163,6 +164,7 @@ struct msm_dsi_host {
>
> struct drm_display_mode *mode;
> struct drm_dsc_config *dsc;
> + struct msm_compression_info comp_info;
>
> /* connected device info */
> unsigned int channel;
> @@ -2600,9 +2602,17 @@ void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host)
> DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER);
> }
>
> -struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
> +struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
> {
> struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
> + struct msm_compression_info *comp_info = NULL;
>
> - return msm_host->dsc;
> + if (msm_host->dsc) {
> + comp_info = &msm_host->comp_info;
> + comp_info->msm_dsc_info.drm_dsc = *msm_host->dsc;
> + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC;
> + comp_info->enabled = true;
> + }
> +
> + return comp_info;
> }
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 6a46ed7..eab0901 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -430,7 +430,7 @@ void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi
> bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi);
> bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi);
> bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi);
> -struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
> +struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
> #else
> static inline void __init msm_dsi_register(void)
> {
> @@ -460,7 +460,7 @@ static inline bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi)
> return false;
> }
>
> -static inline struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> +static inline struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
> {
> return NULL;
> }


2023-01-24 15:19:02

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

On 23/01/2023 19:24, Kuogee Hsieh wrote:
> DSC V1.2 encoder engine is newly added hardware module. This patch
> add support functions to configure and enable DSC V1.2 encoder engine.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/Makefile | 1 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 2 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 60 +++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++++++++++++++++++
> 6 files changed, 463 insertions(+), 17 deletions(-)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 28cf52b..271c29a15 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> disp/dpu1/dpu_hw_catalog.o \
> disp/dpu1/dpu_hw_ctl.o \
> disp/dpu1/dpu_hw_dsc.o \
> + disp/dpu1/dpu_hw_dsc_1_2.o \
> disp/dpu1/dpu_dsc_helper.o \
> disp/dpu1/dpu_hw_interrupts.o \
> disp/dpu1/dpu_hw_intf.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 7f4a439..901e317 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1821,7 +1821,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
> u32 initial_lines)
> {
> if (hw_dsc->ops.dsc_config)
> - hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines);
> + hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);
>
> if (hw_dsc->ops.dsc_config_thresh)
> hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> index 978e3bd..7b0b092 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
> */
>
> @@ -11,6 +11,7 @@
> #include <linux/bug.h>
> #include <linux/bitmap.h>
> #include <linux/err.h>
> +#include "dpu_hw_mdss.h"
>
> /**
> * Max hardware block count: For ex: max 12 SSPP pipes or
> @@ -182,6 +183,7 @@ enum {
> * @DPU_PINGPONG_TE2 Additional tear check block for split pipes
> * @DPU_PINGPONG_SPLIT PP block supports split fifo
> * @DPU_PINGPONG_SLAVE PP block is a suitable slave for split fifo
> + * @DPU_PINGPONG_DSC, Display stream compression blocks
> * @DPU_PINGPONG_DITHER, Dither blocks
> * @DPU_PINGPONG_MAX
> */
> @@ -190,10 +192,32 @@ enum {
> DPU_PINGPONG_TE2,
> DPU_PINGPONG_SPLIT,
> DPU_PINGPONG_SLAVE,
> + DPU_PINGPONG_DSC,
> DPU_PINGPONG_DITHER,
> DPU_PINGPONG_MAX
> };
>
> +
> +/** DSC sub-blocks/features
> + * @DPU_DSC_OUTPUT_CTRL Supports the control of the pp id which gets
> + * the pixel output from this DSC.
> + * @DPU_DSC_HW_REV_1_1 dsc block supports dsc 1.1 only
> + * @DPU_DSC_HW_REV_1_2 dsc block supports dsc 1.1 and 1.2
> + * @DPU_DSC_NATIVE_422_EN, Supports native422 and native420 encoding
> + * @DPU_DSC_ENC, DSC encoder sub block
> + * @DPU_DSC_CTL, DSC ctl sub block
> + * @DPU_DSC_MAX
> + */
> +enum {
> + DPU_DSC_OUTPUT_CTRL = 0x1,
> + DPU_DSC_HW_REV_1_1,
> + DPU_DSC_HW_REV_1_2,
> + DPU_DSC_NATIVE_422_EN,
> + DPU_DSC_ENC,
> + DPU_DSC_CTL,
> + DPU_DSC_MAX
> +};
> +
> /**
> * CTL sub-blocks
> * @DPU_CTL_SPLIT_DISPLAY: CTL supports video mode split display
> @@ -276,15 +300,6 @@ enum {
> };
>
> /**
> - * DSC features
> - * @DPU_DSC_OUTPUT_CTRL Configure which PINGPONG block gets
> - * the pixel output from this DSC.
> - */
> -enum {
> - DPU_DSC_OUTPUT_CTRL = 0x1,
> -};
> -
> -/**
> * MACRO DPU_HW_BLK_INFO - information of HW blocks inside DPU
> * @name: string name for debug purposes
> * @id: enum identifying this block
> @@ -346,6 +361,14 @@ struct dpu_pp_blk {
> };
>
> /**
> + * struct dpu_dsc_blk : DSC Encoder sub-blk information
> + * @info: HW register and features supported by this sub-blk
> + */
> +struct dpu_dsc_blk {
> + DPU_HW_SUBBLK_INFO;
> +};
> +
> +/**
> * enum dpu_qos_lut_usage - define QoS LUT use cases
> */
> enum dpu_qos_lut_usage {
> @@ -403,6 +426,7 @@ struct dpu_rotation_cfg {
> * @pixel_ram_size size of latency hiding and de-tiling buffer in bytes
> * @max_hdeci_exp max horizontal decimation supported (max is 2^value)
> * @max_vdeci_exp max vertical decimation supported (max is 2^value)
> + * @max_dsc_width max dsc line width support.
> */
> struct dpu_caps {
> u32 max_mixer_width;
> @@ -419,6 +443,7 @@ struct dpu_caps {
> u32 pixel_ram_size;
> u32 max_hdeci_exp;
> u32 max_vdeci_exp;
> + u32 max_dsc_width;
> };
>
> /**
> @@ -494,9 +519,20 @@ struct dpu_dspp_sub_blks {
> struct dpu_pingpong_sub_blks {
> struct dpu_pp_blk te;
> struct dpu_pp_blk te2;
> + struct dpu_pp_blk dsc;
> struct dpu_pp_blk dither;
> };
>
> +
> +/**
> + * struct dpu_dsc_sub_blks : DSC sub-blks
> + *
> + */
> +struct dpu_dsc_sub_blks {
> + struct dpu_dsc_blk enc;
> + struct dpu_dsc_blk ctl;
> +};
> +
> /**
> * dpu_clk_ctrl_type - Defines top level clock control signals
> */
> @@ -641,10 +677,14 @@ struct dpu_merge_3d_cfg {
> * struct dpu_dsc_cfg - information of DSC blocks
> * @id enum identifying this block
> * @base register offset of this block
> + * @len: length of hardware block
> * @features bit mask identifying sub-blocks/features
> + * @dsc_pair_mask: Bitmask of DSCs that can be controlled by same CTL
> */
> struct dpu_dsc_cfg {
> DPU_HW_BLK_INFO;
> + DECLARE_BITMAP(dsc_pair_mask, DSC_MAX);
> + const struct dpu_dsc_sub_blks *sblk;
> };
>
> /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> index 619926d..51e8890 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2020-2022, Linaro Limited
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> */
>
> #include "dpu_kms.h"
> @@ -41,10 +42,11 @@ static void dpu_hw_dsc_disable(struct dpu_hw_dsc *dsc)
> static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc,
> u32 mode,
> - u32 initial_lines)
> + u32 initial_lines,
> + bool ich_reset_override)
> {
> struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
> - u32 data;
> + u32 data, lsb, bpp;
> u32 slice_last_group_size;
> u32 det_thresh_flatness;
> bool is_cmd_mode = !(mode & DSC_MODE_VIDEO);
> @@ -58,7 +60,14 @@ static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
> data = (initial_lines << 20);
> data |= ((slice_last_group_size - 1) << 18);
> /* bpp is 6.4 format, 4 LSBs bits are for fractional part */
> - data |= (dsc->bits_per_pixel << 8);
> + data |= dsc->bits_per_pixel << 12;
> + lsb = dsc->bits_per_pixel % 4;
> + bpp = dsc->bits_per_pixel / 4;
> + bpp *= 4;
> + bpp <<= 4;
> + bpp |= lsb;
> +
> + data |= bpp << 8;
> data |= (dsc->block_pred_enable << 7);
> data |= (dsc->line_buf_depth << 3);
> data |= (dsc->simple_422 << 2);
> @@ -221,7 +230,13 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
>
> c->idx = idx;
> c->caps = cfg;
> - _setup_dsc_ops(&c->ops, c->caps->features);
> +
> + if (test_bit(DPU_DSC_HW_REV_1_1, &c->caps->features))
> + _setup_dsc_ops(&c->ops, c->caps->features);
> + else if (test_bit(DPU_DSC_HW_REV_1_2, &c->caps->features))
> + dpu_dsc_1_2_setup_ops(&c->ops, c->caps->features);
> + else
> + _setup_dsc_ops(&c->ops, c->caps->features);
>
> return c;
> }
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> index ae9b5db..a48f572 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
> @@ -1,5 +1,8 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> -/* Copyright (c) 2020-2022, Linaro Limited */
> +/*
> + * Copyright (c) 2020-2022, Linaro Limited
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> + */
>
> #ifndef _DPU_HW_DSC_H
> #define _DPU_HW_DSC_H
> @@ -33,7 +36,8 @@ struct dpu_hw_dsc_ops {
> void (*dsc_config)(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc,
> u32 mode,
> - u32 initial_lines);
> + u32 initial_lines,
> + bool ich_reset_override);
>
> /**
> * dsc_config_thresh - programs panel thresholds
> @@ -43,6 +47,12 @@ struct dpu_hw_dsc_ops {
> void (*dsc_config_thresh)(struct dpu_hw_dsc *hw_dsc,
> struct drm_dsc_config *dsc);
>
> + /**
> + * bind_pingpong_blk - enable/disable the connection with pp
> + * @hw_dsc: Pointer to dsc context
> + * @enable: enable/disable connection
> + * @pp: pingpong blk id
> + */
> void (*dsc_bind_pingpong_blk)(struct dpu_hw_dsc *hw_dsc,
> bool enable,
> enum dpu_pingpong pp);
> @@ -51,6 +61,7 @@ struct dpu_hw_dsc_ops {
> struct dpu_hw_dsc {
> struct dpu_hw_blk base;
> struct dpu_hw_blk_reg_map hw;
> + struct dpu_hw_ctl *hw_ctl;
>
> /* dsc */
> enum dpu_dsc idx;
> @@ -76,9 +87,17 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
> */
> void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);
>
> +/**
> + * dpu_hw_dsc - convert base object dpu_hw_base to container
> + * @hw: Pointer to base hardware block
> + * return: Pointer to hardware block container
> + */
> static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
> {
> return container_of(hw, struct dpu_hw_dsc, base);
> }
>
> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
> + const unsigned long features);
> +
> #endif /* _DPU_HW_DSC_H */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
> new file mode 100644
> index 00000000..2be74ae
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
> @@ -0,0 +1,371 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#include "dpu_kms.h"
> +#include "dpu_hw_catalog.h"
> +#include "dpu_hwio.h"
> +#include "dpu_hw_mdss.h"
> +#include "dpu_hw_dsc.h"
> +
> +
> +#define DSC_CMN_MAIN_CNF 0x00
> +
> +/* DPU_DSC_ENC register offsets */
> +#define ENC_DF_CTRL 0x00
> +#define ENC_GENERAL_STATUS 0x04
> +#define ENC_HSLICE_STATUS 0x08
> +#define ENC_OUT_STATUS 0x0C
> +#define ENC_INT_STAT 0x10
> +#define ENC_INT_CLR 0x14
> +#define ENC_INT_MASK 0x18
> +#define DSC_MAIN_CONF 0x30
> +#define DSC_PICTURE_SIZE 0x34
> +#define DSC_SLICE_SIZE 0x38
> +#define DSC_MISC_SIZE 0x3C
> +#define DSC_HRD_DELAYS 0x40
> +#define DSC_RC_SCALE 0x44
> +#define DSC_RC_SCALE_INC_DEC 0x48
> +#define DSC_RC_OFFSETS_1 0x4C
> +#define DSC_RC_OFFSETS_2 0x50
> +#define DSC_RC_OFFSETS_3 0x54
> +#define DSC_RC_OFFSETS_4 0x58
> +#define DSC_FLATNESS_QP 0x5C
> +#define DSC_RC_MODEL_SIZE 0x60
> +#define DSC_RC_CONFIG 0x64
> +#define DSC_RC_BUF_THRESH_0 0x68
> +#define DSC_RC_BUF_THRESH_1 0x6C
> +#define DSC_RC_BUF_THRESH_2 0x70
> +#define DSC_RC_BUF_THRESH_3 0x74
> +#define DSC_RC_MIN_QP_0 0x78
> +#define DSC_RC_MIN_QP_1 0x7C
> +#define DSC_RC_MIN_QP_2 0x80
> +#define DSC_RC_MAX_QP_0 0x84
> +#define DSC_RC_MAX_QP_1 0x88
> +#define DSC_RC_MAX_QP_2 0x8C
> +#define DSC_RC_RANGE_BPG_OFFSETS_0 0x90
> +#define DSC_RC_RANGE_BPG_OFFSETS_1 0x94
> +#define DSC_RC_RANGE_BPG_OFFSETS_2 0x98
> +
> +/* DPU_DSC_CTL register offsets */
> +#define DSC_CTL 0x00
> +#define DSC_CFG 0x04
> +#define DSC_DATA_IN_SWAP 0x08
> +#define DSC_CLK_CTRL 0x0C
> +
> +
> +static int _dsc_calc_ob_max_addr(struct dpu_hw_dsc *hw_dsc, int num_ss)
> +{
> + enum dpu_dsc idx;
> +
> + idx = hw_dsc->idx;
> +
> + if (!(hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_422_EN))) {
> + if (num_ss == 1)
> + return 2399;
> + else if (num_ss == 2)
> + return 1199;
> + } else {
> + if (num_ss == 1)
> + return 1199;
> + else if (num_ss == 2)
> + return 599;
> + }
> + return 0;
> +}
> +
> +static inline int _dsc_subblk_offset(struct dpu_hw_dsc *hw_dsc, int s_id,
> + u32 *idx)
> +{
> + const struct dpu_dsc_sub_blks *sblk;
> +
> + if (!hw_dsc)
> + return -EINVAL;
> +
> + *idx = 0;
> +
> + sblk = hw_dsc->caps->sblk;
> +
> + switch (s_id) {
> +
> + case DPU_DSC_ENC:
> + *idx = sblk->enc.base;
> + break;
> + case DPU_DSC_CTL:
> + *idx = sblk->ctl.base;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void dpu_hw_dsc_disable_1_2(struct dpu_hw_dsc *hw_dsc)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + u32 idx;
> +
> + if (!hw_dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> + DPU_REG_WRITE(hw, DSC_CFG + idx, 0);
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, 0);
> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, 0);
> +}
> +
> +static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
> + struct drm_dsc_config *dsc, u32 mode,
> + u32 initial_lines, bool ich_reset_override)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + struct msm_display_dsc_info *dsc_info;
> + u32 idx;
> + u32 data = 0;
> + u32 bpp;
> + void __iomem *off;
> +
> + if (!hw_dsc || !dsc)
> + return;
> +
> + hw = &hw_dsc->hw;
> +
> + dsc_info = to_msm_dsc_info(dsc);


Please don't do that, the architecture of dsc_info is crap, if you *really* need some
values that are not part of drm_dsc, then pass this *new" struct as parameter of dsc_config()
and change the dsc_config() declaration, it's not a problem.

> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + if (mode & DSC_MODE_SPLIT_PANEL)
> + data |= BIT(0);
> +
> + if (mode & DSC_MODE_MULTIPLEX)
> + data |= BIT(1);
> +
> + data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7;
> +
> + DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);
> +
> + data = (dsc_info->initial_lines & 0xff);
> + data |= ((mode & DSC_MODE_VIDEO) ? 1 : 0) << 9;
> + if (ich_reset_override)
> + data |= 0xC00; // set bit 10 and 11
> + data |= (_dsc_calc_ob_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18);
> +
> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, data);
> +
> + data = (dsc->dsc_version_minor & 0xf) << 28;
> + if (dsc->dsc_version_minor == 0x2) {
> + if (dsc->native_422)
> + data |= BIT(22);
> + if (dsc->native_420)
> + data |= BIT(21);
> + }
> +
> + bpp = dsc->bits_per_pixel;
> + /* as per hw requirement bpp should be programmed
> + * twice the actual value in case of 420 or 422 encoding
> + */
> + if (dsc->native_422 || dsc->native_420)
> + bpp = 2 * bpp;
> + data |= (dsc->block_pred_enable ? 1 : 0) << 20;
> + data |= (bpp << 10);
> + data |= (dsc->line_buf_depth & 0xf) << 6;
> + data |= dsc->convert_rgb << 4;
> + data |= dsc->bits_per_component & 0xf;
> +
> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, data);
> +
> + data = (dsc->pic_width & 0xffff) |
> + ((dsc->pic_height & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_PICTURE_SIZE + idx, data);
> +
> + data = (dsc->slice_width & 0xffff) |
> + ((dsc->slice_height & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_SLICE_SIZE + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_MISC_SIZE + idx,
> + (dsc->slice_chunk_size) & 0xffff);
> +
> + data = (dsc->initial_xmit_delay & 0xffff) |
> + ((dsc->initial_dec_delay & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_HRD_DELAYS + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_RC_SCALE + idx,
> + dsc->initial_scale_value & 0x3f);
> +
> + data = (dsc->scale_increment_interval & 0xffff) |
> + ((dsc->scale_decrement_interval & 0x7ff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_SCALE_INC_DEC + idx, data);
> +
> + data = (dsc->first_line_bpg_offset & 0x1f) |
> + ((dsc->second_line_bpg_offset & 0x1f) << 5);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_1 + idx, data);
> +
> + data = (dsc->nfl_bpg_offset & 0xffff) |
> + ((dsc->slice_bpg_offset & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_2 + idx, data);
> +
> + data = (dsc->initial_offset & 0xffff) |
> + ((dsc->final_offset & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_3 + idx, data);
> +
> + data = (dsc->nsl_bpg_offset & 0xffff) |
> + ((dsc->second_line_offset_adj & 0xffff) << 16);
> +
> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_4 + idx, data);
> +
> + data = (dsc->flatness_min_qp & 0x1f);
> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
> +
> + DPU_REG_WRITE(hw, DSC_FLATNESS_QP + idx, data);
> +
> + DPU_REG_WRITE(hw, DSC_RC_MODEL_SIZE + idx,
> + (dsc->rc_model_size) & 0xffff);
> +
> + data = dsc->rc_edge_factor & 0xf;
> + data |= (dsc->rc_quant_incr_limit0 & 0x1f) << 8;
> + data |= (dsc->rc_quant_incr_limit1 & 0x1f) << 13;
> + data |= (dsc->rc_tgt_offset_high & 0xf) << 20;
> + data |= (dsc->rc_tgt_offset_low & 0xf) << 24;
> +
> + DPU_REG_WRITE(hw, DSC_RC_CONFIG + idx, data);
> +
> + /* program the dsc wrapper */
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + off = hw->blk_addr + idx;
> +
> + data = BIT(0); /* encoder enable */
> + if (dsc->native_422)
> + data |= BIT(8);
> + else if (dsc->native_420)
> + data |= BIT(9);
> + if (!dsc->convert_rgb)
> + data |= BIT(10);
> + if (dsc->bits_per_component == 8)
> + data |= BIT(11);
> + if (mode & DSC_MODE_SPLIT_PANEL)
> + data |= BIT(12);
> + if (mode & DSC_MODE_MULTIPLEX)
> + data |= BIT(13);
> + if (!(mode & DSC_MODE_VIDEO))
> + data |= BIT(17);
> +
> + if (dsc_info->dsc_4hsmerge_en) {
> + data |= dsc_info->dsc_4hsmerge_padding << 18;
> + data |= dsc_info->dsc_4hsmerge_alignment << 22;
> + data |= BIT(16);
> + }
> +
> + DPU_REG_WRITE(hw, DSC_CFG + idx, data);
> +
> +// DPU_REG_WRITE(hw, DSC_DATA_IN_SWAP + idx, 0x14e5);
> +}
> +
> +static void dpu_hw_dsc_config_thresh_1_2(struct dpu_hw_dsc *hw_dsc,
> + struct drm_dsc_config *dsc)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + struct msm_display_dsc_info *dsc_info;
> + u32 idx, off;
> + int i, j = 0;
> + struct drm_dsc_rc_range_parameters *rc;
> + u32 data = 0, min_qp = 0, max_qp = 0, bpg_off = 0;
> +
> + if (!hw_dsc || !dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> +
> + dsc_info = to_msm_dsc_info(dsc);
> +
> + rc = dsc->rc_range_params;
> +
> + off = 0;
> + for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
> + data |= dsc->rc_buf_thresh[i] << (8*j);
> + j++;
> + if ((j == 4) || (i == DSC_NUM_BUF_RANGES - 2)) {
> + DPU_REG_WRITE(hw, DSC_RC_BUF_THRESH_0 + idx + off,
> + data);
> + off += 4;
> + j = 0;
> + data = 0;
> + }
> + }
> +
> + off = 0;
> + for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
> + min_qp |= (rc[i].range_min_qp & 0x1f) << 5*j;
> + max_qp |= (rc[i].range_max_qp & 0x1f) << 5*j;
> + bpg_off |= (rc[i].range_bpg_offset & 0x3f) << 6*j;
> + j++;
> + if (j == 5) {
> + DPU_REG_WRITE(hw, DSC_RC_MIN_QP_0 + idx + off,
> + min_qp);
> + DPU_REG_WRITE(hw, DSC_RC_MAX_QP_0 + idx + off,
> + max_qp);
> + DPU_REG_WRITE(hw,
> + DSC_RC_RANGE_BPG_OFFSETS_0 + idx + off,
> + bpg_off);
> + off += 4;
> + j = 0;
> + min_qp = 0;
> + max_qp = 0;
> + bpg_off = 0;
> + }
> + }
> +}
> +
> +static void dpu_hw_dsc_bind_pingpong_blk_1_2(
> + struct dpu_hw_dsc *hw_dsc,
> + bool enable,
> + const enum dpu_pingpong pp)
> +{
> + struct dpu_hw_blk_reg_map *hw;
> + int idx;
> + int mux_cfg = 0xF; /* Disabled */
> +
> + if (!hw_dsc)
> + return;
> +
> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
> + return;
> +
> + hw = &hw_dsc->hw;
> + if (enable)
> + mux_cfg = (pp - PINGPONG_0) & 0x7;
> +
> + DPU_REG_WRITE(hw, DSC_CTL + idx, mux_cfg);
> +}
> +
> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
> + const unsigned long features)
> +{
> + ops->dsc_disable = dpu_hw_dsc_disable_1_2;
> + ops->dsc_config = dpu_hw_dsc_config_1_2;
> + ops->dsc_config_thresh = dpu_hw_dsc_config_thresh_1_2;
> + ops->dsc_bind_pingpong_blk = dpu_hw_dsc_bind_pingpong_blk_1_2;
> +}


2023-01-24 17:55:51

by Kuogee Hsieh

[permalink] [raw]
Subject: Re: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC


On 1/24/2023 1:11 AM, Dmitry Baryshkov wrote:
> On 23/01/2023 20:24, Kuogee Hsieh wrote:
>> Current implementation timing engine programming does not consider
>> compression factors. This patch add consideration of DSC factors
>> while programming timing engine.
>>
>> Signed-off-by: Kuogee Hsieh <[email protected]>
>> ---
>>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |   2 +
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h     |  14 ++-
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c        | 132
>> +++++++++++++--------
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h        |  10 +-
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h         |   6 +-
>>   5 files changed, 110 insertions(+), 54 deletions(-)
>>
>
> [skipped]
>
>> @@ -113,82 +124,96 @@ static void
>> dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
>>       /* read interface_cfg */
>>       intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
>>   -    if (ctx->cap->type == INTF_DP)
>> +    if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP)
>>           dp_intf = true;
>>         hsync_period = p->hsync_pulse_width + p->h_back_porch +
>> p->width +
>> -    p->h_front_porch;
>> +            p->h_front_porch;
>>       vsync_period = p->vsync_pulse_width + p->v_back_porch +
>> p->height +
>> -    p->v_front_porch;
>> +            p->v_front_porch;
>
> Actually I went on through the history and found the previous
> submission, https://patchwork.freedesktop.org/patch/471505/.
> Exactly the same piece of code. Did you expect that the comments will
> be different this time?
>
> I really hoped that at that time we already went through this. But it
> seems I was wrong. That series went through v10 or v12 before being
> accepted. And it was just adding wide_bus_en. Back at that time we
> lightly discussed that the code will receive compression support. But
> I never expected to see the original submission again.
>
> It might sound bad, but could you please find somebody who can do
> internal review for you? Good internal review.
>
> That said, I really do not expect to see v2 before the whole series is
> reworked, restructured and prepared for the review on your side.

This timing engine code is derived from our downstream code directly and
it has been used at many mobile devices by many vendors for many years
already.

On the other words, it had been tested very thorough and works on
dsi/dp/hdmi/dsc/widebus applications.

When i brought dsc v1.2 over, I just merged it over and did not consider
too much.

Can we adapt this code so that both upstream and down stream shared same
timing engine programming so that easier to maintain?





>
>>         display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
>> -    hsync_period) + p->hsync_skew;
>> +            hsync_period) + p->hsync_skew;
>>       display_v_end = ((vsync_period - p->v_front_porch) *
>> hsync_period) +
>> -    p->hsync_skew - 1;
>> +            p->hsync_skew - 1;
>> +
>> +    hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
>>         hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
>>       hsync_end_x = hsync_period - p->h_front_porch - 1;
>>   -    if (p->width != p->xres) { /* border fill added */
>> -        active_h_start = hsync_start_x;
>> -        active_h_end = active_h_start + p->xres - 1;
>> -    } else {
>> -        active_h_start = 0;
>> -        active_h_end = 0;
>> -    }
>> -
>> -    if (p->height != p->yres) { /* border fill added */
>> -        active_v_start = display_v_start;
>> -        active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>> -    } else {
>> -        active_v_start = 0;
>> -        active_v_end = 0;
>> -    }
>> -
>> -    if (active_h_end) {
>> -        active_hctl = (active_h_end << 16) | active_h_start;
>> -        intf_cfg |= INTF_CFG_ACTIVE_H_EN;
>> -    } else {
>> -        active_hctl = 0;
>> -    }
>> -
>> -    if (active_v_end)
>> -        intf_cfg |= INTF_CFG_ACTIVE_V_EN;
>> -
>> -    hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
>> -    display_hctl = (hsync_end_x << 16) | hsync_start_x;
>> -
>>       /*
>>        * DATA_HCTL_EN controls data timing which can be different from
>>        * video timing. It is recommended to enable it for all cases,
>> except
>>        * if compression is enabled in 1 pixel per clock mode
>>        */
>> +    if (!p->compression_en || p->wide_bus_en)
>> +        intf_cfg2 |= INTF_CFG2_DATA_HCTL_EN;
>> +
>>       if (p->wide_bus_en)
>> -        intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN | INTF_CFG2_DATA_HCTL_EN;
>> +        intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
>>   +    /*
>> +     * If widebus is disabled:
>> +     * For uncompressed stream, the data is valid for the entire active
>> +     * window period.
>> +     * For compressed stream, data is valid for a shorter time period
>> +     * inside the active window depending on the compression ratio.
>> +     *
>> +     * If widebus is enabled:
>> +     * For uncompressed stream, data is valid for only half the active
>> +     * window, since the data rate is doubled in this mode.
>> +     * p->width holds the adjusted width for DP but unadjusted width
>> for DSI
>> +     * For compressed stream, data validity window needs to be
>> adjusted for
>> +     * compression ratio and then further halved.
>> +     */
>>       data_width = p->width;
>>   +    if (p->compression_en) {
>> +        if (p->wide_bus_en)
>> +            data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6);
>> +        else
>> +            data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3);
>> +    } else if (!dp_intf && p->wide_bus_en) {
>> +        data_width = p->width >> 1;
>> +    } else {
>> +        data_width = p->width;
>> +    }
>> +
>>       hsync_data_start_x = hsync_start_x;
>>       hsync_data_end_x =  hsync_start_x + data_width - 1;
>>   +    display_hctl = (hsync_end_x << 16) | hsync_start_x;
>>       display_data_hctl = (hsync_data_end_x << 16) | hsync_data_start_x;
>>         if (dp_intf) {
>>           /* DP timing adjustment */
>>           display_v_start += p->hsync_pulse_width + p->h_back_porch;
>>           display_v_end   -= p->h_front_porch;
>> +    }
>> +
>> +    intf_cfg |= INTF_CFG_ACTIVE_H_EN;
>> +    intf_cfg |= INTF_CFG_ACTIVE_V_EN;
>> +    active_h_start = hsync_start_x;
>> +    active_h_end = active_h_start + p->xres - 1;
>> +    active_v_start = display_v_start;
>> +    active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>>   -        active_h_start = hsync_start_x;
>> -        active_h_end = active_h_start + p->xres - 1;
>> -        active_v_start = display_v_start;
>> -        active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>> +    active_hctl = (active_h_end << 16) | active_h_start;
>>   -        active_hctl = (active_h_end << 16) | active_h_start;
>> +    if (dp_intf) {
>>           display_hctl = active_hctl;
>>   -        intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN;
>> +        if (p->compression_en) {
>> +            active_data_hctl = (hsync_start_x + p->extra_dto_cycles)
>> << 16;
>> +            active_data_hctl += hsync_start_x;
>> +
>> +            display_data_hctl = active_data_hctl;
>> +        }
>>       }
>>   +    _check_and_set_comp_bit(ctx, p->dsc_4hs_merge,
>> p->compression_en, &intf_cfg2);
>> +
>>       den_polarity = 0;
>>       if (ctx->cap->type == INTF_HDMI) {
>>           hsync_polarity = p->yres >= 720 ? 0 : 1;
>> @@ -202,7 +227,7 @@ static void
>> dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
>

2023-01-24 21:38:02

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC

On 24/01/2023 19:55, Kuogee Hsieh wrote:
>
> On 1/24/2023 1:11 AM, Dmitry Baryshkov wrote:
>> On 23/01/2023 20:24, Kuogee Hsieh wrote:
>>> Current implementation timing engine programming does not consider
>>> compression factors. This patch add consideration of DSC factors
>>> while programming timing engine.
>>>
>>> Signed-off-by: Kuogee Hsieh <[email protected]>
>>> ---
>>>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |   2 +
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h     |  14 ++-
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c        | 132
>>> +++++++++++++--------
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h        |  10 +-
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h         |   6 +-
>>>   5 files changed, 110 insertions(+), 54 deletions(-)
>>>
>>
>> [skipped]
>>
>>> @@ -113,82 +124,96 @@ static void
>>> dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
>>>       /* read interface_cfg */
>>>       intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
>>>   -    if (ctx->cap->type == INTF_DP)
>>> +    if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP)
>>>           dp_intf = true;
>>>         hsync_period = p->hsync_pulse_width + p->h_back_porch +
>>> p->width +
>>> -    p->h_front_porch;
>>> +            p->h_front_porch;
>>>       vsync_period = p->vsync_pulse_width + p->v_back_porch +
>>> p->height +
>>> -    p->v_front_porch;
>>> +            p->v_front_porch;
>>
>> Actually I went on through the history and found the previous
>> submission, https://patchwork.freedesktop.org/patch/471505/.
>> Exactly the same piece of code. Did you expect that the comments will
>> be different this time?
>>
>> I really hoped that at that time we already went through this. But it
>> seems I was wrong. That series went through v10 or v12 before being
>> accepted. And it was just adding wide_bus_en. Back at that time we
>> lightly discussed that the code will receive compression support. But
>> I never expected to see the original submission again.
>>
>> It might sound bad, but could you please find somebody who can do
>> internal review for you? Good internal review.
>>
>> That said, I really do not expect to see v2 before the whole series is
>> reworked, restructured and prepared for the review on your side.
>
> This timing engine code is derived from our downstream code directly and
> it has been used at many mobile devices by many vendors for many years
> already.
>
> On the other words, it had been tested very thorough and works on
> dsi/dp/hdmi/dsc/widebus applications.

As far as I understand, it has been tested on the recent generations of
the hardware. I doubt that anybody retests new techpack drops on
previous hardware generations. Correct?

When was the last time this particular code drop was tested on
INTF_HDMI? I think it was back in the 4.4 era. Newer vendor kernels do
not have hdmi-staging, so at least the claim of testing this codepiece
on HDMI is not correct.

What is the earliest chip that has been driven by this particular code
instance?

> When i brought dsc v1.2 over, I just merged it over and did not consider
> too much.
>
> Can we adapt this code so that both upstream and down stream shared same
> timing engine programming so that easier to maintain?

We have been discussing exactly the same piece of code a year ago. Could
you please recheck the comments that were provided to your patches. And
I actually mean that. There were 12 iterations of wide bus patchset.
Timing engine programming patch had 8. I do not want to start again from
the very beginning.

The basic idea is that you have to evolve the code rather than flushing
us with the 'latest and greatest code dump'. Split this into individual
atomic changes that we can review. Provide justification (= motivation)
for each change. Previously we haven't seen them.

We know that current function works. We must be able to assume that new
instance doesn't break things. Or, if something breaks, understand which
particular change broke it. Consider the case that your patch breaks
msm8998. Or sdm845. How can we cope? Would you be able to spot the place
which did that? I know I wouldn't. The only way would be to revert the
patch completely. And inherently the whole series.

>>
>>>         display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
>>> -    hsync_period) + p->hsync_skew;
>>> +            hsync_period) + p->hsync_skew;
>>>       display_v_end = ((vsync_period - p->v_front_porch) *
>>> hsync_period) +
>>> -    p->hsync_skew - 1;
>>> +            p->hsync_skew - 1;
>>> +
>>> +    hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
>>>         hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
>>>       hsync_end_x = hsync_period - p->h_front_porch - 1;
>>>   -    if (p->width != p->xres) { /* border fill added */
>>> -        active_h_start = hsync_start_x;
>>> -        active_h_end = active_h_start + p->xres - 1;
>>> -    } else {
>>> -        active_h_start = 0;
>>> -        active_h_end = 0;
>>> -    }
>>> -
>>> -    if (p->height != p->yres) { /* border fill added */
>>> -        active_v_start = display_v_start;
>>> -        active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>>> -    } else {
>>> -        active_v_start = 0;
>>> -        active_v_end = 0;
>>> -    }
>>> -
>>> -    if (active_h_end) {
>>> -        active_hctl = (active_h_end << 16) | active_h_start;
>>> -        intf_cfg |= INTF_CFG_ACTIVE_H_EN;
>>> -    } else {
>>> -        active_hctl = 0;
>>> -    }
>>> -
>>> -    if (active_v_end)
>>> -        intf_cfg |= INTF_CFG_ACTIVE_V_EN;
>>> -
>>> -    hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
>>> -    display_hctl = (hsync_end_x << 16) | hsync_start_x;
>>> -
>>>       /*
>>>        * DATA_HCTL_EN controls data timing which can be different from
>>>        * video timing. It is recommended to enable it for all cases,
>>> except
>>>        * if compression is enabled in 1 pixel per clock mode
>>>        */
>>> +    if (!p->compression_en || p->wide_bus_en)
>>> +        intf_cfg2 |= INTF_CFG2_DATA_HCTL_EN;
>>> +
>>>       if (p->wide_bus_en)
>>> -        intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN | INTF_CFG2_DATA_HCTL_EN;
>>> +        intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
>>>   +    /*
>>> +     * If widebus is disabled:
>>> +     * For uncompressed stream, the data is valid for the entire active
>>> +     * window period.
>>> +     * For compressed stream, data is valid for a shorter time period
>>> +     * inside the active window depending on the compression ratio.
>>> +     *
>>> +     * If widebus is enabled:
>>> +     * For uncompressed stream, data is valid for only half the active
>>> +     * window, since the data rate is doubled in this mode.
>>> +     * p->width holds the adjusted width for DP but unadjusted width
>>> for DSI
>>> +     * For compressed stream, data validity window needs to be
>>> adjusted for
>>> +     * compression ratio and then further halved.
>>> +     */
>>>       data_width = p->width;
>>>   +    if (p->compression_en) {
>>> +        if (p->wide_bus_en)
>>> +            data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6);
>>> +        else
>>> +            data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3);
>>> +    } else if (!dp_intf && p->wide_bus_en) {
>>> +        data_width = p->width >> 1;
>>> +    } else {
>>> +        data_width = p->width;
>>> +    }
>>> +
>>>       hsync_data_start_x = hsync_start_x;
>>>       hsync_data_end_x =  hsync_start_x + data_width - 1;
>>>   +    display_hctl = (hsync_end_x << 16) | hsync_start_x;
>>>       display_data_hctl = (hsync_data_end_x << 16) | hsync_data_start_x;
>>>         if (dp_intf) {
>>>           /* DP timing adjustment */
>>>           display_v_start += p->hsync_pulse_width + p->h_back_porch;
>>>           display_v_end   -= p->h_front_porch;
>>> +    }
>>> +
>>> +    intf_cfg |= INTF_CFG_ACTIVE_H_EN;
>>> +    intf_cfg |= INTF_CFG_ACTIVE_V_EN;
>>> +    active_h_start = hsync_start_x;
>>> +    active_h_end = active_h_start + p->xres - 1;
>>> +    active_v_start = display_v_start;
>>> +    active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>>>   -        active_h_start = hsync_start_x;
>>> -        active_h_end = active_h_start + p->xres - 1;
>>> -        active_v_start = display_v_start;
>>> -        active_v_end = active_v_start + (p->yres * hsync_period) - 1;
>>> +    active_hctl = (active_h_end << 16) | active_h_start;
>>>   -        active_hctl = (active_h_end << 16) | active_h_start;
>>> +    if (dp_intf) {
>>>           display_hctl = active_hctl;
>>>   -        intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN;
>>> +        if (p->compression_en) {
>>> +            active_data_hctl = (hsync_start_x + p->extra_dto_cycles)
>>> << 16;
>>> +            active_data_hctl += hsync_start_x;
>>> +
>>> +            display_data_hctl = active_data_hctl;
>>> +        }
>>>       }
>>>   +    _check_and_set_comp_bit(ctx, p->dsc_4hs_merge,
>>> p->compression_en, &intf_cfg2);
>>> +
>>>       den_polarity = 0;
>>>       if (ctx->cap->type == INTF_HDMI) {
>>>           hsync_polarity = p->yres >= 720 ? 0 : 1;
>>> @@ -202,7 +227,7 @@ static void
>>> dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
>>

--
With best wishes
Dmitry


2023-01-24 23:36:44

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC

On 2023-01-24 09:55:24, Kuogee Hsieh wrote:

<snip>

> This timing engine code is derived from our downstream code directly and
> it has been used at many mobile devices by many vendors for many years
> already.
>
> On the other words, it had been tested very thorough and works on
> dsi/dp/hdmi/dsc/widebus applications.

And the code already in mainline has seen 12 rounds of review, with a
focus on inter-SoC compatibility. Regardless of that, we have processes
to make changes on mainline: formatting changes (when actually making an
improvement) go separate from semantic changes. Bugfixes are clearly
described in individual patches with Fixes: tags. If you really think
the code has to be as proposed in this patch, follow Dmitry's advice and
split this accordingly.

> When i brought dsc v1.2 over, I just merged it over and did not consider
> too much.

And that is exactly what is wrong with this *entire* series: copying
over downstream code without "considering too much", stomping over
previous review and even reverting bugfixes [1] [2] without giving it
ANY ATTENTION in your patch description. That's unacceptable and
insulting to contributors and reviewers. Full stop. Or did you expect
us to turn a blind eye? This is mainline, not some techpack playground.

[1]: https://lore.kernel.org/linux-arm-msm/[email protected]/
[2]: https://lore.kernel.org/linux-arm-msm/[email protected]/

> Can we adapt this code so that both upstream and down stream shared same
> timing engine programming so that easier to maintain?

Easy, I've said this before in IRC and will state it again: stop this
techpack nonsense and focus on upstream-first. When something passes
mainline review (and please don't bother maintainers and reviewers with
series like this) it is inevitably good enough to be copied to
techpack... at which point techpack becomes worthless as you can just
backport a mainline patch or use a recent-enough kernel.


tl;dr: it seems like you nor anyone involved in pre-reviewing/vetting
this series is familiar with upstream guidelines. Follow the global
advice from Dmitry [3] to reach a more efficient v2, and please don't
let this run to v10 (or beyond) again.

One suggestion to improve efficiency: split off the DPU v1.2 hardware
block addition (and related changes) into a separate series. A smaller
series (and properly split patches!) will give everyone less moving
parts to worry about, and paves the way for DSI support without blocking
on DP.

[3]: https://lore.kernel.org/linux-arm-msm/[email protected]/

- Marijn

2023-01-24 23:53:08

by Kuogee Hsieh

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine


On 1/23/2023 12:11 PM, Marijn Suijten wrote:
> add support for*
>
> drm/msm/dpu*
>
> On 2023-01-23 10:24:30, Kuogee Hsieh wrote:
>> DSC V1.2 encoder engine is newly added hardware module. This patch
>> add support functions to configure and enable DSC V1.2 encoder engine.
>>
>> Signed-off-by: Kuogee Hsieh <[email protected]>
>> ---
>> drivers/gpu/drm/msm/Makefile | 1 +
>> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 2 +-
>> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 60 +++-
>> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
>> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
>> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++++++++++++++++++
>> 6 files changed, 463 insertions(+), 17 deletions(-)
>> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>>
>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>> index 28cf52b..271c29a15 100644
>> --- a/drivers/gpu/drm/msm/Makefile
>> +++ b/drivers/gpu/drm/msm/Makefile
>> @@ -65,6 +65,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>> disp/dpu1/dpu_hw_catalog.o \
>> disp/dpu1/dpu_hw_ctl.o \
>> disp/dpu1/dpu_hw_dsc.o \
>> + disp/dpu1/dpu_hw_dsc_1_2.o \
>> disp/dpu1/dpu_dsc_helper.o \
>> disp/dpu1/dpu_hw_interrupts.o \
>> disp/dpu1/dpu_hw_intf.o \
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> index 7f4a439..901e317 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> @@ -1821,7 +1821,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
>> u32 initial_lines)
>> {
>> if (hw_dsc->ops.dsc_config)
>> - hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines);
>> + hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines, false);
> As usual, an enum is better: readers have no idea what a free-floating
> bool means.
>
>>
>> if (hw_dsc->ops.dsc_config_thresh)
>> hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
>> index 978e3bd..7b0b092 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
>> @@ -1,6 +1,6 @@
>> /* SPDX-License-Identifier: GPL-2.0-only */
>> /*
>> - * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
>> + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
>> * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
>> */
>>
>> @@ -11,6 +11,7 @@
>> #include <linux/bug.h>
>> #include <linux/bitmap.h>
>> #include <linux/err.h>
>> +#include "dpu_hw_mdss.h"
> Unused if you remove the unused DECLARE_BITMAP(dsc_pair_mask, DSC_MAX).
>
>>
>> /**
>> * Max hardware block count: For ex: max 12 SSPP pipes or
>> @@ -182,6 +183,7 @@ enum {
>> * @DPU_PINGPONG_TE2 Additional tear check block for split pipes
>> * @DPU_PINGPONG_SPLIT PP block supports split fifo
>> * @DPU_PINGPONG_SLAVE PP block is a suitable slave for split fifo
>> + * @DPU_PINGPONG_DSC, Display stream compression blocks
>> * @DPU_PINGPONG_DITHER, Dither blocks
>> * @DPU_PINGPONG_MAX
>> */
>> @@ -190,10 +192,32 @@ enum {
>> DPU_PINGPONG_TE2,
>> DPU_PINGPONG_SPLIT,
>> DPU_PINGPONG_SLAVE,
>> + DPU_PINGPONG_DSC,
> This is not used.
>
>> DPU_PINGPONG_DITHER,
>> DPU_PINGPONG_MAX
>> };
>>
>> +
>> +/** DSC sub-blocks/features
> Newline between /** and the text.
>
>> + * @DPU_DSC_OUTPUT_CTRL Supports the control of the pp id which gets
>> + * the pixel output from this DSC.
> The original comment is much more concise, can we keep it?
>
>> + * @DPU_DSC_HW_REV_1_1 dsc block supports dsc 1.1 only
>> + * @DPU_DSC_HW_REV_1_2 dsc block supports dsc 1.1 and 1.2
> Capitalize DSC just like elsewhere.
>
>> + * @DPU_DSC_NATIVE_422_EN, Supports native422 and native420 encoding
>> + * @DPU_DSC_ENC, DSC encoder sub block
>> + * @DPU_DSC_CTL, DSC ctl sub block
> No need for trailing commas in doc comments; if anything replace them
> with colons?
>
>> + * @DPU_DSC_MAX
>> + */
>> +enum {
>> + DPU_DSC_OUTPUT_CTRL = 0x1,
>> + DPU_DSC_HW_REV_1_1,
>> + DPU_DSC_HW_REV_1_2,
>> + DPU_DSC_NATIVE_422_EN,
>> + DPU_DSC_ENC,
>> + DPU_DSC_CTL,
> These two enum values only have a meaning within the dpu_hw_dsc_1_2.c
> file, and have nothing to do with the other feature flags/block
> description. Please move them there (and give _dsc_subblk_offset a
> proper enum type).
>
>> + DPU_DSC_MAX
>> +};
>> +
>> /**
>> * CTL sub-blocks
>> * @DPU_CTL_SPLIT_DISPLAY: CTL supports video mode split display
>> @@ -276,15 +300,6 @@ enum {
>> };
>>
>> /**
>> - * DSC features
>> - * @DPU_DSC_OUTPUT_CTRL Configure which PINGPONG block gets
>> - * the pixel output from this DSC.
>> - */
>> -enum {
>> - DPU_DSC_OUTPUT_CTRL = 0x1,
> Did this have to move?
>
>> -};
>> -
>> -/**
>> * MACRO DPU_HW_BLK_INFO - information of HW blocks inside DPU
>> * @name: string name for debug purposes
>> * @id: enum identifying this block
>> @@ -346,6 +361,14 @@ struct dpu_pp_blk {
>> };
>>
>> /**
>> + * struct dpu_dsc_blk : DSC Encoder sub-blk information
> Use a hyphen here and everywhere else:
> https://docs.kernel.org/doc-guide/kernel-doc.html
>
>> + * @info: HW register and features supported by this sub-blk
>> + */
>> +struct dpu_dsc_blk {
>> + DPU_HW_SUBBLK_INFO;
>> +};
>> +
>> +/**
>> * enum dpu_qos_lut_usage - define QoS LUT use cases
>> */
>> enum dpu_qos_lut_usage {
>> @@ -403,6 +426,7 @@ struct dpu_rotation_cfg {
>> * @pixel_ram_size size of latency hiding and de-tiling buffer in bytes
>> * @max_hdeci_exp max horizontal decimation supported (max is 2^value)
>> * @max_vdeci_exp max vertical decimation supported (max is 2^value)
>> + * @max_dsc_width max dsc line width support.
> DSC*
>
>> */
>> struct dpu_caps {
>> u32 max_mixer_width;
>> @@ -419,6 +443,7 @@ struct dpu_caps {
>> u32 pixel_ram_size;
>> u32 max_hdeci_exp;
>> u32 max_vdeci_exp;
>> + u32 max_dsc_width;
> This is never read.
>
>> };
>>
>> /**
>> @@ -494,9 +519,20 @@ struct dpu_dspp_sub_blks {
>> struct dpu_pingpong_sub_blks {
>> struct dpu_pp_blk te;
>> struct dpu_pp_blk te2;
>> + struct dpu_pp_blk dsc;
> Unused.
>
>> struct dpu_pp_blk dither;
>> };
>>
>> +
>> +/**
>> + * struct dpu_dsc_sub_blks : DSC sub-blks
>> + *
> A sub-block of sub-blocks? Use the documentation to explain what this
> is for, describe @enc and @ctl.
>
>> + */
>> +struct dpu_dsc_sub_blks {
>> + struct dpu_dsc_blk enc;
>> + struct dpu_dsc_blk ctl;
>> +};
>> +
>> /**
>> * dpu_clk_ctrl_type - Defines top level clock control signals
>> */
>> @@ -641,10 +677,14 @@ struct dpu_merge_3d_cfg {
>> * struct dpu_dsc_cfg - information of DSC blocks
>> * @id enum identifying this block
>> * @base register offset of this block
>> + * @len: length of hardware block
>> * @features bit mask identifying sub-blocks/features
>> + * @dsc_pair_mask: Bitmask of DSCs that can be controlled by same CTL
>> */
>> struct dpu_dsc_cfg {
>> DPU_HW_BLK_INFO;
>> + DECLARE_BITMAP(dsc_pair_mask, DSC_MAX);
> This bitmask is unused.
>
>> + const struct dpu_dsc_sub_blks *sblk;
>> };
>>
>> /**
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
>> index 619926d..51e8890 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
>> @@ -1,6 +1,7 @@
>> // SPDX-License-Identifier: GPL-2.0-only
>> /*
>> * Copyright (c) 2020-2022, Linaro Limited
>> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
>> */
>>
>> #include "dpu_kms.h"
>> @@ -41,10 +42,11 @@ static void dpu_hw_dsc_disable(struct dpu_hw_dsc *dsc)
>> static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
>> struct drm_dsc_config *dsc,
>> u32 mode,
>> - u32 initial_lines)
>> + u32 initial_lines,
>> + bool ich_reset_override)
>> {
>> struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
>> - u32 data;
>> + u32 data, lsb, bpp;
>> u32 slice_last_group_size;
>> u32 det_thresh_flatness;
>> bool is_cmd_mode = !(mode & DSC_MODE_VIDEO);
>> @@ -58,7 +60,14 @@ static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
>> data = (initial_lines << 20);
>> data |= ((slice_last_group_size - 1) << 18);
>> /* bpp is 6.4 format, 4 LSBs bits are for fractional part */
>> - data |= (dsc->bits_per_pixel << 8);
>> + data |= dsc->bits_per_pixel << 12;
>> + lsb = dsc->bits_per_pixel % 4;
>> + bpp = dsc->bits_per_pixel / 4;
>> + bpp *= 4;
>> + bpp <<= 4;
>> + bpp |= lsb;
>> +
>> + data |= bpp << 8;
> Why are you re-adding this nonsense? It was removed in [1] _and_ does
> not account for bits_per_pixel _already being in x.4 format_. This will
> regress existing hardware.
>
> [1]: https://lore.kernel.org/linux-arm-msm/[email protected]/
>
>> data |= (dsc->block_pred_enable << 7);
>> data |= (dsc->line_buf_depth << 3);
>> data |= (dsc->simple_422 << 2);
>> @@ -221,7 +230,13 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
>>
>> c->idx = idx;
>> c->caps = cfg;
>> - _setup_dsc_ops(&c->ops, c->caps->features);
>> +
>> + if (test_bit(DPU_DSC_HW_REV_1_1, &c->caps->features))
>> + _setup_dsc_ops(&c->ops, c->caps->features);
>> + else if (test_bit(DPU_DSC_HW_REV_1_2, &c->caps->features))
>> + dpu_dsc_1_2_setup_ops(&c->ops, c->caps->features);
>> + else
>> + _setup_dsc_ops(&c->ops, c->caps->features);
>>
>> return c;
>> }
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
>> index ae9b5db..a48f572 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
>> @@ -1,5 +1,8 @@
>> /* SPDX-License-Identifier: GPL-2.0-only */
>> -/* Copyright (c) 2020-2022, Linaro Limited */
>> +/*
>> + * Copyright (c) 2020-2022, Linaro Limited
>> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
>> + */
>>
>> #ifndef _DPU_HW_DSC_H
>> #define _DPU_HW_DSC_H
>> @@ -33,7 +36,8 @@ struct dpu_hw_dsc_ops {
>> void (*dsc_config)(struct dpu_hw_dsc *hw_dsc,
>> struct drm_dsc_config *dsc,
>> u32 mode,
>> - u32 initial_lines);
>> + u32 initial_lines,
>> + bool ich_reset_override);
>>
>> /**
>> * dsc_config_thresh - programs panel thresholds
>> @@ -43,6 +47,12 @@ struct dpu_hw_dsc_ops {
>> void (*dsc_config_thresh)(struct dpu_hw_dsc *hw_dsc,
>> struct drm_dsc_config *dsc);
>>
>> + /**
>> + * bind_pingpong_blk - enable/disable the connection with pp
> Inherit docs from the enum.
>
>> + * @hw_dsc: Pointer to dsc context
> DSC*
>
>> + * @enable: enable/disable connection
>> + * @pp: pingpong blk id
> It's documentation, write out block fully.
>
>> + */
>> void (*dsc_bind_pingpong_blk)(struct dpu_hw_dsc *hw_dsc,
>> bool enable,
>> enum dpu_pingpong pp);
>> @@ -51,6 +61,7 @@ struct dpu_hw_dsc_ops {
>> struct dpu_hw_dsc {
>> struct dpu_hw_blk base;
>> struct dpu_hw_blk_reg_map hw;
>> + struct dpu_hw_ctl *hw_ctl;
> Unused.
>
>>
>> /* dsc */
>> enum dpu_dsc idx;
>> @@ -76,9 +87,17 @@ struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
>> */
>> void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);
>>
>> +/**
>> + * dpu_hw_dsc - convert base object dpu_hw_base to container
>> + * @hw: Pointer to base hardware block
>> + * return: Pointer to hardware block container
>> + */
>> static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
>> {
>> return container_of(hw, struct dpu_hw_dsc, base);
>> }
>>
>> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
>> + const unsigned long features);
>> +
>> #endif /* _DPU_HW_DSC_H */
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>> new file mode 100644
>> index 00000000..2be74ae
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>> @@ -0,0 +1,371 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
>> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
>> + */
>> +
>> +#include "dpu_kms.h"
>> +#include "dpu_hw_catalog.h"
>> +#include "dpu_hwio.h"
>> +#include "dpu_hw_mdss.h"
>> +#include "dpu_hw_dsc.h"
>> +
>> +
>> +#define DSC_CMN_MAIN_CNF 0x00
>> +
>> +/* DPU_DSC_ENC register offsets */
>> +#define ENC_DF_CTRL 0x00
>> +#define ENC_GENERAL_STATUS 0x04
>> +#define ENC_HSLICE_STATUS 0x08
>> +#define ENC_OUT_STATUS 0x0C
>> +#define ENC_INT_STAT 0x10
>> +#define ENC_INT_CLR 0x14
>> +#define ENC_INT_MASK 0x18
>> +#define DSC_MAIN_CONF 0x30
>> +#define DSC_PICTURE_SIZE 0x34
>> +#define DSC_SLICE_SIZE 0x38
>> +#define DSC_MISC_SIZE 0x3C
>> +#define DSC_HRD_DELAYS 0x40
>> +#define DSC_RC_SCALE 0x44
>> +#define DSC_RC_SCALE_INC_DEC 0x48
>> +#define DSC_RC_OFFSETS_1 0x4C
>> +#define DSC_RC_OFFSETS_2 0x50
>> +#define DSC_RC_OFFSETS_3 0x54
>> +#define DSC_RC_OFFSETS_4 0x58
>> +#define DSC_FLATNESS_QP 0x5C
>> +#define DSC_RC_MODEL_SIZE 0x60
>> +#define DSC_RC_CONFIG 0x64
>> +#define DSC_RC_BUF_THRESH_0 0x68
>> +#define DSC_RC_BUF_THRESH_1 0x6C
>> +#define DSC_RC_BUF_THRESH_2 0x70
>> +#define DSC_RC_BUF_THRESH_3 0x74
>> +#define DSC_RC_MIN_QP_0 0x78
>> +#define DSC_RC_MIN_QP_1 0x7C
>> +#define DSC_RC_MIN_QP_2 0x80
>> +#define DSC_RC_MAX_QP_0 0x84
>> +#define DSC_RC_MAX_QP_1 0x88
>> +#define DSC_RC_MAX_QP_2 0x8C
>> +#define DSC_RC_RANGE_BPG_OFFSETS_0 0x90
>> +#define DSC_RC_RANGE_BPG_OFFSETS_1 0x94
>> +#define DSC_RC_RANGE_BPG_OFFSETS_2 0x98
> Reindent to line this back up.
>
>> +
>> +/* DPU_DSC_CTL register offsets */
>> +#define DSC_CTL 0x00
>> +#define DSC_CFG 0x04
>> +#define DSC_DATA_IN_SWAP 0x08
>> +#define DSC_CLK_CTRL 0x0C
>> +
>> +
>> +static int _dsc_calc_ob_max_addr(struct dpu_hw_dsc *hw_dsc, int num_ss)
>> +{
>> + enum dpu_dsc idx;
>> +
>> + idx = hw_dsc->idx;
>> +
>> + if (!(hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_422_EN))) {
> Why not swap the bodies instead of inverting this.
>
>> + if (num_ss == 1)
>> + return 2399;
>> + else if (num_ss == 2)
>> + return 1199;
>> + } else {
>> + if (num_ss == 1)
>> + return 1199;
>> + else if (num_ss == 2)
>> + return 599;
>> + }
>> + return 0;
>> +}
>> +
>> +static inline int _dsc_subblk_offset(struct dpu_hw_dsc *hw_dsc, int s_id,
>> + u32 *idx)
>> +{
>> + const struct dpu_dsc_sub_blks *sblk;
>> +
>> + if (!hw_dsc)
>> + return -EINVAL;
>> +
>> + *idx = 0;
>> +
>> + sblk = hw_dsc->caps->sblk;
>> +
>> + switch (s_id) {
>> +
>> + case DPU_DSC_ENC:
>> + *idx = sblk->enc.base;
>> + break;
>> + case DPU_DSC_CTL:
>> + *idx = sblk->ctl.base;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void dpu_hw_dsc_disable_1_2(struct dpu_hw_dsc *hw_dsc)
>> +{
>> + struct dpu_hw_blk_reg_map *hw;
>> + u32 idx;
> Can we rename these to offset or subblk_offset or something more clear?
>
>> +
>> + if (!hw_dsc)
>> + return;
>> +
>> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
>> + return;
> These error checks are excessive: you pass in a non-null hw_dsc and
> known enum constant - _dsc_subblk_offset should perhaps not return
> errors at all.
>
>> +
>> + hw = &hw_dsc->hw;
>> + DPU_REG_WRITE(hw, DSC_CFG + idx, 0);
> Swap the arguments to + so that it's clear that DSC_CFG is a register on
> the subblock offset denoted by "idx", not the other way around.
>
>> +
>> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
>> + return;
>> +
>> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, 0);
>> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, 0);
>> +}
>> +
>> +static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
>> + struct drm_dsc_config *dsc, u32 mode,
>> + u32 initial_lines, bool ich_reset_override)
>> +{
>> + struct dpu_hw_blk_reg_map *hw;
>> + struct msm_display_dsc_info *dsc_info;
>> + u32 idx;
>> + u32 data = 0;
>> + u32 bpp;
>> + void __iomem *off;
>> +
>> + if (!hw_dsc || !dsc)
>> + return;
>> +
>> + hw = &hw_dsc->hw;
>> +
>> + dsc_info = to_msm_dsc_info(dsc);
>> +
>> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
>> + return;
>> +
>> + if (mode & DSC_MODE_SPLIT_PANEL)
>> + data |= BIT(0);
>> +
>> + if (mode & DSC_MODE_MULTIPLEX)
>> + data |= BIT(1);
> These are well known bitwise definitions for a reason, data |= mode will
> do (or out DSC_MODE_VIDEO since you have to shift that one at BIT(9).
>
>> +
>> + data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7;
>> +
>> + DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);
>> +
>> + data = (dsc_info->initial_lines & 0xff);
> You already get initial_lines passed as function argument, but ignore
> it?
>
>> + data |= ((mode & DSC_MODE_VIDEO) ? 1 : 0) << 9;
> Yuck. if (mode & DSC_MODE_VIDEO) data |= BIT(9);.
>
>> + if (ich_reset_override)
>> + data |= 0xC00; // set bit 10 and 11
> Instead of a comment, make this self-describing BIT(10) | BIT(11) code.
>
>> + data |= (_dsc_calc_ob_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18);
>> +
>> + DPU_REG_WRITE(hw, ENC_DF_CTRL + idx, data);
>> +
>> + data = (dsc->dsc_version_minor & 0xf) << 28;
>> + if (dsc->dsc_version_minor == 0x2) {
>> + if (dsc->native_422)
>> + data |= BIT(22);
>> + if (dsc->native_420)
>> + data |= BIT(21);
>> + }
>> +
>> + bpp = dsc->bits_per_pixel;
> As above, don't forget to read the documentation on this field:
>
> Target bits per pixel with 4 fractional bits, bits_per_pixel << 4
>
>> + /* as per hw requirement bpp should be programmed
>> + * twice the actual value in case of 420 or 422 encoding
>> + */
>> + if (dsc->native_422 || dsc->native_420)
>> + bpp = 2 * bpp;
>> + data |= (dsc->block_pred_enable ? 1 : 0) << 20;
>> + data |= (bpp << 10);
> Either wrap everything or nothing in ().
>
>> + data |= (dsc->line_buf_depth & 0xf) << 6;
>> + data |= dsc->convert_rgb << 4;
>> + data |= dsc->bits_per_component & 0xf;
>> +
>> + DPU_REG_WRITE(hw, DSC_MAIN_CONF + idx, data);
>> +
>> + data = (dsc->pic_width & 0xffff) |
>> + ((dsc->pic_height & 0xffff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_PICTURE_SIZE + idx, data);
>> +
>> + data = (dsc->slice_width & 0xffff) |
>> + ((dsc->slice_height & 0xffff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_SLICE_SIZE + idx, data);
>> +
>> + DPU_REG_WRITE(hw, DSC_MISC_SIZE + idx,
>> + (dsc->slice_chunk_size) & 0xffff);
>> +
>> + data = (dsc->initial_xmit_delay & 0xffff) |
>> + ((dsc->initial_dec_delay & 0xffff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_HRD_DELAYS + idx, data);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_SCALE + idx,
>> + dsc->initial_scale_value & 0x3f);
>> +
>> + data = (dsc->scale_increment_interval & 0xffff) |
>> + ((dsc->scale_decrement_interval & 0x7ff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_SCALE_INC_DEC + idx, data);
>> +
>> + data = (dsc->first_line_bpg_offset & 0x1f) |
>> + ((dsc->second_line_bpg_offset & 0x1f) << 5);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_1 + idx, data);
>> +
>> + data = (dsc->nfl_bpg_offset & 0xffff) |
>> + ((dsc->slice_bpg_offset & 0xffff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_2 + idx, data);
>> +
>> + data = (dsc->initial_offset & 0xffff) |
>> + ((dsc->final_offset & 0xffff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_3 + idx, data);
>> +
>> + data = (dsc->nsl_bpg_offset & 0xffff) |
>> + ((dsc->second_line_offset_adj & 0xffff) << 16);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_OFFSETS_4 + idx, data);
>> +
>> + data = (dsc->flatness_min_qp & 0x1f);
>> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
>> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
> dpu_hw_dsc.c computes this on the fly. After removing that, and
> using initial_lines from the function parameters, only
> dsc_info->num_active_ss_per_enc remains. Do you really need that
> msm_display_dsc_info struct here, do you need it at all?

I ported these code from our down stream code base.

I make it work first, then clean it up will follow.

I submit it for review since it looks like you guy like to have code sooner.

yes, eliminate msm_display_dsc_info is my next target and hope it can be
done.





>
>> +
>> + DPU_REG_WRITE(hw, DSC_FLATNESS_QP + idx, data);
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_MODEL_SIZE + idx,
>> + (dsc->rc_model_size) & 0xffff);
>> +
>> + data = dsc->rc_edge_factor & 0xf;
>> + data |= (dsc->rc_quant_incr_limit0 & 0x1f) << 8;
>> + data |= (dsc->rc_quant_incr_limit1 & 0x1f) << 13;
>> + data |= (dsc->rc_tgt_offset_high & 0xf) << 20;
>> + data |= (dsc->rc_tgt_offset_low & 0xf) << 24;
>> +
>> + DPU_REG_WRITE(hw, DSC_RC_CONFIG + idx, data);
>> +
>> + /* program the dsc wrapper */
>> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
>> + return;
>> +
>> + off = hw->blk_addr + idx;
>> +
>> + data = BIT(0); /* encoder enable */
>> + if (dsc->native_422)
>> + data |= BIT(8);
>> + else if (dsc->native_420)
>> + data |= BIT(9);
>> + if (!dsc->convert_rgb)
>> + data |= BIT(10);
>> + if (dsc->bits_per_component == 8)
>> + data |= BIT(11);
>> + if (mode & DSC_MODE_SPLIT_PANEL)
>> + data |= BIT(12);
>> + if (mode & DSC_MODE_MULTIPLEX)
>> + data |= BIT(13);
>> + if (!(mode & DSC_MODE_VIDEO))
>> + data |= BIT(17);
>> +
>> + if (dsc_info->dsc_4hsmerge_en) {
>> + data |= dsc_info->dsc_4hsmerge_padding << 18;
>> + data |= dsc_info->dsc_4hsmerge_alignment << 22;
>> + data |= BIT(16);
>> + }
>> +
>> + DPU_REG_WRITE(hw, DSC_CFG + idx, data);
>> +
>> +// DPU_REG_WRITE(hw, DSC_DATA_IN_SWAP + idx, 0x14e5);
> No commented-out code please, especially not with //
>
>> +}
>> +
>> +static void dpu_hw_dsc_config_thresh_1_2(struct dpu_hw_dsc *hw_dsc,
>> + struct drm_dsc_config *dsc)
>> +{
>> + struct dpu_hw_blk_reg_map *hw;
>> + struct msm_display_dsc_info *dsc_info;
>> + u32 idx, off;
>> + int i, j = 0;
>> + struct drm_dsc_rc_range_parameters *rc;
>> + u32 data = 0, min_qp = 0, max_qp = 0, bpg_off = 0;
>> +
>> + if (!hw_dsc || !dsc)
>> + return;
>> +
>> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_ENC, &idx))
>> + return;
>> +
>> + hw = &hw_dsc->hw;
>> +
>> + dsc_info = to_msm_dsc_info(dsc);
>> +
>> + rc = dsc->rc_range_params;
>> +
>> + off = 0;
>> + for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
>> + data |= dsc->rc_buf_thresh[i] << (8*j);
> Lack of spaces does not make this multiplication any prettier to read.
>
> * has precedence over << but it's better to replicate the () below as
> well.
>
>> + j++;
>> + if ((j == 4) || (i == DSC_NUM_BUF_RANGES - 2)) {
>> + DPU_REG_WRITE(hw, DSC_RC_BUF_THRESH_0 + idx + off,
>> + data);
>> + off += 4;
>> + j = 0;
>> + data = 0;
>> + }
>> + }
>> +
>> + off = 0;
>> + for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
>> + min_qp |= (rc[i].range_min_qp & 0x1f) << 5*j;
>> + max_qp |= (rc[i].range_max_qp & 0x1f) << 5*j;
>> + bpg_off |= (rc[i].range_bpg_offset & 0x3f) << 6*j;
> These values _must_ already be masked to be useful in
> drm_dsc_compute_rc_parameters(), no need to mask them again just like
> the v1.1 block implementation.
>
>> + j++;
>> + if (j == 5) {
>> + DPU_REG_WRITE(hw, DSC_RC_MIN_QP_0 + idx + off,
>> + min_qp);
>> + DPU_REG_WRITE(hw, DSC_RC_MAX_QP_0 + idx + off,
>> + max_qp);
>> + DPU_REG_WRITE(hw,
>> + DSC_RC_RANGE_BPG_OFFSETS_0 + idx + off,
>> + bpg_off);
>> + off += 4;
>> + j = 0;
>> + min_qp = 0;
>> + max_qp = 0;
>> + bpg_off = 0;
>> + }
>> + }
>> +}
>> +
>> +static void dpu_hw_dsc_bind_pingpong_blk_1_2(
>> + struct dpu_hw_dsc *hw_dsc,
>> + bool enable,
>> + const enum dpu_pingpong pp)
>> +{
>> + struct dpu_hw_blk_reg_map *hw;
>> + int idx;
>> + int mux_cfg = 0xF; /* Disabled */
> Lowercase hex (and anywhere else if I skipped any).
>
>> +
>> + if (!hw_dsc)
>> + return;
> As with the v1.1 implementation, we don't check this, and your function
> below also checks it (but it does not need to).
>
>> + if (_dsc_subblk_offset(hw_dsc, DPU_DSC_CTL, &idx))
>> + return;
>> +
>> + hw = &hw_dsc->hw;
>> + if (enable)
>> + mux_cfg = (pp - PINGPONG_0) & 0x7;
>> +
>> + DPU_REG_WRITE(hw, DSC_CTL + idx, mux_cfg);
>> +}
>> +
>> +void dpu_dsc_1_2_setup_ops(struct dpu_hw_dsc_ops *ops,
>> + const unsigned long features)
>> +{
>> + ops->dsc_disable = dpu_hw_dsc_disable_1_2;
>> + ops->dsc_config = dpu_hw_dsc_config_1_2;
>> + ops->dsc_config_thresh = dpu_hw_dsc_config_thresh_1_2;
>> + ops->dsc_bind_pingpong_blk = dpu_hw_dsc_bind_pingpong_blk_1_2;
>> +}
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
>> a Linux Foundation Collaborative Project
>>
> All in all you really need to revise and clean your patches before
> sending them to the lists; these are already far too many comments and
> nits, and massively take away from reviewing code behaviour which I have
> not even started with after looking at 1 out of 14 patches :(
>
> - Marijn

2023-01-25 00:30:36

by Abhinav Kumar

[permalink] [raw]
Subject: Re: [PATCH v1 00/14] add display port DSC feature

Hi Dmitry / Marijn

I have seen your review comments and agree there is much work to be done
to get this in a better shape.

We will post a better V2 to address the concerns.

Would appreciate some patience till then.

Thanks for your support in reviews as always

Abhinav

On 1/23/2023 10:24 AM, Kuogee Hsieh wrote:
> This patch add DSC related supporting functions into to both dp controller and dpu enccoder
>
> Kuogee Hsieh (14):
> drm/msm/dp: add dpcd read of both dsc and fec capability
> drm/msm/dp: add dsc factor into calculation of supported bpp
> drm/msm/dp: add configure mainlink_levels base on lane number
> drm/msm/dp: correct configure Colorimetry Indicator Field at MISC0
> drm/msm/dp: upgrade tu calculation base on newest algorithm
> drm/msm/dp: add display compression related struct
> drm/msm/dp: add dsc helper functions
> drm/msm/dp: add dsc supporting functions to DP controller
> drm/msm/dsi: export struct msm_compression_info to dpu encoder
> drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine
> drm/msm/disp/dpu1: add supports of new flush mechanism
> drm/msm/disp/dpu1: revise timing engine programming to work for DSC
> drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder
> drm/msm/disp/dpu1: add sc7280 dsc block and sub block
>
> drivers/gpu/drm/msm/Makefile | 2 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | 537 +++++++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | 25 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 341 +++++++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 7 +-
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 43 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 50 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 74 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 21 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h | 23 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | 371 +++++++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 132 ++--
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h | 3 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h | 6 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 10 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 10 +-
> drivers/gpu/drm/msm/dp/dp_catalog.c | 176 ++++-
> drivers/gpu/drm/msm/dp/dp_catalog.h | 97 ++-
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 839 ++++++++++++++-------
> drivers/gpu/drm/msm/dp/dp_display.c | 61 +-
> drivers/gpu/drm/msm/dp/dp_link.c | 29 +-
> drivers/gpu/drm/msm/dp/dp_panel.c | 749 +++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_panel.h | 67 +-
> drivers/gpu/drm/msm/dp/dp_reg.h | 40 +-
> drivers/gpu/drm/msm/dsi/dsi.c | 3 +-
> drivers/gpu/drm/msm/dsi/dsi.h | 3 +-
> drivers/gpu/drm/msm/dsi/dsi_host.c | 14 +-
> drivers/gpu/drm/msm/msm_drv.h | 113 ++-
> 32 files changed, 3429 insertions(+), 497 deletions(-)
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
> create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
>

2023-01-25 09:01:18

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v1 13/14] drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder

On 23/01/2023 19:24, Kuogee Hsieh wrote:
> Since display Port is an external peripheral, runtime compression
> detection is added to handle plug in and unplugged events. Currently
> only DSC compression supported. Once DSC compression detected, topology
> is static added and used to allocate system resources to accommodate
> DSC requirement. DSC related parameters are calculated and committed to
> DSC encoder. Also compression information are propagated to phy and
> committed to timing engine at video mode. This patch completes DSC
> implementation.
>
> Signed-off-by: Kuogee Hsieh <[email protected]>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 314 ++++++++++++++++-----
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 5 +-
> .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 34 ++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h | 3 +
> drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 10 +-
> 5 files changed, 292 insertions(+), 74 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index d2625b3..d7f5f93 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -15,6 +15,7 @@
> #include <drm/drm_crtc.h>
> #include <drm/drm_file.h>
> #include <drm/drm_probe_helper.h>
> +#include <drm/drm_bridge.h>
>
> #include "msm_drv.h"
> #include "dpu_kms.h"
> @@ -30,6 +31,7 @@
> #include "dpu_crtc.h"
> #include "dpu_trace.h"
> #include "dpu_core_irq.h"
> +#include "dpu_dsc_helper.h"
> #include "disp/msm_disp_snapshot.h"
>
> #define DPU_DEBUG_ENC(e, fmt, ...) DRM_DEBUG_ATOMIC("enc%d " fmt,\
> @@ -542,12 +544,12 @@ bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
> return (num_dsc > 0) && (num_dsc > intf_count);
> }
>
> -static struct msm_display_topology dpu_encoder_get_topology(
> +static void dpu_encoder_get_topology(
> struct dpu_encoder_virt *dpu_enc,
> struct dpu_kms *dpu_kms,
> - struct drm_display_mode *mode)
> + struct drm_display_mode *mode,
> + struct msm_display_topology *topology)
> {
> - struct msm_display_topology topology = {0};
> int i, intf_count = 0;
>
> for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
> @@ -567,19 +569,19 @@ static struct msm_display_topology dpu_encoder_get_topology(
> * sufficient number
> */
> if (intf_count == 2)
> - topology.num_lm = 2;
> + topology->num_lm = 2;
> else if (!dpu_kms->catalog->caps->has_3d_merge)
> - topology.num_lm = 1;
> + topology->num_lm = 1;
> else
> - topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
> + topology->num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
>
> if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_DSI) {
> if (dpu_kms->catalog->dspp &&
> - (dpu_kms->catalog->dspp_count >= topology.num_lm))
> - topology.num_dspp = topology.num_lm;
> + (dpu_kms->catalog->dspp_count >= topology->num_lm))
> + topology->num_dspp = topology->num_lm;
> }
>
> - topology.num_intf = intf_count;
> + topology->num_intf = intf_count;
>
> if (dpu_enc->dsc) {
> /*
> @@ -588,12 +590,31 @@ static struct msm_display_topology dpu_encoder_get_topology(
> * this is power optimal and can drive up to (including) 4k
> * screens
> */
> - topology.num_dsc = 2;
> - topology.num_lm = 2;
> - topology.num_intf = 1;
> + topology->num_dsc = 2;
> + topology->num_intf = 1;
> + topology->num_lm = 2;
> }
>
> - return topology;
> + /*
> + * default topology for display port DSC implementation.
> + * TODO:
> + * change to runtime resource calculation
> + */
> + if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
> + topology->num_dsc = 0;
> + topology->num_intf = intf_count;
> +
> + if (dpu_enc->comp_info) {
> + /* In case of Display Stream Compression (DSC), we would use
> + * 2 encoders, 2 layer mixers and 1 interface
> + * this is power optimal and can drive up to (including) 4k
> + * screens
> + */
> + topology->num_dsc = 1;
> + topology->num_intf = 1;
> + topology->num_lm = 1;
> + }
> + }
> }
>
> static int dpu_encoder_virt_atomic_check(
> @@ -605,7 +626,7 @@ static int dpu_encoder_virt_atomic_check(
> struct msm_drm_private *priv;
> struct dpu_kms *dpu_kms;
> struct drm_display_mode *adj_mode;
> - struct msm_display_topology topology;
> + struct msm_display_topology *topology;
> struct dpu_global_state *global_state;
> int i = 0;
> int ret = 0;
> @@ -642,7 +663,27 @@ static int dpu_encoder_virt_atomic_check(
> }
> }
>
> - topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode);
> + /*
> + * For display port, at this moment we know panel had been plugged in
> + * and dsc supported is detected.
> + * however we do not know the details of resolution will be used
> + * until mode_set had been done.
> + *
> + * use default topology to reserve system resource for dsc
> + *
> + * TODO: run time calculation of topology instead of hardcode it now
> + */
> + if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
> + struct drm_bridge *bridge;
> +
> + if (!dpu_enc->comp_info) {
> + bridge = drm_bridge_chain_get_first_bridge(drm_enc);
> + dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
> + }
> + }
> +
> + topology = &dpu_enc->topology;
> + dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, topology);
>
> /* Reserve dynamic resources now. */
> if (!ret) {
> @@ -655,7 +696,7 @@ static int dpu_encoder_virt_atomic_check(
>
> if (!crtc_state->active_changed || crtc_state->active)
> ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
> - drm_enc, crtc_state, topology);
> + drm_enc, crtc_state, *topology);
> }
> }
>
> @@ -1009,7 +1050,37 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
>
> if (phys->ops.cleanup_wb_job)
> phys->ops.cleanup_wb_job(phys, job);
> + }
> +}
> +
> +static void dpu_encoder_populate_encoder_phys(struct drm_encoder *drm_enc,
> + struct dpu_encoder_virt *dpu_enc)
> +{
> + struct msm_compression_info *comp_info;
> + struct msm_display_dsc_info *dsc_info;
> + int i;
> +
> + if (!dpu_enc->comp_info)
> + return;
> +
> + comp_info = dpu_enc->comp_info;
> + dsc_info = &comp_info->msm_dsc_info;
> +
> + for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> + struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
> +
> + if (!phys)
> + continue;
> +
> + phys->comp_type = comp_info->comp_type;
> + phys->comp_ratio = comp_info->comp_ratio;
>
> + if (phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC) {
> + phys->dsc_extra_pclk_cycle_cnt = dsc_info->pclk_per_line;
> + phys->dsc_extra_disp_width = dsc_info->extra_width;
> + phys->dce_bytes_per_line =
> + dsc_info->bytes_per_pkt * dsc_info->pkt_per_line;
> + }
> }
> }
>
> @@ -1050,6 +1121,24 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>
> trace_dpu_enc_mode_set(DRMID(drm_enc));
>
> + /*
> + * For display port, msm_dp_bridge_mode_set() will conver panel info
> + * into dp_mode. This including detail dsc information if it is enabled.
> + * after that, msm_dp_bridge_get_compression() will return detail
> + * dsc compression info
> + */
> + if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
> + struct drm_display_mode *mode, *adjusted_mode;
> + struct drm_bridge *bridge;
> +
> + mode = &crtc_state->mode;
> + adjusted_mode = &crtc_state->adjusted_mode;
> + bridge = drm_bridge_chain_get_first_bridge(drm_enc);
> + msm_dp_bridge_mode_set(bridge, mode, adjusted_mode);
> +
> + dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
> + }
> +
> /* Query resource that have been reserved in atomic check step. */
> num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
> drm_enc->base.id, DPU_HW_BLK_PINGPONG, hw_pp,
> @@ -1061,19 +1150,18 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
> dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
> drm_enc->base.id, DPU_HW_BLK_DSPP, hw_dspp,
> ARRAY_SIZE(hw_dspp));
> + num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
> + drm_enc->base.id, DPU_HW_BLK_DSC,
> + hw_dsc, ARRAY_SIZE(hw_dsc));
>
> for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
> dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i])
> : NULL;
>
> - if (dpu_enc->dsc) {
> - num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
> - drm_enc->base.id, DPU_HW_BLK_DSC,
> - hw_dsc, ARRAY_SIZE(hw_dsc));
> - for (i = 0; i < num_dsc; i++) {
> - dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
> - dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
> - }
> + for (i = 0; i < num_dsc; i++) {
> + dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
> + dpu_enc->hw_pp[i]->dsc = dpu_enc->hw_dsc[i]; /* bind dsc to pp */
> + dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
> }
>
> dpu_enc->dsc_mask = dsc_mask;
> @@ -1110,10 +1198,22 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
> phys->hw_pp = dpu_enc->hw_pp[i];
> phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>
> + if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
> + phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
> +
> + /* phys->hw_intf populated at dpu_encoder_setup_display() */
> + if (!phys->hw_intf) {
> + DPU_ERROR_ENC(dpu_enc,
> + "no intf block assigned at idx: %d\n", i);
> + return;
> + }
> +
> phys->cached_mode = crtc_state->adjusted_mode;
> if (phys->ops.atomic_mode_set)
> phys->ops.atomic_mode_set(phys, crtc_state, conn_state);
> }
> +
> + dpu_encoder_populate_encoder_phys(drm_enc, dpu_enc);
> }
>
> static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
> @@ -1208,6 +1308,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
> mutex_unlock(&dpu_enc->enc_lock);
> }
>
> +static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc);
> +
> static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
> {
> struct dpu_encoder_virt *dpu_enc = NULL;
> @@ -1233,6 +1335,10 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
> phys->ops.disable(phys);
> }
>
> + if (dpu_enc->comp_info && (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS)) {
> + dpu_encoder_unprep_dsc(dpu_enc);
> + dpu_enc->comp_info = NULL;
> + }
>
> /* after phys waits for frame-done, should be no more frames pending */
> if (atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
> @@ -1795,40 +1901,16 @@ static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
> nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
> }
>
> -static u32
> -dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc,
> - u32 enc_ip_width)
> -{
> - int ssm_delay, total_pixels, soft_slice_per_enc;
> -
> - soft_slice_per_enc = enc_ip_width / dsc->slice_width;
> -
> - /*
> - * minimum number of initial line pixels is a sum of:
> - * 1. sub-stream multiplexer delay (83 groups for 8bpc,
> - * 91 for 10 bpc) * 3
> - * 2. for two soft slice cases, add extra sub-stream multiplexer * 3
> - * 3. the initial xmit delay
> - * 4. total pipeline delay through the "lock step" of encoder (47)
> - * 5. 6 additional pixels as the output of the rate buffer is
> - * 48 bits wide
> - */
> - ssm_delay = ((dsc->bits_per_component < 10) ? 84 : 92);
> - total_pixels = ssm_delay * 3 + dsc->initial_xmit_delay + 47;
> - if (soft_slice_per_enc > 1)
> - total_pixels += (ssm_delay * 3);
> - return DIV_ROUND_UP(total_pixels, dsc->slice_width);
> -}
> -
> static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
> struct dpu_hw_dsc *hw_dsc,
> struct dpu_hw_pingpong *hw_pp,
> - struct drm_dsc_config *dsc,
> + struct msm_display_dsc_info *dsc_info,
> u32 common_mode,
> u32 initial_lines)
> {
> struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
> struct dpu_hw_ctl *ctl;
> + struct drm_dsc_config *dsc = &dsc_info->drm_dsc;
>
> ctl = cur_master->hw_ctl;
>
> @@ -1852,51 +1934,137 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
>
> }
>
> +static void dpu_encoder_dsc_disable(struct dpu_encoder_virt *dpu_enc,
> + struct dpu_hw_dsc *hw_dsc,
> + struct dpu_hw_pingpong *hw_pp)
> +{
> + struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
> + struct dpu_hw_ctl *ctl;
> +
> + ctl = cur_master->hw_ctl;
> +
> + if (hw_dsc->ops.dsc_disable)
> + hw_dsc->ops.dsc_disable(hw_dsc);
> +
> + if (hw_pp->ops.disable_dsc)
> + hw_pp->ops.disable_dsc(hw_pp);
> +
> +}
> +
> static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
> - struct drm_dsc_config *dsc)
> + struct msm_display_dsc_info *dsc_info)
> {
> /* coding only for 2LM, 2enc, 1 dsc config */
> struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
> struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
> struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
> + struct msm_display_topology *topology = &dpu_enc->topology;
> + enum dpu_3d_blend_mode mode_3d;
> int this_frame_slices;
> int intf_ip_w, enc_ip_w;
> - int dsc_common_mode;
> - int pic_width;
> - u32 initial_lines;
> + int dsc_common_mode = 0;
> + int dsc_pic_width;
> + int num_lm, num_dsc, num_intf;
> + bool dsc_merge, merge_3d, dsc_4hsmerge;
> + bool disable_merge_3d = false;
> + int ich_res;
> int i;
>
> for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
> hw_pp[i] = dpu_enc->hw_pp[i];
> hw_dsc[i] = dpu_enc->hw_dsc[i];
> + }
>
> - if (!hw_pp[i] || !hw_dsc[i]) {
> - DPU_ERROR_ENC(dpu_enc, "invalid params for DSC\n");
> - return;
> - }
> + num_lm = topology->num_lm;
> + num_dsc = topology->num_dsc;
> + num_intf = topology->num_intf;
> +
> +
> + mode_3d = (num_lm > num_dsc) ? BLEND_3D_H_ROW_INT : BLEND_3D_NONE;
> + merge_3d = ((mode_3d != BLEND_3D_NONE) && !(enc_master->hw_intf->cfg.split_link_en)) ?
> + true : false;
> +
> + dsc_merge = ((num_dsc > num_intf) && !dsc_info->half_panel_pu &&
> + !(enc_master->hw_intf->cfg.split_link_en)) ? true : false;
> + disable_merge_3d = (merge_3d && dsc_info->half_panel_pu) ? false : true;
> + dsc_4hsmerge = (dsc_merge && num_dsc == 4 && num_intf == 1) ? true : false;
> +
> + /*
> + * If this encoder is driving more than one DSC encoder, they
> + * operate in tandem, same pic dimension needs to be used by
> + * each of them.(pp-split is assumed to be not supported)
> + *
> + * If encoder is driving more than 2 DSCs, each DSC pair will operate
> + * on half of the picture in tandem.
> + */
> + dsc_pic_width = dsc_info->drm_dsc.pic_width;
> +
> + if (num_dsc > 2) {
> + dsc_pic_width /= 2;
> + dsc_info->dsc_4hsmerge_en = dsc_4hsmerge;
> }
>
> - dsc_common_mode = 0;
> - pic_width = dsc->pic_width;
> + this_frame_slices = dsc_pic_width / dsc_info->drm_dsc.slice_width;
> + intf_ip_w = this_frame_slices * dsc_info->drm_dsc.slice_width;
> + enc_ip_w = intf_ip_w;
> +
> + if (!dsc_info->half_panel_pu)
> + intf_ip_w /= num_intf;
> + if (!dsc_info->half_panel_pu && (num_dsc > 1))
> + dsc_common_mode |= DSC_MODE_SPLIT_PANEL;
> + if (dsc_merge) {
> + dsc_common_mode |= DSC_MODE_MULTIPLEX;
> + /*
> + * in dsc merge case: when using 2 encoders for the same
> + * stream, no. of slices need to be same on both the
> + * encoders.
> + */
> + enc_ip_w = intf_ip_w / 2;
> + }
>
> - dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
> if (enc_master->intf_mode == INTF_MODE_VIDEO)
> dsc_common_mode |= DSC_MODE_VIDEO;
>
> - this_frame_slices = pic_width / dsc->slice_width;
> - intf_ip_w = this_frame_slices * dsc->slice_width;
> + dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count;
> +
> + if (dsc_info->dsc_4hsmerge_en)
> + dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 2;
> + else if ((dsc_common_mode & DSC_MODE_MULTIPLEX) || (dsc_info->half_panel_pu))
> + dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 1;
> +
> + dpu_dsc_populate_dsc_private_params(dsc_info, intf_ip_w);
> +
> + dpu_dsc_initial_line_calc(dsc_info, enc_ip_w, dsc_common_mode);
>
> /*
> * dsc merge case: when using 2 encoders for the same stream,
> * no. of slices need to be same on both the encoders.
> */
> - enc_ip_w = intf_ip_w / 2;
> - initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
> + ich_res = dpu_dsc_ich_reset_override_needed(dsc_info->half_panel_pu, dsc_info);
> +
> + for (i = 0; i < num_dsc; i++) {
> + dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc_info,
> + dsc_common_mode, 0);
> + }
> +}
> +
> +static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc)
> +{
> + /* coding only for 2LM, 2enc, 1 dsc config */
> + struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
> + struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
> + struct msm_display_topology *topology = &dpu_enc->topology;
> + int i, num_dsc;
>
> for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
> - dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc,
> - dsc_common_mode, initial_lines);
> + hw_pp[i] = dpu_enc->hw_pp[i];
> + hw_dsc[i] = dpu_enc->hw_dsc[i];
> }
> +
> + num_dsc = topology->num_dsc;
> +
> + for (i = 0; i < num_dsc; i++)
> + dpu_encoder_dsc_disable(dpu_enc, hw_dsc[i], hw_pp[i]);
> }
>
> void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
> @@ -1904,6 +2072,7 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
> struct dpu_encoder_virt *dpu_enc;
> struct dpu_encoder_phys *phys;
> bool needs_hw_reset = false;
> + bool needs_phy_enable = false;
> unsigned int i;
>
> dpu_enc = to_dpu_encoder_virt(drm_enc);
> @@ -1918,6 +2087,9 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
> phys->ops.prepare_for_kickoff(phys);
> if (phys->enable_state == DPU_ENC_ERR_NEEDS_HW_RESET)
> needs_hw_reset = true;
> +
> + if (phys->enable_state == DPU_ENC_ENABLING)
> + needs_phy_enable = true;
> }
> DPU_ATRACE_END("enc_prepare_for_kickoff");
>
> @@ -1931,8 +2103,10 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
> }
> }
>
> - if (dpu_enc->dsc)
> - dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
> + if (needs_phy_enable && dpu_enc->comp_info)
> + dpu_encoder_prep_dsc(dpu_enc, &dpu_enc->comp_info->msm_dsc_info);
> +
> +
> }
>
> bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
> @@ -2295,7 +2469,7 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
> dpu_kms->catalog->caps->has_idle_pc;
>
> dpu_enc->comp_info = disp_info->comp_info;
> - if (dpu_enc->comp_info)
> + if (dpu_enc->comp_info && dpu_enc->comp_info->enabled)
> dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;
>
> mutex_lock(&dpu_enc->enc_lock);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 0569b36..ae4f6a8 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
> * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
> */
>
> @@ -202,6 +202,9 @@ struct dpu_encoder_phys {
> int irq[INTR_IDX_MAX];
> enum msm_display_compression_type comp_type;
> u32 comp_ratio;
> + u32 dsc_extra_pclk_cycle_cnt;
> + u32 dsc_extra_disp_width;
> + u32 dce_bytes_per_line;
> };
>
> static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> index 3330e185..6c7d791 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> @@ -86,6 +86,11 @@ static void drm_mode_to_intf_timing_params(
> timing->underflow_clr = 0xff;
> timing->hsync_skew = mode->hskew;
>
> + if (phys_enc->comp_type != MSM_DISPLAY_COMPRESSION_NONE) {
> + timing->compression_en = true;
> + timing->dce_bytes_per_line = phys_enc->dce_bytes_per_line;
> + }
> +
> /* DSI controller cannot handle active-low sync signals. */
> if (phys_enc->hw_intf->cap->type == INTF_DSI) {
> timing->hsync_polarity = 0;
> @@ -104,14 +109,36 @@ static void drm_mode_to_intf_timing_params(
>
> /*
> * for DP, divide the horizonal parameters by 2 when
> - * widebus is enabled
> + * widebus or compression is enabled, irrespective of
> + * compression ratio
> */
> - if (phys_enc->hw_intf->cap->type == INTF_DP && timing->wide_bus_en) {
> + if (phys_enc->hw_intf->cap->type == INTF_DP &&
> + (timing->wide_bus_en || (phys_enc->comp_ratio > 1))) {
> timing->width = timing->width >> 1;
> timing->xres = timing->xres >> 1;
> timing->h_back_porch = timing->h_back_porch >> 1;
> timing->h_front_porch = timing->h_front_porch >> 1;
> timing->hsync_pulse_width = timing->hsync_pulse_width >> 1;
> +
> + if (phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
> + (phys_enc->comp_ratio > 1)) {
> + timing->extra_dto_cycles =
> + phys_enc->dsc_extra_pclk_cycle_cnt;
> + timing->width += phys_enc->dsc_extra_disp_width;
> + timing->h_back_porch +=
> + phys_enc->dsc_extra_disp_width;
> + }
> + }
> +
> + /*
> + * for DSI, if compression is enabled, then divide the horizonal active
> + * timing parameters by compression ratio.
> + */
> + if ((phys_enc->hw_intf->cap->type != INTF_DP) &&
> + ((phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC))) {
> + // adjust active dimensions
> + timing->width = DIV_ROUND_UP(timing->width, phys_enc->comp_ratio);
> + timing->xres = DIV_ROUND_UP(timing->xres, phys_enc->comp_ratio);
> }
> }
>
> @@ -281,6 +308,9 @@ static void dpu_encoder_phys_vid_setup_timing_engine(
>
> phys_enc->hw_intf->hw_rev = phys_enc->dpu_kms->core_rev;
>
> + if (phys_enc->hw_pp->dsc)
> + intf_cfg.dsc_num = phys_enc->hw_pp->dsc->idx;

Why ??? it breaks when in multiplex mode and we have multiple DSCs !

> +
> spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
> phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
> &timing_params, fmt);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
> index c002234..ee71cee 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
> @@ -1,5 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> #ifndef _DPU_HW_PINGPONG_H
> @@ -8,6 +9,7 @@
> #include "dpu_hw_catalog.h"
> #include "dpu_hw_mdss.h"
> #include "dpu_hw_util.h"
> +#include "dpu_hw_dsc.h"
>
> #define DITHER_MATRIX_SZ 16
>
> @@ -149,6 +151,7 @@ struct dpu_hw_pingpong {
> enum dpu_pingpong idx;
> const struct dpu_pingpong_cfg *caps;
> struct dpu_hw_merge_3d *merge_3d;
> + struct dpu_hw_dsc *dsc;
>
> /* ops */
> struct dpu_hw_pingpong_ops ops;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> index 396429e..bb22ec8 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> #define pr_fmt(fmt) "[drm:%s] " fmt, __func__
> @@ -246,6 +247,11 @@ int dpu_rm_init(struct dpu_rm *rm,
> struct dpu_hw_dsc *hw;
> const struct dpu_dsc_cfg *dsc = &cat->dsc[i];
>
> + if (dsc->id < DSC_0 || dsc->id >= DSC_MAX) {
> + DPU_ERROR("skip dsc %d with invalid id\n", dsc->id);
> + continue;
> + }
> +
> hw = dpu_hw_dsc_init(dsc->id, mmio, cat);
> if (IS_ERR_OR_NULL(hw)) {
> rc = PTR_ERR(hw);
> @@ -535,8 +541,10 @@ static int _dpu_rm_make_reservation(
> }
>
> ret = _dpu_rm_reserve_dsc(rm, global_state, enc, &reqs->topology);
> - if (ret)
> + if (ret) {
> + DPU_ERROR("unable to find appropriate DSC\n");
> return ret;
> + }
>
> return ret;
> }


2023-01-25 09:09:04

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v1 12/14] drm/msm/disp/dpu1: revise timing engine programming to work for DSC

On 25/01/2023 00:36, Marijn Suijten wrote:
> On 2023-01-24 09:55:24, Kuogee Hsieh wrote:
>
> <snip>
>
>> This timing engine code is derived from our downstream code directly and
>> it has been used at many mobile devices by many vendors for many years
>> already.
>>
>> On the other words, it had been tested very thorough and works on
>> dsi/dp/hdmi/dsc/widebus applications.
>
> And the code already in mainline has seen 12 rounds of review, with a
> focus on inter-SoC compatibility. Regardless of that, we have processes
> to make changes on mainline: formatting changes (when actually making an
> improvement) go separate from semantic changes. Bugfixes are clearly
> described in individual patches with Fixes: tags. If you really think
> the code has to be as proposed in this patch, follow Dmitry's advice and
> split this accordingly.
>
>> When i brought dsc v1.2 over, I just merged it over and did not consider
>> too much.
>
> And that is exactly what is wrong with this *entire* series: copying
> over downstream code without "considering too much", stomping over
> previous review and even reverting bugfixes [1] [2] without giving it
> ANY ATTENTION in your patch description. That's unacceptable and
> insulting to contributors and reviewers. Full stop. Or did you expect
> us to turn a blind eye? This is mainline, not some techpack playground.
>
> [1]: https://lore.kernel.org/linux-arm-msm/[email protected]/
> [2]: https://lore.kernel.org/linux-arm-msm/[email protected]/
>
>> Can we adapt this code so that both upstream and down stream shared same
>> timing engine programming so that easier to maintain?
>
> Easy, I've said this before in IRC and will state it again: stop this
> techpack nonsense and focus on upstream-first. When something passes
> mainline review (and please don't bother maintainers and reviewers with
> series like this) it is inevitably good enough to be copied to
> techpack... at which point techpack becomes worthless as you can just
> backport a mainline patch or use a recent-enough kernel.
>
>
> tl;dr: it seems like you nor anyone involved in pre-reviewing/vetting
> this series is familiar with upstream guidelines. Follow the global
> advice from Dmitry [3] to reach a more efficient v2, and please don't
> let this run to v10 (or beyond) again.
>
> One suggestion to improve efficiency: split off the DPU v1.2 hardware
> block addition (and related changes) into a separate series. A smaller
> series (and properly split patches!) will give everyone less moving
> parts to worry about, and paves the way for DSI support without blocking
> on DP.

Yes to split DSC 1.2 integration and DP+DSC in 2 patchsets, with the various
fixes not necessary to make DP+DSC work in separate patches.

Be aware the rule is to make sure each single change doesn't break boot and builds
without warning, the rule is to make sure each single kernel change can be built
and doesn't break booting. And build the code with "W=1" to the make parameter to
trigger advanced GCC warnings.

This rule exists to permit running a git bisect to determine which commit introduces
a regression.

And the second most important rule is: a single change per patch, and a clear description
of the change in the commit message.
If your message needs a "change this and also change this" then it's wrong and it must be reworked.

Do incremental changes, like introduce a new struct, then use it afterwards.

Neil

>
> [3]: https://lore.kernel.org/linux-arm-msm/[email protected]/
>
> - Marijn


2023-01-30 20:17:05

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

On 2023-01-24 15:52:46, Kuogee Hsieh wrote:

<snip>

If only replying to a small chunk somewhere in the middle of a diff
and/or large review, please cut out unnecessary bits to make your reply
easier to find :)

> >> + data = (dsc->flatness_min_qp & 0x1f);
> >> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
> >> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
> > dpu_hw_dsc.c computes this on the fly. After removing that, and
> > using initial_lines from the function parameters, only
> > dsc_info->num_active_ss_per_enc remains. Do you really need that
> > msm_display_dsc_info struct here, do you need it at all?
>
> I ported these code from our down stream code base.
>
> I make it work first, then clean it up will follow.
>
> I submit it for review since it looks like you guy like to have code sooner.

Correct, I was looking forward to these patches albeit complete with the
promised DSI support from Jessica, which still seems to be pending.

When sending patches to that extent, with the intent of getting quick
turnaround but knowing that they are not ready for prime time yet (or
were they, based on your "submit it for review" mention? Don't you mean
testing?), please annotate the series with an RFC tag accompanied with a
description what still needs to be done and why. That would have saved
a great deal of comments and review.

> yes, eliminate msm_display_dsc_info is my next target and hope it can be
> done.

Thank you. Again, if that was the intent from the get-go, that's
perfect material to put in an RFC series' cover letter.

- Marijn

2023-01-30 20:28:01

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 09/14] drm/msm/dsi: export struct msm_compression_info to dpu encoder

On 2023-01-23 10:24:29, Kuogee Hsieh wrote:
<snip>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index d612419..70a74ed 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
<snip>
> @@ -892,6 +894,10 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
>
> pm_runtime_get_sync(&dpu_kms->pdev->dev);
>
> + for (i = 0; i < cat->dsc_count; i++)
> + msm_disp_snapshot_add_block(disp_state, cat->dsc[i].len,
> + dpu_kms->mmio + cat->dsc[i].base, "dsc_%d", i);
> +

Note that we've landed snapshotting of the DSC block in [1] because we
need it now, and - as discussed elsewhere - is perfect material to be
submitted in a standalone, appropriately described/titled patch (fine to
be part of a series, as long as that patch comprises this single diff).

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?id=a7efe60e36b9c0e966d7f82ac90a89b591d984e9

Keep in mind that it was added at the bottom of dpu_kms_mdp_snapshot()
instead of the top, so git might not clean it up or mark it as conflict
during a rebase; don't forget to drop it from v2 :)

> /* dump CTL sub-blocks HW regs info */
> for (i = 0; i < cat->ctl_count; i++)
> msm_disp_snapshot_add_block(disp_state, cat->ctl[i].len,

<snip>

- Marijn

2023-01-30 21:22:20

by Abhinav Kumar

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

Hi Marijn

On 1/30/2023 12:16 PM, Marijn Suijten wrote:
> On 2023-01-24 15:52:46, Kuogee Hsieh wrote:
>
> <snip>
>
> If only replying to a small chunk somewhere in the middle of a diff
> and/or large review, please cut out unnecessary bits to make your reply
> easier to find :)
>
>>>> + data = (dsc->flatness_min_qp & 0x1f);
>>>> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
>>>> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
>>> dpu_hw_dsc.c computes this on the fly. After removing that, and
>>> using initial_lines from the function parameters, only
>>> dsc_info->num_active_ss_per_enc remains. Do you really need that
>>> msm_display_dsc_info struct here, do you need it at all?
>>
>> I ported these code from our down stream code base.
>>
>> I make it work first, then clean it up will follow.
>>
>> I submit it for review since it looks like you guy like to have code sooner.
>
> Correct, I was looking forward to these patches albeit complete with the
> promised DSI support from Jessica, which still seems to be pending.
>

DSI support is still being worked upon.

I dont think we promised DSC 1.2 will come with DSI together in the same
series. It was always going to be DSC 1.2 + DP followed by another
series from Jessica for DSI.

Lets set the expectations right.

Thanks

Abhinav
> When sending patches to that extent, with the intent of getting quick
> turnaround but knowing that they are not ready for prime time yet (or
> were they, based on your "submit it for review" mention? Don't you mean
> testing?), please annotate the series with an RFC tag accompanied with a
> description what still needs to be done and why. That would have saved
> a great deal of comments and review.
>
>> yes, eliminate msm_display_dsc_info is my next target and hope it can be
>> done.
>
> Thank you. Again, if that was the intent from the get-go, that's
> perfect material to put in an RFC series' cover letter.
>
> - Marijn

2023-01-30 22:31:23

by Marijn Suijten

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine

Abhinav,

On 2023-01-30 13:22:03, Abhinav Kumar wrote:
> Hi Marijn
>
> On 1/30/2023 12:16 PM, Marijn Suijten wrote:
> > On 2023-01-24 15:52:46, Kuogee Hsieh wrote:
> >
> > <snip>
> >
> > If only replying to a small chunk somewhere in the middle of a diff
> > and/or large review, please cut out unnecessary bits to make your reply
> > easier to find :)
> >
> >>>> + data = (dsc->flatness_min_qp & 0x1f);
> >>>> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
> >>>> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
> >>> dpu_hw_dsc.c computes this on the fly. After removing that, and
> >>> using initial_lines from the function parameters, only
> >>> dsc_info->num_active_ss_per_enc remains. Do you really need that
> >>> msm_display_dsc_info struct here, do you need it at all?
> >>
> >> I ported these code from our down stream code base.
> >>
> >> I make it work first, then clean it up will follow.
> >>
> >> I submit it for review since it looks like you guy like to have code sooner.
> >
> > Correct, I was looking forward to these patches albeit complete with the
> > promised DSI support from Jessica, which still seems to be pending.
> >
>
> DSI support is still being worked upon.
>
> I dont think we promised DSC 1.2 will come with DSI together in the same
> series. It was always going to be DSC 1.2 + DP followed by another
> series from Jessica for DSI.
>
> Lets set the expectations right.

Not saying that these patches were promised as part of this series (as
said, "which still seem to be pending"), just making clear that this
series if of no use to me (no hurry to get the code in my hands sooner)
until the DSI patches are also shared which I would have started working
on myself if I didn't know QUIC was picking it up to distract from the
current v1.1 broken-ness on SM8150 and SM8250.

To set my (and at least Neil's) expectations straight as well: DSC 1.2
HW support should come in a separate series without DP support. Smaller
series (not to mention appropriately split-up patches) lead to a
decrease in scope, less dependencies and hopefully more efficient v2 -
for all involved.

- Marijn

2023-01-30 22:42:18

by Abhinav Kumar

[permalink] [raw]
Subject: Re: [PATCH v1 10/14] drm/msm/disp/dpu: add supports of DSC encoder v1.2 engine



On 1/30/2023 2:31 PM, Marijn Suijten wrote:
> Abhinav,
>
> On 2023-01-30 13:22:03, Abhinav Kumar wrote:
>> Hi Marijn
>>
>> On 1/30/2023 12:16 PM, Marijn Suijten wrote:
>>> On 2023-01-24 15:52:46, Kuogee Hsieh wrote:
>>>
>>> <snip>
>>>
>>> If only replying to a small chunk somewhere in the middle of a diff
>>> and/or large review, please cut out unnecessary bits to make your reply
>>> easier to find :)
>>>
>>>>>> + data = (dsc->flatness_min_qp & 0x1f);
>>>>>> + data |= (dsc->flatness_max_qp & 0x1f) << 5;
>>>>>> + data |= (dsc_info->det_thresh_flatness & 0xff) << 10;
>>>>> dpu_hw_dsc.c computes this on the fly. After removing that, and
>>>>> using initial_lines from the function parameters, only
>>>>> dsc_info->num_active_ss_per_enc remains. Do you really need that
>>>>> msm_display_dsc_info struct here, do you need it at all?
>>>>
>>>> I ported these code from our down stream code base.
>>>>
>>>> I make it work first, then clean it up will follow.
>>>>
>>>> I submit it for review since it looks like you guy like to have code sooner.
>>>
>>> Correct, I was looking forward to these patches albeit complete with the
>>> promised DSI support from Jessica, which still seems to be pending.
>>>
>>
>> DSI support is still being worked upon.
>>
>> I dont think we promised DSC 1.2 will come with DSI together in the same
>> series. It was always going to be DSC 1.2 + DP followed by another
>> series from Jessica for DSI.
>>
>> Lets set the expectations right.
>
> Not saying that these patches were promised as part of this series (as
> said, "which still seem to be pending"), just making clear that this
> series if of no use to me (no hurry to get the code in my hands sooner)
> until the DSI patches are also shared which I would have started working
> on myself if I didn't know QUIC was picking it up to distract from the
> current v1.1 broken-ness on SM8150 and SM8250.
>

This is being by Quic for everyone's benefit. So that we can land a
working DSC 1.2 solution for DSI as a working example for all future
panels. We only took it up to help others like you and linaro team to
give a working example of a DSC 1.2 panel with command mode in upstream.


> To set my (and at least Neil's) expectations straight as well: DSC 1.2
> HW support should come in a separate series without DP support. Smaller
> series (not to mention appropriately split-up patches) lead to a
> decrease in scope, less dependencies and hopefully more efficient v2 -
> for all involved.
>

As I already wrote earlier, we will fix the mistakes of v1, make v2
better and it will be split up better. But DSC 1.2 HW support had to be
pushed along with DP or DSI to show its working. We chose DP to go with
it as it aligns better with our upstream plans.


> - Marijn