Skip to content

Instantly share code, notes, and snippets.

@xzn
Last active May 10, 2026 03:00
Show Gist options
  • Select an option

  • Save xzn/028a18f78f6fa6f08a8a0d6a3d29c232 to your computer and use it in GitHub Desktop.

Select an option

Save xzn/028a18f78f6fa6f08a8a0d6a3d29c232 to your computer and use it in GitHub Desktop.
commit 7097b510060d52631504c066faaf684bea8689ad
Author: JS Deck <jsdeckerido@gmail.com>
Date: Sat May 9 15:29:32 2026 -0300
drm/msm: displayport dsc support and misc fixes
ported from https://lore.kernel.org/lkml/1674498274-6010-1-git-send-email-quic_khsieh@quicinc.com/
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 8b94c5f1cb68..2f6abcf4f429 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -81,6 +81,7 @@ msm-display-$(CONFIG_DRM_MSM_DPU) += \
disp/dpu1/dpu_hw_cwb.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 \
disp/dpu1/dpu_hw_lm.o \
@@ -138,6 +139,7 @@ msm-$(CONFIG_DRM_MSM_KMS_FBDEV) += msm_fbdev.o
msm-display-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
dp/dp_ctrl.o \
+ dp/dp_tu_calc.o \
dp/dp_debug.o \
dp/dp_display.o \
dp/dp_drm.o \
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index 0f4921b1a892..49db6571eb54 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -1367,7 +1367,6 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state
}
#define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE
-#define MAX_HDISPLAY_SPLIT 1080
static struct msm_display_topology dpu_crtc_get_topology(
struct drm_crtc *crtc,
@@ -1377,10 +1376,13 @@ static struct msm_display_topology dpu_crtc_get_topology(
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct msm_display_topology topology = {0};
struct drm_encoder *drm_enc;
+ int enc_count = 0;
- drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask)
+ drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) {
dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state,
&crtc_state->adjusted_mode);
+ WARN(enc_count++, "Only one encoder per crtc is supported, topology may be erroneous.\n");
+ }
topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state);
@@ -1402,16 +1404,15 @@ static struct msm_display_topology dpu_crtc_get_topology(
* enabled. This is because in cases where CWB is enabled, num_intf will
* count both the WB and real-time phys encoders.
*
- * For non-DSC CWB usecases, have the num_lm be decided by the
- * (mode->hdisplay > MAX_HDISPLAY_SPLIT) check.
+ * For non-DSC CWB usecases, have the num_lm be the minimum allowed.
*/
if (topology.num_intf == 2 && !topology.cwb_enabled)
topology.num_lm = 2;
- else if (topology.num_dsc == 2)
- topology.num_lm = 2;
+ else if (topology.num_dsc)
+ topology.num_lm = topology.num_dsc;
else if (dpu_kms->catalog->caps->has_3d_merge)
- topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
+ topology.num_lm = (mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) ? 2 : 1;
else
topology.num_lm = 1;
@@ -1442,6 +1443,7 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc,
return PTR_ERR(global_state);
dpu_rm_release(global_state, crtc);
+ cstate->num_mixers = 0;
if (!crtc_state->enable)
return 0;
@@ -1449,8 +1451,11 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc,
topology = dpu_crtc_get_topology(crtc, dpu_kms, crtc_state);
ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
crtc_state->crtc, &topology);
- if (ret)
+ if (ret) {
+ /* Release on failure to avoid persistent bork */
+ dpu_rm_release(global_state, crtc);
return ret;
+ }
cstate = to_dpu_crtc_state(crtc_state);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
index 94392b9b9245..2b5416b56f5d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
@@ -220,9 +220,6 @@ struct dpu_crtc_state {
u32 num_mixers;
struct dpu_crtc_mixer mixers[CRTC_DUAL_MIXERS];
- u32 num_ctls;
- struct dpu_hw_ctl *hw_ctls[CRTC_DUAL_MIXERS];
-
enum dpu_crtc_crc_source crc_source;
int crc_frame_skip_count;
};
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 000000000000..2ab71073d748
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c
@@ -0,0 +1,183 @@
+// 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 "dpu_kms.h"
+#include "dpu_hw_dsc.h"
+#include "dpu_dsc_helper.h"
+
+#include <drm/display/drm_dsc_helper.h>
+
+int msm_populate_dsc_params(struct device *dev, struct drm_dsc_config *dsc)
+{
+ int ret;
+
+ if (dsc->bits_per_pixel & 0xf) {
+ DRM_DEV_ERROR(dev, "DSI does not support fractional bits_per_pixel\n");
+ return -EINVAL;
+ }
+
+ switch (dsc->bits_per_component) {
+ case 8:
+ case 10:
+ case 12:
+ /*
+ * Only 8, 10, and 12 bpc are supported for DSC 1.1 block.
+ * If additional bpc values need to be supported, update
+ * this quard with the appropriate DSC version verification.
+ */
+ break;
+ default:
+ DRM_DEV_ERROR(dev,
+ "Unsupported bits_per_component value: %d\n",
+ dsc->bits_per_component);
+ return -EOPNOTSUPP;
+ }
+
+ dsc->simple_422 = 0;
+ dsc->convert_rgb = 1;
+ dsc->vbr_enable = 0;
+
+ drm_dsc_set_const_params(dsc);
+ drm_dsc_set_rc_buf_thresh(dsc);
+
+ /* DPU supports only pre-SCR panels */
+ ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "could not find DSC RC parameters\n");
+ return ret;
+ }
+
+ dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc);
+ dsc->line_buf_depth = dsc->bits_per_component + 1;
+
+ return drm_dsc_compute_rc_parameters(dsc);
+}
+
+void 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);
+}
+
+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;
+
+ 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;
+
+ 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 000000000000..a6385d2464d5
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h
@@ -0,0 +1,22 @@
+// 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 msm_populate_dsc_params(struct device *dev, struct drm_dsc_config *dsc);
+
+int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info,
+ int intf_width);
+
+void dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc_info, int enc_ip_width,
+ int dsc_cmn_mode);
+
+#endif /* __DPU_DSC_HELPER_H__ */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index eba1d52211f6..906406846d02 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -17,6 +17,7 @@
#include <drm/drm_file.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_bridge.h>
#include "msm_drv.h"
#include "dpu_kms.h"
@@ -31,6 +32,7 @@
#include "dpu_hw_cdm.h"
#include "dpu_formats.h"
#include "dpu_encoder_phys.h"
+#include "dpu_dsc_helper.h"
#include "dpu_crtc.h"
#include "dpu_trace.h"
#include "dpu_core_irq.h"
@@ -183,6 +185,7 @@ struct dpu_encoder_virt {
struct dpu_encoder_phys *cur_master;
struct dpu_encoder_phys *cur_slave;
struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_mixer *hw_mixer[MAX_CHANNELS_PER_ENC];
struct dpu_hw_cwb *hw_cwb[MAX_CWB_PER_ENC];
struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
@@ -207,14 +210,13 @@ struct dpu_encoder_virt {
struct mutex rc_lock;
enum dpu_enc_rc_states rc_state;
struct delayed_work delayed_off_work;
- struct msm_display_topology topology;
u32 idle_timeout;
bool wide_bus_en;
- /* DSC configuration */
- struct drm_dsc_config *dsc;
+ /* DSC compression configuration */
+ struct msm_compression_info *comp_info;
};
#define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base)
@@ -291,18 +293,6 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc)
return false;
}
-/**
- * dpu_encoder_is_dsc_enabled - indicate whether dsc is enabled
- * for the encoder.
- * @drm_enc: Pointer to previously created drm encoder structure
- */
-bool dpu_encoder_is_dsc_enabled(const struct drm_encoder *drm_enc)
-{
- const struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
-
- return dpu_enc->dsc ? true : false;
-}
-
/**
* dpu_encoder_get_crc_values_cnt - get number of physical encoders contained
* in virtual encoder that can collect CRC values
@@ -641,7 +631,7 @@ bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
* used for this encoder.
* @drm_enc: Pointer to encoder structure
*/
-struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc)
+struct msm_compression_info *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc)
{
struct msm_drm_private *priv = drm_enc->dev->dev_private;
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
@@ -649,6 +639,22 @@ struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc)
if (dpu_enc->disp_info.intf_type == INTF_DSI)
return msm_dsi_get_dsc_config(priv->kms->dsi[index]);
+ else if (dpu_enc->disp_info.intf_type == INTF_DP)
+ return msm_dp_get_dsc_config(priv->kms->dp[index]);
+
+ return NULL;
+}
+
+static bool dpu_encoder_dsc_enabled_for_mode(struct drm_encoder *drm_enc, const struct drm_display_mode *adj_mode)
+{
+ struct msm_drm_private *priv = drm_enc->dev->dev_private;
+ struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
+ int index = dpu_enc->disp_info.h_tile_instance[0];
+
+ if (dpu_enc->disp_info.intf_type == INTF_DSI)
+ return msm_dsi_dsc_enabled_for_mode(priv->kms->dsi[index], adj_mode);
+ else if (dpu_enc->disp_info.intf_type == INTF_DP)
+ return msm_dp_dsc_enabled_for_mode(priv->kms->dp[index], adj_mode);
return NULL;
}
@@ -665,7 +671,6 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_framebuffer *fb;
- struct drm_dsc_config *dsc;
int i;
@@ -673,10 +678,8 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
if (dpu_enc->phys_encs[i])
topology->num_intf++;
- dsc = dpu_encoder_get_dsc_config(drm_enc);
-
/* We only support 2 DSC mode (with 2 LM and 1 INTF) */
- if (dsc) {
+ if (dpu_encoder_dsc_enabled_for_mode(drm_enc, adj_mode)) {
/*
* Use 2 DSC encoders, 2 layer mixers and 1 or 2 interfaces
* when Display Stream Compression (DSC) is enabled,
@@ -684,12 +687,15 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
* This is power-optimal and can drive up to (including) 4k
* screens.
*/
- WARN(topology->num_intf > 2,
- "DSC topology cannot support more than 2 interfaces\n");
- if (topology->num_intf >= 2 || dpu_kms->catalog->dsc_count >= 2)
+ if ((topology->num_intf == 2 || adj_mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) &&
+ dpu_kms->catalog->dsc_count >= 2)
topology->num_dsc = 2;
- else
+ else if ((topology->num_intf == 1 && adj_mode->hdisplay <= dpu_kms->catalog->caps->max_mixer_width)
+ && dpu_kms->catalog->dsc_count)
topology->num_dsc = 1;
+ else
+ pr_warn("Unsupported DSC topology for configuration num_intf=%d hdisplay=%d dsc_count=%d\n",
+ topology->num_intf, adj_mode->hdisplay, dpu_kms->catalog->dsc_count);
}
connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc);
@@ -1149,6 +1155,43 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
}
}
+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;
+
+ for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+ struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
+
+ if (!phys)
+ continue;
+
+ phys->dsc_extra_pclk_cycle_cnt = 0;
+ phys->dsc_extra_disp_width = 0;
+ phys->dce_bytes_per_line = 0;
+
+ if (!dpu_enc->comp_info) {
+ phys->comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+ phys->comp_ratio = 1;
+ } else {
+ comp_info = dpu_enc->comp_info;
+ dsc_info = &comp_info->msm_dsc_info;
+
+ 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;
+ }
+ }
+ }
+}
+
static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
@@ -1159,9 +1202,10 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
struct dpu_global_state *global_state;
struct dpu_hw_blk *hw_pp[MAX_CHANNELS_PER_ENC];
struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC];
struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
struct dpu_hw_blk *hw_cwb[MAX_CHANNELS_PER_ENC];
- int num_ctl, num_pp, num_dsc, num_pp_per_intf;
+ int num_ctl, num_pp, num_dsc, num_pp_per_intf, num_lm;
int num_cwb = 0;
bool is_cwb_encoder;
unsigned int dsc_mask = 0;
@@ -1220,12 +1264,23 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i])
: NULL;
+ num_lm = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
+ drm_enc->crtc, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm));
+
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
+ dpu_enc->hw_mixer[i] = i < num_lm ? to_dpu_hw_mixer(hw_lm[i])
+ : NULL;
+
num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
drm_enc->crtc, 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 < MAX_CHANNELS_PER_ENC; i++) {
+ if (i < num_dsc) {
+ dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
+ dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
+ } else {
+ dpu_enc->hw_dsc[i] = NULL;
+ }
}
dpu_enc->dsc_mask = dsc_mask;
@@ -1342,7 +1397,9 @@ static void dpu_encoder_virt_atomic_enable(struct drm_encoder *drm_enc,
struct drm_display_mode *cur_mode = NULL;
dpu_enc = to_dpu_encoder_virt(drm_enc);
- dpu_enc->dsc = dpu_encoder_get_dsc_config(drm_enc);
+
+ dpu_enc->comp_info = dpu_encoder_get_dsc_config(drm_enc);
+ _dpu_encoder_populate_encoder_phys(drm_enc, dpu_enc);
atomic_set(&dpu_enc->frame_done_timeout_cnt, 0);
@@ -1661,6 +1718,10 @@ 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 (ctl->ops.update_pending_flush_periph && dpu_encoder_needs_periph_flush(phys)) {
+ ctl->ops.update_pending_flush_periph(ctl, phys->hw_intf->idx);
+ }
+
ctl->ops.trigger_flush(ctl);
if (ctl->ops.get_pending_flush)
@@ -1963,31 +2024,6 @@ int dpu_encoder_vsync_time(struct drm_encoder *drm_enc, ktime_t *wakeup_time)
return 0;
}
-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_hw_ctl *ctl,
struct dpu_hw_dsc *hw_dsc,
struct dpu_hw_pingpong *hw_pp,
@@ -2015,7 +2051,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_ctl *ctl,
}
static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
- struct drm_dsc_config *dsc)
+ struct msm_display_dsc_info *dsc_info)
{
struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
struct dpu_hw_ctl *ctl = enc_master->hw_ctl;
@@ -2023,10 +2059,10 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
int this_frame_slices;
int intf_ip_w, enc_ip_w;
- int dsc_common_mode;
- int pic_width;
- u32 initial_lines;
- int num_dsc = 0;
+ int dsc_common_mode = 0;
+ int dsc_pic_width;
+ int num_dsc, num_intf;
+ bool dsc_merge;
int i;
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
@@ -2035,29 +2071,106 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
if (!hw_pp[i] || !hw_dsc[i])
break;
-
num_dsc++;
}
- pic_width = dsc->pic_width;
+ for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
+ if (dpu_enc->phys_encs[i])
+ num_intf++;
- dsc_common_mode = 0;
+ dsc_merge = num_dsc > num_intf;
+
+ /*
+ * 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;
+ 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;
+
+ intf_ip_w /= num_intf;
if (num_dsc > 1)
dsc_common_mode |= DSC_MODE_SPLIT_PANEL;
- if (dpu_encoder_use_dsc_merge(enc_master->parent))
+ 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;
+ }
+
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_common_mode & DSC_MODE_MULTIPLEX)
+ 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);
- enc_ip_w = intf_ip_w / num_dsc;
- initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_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.
+ */
for (i = 0; i < num_dsc; i++)
dpu_encoder_dsc_pipe_cfg(ctl, hw_dsc[i], hw_pp[i],
- dsc, dsc_common_mode, initial_lines);
+ &dsc_info->drm_dsc, dsc_common_mode, dsc_info->initial_lines);
+}
+
+static void dpu_encoder_dsc_pipe_clr(struct dpu_hw_ctl *ctl,
+ struct dpu_hw_dsc *hw_dsc,
+ struct dpu_hw_pingpong *hw_pp)
+{
+ 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);
+
+ if (hw_dsc->ops.dsc_bind_pingpong_blk)
+ hw_dsc->ops.dsc_bind_pingpong_blk(hw_dsc, PINGPONG_NONE);
+
+ if (ctl->ops.update_pending_flush_dsc)
+ ctl->ops.update_pending_flush_dsc(ctl, hw_dsc->idx);
+}
+
+static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc)
+{
+ /* coding only for 2LM, 2enc, 1 dsc config */
+ struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
+ struct dpu_hw_ctl *ctl = enc_master->hw_ctl;
+ struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+ 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_dsc[i] && hw_pp[i])
+ dpu_encoder_dsc_pipe_clr(ctl, hw_dsc[i], hw_pp[i]);
+ }
+}
+
+void dpu_encoder_dsc_cleanup(struct dpu_encoder_phys *phys_enc)
+{
+ struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
+
+ if (dpu_enc->comp_info) {
+ if (phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC)
+ dpu_encoder_unprep_dsc(dpu_enc);
+ dpu_enc->comp_info = NULL;
+ }
}
/**
@@ -2071,7 +2184,9 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
{
struct dpu_encoder_virt *dpu_enc;
struct dpu_encoder_phys *phys;
+ struct msm_compression_info *comp_info;
bool needs_hw_reset = false;
+ bool needs_phy_enable = false;
unsigned int i;
dpu_enc = to_dpu_encoder_virt(drm_enc);
@@ -2086,6 +2201,8 @@ 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");
@@ -2099,8 +2216,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);
+ comp_info = dpu_enc->comp_info;
+ if (comp_info && phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
+ (needs_phy_enable || dpu_enc->disp_info.is_cmd_mode))
+ dpu_encoder_prep_dsc(dpu_enc, &comp_info->msm_dsc_info);
}
/**
@@ -2176,79 +2295,43 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
DPU_ATRACE_END("encoder_kickoff");
}
-static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
+void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
{
- int i, num_lm;
- struct dpu_global_state *global_state;
- struct dpu_hw_blk *hw_lm[2];
- struct dpu_hw_mixer *hw_mixer[2];
struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
+ int i;
+ struct dpu_encoder_virt *dpu_enc;
- /* reset all mixers for this encoder */
- if (ctl->ops.clear_all_blendstages)
- ctl->ops.clear_all_blendstages(ctl);
-
- global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms);
-
- num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state,
- phys_enc->parent->crtc, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm));
-
- for (i = 0; i < num_lm; i++) {
- hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]);
- if (ctl->ops.update_pending_flush_mixer)
- ctl->ops.update_pending_flush_mixer(ctl, hw_mixer[i]->idx);
-
- /* clear all blendstages */
- if (ctl->ops.setup_blendstage)
- ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL);
+ dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
- if (hw_mixer[i]->ops.clear_all_blendstages)
- hw_mixer[i]->ops.clear_all_blendstages(hw_mixer[i]);
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ if (dpu_enc->hw_mixer[i]) {
+ if (ctl->ops.update_pending_flush_mixer)
+ ctl->ops.update_pending_flush_mixer(ctl, dpu_enc->hw_mixer[i]->idx);
- if (ctl->ops.set_active_lms)
- ctl->ops.set_active_lms(ctl, NULL);
+ /* clear all blendstages */
+ if (ctl->ops.setup_blendstage)
+ ctl->ops.setup_blendstage(ctl, dpu_enc->hw_mixer[i]->idx, NULL);
- if (ctl->ops.set_active_fetch_pipes)
- ctl->ops.set_active_fetch_pipes(ctl, NULL);
+ if (dpu_enc->hw_mixer[i]->ops.clear_all_blendstages)
+ dpu_enc->hw_mixer[i]->ops.clear_all_blendstages(dpu_enc->hw_mixer[i]);
- if (ctl->ops.set_active_pipes)
- ctl->ops.set_active_pipes(ctl, NULL);
+ if (dpu_enc->hw_mixer[i]->ops.setup_blendstage)
+ dpu_enc->hw_mixer[i]->ops.setup_blendstage(dpu_enc->hw_mixer[i], dpu_enc->hw_mixer[i]->idx, NULL);
+ }
}
-}
-
-static void dpu_encoder_dsc_pipe_clr(struct dpu_hw_ctl *ctl,
- struct dpu_hw_dsc *hw_dsc,
- struct dpu_hw_pingpong *hw_pp)
-{
- 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);
-
- if (hw_dsc->ops.dsc_bind_pingpong_blk)
- hw_dsc->ops.dsc_bind_pingpong_blk(hw_dsc, PINGPONG_NONE);
+ /* reset all mixers for this encoder */
+ if (ctl->ops.clear_all_blendstages)
+ ctl->ops.clear_all_blendstages(ctl);
- if (ctl->ops.update_pending_flush_dsc)
- ctl->ops.update_pending_flush_dsc(ctl, hw_dsc->idx);
-}
+ if (ctl->ops.set_active_lms)
+ ctl->ops.set_active_lms(ctl, NULL);
-static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc)
-{
- /* coding only for 2LM, 2enc, 1 dsc config */
- struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
- struct dpu_hw_ctl *ctl = enc_master->hw_ctl;
- struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
- struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
- int i;
+ if (ctl->ops.set_active_fetch_pipes)
+ ctl->ops.set_active_fetch_pipes(ctl, NULL);
- 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_encoder_dsc_pipe_clr(ctl, hw_dsc[i], hw_pp[i]);
- }
+ if (ctl->ops.set_active_pipes)
+ ctl->ops.set_active_pipes(ctl, NULL);
}
/**
@@ -2319,10 +2402,7 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
phys_enc->hw_cdm->idx);
}
- if (dpu_enc->dsc) {
- dpu_encoder_unprep_dsc(dpu_enc);
- dpu_enc->dsc = NULL;
- }
+ dpu_encoder_dsc_cleanup(phys_enc);
intf_cfg.stream_sel = 0; /* Don't care value for video mode */
intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
@@ -2355,8 +2435,8 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc,
struct dpu_kms *dpu_kms;
struct dpu_global_state *global_state;
- struct dpu_hw_blk *rt_pp_list[MAX_CHANNELS_PER_ENC];
- int num_pp;
+ struct dpu_hw_blk *rt_pp_list[MAX_CHANNELS_PER_ENC] = {0};
+ int num_pp = 0;
if (!phys_enc->hw_wb)
return;
@@ -2369,16 +2449,18 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc,
return;
}
- dpu_kms = phys_enc->dpu_kms;
- global_state = dpu_kms_get_existing_global_state(dpu_kms);
- num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
- phys_enc->parent->crtc,
- DPU_HW_BLK_PINGPONG, rt_pp_list,
- ARRAY_SIZE(rt_pp_list));
+ if (enable) {
+ dpu_kms = phys_enc->dpu_kms;
+ global_state = dpu_kms_get_existing_global_state(dpu_kms);
+ num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
+ phys_enc->parent->crtc,
+ DPU_HW_BLK_PINGPONG, rt_pp_list,
+ ARRAY_SIZE(rt_pp_list));
- if (num_pp == 0 || num_pp > MAX_CHANNELS_PER_ENC) {
- DPU_DEBUG_ENC(dpu_enc, "invalid num_pp %d\n", num_pp);
- return;
+ if (num_pp == 0 || num_pp > MAX_CHANNELS_PER_ENC) {
+ DPU_DEBUG_ENC(dpu_enc, "invalid num_pp %d\n", num_pp);
+ return;
+ }
}
/*
@@ -2393,9 +2475,9 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc,
continue;
if (enable) {
- struct dpu_hw_pingpong *hw_pp =
- to_dpu_hw_pingpong(rt_pp_list[i]);
- cwb_cfg.pp_idx = hw_pp->idx;
+ struct dpu_hw_pingpong *hw_pp = i < num_pp ?
+ to_dpu_hw_pingpong(rt_pp_list[i]) : NULL;
+ cwb_cfg.pp_idx = hw_pp ? hw_pp->idx : PINGPONG_NONE;
} else {
cwb_cfg.pp_idx = PINGPONG_NONE;
}
@@ -2911,6 +2993,31 @@ unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc)
return dpu_enc->cwb_mask;
}
+enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode(
+ struct dpu_encoder_phys *phys_enc)
+{
+ struct drm_encoder *encoder = phys_enc->parent;
+ struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(encoder);
+ int i, num_mixers = 0;
+
+ if (!phys_enc || phys_enc->enable_state == DPU_ENC_DISABLING)
+ return BLEND_3D_NONE;
+
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ if (!dpu_enc->hw_mixer[i])
+ break;
+ num_mixers++;
+ }
+
+ /* Use merge_3d unless DSC MERGE topology is used */
+ if (phys_enc->split_role == ENC_ROLE_SOLO &&
+ num_mixers == CRTC_DUAL_MIXERS &&
+ !dpu_encoder_use_dsc_merge(phys_enc->parent))
+ return BLEND_3D_H_ROW_INT;
+
+ return BLEND_3D_NONE;
+}
+
/**
* dpu_encoder_helper_get_dsc - get DSC blocks mask for the DPU encoder
* This helper function is used by physical encoder to get DSC blocks mask
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index ca1ca2e51d7e..9f8b7436c332 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -72,8 +72,6 @@ int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc);
bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc);
-bool dpu_encoder_is_dsc_enabled(const struct drm_encoder *drm_enc);
-
int dpu_encoder_get_crc_values_cnt(const struct drm_encoder *drm_enc);
void dpu_encoder_setup_misr(const struct drm_encoder *drm_encoder);
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 61b22d949454..522f6e87967d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -197,6 +197,11 @@ struct dpu_encoder_phys {
atomic_t pending_kickoff_cnt;
wait_queue_head_t pending_kickoff_wq;
unsigned 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;
bool has_intf_te;
};
@@ -290,30 +295,12 @@ struct dpu_encoder_phys *dpu_encoder_phys_wb_init(struct drm_device *dev,
void dpu_encoder_helper_trigger_start(struct dpu_encoder_phys *phys_enc);
-static inline enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode(
- struct dpu_encoder_phys *phys_enc)
-{
- struct dpu_crtc_state *dpu_cstate;
-
- if (!phys_enc || phys_enc->enable_state == DPU_ENC_DISABLING)
- return BLEND_3D_NONE;
-
- dpu_cstate = to_dpu_crtc_state(phys_enc->parent->crtc->state);
-
- /* Use merge_3d unless DSC MERGE topology is used */
- if (phys_enc->split_role == ENC_ROLE_SOLO &&
- dpu_cstate->num_mixers == CRTC_DUAL_MIXERS &&
- !dpu_encoder_use_dsc_merge(phys_enc->parent))
- return BLEND_3D_H_ROW_INT;
-
- return BLEND_3D_NONE;
-}
-
+enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode(struct dpu_encoder_phys *phys_enc);
unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc);
unsigned int dpu_encoder_helper_get_dsc(struct dpu_encoder_phys *phys_enc);
-struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc);
+struct msm_compression_info *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc);
u32 dpu_encoder_get_drm_fmt(struct dpu_encoder_phys *phys_enc);
@@ -331,6 +318,10 @@ int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc,
void (*func)(void *arg),
struct dpu_encoder_wait_info *wait_info);
+void dpu_encoder_dsc_cleanup(struct dpu_encoder_phys *phys_enc);
+
+void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc);
+
void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc);
void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc,
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
index 93db1484f606..b97df3ba1295 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
@@ -532,13 +532,18 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc)
{
struct dpu_encoder_phys_cmd *cmd_enc =
to_dpu_encoder_phys_cmd(phys_enc);
- struct dpu_hw_ctl *ctl;
+ struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
+ struct dpu_hw_intf_cfg intf_cfg = { 0 };
if (phys_enc->enable_state == DPU_ENC_DISABLED) {
DPU_ERROR_CMDENC(cmd_enc, "already disabled\n");
return;
}
+ ctl->ops.reset(ctl);
+
+ dpu_encoder_helper_reset_mixers(phys_enc);
+
if (phys_enc->has_intf_te) {
DRM_DEBUG_KMS("id:%u intf:%d state:%d\n", DRMID(phys_enc->parent),
phys_enc->hw_intf->idx - INTF_0,
@@ -565,10 +570,26 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc)
phys_enc->hw_intf,
PINGPONG_NONE);
- ctl = phys_enc->hw_ctl;
ctl->ops.update_pending_flush_intf(ctl, phys_enc->hw_intf->idx);
}
+ dpu_encoder_dsc_cleanup(phys_enc);
+
+ if (ctl->ops.reset_intf_cfg) {
+ intf_cfg.intf = phys_enc->hw_intf->idx;
+ if (phys_enc->split_role == ENC_ROLE_MASTER)
+ intf_cfg.intf_master = phys_enc->hw_intf->idx;
+ intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_CMD;
+ intf_cfg.stream_sel = cmd_enc->stream_sel;
+ intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
+ intf_cfg.dsc = dpu_encoder_helper_get_dsc(phys_enc);
+ ctl->ops.reset_intf_cfg(ctl, &intf_cfg);
+ }
+
+ ctl->ops.trigger_flush(ctl);
+ ctl->ops.trigger_start(ctl);
+ ctl->ops.clear_pending_flush(ctl);
+
phys_enc->enable_state = DPU_ENC_DISABLED;
}
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 0ba777bda253..335cd63de798 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
@@ -45,6 +45,7 @@ static void drm_mode_to_intf_timing_params(
const struct drm_display_mode *mode,
struct dpu_hw_intf_timing_params *timing)
{
+ bool comp_ratio_gt_1 = false;
memset(timing, 0, sizeof(*timing));
if ((mode->htotal < mode->hsync_end)
@@ -95,7 +96,11 @@ static void drm_mode_to_intf_timing_params(
}
timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent);
- timing->compression_en = dpu_encoder_is_dsc_enabled(phys_enc->parent);
+ timing->compression_en = phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC;
+ if (timing->compression_en) {
+ timing->dce_bytes_per_line = phys_enc->dce_bytes_per_line;
+ comp_ratio_gt_1 = phys_enc->comp_ratio > 1;
+ }
/*
* For DP/EDP, Shift timings to align it to bottom right.
@@ -110,31 +115,38 @@ static void drm_mode_to_intf_timing_params(
}
/*
- * for DP, divide the horizonal parameters by 2 when
+ * for DP, divide the horizontal parameters by 2 when
* widebus is enabled
*/
- 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 || comp_ratio_gt_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 (comp_ratio_gt_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
+ * for DSI, if compression is enabled, then divide the horizontal active
* timing parameters by compression ratio. bits of 3 components(R/G/B)
* is compressed into bits of 1 pixel.
*/
if (phys_enc->hw_intf->cap->type != INTF_DP && timing->compression_en) {
- struct drm_dsc_config *dsc =
- dpu_encoder_get_dsc_config(phys_enc->parent);
- /*
- * TODO: replace drm_dsc_get_bpp_int with logic to handle
- * fractional part if there is fraction
- */
- timing->width = timing->width * drm_dsc_get_bpp_int(dsc) /
- (dsc->bits_per_component * 3);
+ struct msm_compression_info *comp_info =
+ dpu_encoder_get_dsc_config(phys_enc->parent);
+ struct drm_dsc_config *dsc = &comp_info->msm_dsc_info.drm_dsc;
+
+ timing->width = (timing->width * dsc->bits_per_pixel /
+ (dsc->bits_per_component * 3)) >> 4;
timing->xres = timing->width;
}
}
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
index b3395e9c34a1..04f17c610faa 100644
--- 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
@@ -91,9 +91,9 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
{
struct dpu_hw_blk_reg_map *hw;
const struct dpu_dsc_sub_blks *sblk;
+ struct msm_display_dsc_info *dsc_info;
u32 data = 0;
u32 det_thresh_flatness;
- u32 num_active_slice_per_enc;
u32 bpp;
if (!hw_dsc || !dsc)
@@ -101,6 +101,8 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
hw = &hw_dsc->hw;
+ dsc_info = to_msm_dsc_info(dsc);
+
sblk = hw_dsc->caps->sblk;
if (mode & DSC_MODE_SPLIT_PANEL)
@@ -109,11 +111,7 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
if (mode & DSC_MODE_MULTIPLEX)
data |= BIT(1);
- num_active_slice_per_enc = dsc->slice_count;
- if (mode & DSC_MODE_MULTIPLEX)
- num_active_slice_per_enc = dsc->slice_count / 2;
-
- data |= (num_active_slice_per_enc & 0x3) << 7;
+ data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7;
DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);
@@ -122,7 +120,7 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
if (mode & DSC_MODE_VIDEO)
data |= BIT(9);
- data |= (_dsc_calc_output_buf_max_addr(hw_dsc, num_active_slice_per_enc) << 18);
+ data |= (_dsc_calc_output_buf_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18);
DPU_REG_WRITE(hw, sblk->enc.base + ENC_DF_CTRL, data);
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 7e620f590984..b3604c7248dc 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
@@ -122,7 +122,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *intf,
/* read interface_cfg */
intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
- if (intf->cap->type == INTF_DP)
+ if (intf->cap->type == INTF_EDP || intf->cap->type == INTF_DP)
dp_intf = true;
hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width +
@@ -170,19 +170,24 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *intf,
if (p->wide_bus_en)
intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
- data_width = p->width;
-
/*
* If widebus is enabled, data is valid for only half the active window
* since the data rate is doubled in this mode. But for the compression
* mode in DP case, the p->width is already adjusted in
* drm_mode_to_intf_timing_params()
*/
- if (p->wide_bus_en && !dp_intf)
+ 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;
+ }
- /* TODO: handle DSC+DP case, we only handle DSC+DSI case so far */
- if (p->compression_en && !dp_intf &&
+ if (p->compression_en &&
intf->mdss_ver->core_major_ver >= 7)
intf_cfg2 |= INTF_CFG2_DCE_DATA_COMPRESS;
@@ -205,6 +210,13 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *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;
+ }
}
den_polarity = 0;
@@ -578,9 +590,13 @@ static void dpu_hw_intf_program_intf_cmd_cfg(struct dpu_hw_intf *intf,
if (cmd_mode_cfg->data_compress)
intf_cfg2 |= INTF_CFG2_DCE_DATA_COMPRESS;
+ else
+ intf_cfg2 &= ~INTF_CFG2_DCE_DATA_COMPRESS;
if (cmd_mode_cfg->wide_bus_en)
intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN;
+ else
+ intf_cfg2 &= ~INTF_CFG2_DATABUS_WIDEN;
DPU_REG_WRITE(&intf->hw, INTF_CONFIG2, intf_cfg2);
}
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 f6ef2c21b66d..5423c171b65d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
@@ -35,6 +35,8 @@ struct dpu_hw_intf_timing_params {
bool wide_bus_en;
bool compression_en;
+ u32 extra_dto_cycles; /* for DP only */
+ u32 dce_bytes_per_line;
};
struct dpu_hw_intf_prog_fetch {
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 61d7e65469b3..7b95debe6eea 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -663,6 +663,7 @@ 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 = INTF_DP;
+ info.is_cmd_mode = 0; /* dp always video mode */
encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_TMDS, &info);
if (IS_ERR(encoder)) {
@@ -877,9 +878,11 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
}
}
- /* All CRTCs are compatible with all encoders */
- drm_for_each_encoder(encoder, dev)
- encoder->possible_crtcs = (1 << dev->mode_config.num_crtc) - 1;
+ num_encoders = 0;
+ drm_for_each_encoder(encoder, dev) {
+ encoder->possible_crtcs = 1 << num_encoders;
+ num_encoders++;
+ }
return 0;
}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
index 7e77d88f8959..498d0336ce70 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
@@ -353,13 +353,15 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm,
static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
struct dpu_global_state *global_state,
uint32_t crtc_id,
- struct msm_display_topology *topology)
+ struct msm_display_topology *topology,
+ bool pp_even_odd[MAX_BLOCKS])
{
int lm_idx[MAX_BLOCKS];
int pp_idx[MAX_BLOCKS];
int dspp_idx[MAX_BLOCKS] = {0};
int i, lm_count = 0;
+ const bool dsc_merge = topology->num_dsc > topology->num_intf;
if (!topology->num_lm) {
DPU_ERROR("zero LMs in topology\n");
@@ -377,7 +379,6 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
* primary mixer if failed to find its peer.
*/
lm_count &= ~1;
- lm_idx[lm_count] = i;
if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state,
crtc_id, i, &pp_idx[lm_count],
@@ -385,6 +386,10 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
continue;
}
+ lm_idx[lm_count] = i;
+ pp_even_odd[lm_count] = pp_idx[lm_count] & 1;
+ if (dsc_merge && pp_even_odd[lm_count])
+ continue;
++lm_count;
/* Valid primary mixer found, find matching peers */
@@ -406,6 +411,7 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
}
lm_idx[lm_count] = j;
+ pp_even_odd[lm_count] = pp_idx[lm_count] & 1;
++lm_count;
}
}
@@ -519,15 +525,19 @@ static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx)
static int _dpu_rm_dsc_alloc(struct dpu_rm *rm,
struct dpu_global_state *global_state,
uint32_t crtc_id,
- const struct msm_display_topology *top)
+ const struct msm_display_topology *top,
+ bool pp_even_odd[MAX_BLOCKS])
{
int num_dsc = 0;
int pp_idx = 0;
- int dsc_idx;
+ int dsc_idx, dsc_idx_even = 0, dsc_idx_odd = 1, idx_even_odd;
int ret;
- for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) &&
- num_dsc < top->num_dsc; dsc_idx++) {
+ for (;
+ (idx_even_odd = (pp_even_odd[num_dsc & ~1] ^ num_dsc) & 1),
+ (dsc_idx = idx_even_odd ? dsc_idx_odd : dsc_idx_even),
+ dsc_idx < ARRAY_SIZE(rm->dsc_blks) &&
+ num_dsc < top->num_dsc;) {
if (!rm->dsc_blks[dsc_idx])
continue;
@@ -545,6 +555,11 @@ static int _dpu_rm_dsc_alloc(struct dpu_rm *rm,
global_state->dsc_to_crtc_id[dsc_idx] = crtc_id;
num_dsc++;
pp_idx++;
+
+ if (idx_even_odd)
+ dsc_idx_odd = dsc_idx + 2;
+ else
+ dsc_idx_even = dsc_idx + 2;
}
if (num_dsc < top->num_dsc) {
@@ -615,7 +630,8 @@ static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm,
static int _dpu_rm_reserve_dsc(struct dpu_rm *rm,
struct dpu_global_state *global_state,
uint32_t crtc_id,
- const struct msm_display_topology *top)
+ const struct msm_display_topology *top,
+ bool pp_even_odd[MAX_BLOCKS])
{
if (!top->num_dsc || !top->num_intf)
return 0;
@@ -634,7 +650,7 @@ static int _dpu_rm_reserve_dsc(struct dpu_rm *rm,
if (top->num_dsc > top->num_intf) /* merge mode */
return _dpu_rm_dsc_alloc_pair(rm, global_state, crtc_id, top);
else
- return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top);
+ return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top, pp_even_odd);
return 0;
}
@@ -672,8 +688,9 @@ static int _dpu_rm_make_reservation(
struct msm_display_topology *topology)
{
int ret;
+ bool pp_even_odd[MAX_BLOCKS];
- ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology);
+ ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology, pp_even_odd);
if (ret) {
DPU_ERROR("unable to find appropriate mixers\n");
return ret;
@@ -693,9 +710,11 @@ static int _dpu_rm_make_reservation(
return ret;
}
- ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology);
- if (ret)
+ ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology, pp_even_odd);
+ if (ret) {
+ DPU_ERROR("unable to find appropriate DSC\n");
return ret;
+ }
if (topology->num_cdm > 0) {
ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id, topology->num_cdm);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index ef298c7d3e5e..cbb6d653fbec 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -18,12 +18,13 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_device.h>
-#include <drm/drm_fixed.h>
#include <drm/drm_print.h>
#include "dp_reg.h"
#include "dp_ctrl.h"
+#include "dp_panel.h"
#include "dp_link.h"
+#include "dp_tu_calc.h"
#define POLLING_SLEEP_US 1000
#define POLLING_TIMEOUT_US 10000
@@ -80,33 +81,9 @@ enum {
DP_TRAINING_2,
};
-struct msm_dp_tu_calc_input {
- u64 lclk; /* 162, 270, 540 and 810 */
- u64 pclk_khz; /* in KHz */
- u64 hactive; /* active h-width */
- u64 hporch; /* bp + fp + pulse */
- int nlanes; /* no.of.lanes */
- int bpp; /* bits */
- int pixel_enc; /* 444, 420, 422 */
- int dsc_en; /* dsc on/off */
- int async_en; /* async mode */
- int fec_en; /* fec */
- int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
- int num_of_dsc_slices; /* number of slices per line */
-};
-
-struct msm_dp_vc_tu_mapping_table {
- u32 vic;
- u8 lanes;
- u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
- u8 bpp;
- u8 valid_boundary_link;
- u16 delay_start_link;
- bool boundary_moderation_en;
- u8 valid_lower_boundary_link;
- u8 upper_boundary_count;
- u8 lower_boundary_count;
- u8 tu_size_minus1;
+enum msm_dp_flush_bit {
+ DP_PPS_FLUSH,
+ DP_DHDR_FLUSH,
};
struct msm_dp_ctrl_private {
@@ -140,6 +117,7 @@ struct msm_dp_ctrl_private {
bool core_clks_on;
bool link_clks_on;
bool stream_clks_on;
+ struct msm_dp_dsc_cfg_data dsc_data;
};
static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
@@ -307,6 +285,37 @@ static void msm_dp_ctrl_psr_mainlink_disable(struct msm_dp_ctrl_private *ctrl)
msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, val);
}
+static void msm_dp_ctrl_mainlink_levels(struct msm_dp_ctrl_private *ctrl)
+{
+ u32 mainlink_levels, safe_to_exit_level = 14;
+ u8 lane_cnt = ctrl->link->link_params.num_lanes;
+
+ switch (lane_cnt) {
+ case 1:
+ safe_to_exit_level = 14;
+ break;
+ case 2:
+ safe_to_exit_level = 8;
+ break;
+ case 4:
+ safe_to_exit_level = 5;
+ break;
+ default:
+ drm_dbg_dp(ctrl->drm_dev, "setting the default safe_to_exit_level=%u\n",
+ safe_to_exit_level);
+ break;
+ }
+
+ mainlink_levels = msm_dp_read_link(ctrl, REG_DP_MAINLINK_LEVELS);
+ mainlink_levels &= 0xFE0;
+ mainlink_levels |= safe_to_exit_level;
+
+ drm_dbg_dp(ctrl->drm_dev, "mainlink_level=0x%x, safe_to_exit_level=0x%x\n",
+ mainlink_levels, safe_to_exit_level);
+
+ msm_dp_write_link(ctrl, REG_DP_MAINLINK_LEVELS, mainlink_levels);
+}
+
static void msm_dp_ctrl_mainlink_enable(struct msm_dp_ctrl_private *ctrl)
{
u32 mainlink_ctrl;
@@ -403,8 +412,15 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
if (drm_dp_alternate_scrambler_reset_cap(dpcd))
config |= DP_CONFIGURATION_CTRL_ASSR;
- tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
- ctrl->panel->msm_dp_mode.bpp);
+ /*
+ * since dsc encoder output byte stream to dp controller,
+ * 8 bits bpc should be used as long as dsc enabled
+ */
+ if (DP_DSC_PANEL_EN(ctrl->panel))
+ tbd = DP_TEST_BIT_DEPTH_8 >> DP_TEST_BIT_DEPTH_SHIFT;
+ else
+ tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
+ ctrl->panel->msm_dp_mode.bpp);
config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
@@ -429,6 +445,48 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
}
+static void msm_dp_ctrl_fec_config(struct msm_dp_ctrl_private *ctrl, bool enable)
+{
+ u32 reg;
+
+ reg = msm_dp_read_link(ctrl, 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);
+
+ msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, reg);
+ /* make sure mainlink configuration is updated with fec sequence */
+ wmb();
+}
+
+static void msm_dp_ctrl_config_dsc_dto(struct msm_dp_ctrl_private *ctrl)
+{
+ struct msm_dp_dsc_cfg_data *dsc_data = &ctrl->dsc_data;
+ u32 reg;
+
+ msm_dp_panel_config_dsc_dto(ctrl->panel, dsc_data);
+
+ 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);
+ }
+ msm_dp_write_link(ctrl, DP_COMPRESSION_MODE_CTRL, reg);
+
+ drm_dbg_dp(ctrl->drm_dev, "compression:0x%x\n", reg);
+}
+
static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl)
{
u32 *lane_map = ctrl->link->lane_map;
@@ -443,17 +501,24 @@ static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl)
ln_mapping);
}
+static void msm_dp_ctrl_sdp_update(struct msm_dp_ctrl_private *ctrl)
+{
+ msm_dp_write_link(ctrl, MMSS_DP_SDP_CFG3, 0x01);
+ msm_dp_write_link(ctrl, MMSS_DP_SDP_CFG3, 0x00);
+}
+
static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl)
{
u32 colorimetry_cfg, test_bits_depth, misc_val;
msm_dp_ctrl_lane_mapping(ctrl);
+ msm_dp_ctrl_mainlink_levels(ctrl);
msm_dp_setup_peripheral_flush(ctrl);
msm_dp_ctrl_config_ctrl(ctrl);
test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->msm_dp_mode.bpp);
- colorimetry_cfg = msm_dp_link_get_colorimetry_config(ctrl->link);
+ colorimetry_cfg = msm_dp_panel_get_colorimetry_config(ctrl->panel);
misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0);
@@ -470,778 +535,16 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en);
}
-/*
- * The structure and few functions present below are IP/Hardware
- * specific implementation. Most of the implementation will not
- * have coding comments
- */
-struct tu_algo_data {
- s64 lclk_fp;
- s64 pclk_fp;
- s64 lwidth;
- s64 lwidth_fp;
- s64 hbp_relative_to_pclk;
- s64 hbp_relative_to_pclk_fp;
- int nlanes;
- int bpp;
- int pixelEnc;
- int dsc_en;
- int async_en;
- int bpc;
-
- uint delay_start_link_extra_pixclk;
- int extra_buffer_margin;
- s64 ratio_fp;
- s64 original_ratio_fp;
-
- s64 err_fp;
- s64 n_err_fp;
- s64 n_n_err_fp;
- int tu_size;
- int tu_size_desired;
- int tu_size_minus1;
-
- int valid_boundary_link;
- s64 resulting_valid_fp;
- s64 total_valid_fp;
- s64 effective_valid_fp;
- s64 effective_valid_recorded_fp;
- int n_tus;
- int n_tus_per_lane;
- int paired_tus;
- int remainder_tus;
- int remainder_tus_upper;
- int remainder_tus_lower;
- int extra_bytes;
- int filler_size;
- int delay_start_link;
-
- int extra_pclk_cycles;
- int extra_pclk_cycles_in_link_clk;
- s64 ratio_by_tu_fp;
- s64 average_valid2_fp;
- int new_valid_boundary_link;
- int remainder_symbols_exist;
- int n_symbols;
- s64 n_remainder_symbols_per_lane_fp;
- s64 last_partial_tu_fp;
- s64 TU_ratio_err_fp;
-
- int n_tus_incl_last_incomplete_tu;
- int extra_pclk_cycles_tmp;
- int extra_pclk_cycles_in_link_clk_tmp;
- int extra_required_bytes_new_tmp;
- int filler_size_tmp;
- int lower_filler_size_tmp;
- int delay_start_link_tmp;
-
- bool boundary_moderation_en;
- int boundary_mod_lower_err;
- int upper_boundary_count;
- int lower_boundary_count;
- int i_upper_boundary_count;
- int i_lower_boundary_count;
- int valid_lower_boundary_link;
- int even_distribution_BF;
- int even_distribution_legacy;
- int even_distribution;
- int min_hblank_violated;
- s64 delay_start_time_fp;
- s64 hbp_time_fp;
- s64 hactive_time_fp;
- s64 diff_abs_fp;
-
- s64 ratio;
-};
-
-static int _tu_param_compare(s64 a, s64 b)
-{
- u32 a_sign;
- u32 b_sign;
- s64 a_temp, b_temp, minus_1;
-
- if (a == b)
- return 0;
-
- minus_1 = drm_fixp_from_fraction(-1, 1);
-
- a_sign = (a >> 32) & 0x80000000 ? 1 : 0;
-
- b_sign = (b >> 32) & 0x80000000 ? 1 : 0;
-
- if (a_sign > b_sign)
- return 2;
- else if (b_sign > a_sign)
- return 1;
-
- if (!a_sign && !b_sign) { /* positive */
- if (a > b)
- return 1;
- else
- return 2;
- } else { /* negative */
- a_temp = drm_fixp_mul(a, minus_1);
- b_temp = drm_fixp_mul(b, minus_1);
-
- if (a_temp > b_temp)
- return 2;
- else
- return 1;
- }
-}
-
-static void msm_dp_panel_update_tu_timings(struct msm_dp_tu_calc_input *in,
- struct tu_algo_data *tu)
-{
- int nlanes = in->nlanes;
- int dsc_num_slices = in->num_of_dsc_slices;
- int dsc_num_bytes = 0;
- int numerator;
- s64 pclk_dsc_fp;
- s64 dwidth_dsc_fp;
- s64 hbp_dsc_fp;
-
- int tot_num_eoc_symbols = 0;
- int tot_num_hor_bytes = 0;
- int tot_num_dummy_bytes = 0;
- int dwidth_dsc_bytes = 0;
- int eoc_bytes = 0;
-
- s64 temp1_fp, temp2_fp, temp3_fp;
-
- tu->lclk_fp = drm_fixp_from_fraction(in->lclk, 1);
- tu->pclk_fp = drm_fixp_from_fraction(in->pclk_khz, 1000);
- tu->lwidth = in->hactive;
- tu->hbp_relative_to_pclk = in->hporch;
- tu->nlanes = in->nlanes;
- tu->bpp = in->bpp;
- tu->pixelEnc = in->pixel_enc;
- tu->dsc_en = in->dsc_en;
- tu->async_en = in->async_en;
- tu->lwidth_fp = drm_fixp_from_fraction(in->hactive, 1);
- tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1);
-
- if (tu->pixelEnc == 420) {
- temp1_fp = drm_fixp_from_fraction(2, 1);
- tu->pclk_fp = drm_fixp_div(tu->pclk_fp, temp1_fp);
- tu->lwidth_fp = drm_fixp_div(tu->lwidth_fp, temp1_fp);
- tu->hbp_relative_to_pclk_fp =
- drm_fixp_div(tu->hbp_relative_to_pclk_fp, 2);
- }
-
- if (tu->pixelEnc == 422) {
- switch (tu->bpp) {
- case 24:
- tu->bpp = 16;
- tu->bpc = 8;
- break;
- case 30:
- tu->bpp = 20;
- tu->bpc = 10;
- break;
- default:
- tu->bpp = 16;
- tu->bpc = 8;
- break;
- }
- } else {
- tu->bpc = tu->bpp/3;
- }
-
- if (!in->dsc_en)
- goto fec_check;
-
- 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);
- temp2_fp = drm_fixp_mul(tu->lwidth_fp, temp3_fp);
-
- temp1_fp = drm_fixp_from_fraction(8, 1);
- temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
-
- numerator = drm_fixp2int(temp3_fp);
-
- dsc_num_bytes = numerator / dsc_num_slices;
- eoc_bytes = dsc_num_bytes % nlanes;
- tot_num_eoc_symbols = nlanes * dsc_num_slices;
- tot_num_hor_bytes = dsc_num_bytes * dsc_num_slices;
- tot_num_dummy_bytes = (nlanes - eoc_bytes) * dsc_num_slices;
-
- if (dsc_num_bytes == 0)
- pr_info("incorrect no of bytes per slice=%d\n", dsc_num_bytes);
-
- dwidth_dsc_bytes = (tot_num_hor_bytes +
- tot_num_eoc_symbols +
- (eoc_bytes == 0 ? 0 : tot_num_dummy_bytes));
-
- dwidth_dsc_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, 3);
-
- temp2_fp = drm_fixp_mul(tu->pclk_fp, dwidth_dsc_fp);
- temp1_fp = drm_fixp_div(temp2_fp, tu->lwidth_fp);
- pclk_dsc_fp = temp1_fp;
-
- temp1_fp = drm_fixp_div(pclk_dsc_fp, tu->pclk_fp);
- temp2_fp = drm_fixp_mul(tu->hbp_relative_to_pclk_fp, temp1_fp);
- hbp_dsc_fp = temp2_fp;
-
- /* output */
- tu->pclk_fp = pclk_dsc_fp;
- tu->lwidth_fp = dwidth_dsc_fp;
- tu->hbp_relative_to_pclk_fp = hbp_dsc_fp;
-
-fec_check:
- if (in->fec_en) {
- temp1_fp = drm_fixp_from_fraction(976, 1000); /* 0.976 */
- tu->lclk_fp = drm_fixp_mul(tu->lclk_fp, temp1_fp);
- }
-}
-
-static void _tu_valid_boundary_calc(struct tu_algo_data *tu)
-{
- s64 temp1_fp, temp2_fp, temp, temp1, temp2;
- int compare_result_1, compare_result_2, compare_result_3;
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
-
- tu->new_valid_boundary_link = drm_fixp2int_ceil(temp2_fp);
-
- temp = (tu->i_upper_boundary_count *
- tu->new_valid_boundary_link +
- tu->i_lower_boundary_count *
- (tu->new_valid_boundary_link-1));
- tu->average_valid2_fp = drm_fixp_from_fraction(temp,
- (tu->i_upper_boundary_count +
- tu->i_lower_boundary_count));
-
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp2_fp = tu->lwidth_fp;
- temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
- temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
- tu->n_tus = drm_fixp2int(temp2_fp);
- if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
- tu->n_tus += 1;
-
- temp1_fp = drm_fixp_from_fraction(tu->n_tus, 1);
- temp2_fp = drm_fixp_mul(temp1_fp, tu->average_valid2_fp);
- temp1_fp = drm_fixp_from_fraction(tu->n_symbols, 1);
- temp2_fp = temp1_fp - temp2_fp;
- temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
- temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
- tu->n_remainder_symbols_per_lane_fp = temp2_fp;
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- tu->last_partial_tu_fp =
- drm_fixp_div(tu->n_remainder_symbols_per_lane_fp,
- temp1_fp);
-
- if (tu->n_remainder_symbols_per_lane_fp != 0)
- tu->remainder_symbols_exist = 1;
- else
- tu->remainder_symbols_exist = 0;
-
- temp1_fp = drm_fixp_from_fraction(tu->n_tus, tu->nlanes);
- tu->n_tus_per_lane = drm_fixp2int(temp1_fp);
-
- tu->paired_tus = (int)((tu->n_tus_per_lane) /
- (tu->i_upper_boundary_count +
- tu->i_lower_boundary_count));
-
- tu->remainder_tus = tu->n_tus_per_lane - tu->paired_tus *
- (tu->i_upper_boundary_count +
- tu->i_lower_boundary_count);
-
- if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) {
- tu->remainder_tus_upper = tu->i_upper_boundary_count;
- tu->remainder_tus_lower = tu->remainder_tus -
- tu->i_upper_boundary_count;
- } else {
- tu->remainder_tus_upper = tu->remainder_tus;
- tu->remainder_tus_lower = 0;
- }
-
- temp = tu->paired_tus * (tu->i_upper_boundary_count *
- tu->new_valid_boundary_link +
- tu->i_lower_boundary_count *
- (tu->new_valid_boundary_link - 1)) +
- (tu->remainder_tus_upper *
- tu->new_valid_boundary_link) +
- (tu->remainder_tus_lower *
- (tu->new_valid_boundary_link - 1));
- tu->total_valid_fp = drm_fixp_from_fraction(temp, 1);
-
- if (tu->remainder_symbols_exist) {
- temp1_fp = tu->total_valid_fp +
- tu->n_remainder_symbols_per_lane_fp;
- temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
- temp2_fp = temp2_fp + tu->last_partial_tu_fp;
- temp1_fp = drm_fixp_div(temp1_fp, temp2_fp);
- } else {
- temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
- temp1_fp = drm_fixp_div(tu->total_valid_fp, temp2_fp);
- }
- tu->effective_valid_fp = temp1_fp;
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
- tu->n_n_err_fp = tu->effective_valid_fp - temp2_fp;
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
- tu->n_err_fp = tu->average_valid2_fp - temp2_fp;
-
- tu->even_distribution = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
-
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp2_fp = tu->lwidth_fp;
- temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
- temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
-
- if (temp2_fp)
- tu->n_tus_incl_last_incomplete_tu = drm_fixp2int_ceil(temp2_fp);
- else
- tu->n_tus_incl_last_incomplete_tu = 0;
-
- temp1 = 0;
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
- temp1_fp = tu->average_valid2_fp - temp2_fp;
- temp2_fp = drm_fixp_from_fraction(tu->n_tus_incl_last_incomplete_tu, 1);
- temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
-
- if (temp1_fp)
- temp1 = drm_fixp2int_ceil(temp1_fp);
-
- temp = tu->i_upper_boundary_count * tu->nlanes;
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
- temp1_fp = drm_fixp_from_fraction(tu->new_valid_boundary_link, 1);
- temp2_fp = temp1_fp - temp2_fp;
- temp1_fp = drm_fixp_from_fraction(temp, 1);
- temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
-
- if (temp2_fp)
- temp2 = drm_fixp2int_ceil(temp2_fp);
- else
- temp2 = 0;
- tu->extra_required_bytes_new_tmp = (int)(temp1 + temp2);
-
- temp1_fp = drm_fixp_from_fraction(8, tu->bpp);
- temp2_fp = drm_fixp_from_fraction(
- tu->extra_required_bytes_new_tmp, 1);
- temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
-
- if (temp1_fp)
- tu->extra_pclk_cycles_tmp = drm_fixp2int_ceil(temp1_fp);
- else
- tu->extra_pclk_cycles_tmp = 0;
-
- temp1_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles_tmp, 1);
- temp2_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
- temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
-
- if (temp1_fp)
- tu->extra_pclk_cycles_in_link_clk_tmp =
- drm_fixp2int_ceil(temp1_fp);
- else
- tu->extra_pclk_cycles_in_link_clk_tmp = 0;
-
- tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link;
-
- tu->lower_filler_size_tmp = tu->filler_size_tmp + 1;
-
- tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp +
- tu->lower_filler_size_tmp +
- tu->extra_buffer_margin;
-
- temp1_fp = drm_fixp_from_fraction(tu->delay_start_link_tmp, 1);
- tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
-
- compare_result_1 = _tu_param_compare(tu->n_n_err_fp, tu->diff_abs_fp);
- if (compare_result_1 == 2)
- compare_result_1 = 1;
- else
- compare_result_1 = 0;
-
- compare_result_2 = _tu_param_compare(tu->n_n_err_fp, tu->err_fp);
- if (compare_result_2 == 2)
- compare_result_2 = 1;
- else
- compare_result_2 = 0;
-
- compare_result_3 = _tu_param_compare(tu->hbp_time_fp,
- tu->delay_start_time_fp);
- if (compare_result_3 == 2)
- compare_result_3 = 0;
- else
- compare_result_3 = 1;
-
- if (((tu->even_distribution == 1) ||
- ((tu->even_distribution_BF == 0) &&
- (tu->even_distribution_legacy == 0))) &&
- tu->n_err_fp >= 0 && tu->n_n_err_fp >= 0 &&
- compare_result_2 &&
- (compare_result_1 || (tu->min_hblank_violated == 1)) &&
- (tu->new_valid_boundary_link - 1) > 0 &&
- compare_result_3 &&
- (tu->delay_start_link_tmp <= 1023)) {
- tu->upper_boundary_count = tu->i_upper_boundary_count;
- tu->lower_boundary_count = tu->i_lower_boundary_count;
- tu->err_fp = tu->n_n_err_fp;
- tu->boundary_moderation_en = true;
- tu->tu_size_desired = tu->tu_size;
- tu->valid_boundary_link = tu->new_valid_boundary_link;
- tu->effective_valid_recorded_fp = tu->effective_valid_fp;
- tu->even_distribution_BF = 1;
- tu->delay_start_link = tu->delay_start_link_tmp;
- } else if (tu->boundary_mod_lower_err == 0) {
- compare_result_1 = _tu_param_compare(tu->n_n_err_fp,
- tu->diff_abs_fp);
- if (compare_result_1 == 2)
- tu->boundary_mod_lower_err = 1;
- }
-}
-
-static void _dp_ctrl_calc_tu(struct msm_dp_ctrl_private *ctrl,
- struct msm_dp_tu_calc_input *in,
- struct msm_dp_vc_tu_mapping_table *tu_table)
-{
- struct tu_algo_data *tu;
- int compare_result_1, compare_result_2;
- u64 temp = 0;
- s64 temp_fp = 0, temp1_fp = 0, temp2_fp = 0;
-
- s64 LCLK_FAST_SKEW_fp = drm_fixp_from_fraction(6, 10000); /* 0.0006 */
- s64 const_p49_fp = drm_fixp_from_fraction(49, 100); /* 0.49 */
- s64 const_p56_fp = drm_fixp_from_fraction(56, 100); /* 0.56 */
- s64 RATIO_SCALE_fp = drm_fixp_from_fraction(1001, 1000);
-
- u8 DP_BRUTE_FORCE = 1;
- s64 BRUTE_FORCE_THRESHOLD_fp = drm_fixp_from_fraction(1, 10); /* 0.1 */
- uint EXTRA_PIXCLK_CYCLE_DELAY = 4;
- uint HBLANK_MARGIN = 4;
-
- tu = kzalloc_obj(*tu);
- if (!tu)
- return;
-
- msm_dp_panel_update_tu_timings(in, tu);
-
- tu->err_fp = drm_fixp_from_fraction(1000, 1); /* 1000 */
-
- temp1_fp = drm_fixp_from_fraction(4, 1);
- temp2_fp = drm_fixp_mul(temp1_fp, tu->lclk_fp);
- temp_fp = drm_fixp_div(temp2_fp, tu->pclk_fp);
- tu->extra_buffer_margin = drm_fixp2int_ceil(temp_fp);
-
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp2_fp = drm_fixp_mul(tu->pclk_fp, temp1_fp);
- temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
- temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
- tu->ratio_fp = drm_fixp_div(temp2_fp, tu->lclk_fp);
-
- tu->original_ratio_fp = tu->ratio_fp;
- tu->boundary_moderation_en = false;
- tu->upper_boundary_count = 0;
- tu->lower_boundary_count = 0;
- tu->i_upper_boundary_count = 0;
- tu->i_lower_boundary_count = 0;
- tu->valid_lower_boundary_link = 0;
- tu->even_distribution_BF = 0;
- tu->even_distribution_legacy = 0;
- tu->even_distribution = 0;
- tu->delay_start_time_fp = 0;
-
- tu->err_fp = drm_fixp_from_fraction(1000, 1);
- tu->n_err_fp = 0;
- tu->n_n_err_fp = 0;
-
- tu->ratio = drm_fixp2int(tu->ratio_fp);
- temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
- div64_u64_rem(tu->lwidth_fp, temp1_fp, &temp2_fp);
- if (temp2_fp != 0 &&
- !tu->ratio && tu->dsc_en == 0) {
- tu->ratio_fp = drm_fixp_mul(tu->ratio_fp, RATIO_SCALE_fp);
- tu->ratio = drm_fixp2int(tu->ratio_fp);
- if (tu->ratio)
- tu->ratio_fp = drm_fixp_from_fraction(1, 1);
- }
-
- if (tu->ratio > 1)
- tu->ratio = 1;
-
- if (tu->ratio == 1)
- goto tu_size_calc;
-
- compare_result_1 = _tu_param_compare(tu->ratio_fp, const_p49_fp);
- if (!compare_result_1 || compare_result_1 == 1)
- compare_result_1 = 1;
- else
- compare_result_1 = 0;
-
- compare_result_2 = _tu_param_compare(tu->ratio_fp, const_p56_fp);
- if (!compare_result_2 || compare_result_2 == 2)
- compare_result_2 = 1;
- else
- compare_result_2 = 0;
-
- if (tu->dsc_en && compare_result_1 && compare_result_2) {
- HBLANK_MARGIN += 4;
- drm_dbg_dp(ctrl->drm_dev,
- "increase HBLANK_MARGIN to %d\n", HBLANK_MARGIN);
- }
-
-tu_size_calc:
- for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) {
- temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
- temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
- temp = drm_fixp2int_ceil(temp2_fp);
- temp1_fp = drm_fixp_from_fraction(temp, 1);
- tu->n_err_fp = temp1_fp - temp2_fp;
-
- if (tu->n_err_fp < tu->err_fp) {
- tu->err_fp = tu->n_err_fp;
- tu->tu_size_desired = tu->tu_size;
- }
- }
-
- tu->tu_size_minus1 = tu->tu_size_desired - 1;
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
- temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
- tu->valid_boundary_link = drm_fixp2int_ceil(temp2_fp);
-
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp2_fp = tu->lwidth_fp;
- temp2_fp = drm_fixp_mul(temp2_fp, temp1_fp);
-
- temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1);
- temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
- tu->n_tus = drm_fixp2int(temp2_fp);
- if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
- tu->n_tus += 1;
-
- tu->even_distribution_legacy = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
-
- drm_dbg_dp(ctrl->drm_dev,
- "n_sym = %d, num_of_tus = %d\n",
- tu->valid_boundary_link, tu->n_tus);
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
- temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
- temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1);
- temp2_fp = temp1_fp - temp2_fp;
- temp1_fp = drm_fixp_from_fraction(tu->n_tus + 1, 1);
- temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
-
- temp = drm_fixp2int(temp2_fp);
- if (temp && temp2_fp)
- tu->extra_bytes = drm_fixp2int_ceil(temp2_fp);
- else
- tu->extra_bytes = 0;
-
- temp1_fp = drm_fixp_from_fraction(tu->extra_bytes, 1);
- temp2_fp = drm_fixp_from_fraction(8, tu->bpp);
- temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
-
- if (temp && temp1_fp)
- tu->extra_pclk_cycles = drm_fixp2int_ceil(temp1_fp);
- else
- tu->extra_pclk_cycles = drm_fixp2int(temp1_fp);
-
- temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
- temp2_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles, 1);
- temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
-
- if (temp1_fp)
- tu->extra_pclk_cycles_in_link_clk = drm_fixp2int_ceil(temp1_fp);
- else
- tu->extra_pclk_cycles_in_link_clk = drm_fixp2int(temp1_fp);
-
- tu->filler_size = tu->tu_size_desired - tu->valid_boundary_link;
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
- tu->ratio_by_tu_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
-
- tu->delay_start_link = tu->extra_pclk_cycles_in_link_clk +
- tu->filler_size + tu->extra_buffer_margin;
-
- tu->resulting_valid_fp =
- drm_fixp_from_fraction(tu->valid_boundary_link, 1);
-
- temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
- temp2_fp = drm_fixp_div(tu->resulting_valid_fp, temp1_fp);
- tu->TU_ratio_err_fp = temp2_fp - tu->original_ratio_fp;
-
- temp1_fp = drm_fixp_from_fraction(HBLANK_MARGIN, 1);
- temp1_fp = tu->hbp_relative_to_pclk_fp - temp1_fp;
- tu->hbp_time_fp = drm_fixp_div(temp1_fp, tu->pclk_fp);
-
- temp1_fp = drm_fixp_from_fraction(tu->delay_start_link, 1);
- tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
-
- compare_result_1 = _tu_param_compare(tu->hbp_time_fp,
- tu->delay_start_time_fp);
- if (compare_result_1 == 2) /* if (hbp_time_fp < delay_start_time_fp) */
- tu->min_hblank_violated = 1;
-
- tu->hactive_time_fp = drm_fixp_div(tu->lwidth_fp, tu->pclk_fp);
-
- compare_result_2 = _tu_param_compare(tu->hactive_time_fp,
- tu->delay_start_time_fp);
- if (compare_result_2 == 2)
- tu->min_hblank_violated = 1;
-
- tu->delay_start_time_fp = 0;
-
- /* brute force */
-
- tu->delay_start_link_extra_pixclk = EXTRA_PIXCLK_CYCLE_DELAY;
- tu->diff_abs_fp = tu->resulting_valid_fp - tu->ratio_by_tu_fp;
-
- temp = drm_fixp2int(tu->diff_abs_fp);
- if (!temp && tu->diff_abs_fp <= 0xffff)
- tu->diff_abs_fp = 0;
-
- /* if(diff_abs < 0) diff_abs *= -1 */
- if (tu->diff_abs_fp < 0)
- tu->diff_abs_fp = drm_fixp_mul(tu->diff_abs_fp, -1);
-
- tu->boundary_mod_lower_err = 0;
- if ((tu->diff_abs_fp != 0 &&
- ((tu->diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) ||
- (tu->even_distribution_legacy == 0) ||
- (DP_BRUTE_FORCE == 1))) ||
- (tu->min_hblank_violated == 1)) {
- do {
- tu->err_fp = drm_fixp_from_fraction(1000, 1);
-
- temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
- temp2_fp = drm_fixp_from_fraction(
- tu->delay_start_link_extra_pixclk, 1);
- temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
-
- if (temp1_fp)
- tu->extra_buffer_margin =
- drm_fixp2int_ceil(temp1_fp);
- else
- tu->extra_buffer_margin = 0;
-
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp);
-
- if (temp1_fp)
- tu->n_symbols = drm_fixp2int_ceil(temp1_fp);
- else
- tu->n_symbols = 0;
-
- for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) {
- for (tu->i_upper_boundary_count = 1;
- tu->i_upper_boundary_count <= 15;
- tu->i_upper_boundary_count++) {
- for (tu->i_lower_boundary_count = 1;
- tu->i_lower_boundary_count <= 15;
- tu->i_lower_boundary_count++) {
- _tu_valid_boundary_calc(tu);
- }
- }
- }
- tu->delay_start_link_extra_pixclk--;
- } while (tu->boundary_moderation_en != true &&
- tu->boundary_mod_lower_err == 1 &&
- tu->delay_start_link_extra_pixclk != 0);
-
- if (tu->boundary_moderation_en == true) {
- temp1_fp = drm_fixp_from_fraction(
- (tu->upper_boundary_count *
- tu->valid_boundary_link +
- tu->lower_boundary_count *
- (tu->valid_boundary_link - 1)), 1);
- temp2_fp = drm_fixp_from_fraction(
- (tu->upper_boundary_count +
- tu->lower_boundary_count), 1);
- tu->resulting_valid_fp =
- drm_fixp_div(temp1_fp, temp2_fp);
-
- temp1_fp = drm_fixp_from_fraction(
- tu->tu_size_desired, 1);
- tu->ratio_by_tu_fp =
- drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
-
- tu->valid_lower_boundary_link =
- tu->valid_boundary_link - 1;
-
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp);
- temp2_fp = drm_fixp_div(temp1_fp,
- tu->resulting_valid_fp);
- tu->n_tus = drm_fixp2int(temp2_fp);
-
- tu->tu_size_minus1 = tu->tu_size_desired - 1;
- tu->even_distribution_BF = 1;
-
- temp1_fp =
- drm_fixp_from_fraction(tu->tu_size_desired, 1);
- temp2_fp =
- drm_fixp_div(tu->resulting_valid_fp, temp1_fp);
- tu->TU_ratio_err_fp = temp2_fp - tu->original_ratio_fp;
- }
- }
-
- temp2_fp = drm_fixp_mul(LCLK_FAST_SKEW_fp, tu->lwidth_fp);
-
- if (temp2_fp)
- temp = drm_fixp2int_ceil(temp2_fp);
- else
- temp = 0;
-
- temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
- temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
- temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
- temp2_fp = drm_fixp_div(temp1_fp, temp2_fp);
- temp1_fp = drm_fixp_from_fraction(temp, 1);
- temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
- temp = drm_fixp2int(temp2_fp);
-
- if (tu->async_en)
- tu->delay_start_link += (int)temp;
-
- temp1_fp = drm_fixp_from_fraction(tu->delay_start_link, 1);
- tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
-
- /* OUTPUTS */
- tu_table->valid_boundary_link = tu->valid_boundary_link;
- tu_table->delay_start_link = tu->delay_start_link;
- tu_table->boundary_moderation_en = tu->boundary_moderation_en;
- tu_table->valid_lower_boundary_link = tu->valid_lower_boundary_link;
- tu_table->upper_boundary_count = tu->upper_boundary_count;
- tu_table->lower_boundary_count = tu->lower_boundary_count;
- tu_table->tu_size_minus1 = tu->tu_size_minus1;
-
- drm_dbg_dp(ctrl->drm_dev, "TU: valid_boundary_link: %d\n",
- tu_table->valid_boundary_link);
- drm_dbg_dp(ctrl->drm_dev, "TU: delay_start_link: %d\n",
- tu_table->delay_start_link);
- drm_dbg_dp(ctrl->drm_dev, "TU: boundary_moderation_en: %d\n",
- tu_table->boundary_moderation_en);
- drm_dbg_dp(ctrl->drm_dev, "TU: valid_lower_boundary_link: %d\n",
- tu_table->valid_lower_boundary_link);
- drm_dbg_dp(ctrl->drm_dev, "TU: upper_boundary_count: %d\n",
- tu_table->upper_boundary_count);
- drm_dbg_dp(ctrl->drm_dev, "TU: lower_boundary_count: %d\n",
- tu_table->lower_boundary_count);
- drm_dbg_dp(ctrl->drm_dev, "TU: tu_size_minus1: %d\n",
- tu_table->tu_size_minus1);
-
- kfree(tu);
-}
-
static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl,
struct msm_dp_vc_tu_mapping_table *tu_table)
{
struct msm_dp_tu_calc_input in;
struct drm_display_mode *drm_mode;
+ struct msm_dp_panel_info *timing;
+ struct msm_compression_info *comp_info;
+ timing = &ctrl->panel->msm_dp_mode.timing;
+ comp_info = &timing->comp_info;
drm_mode = &ctrl->panel->msm_dp_mode.drm_mode;
in.lclk = ctrl->link->link_params.rate / 1000;
@@ -1251,13 +554,22 @@ static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl,
in.nlanes = ctrl->link->link_params.num_lanes;
in.bpp = ctrl->panel->msm_dp_mode.bpp;
in.pixel_enc = ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420 ? 420 : 444;
- in.dsc_en = 0;
+ in.dsc_en = DP_DSC_PANEL_EN(ctrl->panel);
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;
- _dp_ctrl_calc_tu(ctrl, &in, tu_table);
+ /*
+ * TODO: only one dsc slice supported for now
+ */
+ if (in.dsc_en) {
+ in.num_of_dsc_slices = 1;
+ in.compress_ratio = mult_frac(100, comp_info->src_bpp,
+ comp_info->tgt_bpp);
+ }
+
+ msm_dp_ctrl_calc_tu(ctrl->drm_dev, &in, tu_table);
}
static void msm_dp_ctrl_setup_tr_unit(struct msm_dp_ctrl_private *ctrl)
@@ -1675,12 +987,73 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
return ret;
}
+static void msm_dp_ctrl_sink_fec_enable(struct msm_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 msm_dp_ctrl_host_fec_start(struct msm_dp_ctrl_private *ctrl)
+{
+ u8 fec_sts = 0;
+ int i, max_retries = 3;
+ bool fec_en = ctrl->panel->fec_en;
+ bool fec_en_detected;
+
+ /* Need to try to enable multiple times due to BS symbols collisions */
+ for (i = 0; i < max_retries; i++) {
+ msm_dp_ctrl_fec_config(ctrl, fec_en);
+
+ /* 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);
+ fec_en_detected = !!(fec_sts & DP_FEC_DECODE_EN_DETECTED);
+
+ if (fec_en_detected == fec_en)
+ break;
+ }
+
+ drm_dbg_dp(ctrl->drm_dev, "retries %d, fec_en_detected %d\n",
+ i, fec_en_detected);
+
+ if (fec_en_detected != fec_en)
+ DRM_ERROR("failed to set sink fec\n");
+}
+
+static void msm_dp_ctrl_host_fec_stop(struct msm_dp_ctrl_private *ctrl)
+{
+ msm_dp_ctrl_fec_config(ctrl, false);
+}
+
+static void msm_dp_ctrl_sink_dsc_enable(struct msm_dp_ctrl_private *ctrl)
+{
+ int rlen;
+ u32 dsc_enable;
+ u8 xx = 0;
+
+ dsc_enable = DP_DSC_PANEL_EN(ctrl->panel) ? 1 : 0;
+ rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_DSC_ENABLE, dsc_enable);
+ if (rlen < 1)
+ DRM_ERROR("failed to set sink dsc\n");
+
+
+ dsc_enable = 0;
+ drm_dp_dpcd_readb(ctrl->aux, DP_DSC_ENABLE, &xx);
+
+}
+
static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl,
int *training_step)
{
int ret = 0;
msm_dp_ctrl_mainlink_enable(ctrl);
+ msm_dp_ctrl_mainlink_levels(ctrl);
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
return ret;
@@ -1691,6 +1064,9 @@ static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl,
* a link training pattern, we have to first do soft reset.
*/
+ if (ctrl->panel->fec_en)
+ msm_dp_ctrl_sink_fec_enable(ctrl);
+
ret = msm_dp_ctrl_link_train(ctrl, training_step);
return ret;
@@ -2472,6 +1848,51 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid);
}
+static void msm_dp_ctrl_dsc_commit_pps(struct msm_dp_ctrl_private *ctrl)
+{
+ struct msm_dp_dsc_cfg_data *dsc_data = &ctrl->dsc_data;
+ int i;
+
+ msm_dp_write_link(ctrl, DP_PPS_HB_0_3, 0x7F1000);
+ msm_dp_write_link(ctrl, DP_PPS_PB_0_3, 0xA22300);
+
+ for (i = 0; i < dsc_data->parity_word_len; i++)
+ msm_dp_write_link(ctrl, DP_PPS_PB_4_7 + (i << 2),
+ dsc_data->parity_word[i]);
+
+ for (i = 0; i < dsc_data->pps_word_len; i++)
+ msm_dp_write_link(ctrl, DP_PPS_PPS_0_3 + (i << 2),
+ dsc_data->pps_word[i]);
+}
+
+static void msm_dp_ctrl_dp_flush(struct msm_dp_ctrl_private *ctrl,
+ enum msm_dp_flush_bit flush_bit)
+{
+ u32 dp_flush;
+ struct msm_dp_dsc_cfg_data *dsc_data = &ctrl->dsc_data;
+
+ dp_flush = msm_dp_read_link(ctrl, 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);
+ msm_dp_write_link(ctrl, MMSS_DP_FLUSH, dp_flush);
+
+ /*
+ * TODO: no dp_config_sdp_update() required?
+ */
+ msm_dp_ctrl_sdp_update(ctrl);
+}
+
+static void msm_dp_ctrl_pps_flush(struct msm_dp_ctrl_private *ctrl)
+{
+ msm_dp_ctrl_dp_flush(ctrl, DP_PPS_FLUSH);
+ drm_dbg_dp(ctrl->drm_dev, "pps flush\n");
+}
+
int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
{
int ret = 0;
@@ -2535,6 +1956,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
*/
reinit_completion(&ctrl->video_comp);
+ msm_dp_panel_dsc_config(ctrl->panel, &ctrl->dsc_data, true);
+
msm_dp_ctrl_configure_source_params(ctrl);
msm_dp_ctrl_config_msa(ctrl,
@@ -2542,10 +1965,14 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
pixel_rate_orig,
ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420);
- msm_dp_panel_clear_dsc_dto(ctrl->panel);
+ msm_dp_ctrl_config_dsc_dto(ctrl);
+ msm_dp_ctrl_dsc_commit_pps(ctrl);
+ msm_dp_ctrl_pps_flush(ctrl);
msm_dp_ctrl_setup_tr_unit(ctrl);
+ msm_dp_panel_override_ack_dto(ctrl->panel, true);
+
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
ret = msm_dp_ctrl_wait4video_ready(ctrl);
@@ -2556,6 +1983,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
drm_dbg_dp(ctrl->drm_dev,
"mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
+ /* wait for link training completion before fec config as per spec */
+ msm_dp_ctrl_host_fec_start(ctrl);
+ msm_dp_ctrl_sink_dsc_enable(ctrl);
+
end:
return ret;
}
@@ -2569,6 +2000,7 @@ void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl)
phy = ctrl->phy;
msm_dp_panel_disable_vsc_sdp(ctrl->panel);
+ msm_dp_panel_dsc_config(ctrl->panel, &ctrl->dsc_data, false);
/* set dongle to D3 (power off) mode */
msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
@@ -2625,6 +2057,12 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_panel_disable_vsc_sdp(ctrl->panel);
+ msm_dp_ctrl_host_fec_stop(ctrl);
+ msm_dp_panel_dsc_config(ctrl->panel, &ctrl->dsc_data, false);
+ msm_dp_ctrl_config_dsc_dto(ctrl);
+
+ msm_dp_panel_override_ack_dto(ctrl->panel, false);
+
msm_dp_ctrl_mainlink_disable(ctrl);
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 476848bf8cd1..6a8a36259236 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -91,7 +91,7 @@ struct msm_dp_display_private {
struct msm_dp_panel *panel;
struct msm_dp_ctrl *ctrl;
- struct msm_dp_display_mode msm_dp_mode;
+ struct msm_dp_display_mode *msm_dp_mode;
struct msm_dp msm_dp_display;
/* wait for audio signaling */
@@ -799,6 +799,8 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
dp->panel = NULL;
goto error_link;
}
+ /* both msm_dp_display and msm_dp_panel shared same msm_dp_mode */
+ dp->msm_dp_mode = &dp->panel->msm_dp_mode;
dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
phy, dp->ahb_base, dp->link_base);
@@ -827,20 +829,6 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
return rc;
}
-static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
- struct msm_dp_display_mode *mode)
-{
- struct msm_dp_display_private *dp;
-
- dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
-
- drm_mode_copy(&dp->panel->msm_dp_mode.drm_mode, &mode->drm_mode);
- dp->panel->msm_dp_mode.bpp = mode->bpp;
- dp->panel->msm_dp_mode.out_fmt_is_yuv_420 = mode->out_fmt_is_yuv_420;
- msm_dp_panel_init_panel_info(dp->panel);
- return 0;
-}
-
static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_link_train)
{
int rc = 0;
@@ -921,27 +909,17 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
return 0;
}
-/**
- * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid
- * @bridge: Pointer to drm bridge structure
- * @info: display info
- * @mode: Pointer to drm mode structure
- * Returns: Validity status for specified mode
- */
-enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode)
+static enum drm_mode_status _dp_bridge_mode_valid(struct msm_dp *dp,
+ const struct drm_display_mode *mode,
+ enum msm_display_compression_type *comp_type)
{
const u32 num_components = 3, default_bpp = 24;
struct msm_dp_display_private *msm_dp_display;
struct msm_dp_link_info *link_info;
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
- struct msm_dp *dp;
int mode_pclk_khz = mode->clock;
- dp = to_dp_bridge(bridge)->msm_dp_display;
-
- if (!dp || !mode_pclk_khz || !dp->connector) {
+ if (!dp || !mode_pclk_khz || !dp->connector || !comp_type) {
DRM_ERROR("invalid params\n");
return -EINVAL;
}
@@ -950,8 +928,8 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
link_info = &msm_dp_display->panel->link_info;
if ((drm_mode_is_420_only(&dp->connector->display_info, mode) &&
- msm_dp_display->panel->vsc_sdp_supported) ||
- msm_dp_wide_bus_available(dp))
+ msm_dp_display->panel->vsc_sdp_supported) ||
+ msm_dp_wide_bus_available(dp))
mode_pclk_khz /= 2;
if (mode_pclk_khz > DP_MAX_PIXEL_CLK_KHZ)
@@ -962,9 +940,11 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
mode_bpp = default_bpp;
mode_bpp = msm_dp_panel_get_mode_bpp(msm_dp_display->panel,
- mode_bpp, mode_pclk_khz);
+ mode_bpp, mode->clock, comp_type);
- mode_rate_khz = mode_pclk_khz * mode_bpp;
+ if (*comp_type == MSM_DISPLAY_COMPRESSION_DSC)
+ mode_bpp /= DP_DSC_COMP_RATIO;
+ mode_rate_khz = mode->clock * mode_bpp;
supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
if (mode_rate_khz > supported_rate_khz)
@@ -973,6 +953,50 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
return MODE_OK;
}
+/**
+ * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid
+ * @bridge: Pointer to drm bridge structure
+ * @info: display info
+ * @mode: Pointer to drm mode structure
+ * Returns: Validity status for specified mode
+ */
+enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct msm_dp *dp = to_dp_bridge(bridge)->msm_dp_display;
+ enum msm_display_compression_type comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+
+ return _dp_bridge_mode_valid(dp, mode, &comp_type);
+}
+
+struct msm_compression_info *msm_dp_get_dsc_config(struct msm_dp *dp)
+{
+ struct msm_dp_display_private *msm_dp_display;
+ struct msm_compression_info *comp_info;
+
+ if (!dp) {
+ DRM_ERROR("invalid params\n");
+ return 0;
+ }
+
+ msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
+ if (msm_dp_display->panel->dsc_en) {
+ comp_info = &msm_dp_display->panel->msm_dp_mode.timing.comp_info;
+ if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC)
+ return comp_info;
+ }
+
+ return NULL;
+}
+
+bool msm_dp_dsc_enabled_for_mode(struct msm_dp *dp, const struct drm_display_mode *adj_mode)
+{
+ enum msm_display_compression_type comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+ return _dp_bridge_mode_valid(dp, adj_mode, &comp_type) == MODE_OK &&
+ comp_type == MSM_DISPLAY_COMPRESSION_DSC;
+}
+
int msm_dp_display_get_modes(struct msm_dp *dp)
{
struct msm_dp_display_private *msm_dp_display;
@@ -1529,7 +1553,21 @@ bool msm_dp_is_yuv_420_enabled(const struct msm_dp *msm_dp_display,
bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display,
const struct drm_display_mode *mode)
{
- return msm_dp_is_yuv_420_enabled(msm_dp_display, mode);
+ struct msm_dp_display_private *dp;
+ struct msm_compression_info *comp_info;
+
+ if (!msm_dp_display) {
+ DRM_ERROR("invalid params\n");
+ return 0;
+ }
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+ comp_info = &dp->panel->msm_dp_mode.timing.comp_info;
+
+ return
+ (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
+ comp_info->comp_ratio) ||
+ msm_dp_is_yuv_420_enabled(msm_dp_display, mode);
}
bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display)
@@ -1538,7 +1576,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display)
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- if (dp->msm_dp_mode.out_fmt_is_yuv_420)
+ if (dp->msm_dp_mode->out_fmt_is_yuv_420)
return false;
return dp->wide_bus_supported;
@@ -1600,7 +1638,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
bool force_link_train = false;
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
- if (!msm_dp_display->msm_dp_mode.drm_mode.clock) {
+ if (!msm_dp_display->msm_dp_mode->drm_mode.clock) {
DRM_ERROR("invalid params\n");
return;
}
@@ -1620,16 +1658,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
mutex_unlock(&msm_dp_display->event_mutex);
return;
}
-
- rc = msm_dp_display_set_mode(dp, &msm_dp_display->msm_dp_mode);
- if (rc) {
- DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
- mutex_unlock(&msm_dp_display->event_mutex);
- return;
- }
-
- hpd_state = msm_dp_display->hpd_state;
-
if (hpd_state == ST_DISPLAY_OFF) {
msm_dp_display_host_phy_init(msm_dp_display);
force_link_train = true;
@@ -1688,6 +1716,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
if (hpd_state == ST_DISCONNECT_PENDING) {
/* completed disconnection */
msm_dp_display->hpd_state = ST_DISCONNECTED;
+ if (msm_dp_display->panel->dsc_en) {
+ msm_dp_display->msm_dp_mode->timing.comp_info.comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+ msm_dp_display->panel->dsc_en = false;
+ }
} else {
msm_dp_display->hpd_state = ST_DISPLAY_OFF;
}
@@ -1710,31 +1742,38 @@ void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
msm_dp_panel = msm_dp_display->panel;
- memset(&msm_dp_display->msm_dp_mode, 0x0, sizeof(struct msm_dp_display_mode));
+ memset(msm_dp_display->msm_dp_mode, 0x0, sizeof(struct msm_dp_display_mode));
if (msm_dp_display_check_video_test(dp))
- msm_dp_display->msm_dp_mode.bpp = msm_dp_display_get_test_bpp(dp);
+ msm_dp_display->msm_dp_mode->bpp = msm_dp_display_get_test_bpp(dp);
else /* Default num_components per pixel = 3 */
- msm_dp_display->msm_dp_mode.bpp = dp->connector->display_info.bpc * 3;
+ msm_dp_display->msm_dp_mode->bpp = dp->connector->display_info.bpc * 3;
- if (!msm_dp_display->msm_dp_mode.bpp)
- msm_dp_display->msm_dp_mode.bpp = 24; /* Default bpp */
+ if (!msm_dp_display->msm_dp_mode->bpp)
+ msm_dp_display->msm_dp_mode->bpp = 24; /* Default bpp */
- drm_mode_copy(&msm_dp_display->msm_dp_mode.drm_mode, adjusted_mode);
+ drm_mode_copy(&msm_dp_display->msm_dp_mode->drm_mode, adjusted_mode);
- msm_dp_display->msm_dp_mode.v_active_low =
- !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
+ msm_dp_display->msm_dp_mode->v_active_low =
+ !!(msm_dp_display->msm_dp_mode->drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
- msm_dp_display->msm_dp_mode.h_active_low =
- !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
+ msm_dp_display->msm_dp_mode->h_active_low =
+ !!(msm_dp_display->msm_dp_mode->drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
- msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 =
- drm_mode_is_420_only(&dp->connector->display_info, adjusted_mode) &&
- msm_dp_panel->vsc_sdp_supported;
+ msm_dp_panel_init_panel_info(msm_dp_display->panel);
+ msm_dp_display->msm_dp_mode->out_fmt_is_yuv_420 = false;
+ if (drm_mode_is_420_only(&msm_dp_panel->connector->display_info,
+ &msm_dp_display->msm_dp_mode->drm_mode)) {
+ if (msm_dp_panel->vsc_sdp_supported)
+ msm_dp_display->msm_dp_mode->out_fmt_is_yuv_420 = true;
+ else
+ drm_dbg_dp(msm_dp_display->drm_dev, "YCBCR420 was not supported");
+ }
/* populate wide_bus_support to different layers */
msm_dp_display->ctrl->wide_bus_en =
- msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 ? false : msm_dp_display->wide_bus_supported;
+ msm_dp_display->msm_dp_mode->out_fmt_is_yuv_420 ? false : msm_dp_display->wide_bus_supported;
+ msm_dp_panel->wide_bus_en = msm_dp_display->ctrl->wide_bus_en;
}
void msm_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 891211b23202..28bbf9693657 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -6,16 +6,21 @@
#include "dp_panel.h"
#include "dp_reg.h"
#include "dp_utils.h"
+#include "dpu_dsc_helper.h"
+#include <drm/drm_fixed.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
+#include <drm/display/drm_dsc_helper.h>
#include <linux/io.h>
#include <linux/types.h>
#include <asm/byteorder.h>
+/* set to 0 to automatically disable dsc if bandwidth allows */
+#define DP_DSC_EN_ALWAYS 0
#define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4)
struct msm_dp_panel_private {
@@ -211,25 +216,149 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel)
return rc;
}
-static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
- u32 mode_edid_bpp, u32 mode_pclk_khz)
+static u32 msm_dp_panel_get_supported_bpp_no_comp(u32 link_bitrate, u32 mode_edid_bpp, u32 mode_pclk_khz)
{
- const struct msm_dp_link_info *link_info;
- const u32 max_supported_bpp = 30, min_supported_bpp = 18;
- u32 bpp, data_rate_khz;
+ const u32 max_supported_bpp = 30;
+ u32 min_supported_bpp = 18;
+ u32 bpp = 0, mode_bitrate;
bpp = min(mode_edid_bpp, max_supported_bpp);
+ for (; bpp > min_supported_bpp; bpp -= 6) {
+ mode_bitrate = mode_pclk_khz * bpp;
+ if (mode_bitrate <= link_bitrate)
+ break;
+ }
+
+ if (bpp < min_supported_bpp) {
+ DRM_DEBUG("bpp %d is below minimum supported bpp %d\n", bpp,
+ min_supported_bpp);
+ bpp = min_supported_bpp;
+ }
+
+ return bpp;
+}
+
+static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
+ u32 mode_edid_bpp, u32 mode_pclk_khz, enum msm_display_compression_type *comp_type)
+{
+ const struct msm_dp_link_info *link_info;
+ const u32 max_supported_bpp = 30;
+ u32 min_supported_bpp = 24;
+ u32 bpp = 0, link_bitrate = 0, mode_bitrate;
+ s64 rate_fp = 0;
+
link_info = &msm_dp_panel->link_info;
- data_rate_khz = link_info->num_lanes * link_info->rate * 8;
+ rate_fp = drm_int2fixp(link_info->num_lanes * link_info->rate * 8);
+ if (msm_dp_panel->fec_en)
+ rate_fp = drm_fixp_div(rate_fp, msm_dp_panel->fec_overhead_fp);
+
+ link_bitrate = drm_fixp2int(rate_fp);
- do {
- if (mode_pclk_khz * bpp <= data_rate_khz)
+ *comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+
+ if (DP_DSC_EN_ALWAYS) {
+ if (!msm_dp_panel->dsc_en)
+ return msm_dp_panel_get_supported_bpp_no_comp(link_bitrate, mode_edid_bpp, mode_pclk_khz);
+ } else {
+ bpp = msm_dp_panel_get_supported_bpp_no_comp(link_bitrate, mode_edid_bpp, mode_pclk_khz);
+ if (bpp >= min_supported_bpp || !msm_dp_panel->dsc_en)
return bpp;
- bpp -= 6;
- } while (bpp > min_supported_bpp);
+ }
+
+ bpp = min(mode_edid_bpp, max_supported_bpp);
- return min_supported_bpp;
+ for (; bpp > min_supported_bpp; bpp -= 6) {
+ if (bpp == 30 && !(msm_dp_panel->sink_dsc_caps.color_depth & DP_DSC_10_BPC))
+ continue;
+ else if (bpp == 24 && !(msm_dp_panel->sink_dsc_caps.color_depth & DP_DSC_8_BPC))
+ continue;
+
+ mode_bitrate = mode_pclk_khz * bpp / 3;
+
+ if (mode_bitrate <= link_bitrate)
+ break;
+ }
+
+ if (bpp < min_supported_bpp) {
+ DRM_ERROR("bpp %d is below minimum supported bpp with dsc enabled %d\n", bpp,
+ min_supported_bpp);
+ bpp = min_supported_bpp;
+ }
+
+ *comp_type = MSM_DISPLAY_COMPRESSION_DSC;
+ return bpp;
+}
+
+static void msm_dp_panel_decode_dsc_dpcd(struct msm_dp_panel *msm_dp_panel)
+{
+ if (msm_dp_panel->dsc_dpcd[0]) {
+ msm_dp_panel->sink_dsc_caps.dsc_capable = true;
+ msm_dp_panel->sink_dsc_caps.version = msm_dp_panel->dsc_dpcd[1];
+ msm_dp_panel->sink_dsc_caps.block_pred_en =
+ msm_dp_panel->dsc_dpcd[6] ? true : false;
+ msm_dp_panel->sink_dsc_caps.color_depth =
+ msm_dp_panel->dsc_dpcd[10];
+
+ if (msm_dp_panel->sink_dsc_caps.version >= 0x11)
+ msm_dp_panel->dsc_en = true;
+ } else {
+ msm_dp_panel->sink_dsc_caps.dsc_capable = false;
+ msm_dp_panel->dsc_en = false;
+ }
+}
+
+static void msm_dp_panel_read_sink_dsc_caps(struct msm_dp_panel *msm_dp_panel)
+{
+ int rlen;
+ struct msm_dp_panel_private *panel;
+ int dpcd_rev;
+
+ if (!msm_dp_panel) {
+ DRM_ERROR("invalid input\n");
+ return;
+ }
+
+ dpcd_rev = msm_dp_panel->dpcd[DP_DPCD_REV];
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+ if (dpcd_rev >= 0x14) {
+ rlen = drm_dp_dpcd_read(panel->aux, DP_DSC_SUPPORT,
+ msm_dp_panel->dsc_dpcd, (DP_DSC_RECEIVER_CAP_SIZE));
+ if (rlen < (DP_DSC_RECEIVER_CAP_SIZE)) {
+ drm_dbg_dp(panel->drm_dev, "dsc dpcd read failed, rlen=%d\n", rlen);
+ return;
+ }
+
+ msm_dp_panel_decode_dsc_dpcd(msm_dp_panel);
+ }
+}
+
+static void msm_dp_panel_read_sink_fec_caps(struct msm_dp_panel *msm_dp_panel)
+{
+ int rlen;
+ struct msm_dp_panel_private *panel;
+ s64 fec_overhead_fp = drm_fixp_from_fraction(1, 1);
+
+ if (!msm_dp_panel) {
+ DRM_ERROR("invalid input\n");
+ return;
+ }
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+ rlen = drm_dp_dpcd_readb(panel->aux, DP_FEC_CAPABILITY,
+ &msm_dp_panel->fec_dpcd);
+ if (rlen < 1) {
+ DRM_ERROR("fec capability read failed, rlen=%d\n", rlen);
+ return;
+ }
+
+ msm_dp_panel->fec_en = msm_dp_panel->fec_dpcd & DP_FEC_CAPABLE;
+
+ if (msm_dp_panel->fec_en)
+ fec_overhead_fp = drm_fixp_from_fraction(100000, 97582);
+
+ msm_dp_panel->fec_overhead_fp = fec_overhead_fp;
}
int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
@@ -240,7 +369,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
struct msm_dp_panel_private *panel;
if (!msm_dp_panel || !connector) {
- DRM_ERROR("invalid input\n");
+ drm_dbg_dp(panel->drm_dev, "invalid input\n");
return -EINVAL;
}
@@ -248,7 +377,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
rc = msm_dp_panel_read_dpcd(msm_dp_panel);
if (rc) {
- DRM_ERROR("read dpcd failed %d\n", rc);
+ drm_dbg_dp(panel->drm_dev, "read dpcd failed %d\n", rc);
return rc;
}
@@ -256,7 +385,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
if (!is_link_rate_valid(bw_code) ||
!is_lane_count_valid(msm_dp_panel->link_info.num_lanes) ||
(bw_code > msm_dp_panel->max_bw_code)) {
- DRM_ERROR("Illegal link rate=%d lane=%d\n", msm_dp_panel->link_info.rate,
+ drm_dbg_dp(panel->drm_dev, "Illegal link rate=%d lane=%d\n", msm_dp_panel->link_info.rate,
msm_dp_panel->link_info.num_lanes);
return -EINVAL;
}
@@ -281,7 +410,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
if (!msm_dp_panel->drm_edid) {
- DRM_ERROR("panel edid read failed\n");
+ drm_dbg_dp(panel->drm_dev, "panel edid read failed\n");
/* check edid read fail is due to unplug */
if (!msm_dp_aux_is_link_connected(panel->aux)) {
rc = -ETIMEDOUT;
@@ -289,12 +418,19 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
}
}
+ msm_dp_panel_read_sink_fec_caps(msm_dp_panel);
+
+ if (msm_dp_panel->fec_en)
+ msm_dp_panel_read_sink_dsc_caps(msm_dp_panel);
+
+ drm_dbg_dp(panel->drm_dev, "fec_en=%d dsc_en=%d\n", msm_dp_panel->fec_en, msm_dp_panel->dsc_en);
+
end:
return rc;
}
u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
- u32 mode_edid_bpp, u32 mode_pclk_khz)
+ u32 mode_edid_bpp, u32 mode_pclk_khz, enum msm_display_compression_type *comp_type)
{
struct msm_dp_panel_private *panel;
u32 bpp;
@@ -311,7 +447,7 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
panel->link->test_video.test_bit_depth);
else
bpp = msm_dp_panel_get_supported_bpp(msm_dp_panel, mode_edid_bpp,
- mode_pclk_khz);
+ mode_pclk_khz, comp_type);
return bpp;
}
@@ -466,14 +602,6 @@ void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable)
msm_dp_panel_tpg_enable(msm_dp_panel, &panel->msm_dp_panel.msm_dp_mode.drm_mode);
}
-void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel)
-{
- struct msm_dp_panel_private *panel =
- container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
-
- msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, 0x0);
-}
-
static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct dp_sdp *vsc_sdp)
{
u32 header[2];
@@ -679,15 +807,613 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
return 0;
}
-int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel)
+u8 msm_dp_panel_get_colorimetry_config(struct msm_dp_panel *msm_dp_panel)
{
- struct drm_display_mode *drm_mode;
+ u8 colorimetry;
+ u32 colorspace;
+ u32 cc;
struct msm_dp_panel_private *panel;
- drm_mode = &msm_dp_panel->msm_dp_mode.drm_mode;
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+ cc = msm_dp_link_get_colorimetry_config(panel->link);
+ /*
+ * If there is a non-zero value then compliance test-case
+ * is going on, otherwise we can honor the colorspace setting
+ */
+ if (cc)
+ return cc;
+
+ colorspace = msm_dp_panel->connector->state->colorspace;
+ switch (colorspace) {
+ case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65:
+ case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER:
+ colorimetry = 0x7;
+ break;
+ case DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED:
+ colorimetry = 0x3;
+ break;
+ case DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT:
+ colorimetry = 0xb;
+ break;
+ case DRM_MODE_COLORIMETRY_OPRGB:
+ colorimetry = 0xc;
+ break;
+ default:
+ colorimetry = 0;
+ }
+
+ return colorimetry;
+}
+
+static inline int msm_fixp2int_ceil(s64 a)
+{
+ return (a ? drm_fixp2int_ceil(a) : 0);
+}
+
+struct msm_dp_dsc_slices_per_line {
+ u32 min_ppr;
+ u32 max_ppr;
+ u8 num_slices;
+};
+
+struct msm_dp_dsc_peak_throughput {
+ u32 index;
+ u32 peak_throughput;
+};
+
+struct msm_dp_dsc_slice_caps_bit_map {
+ u32 num_slices;
+ u32 bit_index;
+};
+
+static const struct msm_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 }
+};
+
+static const struct msm_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},
+};
+
+static const struct msm_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 msm_dp_panel_check_slice_support(u32 num_slices, u32 raw_data_1,
+ u32 raw_data_2)
+{
+ const struct msm_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 msm_dp_panel_dsc_prepare_basic_params(
+ struct msm_compression_info *comp_info,
+ const struct msm_dp_display_mode *msm_dp_mode,
+ struct msm_dp_panel *msm_dp_panel)
+{
+ struct msm_dp_panel_private *panel;
+ struct drm_dsc_config *dsc;
+ int i;
+ const struct msm_dp_dsc_slices_per_line *rec;
+ const struct msm_dp_dsc_peak_throughput *tput;
+ u32 slice_width;
+ u32 ppr = msm_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(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+ dsc_version_major = msm_dp_panel->sink_dsc_caps.version & 0xF;
+ dsc_version_minor = (msm_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,
+ msm_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 = msm_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;
+
+ /* assuming we are using 2 dsc enc */
+ max_slice_width = min(msm_dp_panel->dsc_dpcd[12] * 320, msm_dp_mode->timing.h_active / 2);
+ slice_width = (msm_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 = msm_dp_panel->dsc_dpcd[4];
+ slice_caps_2 = msm_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 ||
+ !msm_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 = (msm_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 = msm_dp_panel->sink_dsc_caps.block_pred_en;
+
+ dsc->pic_width = msm_dp_mode->timing.h_active;
+ dsc->pic_height = msm_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;
+
+ comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC;
+ comp_info->comp_ratio = DP_DSC_COMP_RATIO;
+ comp_info->src_bpp = msm_dp_mode->timing.bpp;
+ comp_info->tgt_bpp = msm_dp_mode->timing.bpp / comp_info->comp_ratio;
+
+ dsc->bits_per_component = (msm_dp_mode->timing.bpp / 3);
+ dsc->bits_per_pixel = comp_info->tgt_bpp << 4;
+ dsc->slice_count = DIV_ROUND_UP(msm_dp_mode->timing.h_active, slice_width);
+
+ return 0;
+}
+
+void msm_dp_panel_config_dsc_dto(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data)
+{
+ struct msm_dp_panel_private *panel;
+ u32 reg;
+
+ if (!msm_dp_panel || !dsc_data) {
+ DRM_ERROR("invalid input\n");
+ return;
+ }
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+ msm_dp_write_p0(panel, MMSS_DP_DSC_DTO_COUNT, dsc_data->dto_count);
+ reg = msm_dp_read_p0(panel, 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);
+ }
+ msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, reg);
+}
+
+void msm_dp_panel_override_ack_dto(struct msm_dp_panel *msm_dp_panel, bool not_ack)
+{
+ struct msm_dp_panel_private *panel;
+ u32 dsc_dto;
+
+ if (!msm_dp_panel) {
+ DRM_ERROR("invalid input\n");
+ return;
+ }
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+ dsc_dto = msm_dp_read_p0(panel, MMSS_DP_DSC_DTO);
+ if (not_ack)
+ dsc_dto &= ~BIT(1);
+ else
+ dsc_dto = BIT(1);
+
+ msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, dsc_dto);
+}
+
+static void msm_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 inline u8 msm_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 msm_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 msm_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 ^ msm_dp_ecc_get_g1_value(ci);
+ x0 = msm_dp_ecc_get_g0_value(ci);
+ }
+
+ parity_byte = x1 | (x0 << 4);
+
+ return parity_byte;
+}
+
+static void msm_dp_panel_dsc_prepare_pps_packet(struct msm_dp_panel_private *panel, struct msm_dp_dsc_cfg_data *dsc_data)
+{
+ u8 *pps, *parity;
+ u32 *pps_word, *parity_word;
+ int i, index_4;
+
+ 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] = msm_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 msm_dp_panel_dsc_config(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data, bool enable)
+{
+ struct msm_dp_panel_private *panel;
+ struct msm_dp_panel_info *timing;
+ struct msm_compression_info *comp_info;
+ struct drm_dsc_picture_parameter_set *pps_payload;
+ struct drm_dsc_config *dsc;
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+ timing = &msm_dp_panel->msm_dp_mode.timing;
+ comp_info = &timing->comp_info;
+ dsc = &comp_info->msm_dsc_info.drm_dsc;
+
+ memset(dsc_data, 0, sizeof(*dsc_data));
+
+ if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC && enable) {
+ 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;
+ msm_dp_panel_dsc_prepare_pps_packet(panel, dsc_data);
+
+ 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;
+ msm_dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dsc_data->dto_n,
+ &dsc_data->dto_d);
+ }
+}
+
+static void msm_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;
+
+ msm_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 msm_dp_panel_dsc_bw_overhead_calc(struct msm_dp_panel *msm_dp_panel,
+ struct msm_display_dsc_info *dsc_info,
+ struct msm_dp_display_mode *msm_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 msm_dp_panel_private *panel;
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_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);
+
+ msm_dp_mode->dsc_overhead_fp = drm_fixp_from_fraction(dwidth_dsc_bytes,
+ tot_num_hor_bytes);
+
+ msm_dp_mode->timing.dsc_overhead_fp = msm_dp_mode->dsc_overhead_fp;
+}
+
+static void msm_dp_panel_dsc_pclk_param_calc(struct msm_dp_panel *msm_dp_panel,
+ struct msm_compression_info *comp_info,
+ struct msm_dp_display_mode *msm_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 = msm_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 = msm_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 = msm_fixp2int_ceil(temp2_fp);
+
+ msm_dp_panel_dsc_get_num_extra_pclk(comp_info);
+ dsc_info->pclk_per_line--;
+
+ msm_dp_panel_dsc_bw_overhead_calc(msm_dp_panel, dsc_info, msm_dp_mode, dsc_byte_count);
+}
+
+void msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel)
+{
+ struct msm_dp_panel_private *panel;
+ const u32 default_bpp = 24;
+ struct drm_display_mode *drm_mode;
+ struct msm_dp_panel_info *timing;
+ struct msm_compression_info *comp_info;
+ struct msm_dp_display_mode *msm_dp_mode = &msm_dp_panel->msm_dp_mode;
+ int rc;
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+ drm_mode = &msm_dp_mode->drm_mode;
+ timing = &msm_dp_mode->timing;
+
/*
* print resolution info as this is a result
* of user initiated action of cable connection
@@ -709,13 +1435,75 @@ int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel)
drm_mode->clock);
drm_dbg_dp(panel->drm_dev, "bpp = %d\n", msm_dp_panel->msm_dp_mode.bpp);
- msm_dp_panel->msm_dp_mode.bpp = msm_dp_panel_get_mode_bpp(msm_dp_panel, msm_dp_panel->msm_dp_mode.bpp,
- msm_dp_panel->msm_dp_mode.drm_mode.clock);
+ 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 + msm_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 + msm_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 = msm_dp_panel->msm_dp_mode.bpp;
+
+ timing->wide_bus_en = msm_dp_panel->wide_bus_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;
+
+ timing->bpp = msm_dp_panel_get_mode_bpp(msm_dp_panel,
+ timing->bpp, timing->pixel_clk_khz, &comp_info->comp_type);
+ msm_dp_panel->msm_dp_mode.bpp = timing->bpp;
drm_dbg_dp(panel->drm_dev, "updated bpp = %d\n",
msm_dp_panel->msm_dp_mode.bpp);
- return 0;
+ if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC) {
+ if (msm_dp_panel_dsc_prepare_basic_params(comp_info,
+ msm_dp_mode, msm_dp_panel)) {
+ drm_dbg_dp(panel->drm_dev,
+ "prepare DSC basic params failed\n");
+ return;
+ }
+
+ rc = msm_populate_dsc_params(panel->dev, &comp_info->msm_dsc_info.drm_dsc);
+ 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,
+ msm_dp_mode->timing.h_active);
+ if (rc) {
+ drm_dbg_dp(panel->drm_dev,
+ "failed populating other dsc params\n");
+ return;
+ }
+
+ msm_dp_panel_dsc_pclk_param_calc(msm_dp_panel, comp_info, msm_dp_mode);
+ }
+ msm_dp_mode->fec_overhead_fp = msm_dp_panel->fec_overhead_fp;
}
struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux,
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 177c1328fd99..941da207b1f6 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -11,11 +11,35 @@
#include "dp_aux.h"
#include "dp_link.h"
+#include "../msm_drv.h"
struct edid;
+struct msm_dp_panel_info {
+ u32 h_active;
+ u32 v_active;
+ u32 h_back_porch;
+ u32 h_front_porch;
+ u32 h_sync_width;
+ u32 h_active_low;
+ u32 v_back_porch;
+ u32 v_front_porch;
+ u32 v_sync_width;
+ u32 v_active_low;
+ u32 h_skew;
+ u32 refresh_rate;
+ u32 pixel_clk_khz;
+ u32 bpp;
+ bool wide_bus_en;
+ struct msm_compression_info comp_info;
+ s64 dsc_overhead_fp;
+};
+
struct msm_dp_display_mode {
struct drm_display_mode drm_mode;
+ struct msm_dp_panel_info timing;
+ s64 fec_overhead_fp;
+ s64 dsc_overhead_fp;
u32 bpp;
u32 h_active_low;
u32 v_active_low;
@@ -27,10 +51,19 @@ struct msm_dp_panel_psr {
u8 capabilities;
};
+struct msm_dp_dsc_caps {
+ bool dsc_capable;
+ u8 version;
+ bool block_pred_en;
+ u8 color_depth;
+};
+
struct msm_dp_panel {
/* dpcd raw data */
u8 dpcd[DP_RECEIVER_CAP_SIZE];
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
+ u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE];
+ u8 fec_dpcd;
struct msm_dp_link_info link_info;
const struct drm_edid *drm_edid;
@@ -42,25 +75,38 @@ struct msm_dp_panel {
u32 hw_revision;
u32 max_bw_code;
+
+ struct msm_dp_dsc_caps sink_dsc_caps;
+ bool dsc_en;
+ bool fec_en;
+ bool wide_bus_en;
+ s64 fec_overhead_fp;
};
-int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel);
+#define DP_DSC_COMP_RATIO 3
+
+#define DP_DSC_PANEL_EN(msm_dp_panel) ((msm_dp_panel)->msm_dp_mode.timing.comp_info.comp_type == MSM_DISPLAY_COMPRESSION_DSC)
+
+void msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel);
int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel);
int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en);
int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
struct drm_connector *connector);
u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp,
- u32 mode_pclk_khz);
+ u32 mode_pclk_khz, enum msm_display_compression_type *comp_type);
int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
struct drm_connector *connector);
void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel);
void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable);
-
-void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel);
+u8 msm_dp_panel_get_colorimetry_config(struct msm_dp_panel *msm_dp_panel);
+void msm_dp_panel_dsc_config(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data, bool enable);
void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sdp *vsc_sdp);
void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel);
+void msm_dp_panel_config_dsc_dto(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data);
+void msm_dp_panel_override_ack_dto(struct msm_dp_panel *msm_dp_panel, bool not_ack);
+
/**
* is_link_rate_valid() - validates the link rate
* @bw_code: link rate requested by the sink
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 7c44d4e2cf13..8f61f1a1e74e 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -220,7 +220,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)
@@ -239,7 +270,8 @@
#define MMSS_DP_AUDIO_STREAM_0 (0x00000240)
#define MMSS_DP_AUDIO_STREAM_1 (0x00000244)
-#define MMSS_DP_SDP_CFG3 (0x0000024c)
+#define MMSS_DP_SDP_CFG3 (0x0000024C)
+#define MMSS_DP_SDP_CFG4 (0x000004EC)
#define UPDATE_SDP (0x00000001)
#define MMSS_DP_EXTENSION_0 (0x00000250)
@@ -268,6 +300,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)
@@ -326,6 +361,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/dp/dp_tu_calc.c b/drivers/gpu/drm/msm/dp/dp_tu_calc.c
new file mode 100644
index 000000000000..2aa607c03492
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_tu_calc.c
@@ -0,0 +1,894 @@
+#include "dp_tu_calc.h"
+
+#include <drm/drm_fixed.h>
+#include <drm/drm_print.h>
+
+/*
+ * The structure and few functions present below are IP/Hardware
+ * specific implementation. Most of the implementation will not
+ * have coding comments
+ */
+struct tu_algo_data {
+ s64 lclk_fp;
+ s64 orig_lclk_fp;
+ s64 pclk_fp;
+ s64 orig_pclk_fp;
+ s64 lwidth;
+ s64 lwidth_fp;
+ int orig_lwidth;
+ s64 hbp_relative_to_pclk;
+ s64 hbp_relative_to_pclk_fp;
+ int nlanes;
+ int orig_hbp;
+ int bpp;
+ int pixelEnc;
+ int dsc_en;
+ int async_en;
+ int fec_en;
+ int bpc;
+
+ int rb2;
+ uint delay_start_link_extra_pixclk;
+ int extra_buffer_margin;
+ s64 ratio_fp;
+ s64 original_ratio_fp;
+
+ s64 err_fp;
+ s64 n_err_fp;
+ s64 n_n_err_fp;
+ int tu_size;
+ int tu_size_desired;
+ int tu_size_minus1;
+
+ int valid_boundary_link;
+ s64 resulting_valid_fp;
+ s64 total_valid_fp;
+ s64 effective_valid_fp;
+ s64 effective_valid_recorded_fp;
+ int n_tus;
+ int n_tus_per_lane;
+ int paired_tus;
+ int remainder_tus;
+ int remainder_tus_upper;
+ int remainder_tus_lower;
+ int extra_bytes;
+ int filler_size;
+ int delay_start_link;
+
+ int extra_pclk_cycles;
+ int extra_pclk_cycles_in_link_clk;
+ s64 ratio_by_tu_fp;
+ s64 average_valid2_fp;
+ int new_valid_boundary_link;
+ int remainder_symbols_exist;
+ int n_symbols;
+ s64 n_remainder_symbols_per_lane_fp;
+ s64 last_partial_tu_fp;
+ s64 TU_ratio_err_fp;
+
+ int n_tus_incl_last_incomplete_tu;
+ int extra_pclk_cycles_tmp;
+ int extra_pclk_cycles_in_link_clk_tmp;
+ int extra_required_bytes_new_tmp;
+ int filler_size_tmp;
+ int lower_filler_size_tmp;
+ int delay_start_link_tmp;
+
+ bool boundary_moderation_en;
+ int boundary_mod_lower_err;
+ int upper_boundary_count;
+ int lower_boundary_count;
+ int i_upper_boundary_count;
+ int i_lower_boundary_count;
+ int valid_lower_boundary_link;
+ int even_distribution_BF;
+ int even_distribution_legacy;
+ int even_distribution;
+
+ int hbp_delayStartCheck;
+ int pre_tu_hw_pipe_delay;
+ int post_tu_hw_pipe_delay;
+ int link_config_hactive_time;
+ int delay_start_link_lclk;
+ int tu_active_cycles;
+ s64 parity_symbols;
+ int resolution_line_time;
+ int last_partial_lclk;
+
+ int min_hblank_violated;
+ s64 delay_start_time_fp;
+ s64 hbp_time_fp;
+ s64 hactive_time_fp;
+ s64 diff_abs_fp;
+ int second_loop_set;
+ s64 ratio;
+};
+
+static int _tu_param_compare(s64 a, s64 b)
+{
+ u32 a_sign, b_sign;
+ s64 a_temp, b_temp, minus_1;
+
+ if (a == b)
+ return 0;
+
+ minus_1 = drm_fixp_from_fraction(-1, 1);
+
+ a_sign = (a >> 32) & 0x80000000 ? 1 : 0;
+ b_sign = (b >> 32) & 0x80000000 ? 1 : 0;
+
+ if (a_sign > b_sign)
+ return 2;
+ else if (b_sign > a_sign)
+ return 1;
+
+ if (!a_sign && !b_sign) { /* positive */
+ if (a > b)
+ return 1;
+ else
+ return 2;
+ } else { /* negative */
+ a_temp = drm_fixp_mul(a, minus_1);
+ b_temp = drm_fixp_mul(b, minus_1);
+
+ if (a_temp > b_temp)
+ return 2;
+ else
+ return 1;
+ }
+}
+
+static s64 msm_fixp_subtract(s64 a, s64 b)
+{
+ s64 minus_1 = drm_fixp_from_fraction(-1, 1);
+
+ if (a >= b)
+ return a - b;
+
+ return drm_fixp_mul(b - a, minus_1);
+}
+
+static inline int msm_fixp2int_ceil(s64 a)
+{
+ return a ? drm_fixp2int_ceil(a) : 0;
+}
+
+static void msm_dp_panel_update_tu_timings(struct msm_dp_tu_calc_input *in,
+ struct tu_algo_data *tu)
+{
+ int nlanes = in->nlanes;
+ int dsc_num_slices = in->num_of_dsc_slices;
+ int dsc_num_bytes = 0;
+ int numerator;
+ s64 pclk_dsc_fp;
+ s64 dwidth_dsc_fp;
+ s64 hbp_dsc_fp;
+
+ int tot_num_eoc_symbols = 0;
+ int tot_num_hor_bytes = 0;
+ int tot_num_dummy_bytes = 0;
+ int dwidth_dsc_bytes = 0;
+ int eoc_bytes = 0;
+
+ s64 temp1_fp, temp2_fp, temp3_fp;
+
+ tu->lclk_fp = drm_fixp_from_fraction(in->lclk, 1);
+ tu->orig_lclk_fp = tu->lclk_fp;
+ tu->pclk_fp = drm_fixp_from_fraction(in->pclk_khz, 1000);
+ tu->orig_pclk_fp = tu->pclk_fp;
+ tu->lwidth = in->hactive;
+ tu->hbp_relative_to_pclk = in->hporch;
+ tu->nlanes = in->nlanes;
+ tu->bpp = in->bpp;
+ tu->pixelEnc = in->pixel_enc;
+ tu->dsc_en = in->dsc_en;
+ tu->fec_en = in->fec_en;
+ tu->async_en = in->async_en;
+ 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 < 160) ? 1 : 0;
+
+ if (tu->pixelEnc == 420) {
+ temp1_fp = drm_fixp_from_fraction(2, 1);
+ tu->pclk_fp = drm_fixp_div(tu->pclk_fp, temp1_fp);
+ tu->lwidth_fp = drm_fixp_div(tu->lwidth_fp, temp1_fp);
+ tu->hbp_relative_to_pclk_fp =
+ drm_fixp_div(tu->hbp_relative_to_pclk_fp, 2);
+ }
+
+ if (tu->pixelEnc == 422) {
+ switch (tu->bpp) {
+ case 24:
+ tu->bpp = 16;
+ tu->bpc = 8;
+ break;
+ case 30:
+ tu->bpp = 20;
+ tu->bpc = 10;
+ break;
+ default:
+ tu->bpp = 16;
+ tu->bpc = 8;
+ break;
+ }
+ } else {
+ tu->bpc = tu->bpp/3;
+ }
+
+ 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);
+ temp2_fp = drm_fixp_mul(tu->lwidth_fp, temp3_fp);
+
+ temp1_fp = drm_fixp_from_fraction(8, 1);
+ temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
+
+ numerator = drm_fixp2int(temp3_fp);
+
+ dsc_num_bytes = numerator / dsc_num_slices;
+ eoc_bytes = dsc_num_bytes % nlanes;
+ tot_num_eoc_symbols = nlanes * dsc_num_slices;
+ tot_num_hor_bytes = dsc_num_bytes * dsc_num_slices;
+ tot_num_dummy_bytes = (nlanes - eoc_bytes) * dsc_num_slices;
+
+ if (dsc_num_bytes == 0)
+ pr_info("incorrect no of bytes per slice=%d\n", dsc_num_bytes);
+
+ dwidth_dsc_bytes = (tot_num_hor_bytes +
+ tot_num_eoc_symbols +
+ (eoc_bytes == 0 ? 0 : tot_num_dummy_bytes));
+
+ dwidth_dsc_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, 3);
+
+ temp2_fp = drm_fixp_mul(tu->pclk_fp, dwidth_dsc_fp);
+ temp1_fp = drm_fixp_div(temp2_fp, tu->lwidth_fp);
+ pclk_dsc_fp = temp1_fp;
+
+ temp1_fp = drm_fixp_div(pclk_dsc_fp, tu->pclk_fp);
+ temp2_fp = drm_fixp_mul(tu->hbp_relative_to_pclk_fp, temp1_fp);
+ hbp_dsc_fp = temp2_fp;
+
+ /* output */
+ tu->pclk_fp = pclk_dsc_fp;
+ tu->lwidth_fp = dwidth_dsc_fp;
+ tu->hbp_relative_to_pclk_fp = hbp_dsc_fp;
+
+fec_check:
+ if (in->fec_en) {
+ temp1_fp = drm_fixp_from_fraction(976, 1000); /* 0.976 */
+ tu->lclk_fp = drm_fixp_mul(tu->lclk_fp, temp1_fp);
+ }
+}
+
+static void _tu_valid_boundary_calc(struct tu_algo_data *tu)
+{
+ s64 temp1_fp, temp2_fp, temp, temp1, temp2;
+ int compare_result_1, compare_result_2, compare_result_3;
+
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+ temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
+
+ tu->new_valid_boundary_link = msm_fixp2int_ceil(temp2_fp);
+
+ temp = (tu->i_upper_boundary_count *
+ tu->new_valid_boundary_link +
+ tu->i_lower_boundary_count *
+ (tu->new_valid_boundary_link - 1));
+ tu->average_valid2_fp = drm_fixp_from_fraction(temp,
+ (tu->i_upper_boundary_count +
+ tu->i_lower_boundary_count));
+
+ temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
+ temp2_fp = tu->lwidth_fp;
+ temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+ temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
+ tu->n_tus = drm_fixp2int(temp2_fp);
+ if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
+ tu->n_tus += 1;
+
+ temp1_fp = drm_fixp_from_fraction(tu->n_tus, 1);
+ temp2_fp = drm_fixp_mul(temp1_fp, tu->average_valid2_fp);
+ temp1_fp = drm_fixp_from_fraction(tu->n_symbols, 1);
+ temp2_fp = temp1_fp - temp2_fp;
+ temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
+ temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
+ tu->n_remainder_symbols_per_lane_fp = temp2_fp;
+
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+ tu->last_partial_tu_fp =
+ drm_fixp_div(tu->n_remainder_symbols_per_lane_fp,
+ temp1_fp);
+
+ if (tu->n_remainder_symbols_per_lane_fp != 0)
+ tu->remainder_symbols_exist = 1;
+ else
+ tu->remainder_symbols_exist = 0;
+
+ temp1_fp = drm_fixp_from_fraction(tu->n_tus, tu->nlanes);
+ tu->n_tus_per_lane = drm_fixp2int(temp1_fp);
+
+ tu->paired_tus = (int)((tu->n_tus_per_lane) /
+ (tu->i_upper_boundary_count +
+ tu->i_lower_boundary_count));
+
+ tu->remainder_tus = tu->n_tus_per_lane - tu->paired_tus *
+ (tu->i_upper_boundary_count +
+ tu->i_lower_boundary_count);
+
+ if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) {
+ tu->remainder_tus_upper = tu->i_upper_boundary_count;
+ tu->remainder_tus_lower = tu->remainder_tus -
+ tu->i_upper_boundary_count;
+ } else {
+ tu->remainder_tus_upper = tu->remainder_tus;
+ tu->remainder_tus_lower = 0;
+ }
+
+ temp = tu->paired_tus * (tu->i_upper_boundary_count *
+ tu->new_valid_boundary_link +
+ tu->i_lower_boundary_count *
+ (tu->new_valid_boundary_link - 1)) +
+ (tu->remainder_tus_upper *
+ tu->new_valid_boundary_link) +
+ (tu->remainder_tus_lower *
+ (tu->new_valid_boundary_link - 1));
+ tu->total_valid_fp = drm_fixp_from_fraction(temp, 1);
+
+ if (tu->remainder_symbols_exist) {
+ temp1_fp = tu->total_valid_fp +
+ tu->n_remainder_symbols_per_lane_fp;
+ temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
+ temp2_fp = temp2_fp + tu->last_partial_tu_fp;
+ temp1_fp = drm_fixp_div(temp1_fp, temp2_fp);
+ } else {
+ temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
+ temp1_fp = drm_fixp_div(tu->total_valid_fp, temp2_fp);
+ }
+ tu->effective_valid_fp = temp1_fp;
+
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+ temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
+ tu->n_n_err_fp = msm_fixp_subtract(tu->effective_valid_fp, temp2_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+ temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
+ tu->n_err_fp = msm_fixp_subtract(tu->average_valid2_fp, temp2_fp);
+
+ tu->even_distribution = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
+
+ temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
+ temp2_fp = tu->lwidth_fp;
+ temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+ temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
+ tu->n_tus_incl_last_incomplete_tu = msm_fixp2int_ceil(temp2_fp);
+
+ temp1 = 0;
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+ temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
+ temp1_fp = tu->average_valid2_fp - temp2_fp;
+ temp2_fp = drm_fixp_from_fraction(tu->n_tus_incl_last_incomplete_tu, 1);
+ temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+ temp1 = msm_fixp2int_ceil(temp1_fp);
+
+ temp = tu->i_upper_boundary_count * tu->nlanes;
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
+ temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
+ temp1_fp = drm_fixp_from_fraction(tu->new_valid_boundary_link, 1);
+ temp2_fp = temp1_fp - temp2_fp;
+ temp1_fp = drm_fixp_from_fraction(temp, 1);
+ temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ temp2 = msm_fixp2int_ceil(temp2_fp);
+
+ tu->extra_required_bytes_new_tmp = (int)(temp1 + temp2);
+
+ temp1_fp = drm_fixp_from_fraction(8, tu->bpp);
+ temp2_fp = drm_fixp_from_fraction(
+ tu->extra_required_bytes_new_tmp, 1);
+ temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+ tu->extra_pclk_cycles_tmp = msm_fixp2int_ceil(temp1_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles_tmp, 1);
+ temp2_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
+ temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ tu->extra_pclk_cycles_in_link_clk_tmp = msm_fixp2int_ceil(temp1_fp);
+
+ tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link;
+
+ tu->lower_filler_size_tmp = tu->filler_size_tmp + 1;
+
+ tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp +
+ tu->lower_filler_size_tmp +
+ tu->extra_buffer_margin;
+
+ temp1_fp = drm_fixp_from_fraction(tu->delay_start_link_tmp, 1);
+ tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
+
+ if (tu->rb2) {
+ temp1_fp = drm_fixp_mul(tu->delay_start_time_fp, tu->lclk_fp);
+ tu->delay_start_link_lclk = msm_fixp2int_ceil(temp1_fp);
+
+ if (tu->remainder_tus > tu->i_upper_boundary_count) {
+ temp = (tu->remainder_tus - tu->i_upper_boundary_count) *
+ (tu->new_valid_boundary_link - 1);
+ temp += (tu->i_upper_boundary_count * tu->new_valid_boundary_link);
+ temp *= tu->nlanes;
+ } else {
+ temp = tu->nlanes * tu->remainder_tus * tu->new_valid_boundary_link;
+ }
+
+ temp1 = tu->i_lower_boundary_count * (tu->new_valid_boundary_link - 1);
+ temp1 += tu->i_upper_boundary_count * tu->new_valid_boundary_link;
+ temp1 *= tu->paired_tus * tu->nlanes;
+ temp1_fp = drm_fixp_from_fraction(tu->n_symbols - temp1 - temp, tu->nlanes);
+ tu->last_partial_lclk = msm_fixp2int_ceil(temp1_fp);
+
+ tu->tu_active_cycles = (int)((tu->n_tus_per_lane * tu->tu_size) +
+ tu->last_partial_lclk);
+ tu->post_tu_hw_pipe_delay = 4 /*BS_on_the_link*/ + 1 /*BE_next_ren*/;
+ temp = tu->pre_tu_hw_pipe_delay + tu->delay_start_link_lclk +
+ tu->tu_active_cycles + tu->post_tu_hw_pipe_delay;
+
+ if (tu->fec_en == 1) {
+ if (tu->nlanes == 1) {
+ temp1_fp = drm_fixp_from_fraction(temp, 500);
+ tu->parity_symbols = msm_fixp2int_ceil(temp1_fp) * 12 + 1;
+ } else {
+ temp1_fp = drm_fixp_from_fraction(temp, 250);
+ tu->parity_symbols = msm_fixp2int_ceil(temp1_fp) * 6 + 1;
+ }
+ } else { //no fec BW impact
+ tu->parity_symbols = 0;
+ }
+
+ tu->link_config_hactive_time = temp + tu->parity_symbols;
+
+ if (tu->resolution_line_time >= tu->link_config_hactive_time + 1 /*margin*/)
+ tu->hbp_delayStartCheck = 1;
+ else
+ tu->hbp_delayStartCheck = 0;
+ } else {
+ compare_result_3 = _tu_param_compare(tu->hbp_time_fp, tu->delay_start_time_fp);
+ if (compare_result_3 < 2)
+ tu->hbp_delayStartCheck = 1;
+ else
+ tu->hbp_delayStartCheck = 0;
+ }
+
+ compare_result_1 = _tu_param_compare(tu->n_n_err_fp, tu->diff_abs_fp);
+ if (compare_result_1 == 2)
+ compare_result_1 = 1;
+ else
+ compare_result_1 = 0;
+
+ compare_result_2 = _tu_param_compare(tu->n_n_err_fp, tu->err_fp);
+ if (compare_result_2 == 2)
+ compare_result_2 = 1;
+ else
+ compare_result_2 = 0;
+
+ if (((tu->even_distribution == 1) ||
+ ((tu->even_distribution_BF == 0) &&
+ (tu->even_distribution_legacy == 0))) &&
+ tu->n_err_fp >= 0 && tu->n_n_err_fp >= 0 &&
+ compare_result_2 &&
+ (compare_result_1 || (tu->min_hblank_violated == 1)) &&
+ (tu->new_valid_boundary_link - 1) > 0 &&
+ (tu->hbp_delayStartCheck == 1) &&
+ (tu->delay_start_link_tmp <= 1023)) {
+ tu->upper_boundary_count = tu->i_upper_boundary_count;
+ tu->lower_boundary_count = tu->i_lower_boundary_count;
+ tu->err_fp = tu->n_n_err_fp;
+ tu->boundary_moderation_en = true;
+ tu->tu_size_desired = tu->tu_size;
+ tu->valid_boundary_link = tu->new_valid_boundary_link;
+ tu->effective_valid_recorded_fp = tu->effective_valid_fp;
+ tu->even_distribution_BF = 1;
+ tu->delay_start_link = tu->delay_start_link_tmp;
+ } else if (tu->boundary_mod_lower_err == 0) {
+ compare_result_1 = _tu_param_compare(tu->n_n_err_fp,
+ tu->diff_abs_fp);
+ if (compare_result_1 == 2)
+ tu->boundary_mod_lower_err = 1;
+ }
+}
+
+static void _dp_calc_boundary(struct tu_algo_data *tu)
+{
+ s64 temp1_fp = 0, temp2_fp = 0;
+
+ do {
+ tu->err_fp = drm_fixp_from_fraction(1000, 1);
+
+ temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
+ temp2_fp = drm_fixp_from_fraction(
+ tu->delay_start_link_extra_pixclk, 1);
+ temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+ tu->extra_buffer_margin = msm_fixp2int_ceil(temp1_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
+ temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp);
+ tu->n_symbols = msm_fixp2int_ceil(temp1_fp);
+
+ for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) {
+ for (tu->i_upper_boundary_count = 1;
+ tu->i_upper_boundary_count <= 15;
+ tu->i_upper_boundary_count++) {
+ for (tu->i_lower_boundary_count = 1;
+ tu->i_lower_boundary_count <= 15;
+ tu->i_lower_boundary_count++) {
+ _tu_valid_boundary_calc(tu);
+ }
+ }
+ }
+ tu->delay_start_link_extra_pixclk--;
+ } while (!tu->boundary_moderation_en &&
+ tu->boundary_mod_lower_err == 1 &&
+ tu->delay_start_link_extra_pixclk != 0 &&
+ ((tu->second_loop_set == 0 && tu->rb2 == 1) || tu->rb2 == 0));
+}
+
+static void _dp_calc_extra_bytes(struct tu_algo_data *tu)
+{
+ u64 temp = 0;
+ s64 temp1_fp = 0, temp2_fp = 0;
+
+ temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
+ temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
+ temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1);
+ temp2_fp = temp1_fp - temp2_fp;
+ temp1_fp = drm_fixp_from_fraction(tu->n_tus + 1, 1);
+ temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+
+ temp = drm_fixp2int(temp2_fp);
+ if (temp)
+ tu->extra_bytes = msm_fixp2int_ceil(temp2_fp);
+ else
+ tu->extra_bytes = 0;
+
+ temp1_fp = drm_fixp_from_fraction(tu->extra_bytes, 1);
+ temp2_fp = drm_fixp_from_fraction(8, tu->bpp);
+ temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ tu->extra_pclk_cycles = msm_fixp2int_ceil(temp1_fp);
+
+ temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
+ temp2_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles, 1);
+ temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+ tu->extra_pclk_cycles_in_link_clk = msm_fixp2int_ceil(temp1_fp);
+}
+
+
+void msm_dp_ctrl_calc_tu(struct drm_device *drm_dev,
+ struct msm_dp_tu_calc_input *in,
+ struct msm_dp_vc_tu_mapping_table *tu_table)
+{
+ struct tu_algo_data tu;
+ int compare_result_1, compare_result_2;
+ u64 temp = 0, temp1;
+ s64 temp_fp = 0, temp1_fp = 0, temp2_fp = 0;
+
+ s64 LCLK_FAST_SKEW_fp = drm_fixp_from_fraction(6, 10000); /* 0.0006 */
+ s64 RATIO_SCALE_fp = drm_fixp_from_fraction(1001, 1000);
+
+ u8 DP_BRUTE_FORCE = 1;
+ s64 BRUTE_FORCE_THRESHOLD_fp = drm_fixp_from_fraction(1, 10); /* 0.1 */
+ uint EXTRA_PIXCLK_CYCLE_DELAY = 4;
+ s64 HBLANK_MARGIN = drm_fixp_from_fraction(4, 1);
+ s64 HBLANK_MARGIN_EXTRA = 0;
+
+
+ memset(&tu, 0, sizeof(tu));
+
+ msm_dp_panel_update_tu_timings(in, &tu);
+
+ tu.err_fp = drm_fixp_from_fraction(1000, 1); /* 1000 */
+
+ temp1_fp = drm_fixp_from_fraction(4, 1);
+ temp2_fp = drm_fixp_mul(temp1_fp, tu.lclk_fp);
+ temp_fp = drm_fixp_div(temp2_fp, tu.pclk_fp);
+ tu.extra_buffer_margin = msm_fixp2int_ceil(temp_fp);
+
+ if (in->compress_ratio == 375 && tu.bpp == 30)
+ temp1_fp = drm_fixp_from_fraction(24, 8);
+ else
+ temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+
+ temp2_fp = drm_fixp_mul(tu.pclk_fp, temp1_fp);
+ temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1);
+ temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
+ tu.ratio_fp = drm_fixp_div(temp2_fp, tu.lclk_fp);
+
+ tu.original_ratio_fp = tu.ratio_fp;
+ tu.boundary_moderation_en = false;
+ tu.upper_boundary_count = 0;
+ tu.lower_boundary_count = 0;
+ tu.i_upper_boundary_count = 0;
+ tu.i_lower_boundary_count = 0;
+ tu.valid_lower_boundary_link = 0;
+ tu.even_distribution_BF = 0;
+ tu.even_distribution_legacy = 0;
+ tu.even_distribution = 0;
+ tu.hbp_delayStartCheck = 0;
+ tu.pre_tu_hw_pipe_delay = 0;
+ tu.post_tu_hw_pipe_delay = 0;
+ tu.link_config_hactive_time = 0;
+ tu.delay_start_link_lclk = 0;
+ tu.tu_active_cycles = 0;
+ tu.resolution_line_time = 0;
+ tu.last_partial_lclk = 0;
+ tu.delay_start_time_fp = 0;
+ tu.second_loop_set = 0;
+
+ tu.err_fp = drm_fixp_from_fraction(1000, 1);
+ tu.n_err_fp = 0;
+ tu.n_n_err_fp = 0;
+
+ temp = drm_fixp2int(tu.lwidth_fp);
+ if ((((u32)temp % tu.nlanes) != 0) && (_tu_param_compare(tu.ratio_fp, DRM_FIXED_ONE) == 2)
+ && (tu.dsc_en == 0)) {
+ tu.ratio_fp = drm_fixp_mul(tu.ratio_fp, RATIO_SCALE_fp);
+ if (_tu_param_compare(tu.ratio_fp, DRM_FIXED_ONE) == 1)
+ tu.ratio_fp = DRM_FIXED_ONE;
+ }
+
+ if (_tu_param_compare(tu.ratio_fp, DRM_FIXED_ONE) == 1)
+ tu.ratio_fp = DRM_FIXED_ONE;
+
+ if (HBLANK_MARGIN_EXTRA != 0) {
+ HBLANK_MARGIN += HBLANK_MARGIN_EXTRA;
+ drm_dbg_dp(drm_dev, "Info: increase HBLANK_MARGIN to %lld. (PLUS%lld)\n", HBLANK_MARGIN,
+ HBLANK_MARGIN_EXTRA);
+ }
+
+ for (tu.tu_size = 32; tu.tu_size <= 64; tu.tu_size++) {
+ temp1_fp = drm_fixp_from_fraction(tu.tu_size, 1);
+ temp2_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp);
+ temp = msm_fixp2int_ceil(temp2_fp);
+ temp1_fp = drm_fixp_from_fraction(temp, 1);
+ tu.n_err_fp = temp1_fp - temp2_fp;
+
+ if (tu.n_err_fp < tu.err_fp) {
+ tu.err_fp = tu.n_err_fp;
+ tu.tu_size_desired = tu.tu_size;
+ }
+ }
+
+ tu.tu_size_minus1 = tu.tu_size_desired - 1;
+
+ temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+ temp2_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp);
+ tu.valid_boundary_link = msm_fixp2int_ceil(temp2_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+ temp2_fp = tu.lwidth_fp;
+ temp2_fp = drm_fixp_mul(temp2_fp, temp1_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu.valid_boundary_link, 1);
+ temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
+ tu.n_tus = drm_fixp2int(temp2_fp);
+ if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
+ tu.n_tus += 1;
+
+ tu.even_distribution_legacy = tu.n_tus % tu.nlanes == 0 ? 1 : 0;
+ drm_dbg_dp(drm_dev, "Info: n_sym = %d, num_of_tus = %d\n",
+ tu.valid_boundary_link, tu.n_tus);
+
+ _dp_calc_extra_bytes(&tu);
+
+ tu.filler_size = tu.tu_size_desired - tu.valid_boundary_link;
+
+ temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+ tu.ratio_by_tu_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp);
+
+ tu.delay_start_link = tu.extra_pclk_cycles_in_link_clk +
+ tu.filler_size + tu.extra_buffer_margin;
+
+ tu.resulting_valid_fp =
+ drm_fixp_from_fraction(tu.valid_boundary_link, 1);
+
+ temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1);
+ temp2_fp = drm_fixp_div(tu.resulting_valid_fp, temp1_fp);
+ tu.TU_ratio_err_fp = temp2_fp - tu.original_ratio_fp;
+
+ temp1_fp = drm_fixp_from_fraction((tu.hbp_relative_to_pclk - HBLANK_MARGIN), 1);
+ tu.hbp_time_fp = drm_fixp_div(temp1_fp, tu.pclk_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu.delay_start_link, 1);
+ tu.delay_start_time_fp = drm_fixp_div(temp1_fp, tu.lclk_fp);
+
+ compare_result_1 = _tu_param_compare(tu.hbp_time_fp,
+ tu.delay_start_time_fp);
+ if (compare_result_1 == 2) /* hbp_time_fp < delay_start_time_fp */
+ tu.min_hblank_violated = 1;
+
+ tu.hactive_time_fp = drm_fixp_div(tu.lwidth_fp, tu.pclk_fp);
+
+ compare_result_2 = _tu_param_compare(tu.hactive_time_fp,
+ tu.delay_start_time_fp);
+ if (compare_result_2 == 2)
+ tu.min_hblank_violated = 1;
+
+ /* brute force */
+
+ tu.delay_start_link_extra_pixclk = EXTRA_PIXCLK_CYCLE_DELAY;
+ tu.diff_abs_fp = tu.resulting_valid_fp - tu.ratio_by_tu_fp;
+
+ temp = drm_fixp2int(tu.diff_abs_fp);
+ if (!temp && tu.diff_abs_fp <= 0xffff)
+ tu.diff_abs_fp = 0;
+
+ /* if(diff_abs < 0) diff_abs *= -1 */
+ if (tu.diff_abs_fp < 0)
+ tu.diff_abs_fp = drm_fixp_mul(tu.diff_abs_fp, -1);
+
+ tu.boundary_mod_lower_err = 0;
+
+ temp1_fp = drm_fixp_div(tu.orig_lclk_fp, tu.orig_pclk_fp);
+
+ temp2_fp = drm_fixp_from_fraction(tu.orig_lwidth + tu.orig_hbp, 2);
+ temp_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ tu.resolution_line_time = drm_fixp2int(temp_fp);
+ tu.pre_tu_hw_pipe_delay = msm_fixp2int_ceil(temp1_fp) + 2 /*cdc fifo write jitter+2*/
+ + 3 /*pre-delay start cycles*/
+ + 3 /*post-delay start cycles*/ + 1 /*BE on the link*/;
+ tu.post_tu_hw_pipe_delay = 4 /*BS_on_the_link*/ + 1 /*BE_next_ren*/;
+
+ temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+ temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp);
+ tu.n_symbols = msm_fixp2int_ceil(temp1_fp);
+
+ if (tu.rb2) {
+ temp1_fp = drm_fixp_mul(tu.delay_start_time_fp, tu.lclk_fp);
+ tu.delay_start_link_lclk = msm_fixp2int_ceil(temp1_fp);
+
+ tu.new_valid_boundary_link = tu.valid_boundary_link;
+ tu.i_upper_boundary_count = 1;
+ tu.i_lower_boundary_count = 0;
+
+ temp1 = tu.i_upper_boundary_count * tu.new_valid_boundary_link;
+ temp1 += tu.i_lower_boundary_count * (tu.new_valid_boundary_link - 1);
+ tu.average_valid2_fp = drm_fixp_from_fraction(temp1,
+ (tu.i_upper_boundary_count + tu.i_lower_boundary_count));
+
+ temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+ temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp);
+ temp2_fp = drm_fixp_div(temp1_fp, tu.average_valid2_fp);
+ tu.n_tus = drm_fixp2int(temp2_fp);
+
+ tu.n_tus_per_lane = tu.n_tus / tu.nlanes;
+ tu.paired_tus = (int)((tu.n_tus_per_lane) /
+ (tu.i_upper_boundary_count + tu.i_lower_boundary_count));
+
+ tu.remainder_tus = tu.n_tus_per_lane - tu.paired_tus *
+ (tu.i_upper_boundary_count + tu.i_lower_boundary_count);
+
+ if (tu.remainder_tus > tu.i_upper_boundary_count) {
+ temp = (tu.remainder_tus - tu.i_upper_boundary_count) *
+ (tu.new_valid_boundary_link - 1);
+ temp += (tu.i_upper_boundary_count * tu.new_valid_boundary_link);
+ temp *= tu.nlanes;
+ } else {
+ temp = tu.nlanes * tu.remainder_tus * tu.new_valid_boundary_link;
+ }
+
+ temp1 = tu.i_lower_boundary_count * (tu.new_valid_boundary_link - 1);
+ temp1 += tu.i_upper_boundary_count * tu.new_valid_boundary_link;
+ temp1 *= tu.paired_tus * tu.nlanes;
+ temp1_fp = drm_fixp_from_fraction(tu.n_symbols - temp1 - temp, tu.nlanes);
+ tu.last_partial_lclk = msm_fixp2int_ceil(temp1_fp);
+
+ tu.tu_active_cycles = (int)((tu.n_tus_per_lane * tu.tu_size) +
+ tu.last_partial_lclk);
+
+ temp = tu.pre_tu_hw_pipe_delay + tu.delay_start_link_lclk +
+ tu.tu_active_cycles + tu.post_tu_hw_pipe_delay;
+
+ if (tu.fec_en == 1) {
+ if (tu.nlanes == 1) {
+ temp1_fp = drm_fixp_from_fraction(temp, 500);
+ tu.parity_symbols = msm_fixp2int_ceil(temp1_fp) * 12 + 1;
+ } else {
+ temp1_fp = drm_fixp_from_fraction(temp, 250);
+ tu.parity_symbols = msm_fixp2int_ceil(temp1_fp) * 6 + 1;
+ }
+ } else { //no fec BW impact
+ tu.parity_symbols = 0;
+ }
+
+ tu.link_config_hactive_time = temp + tu.parity_symbols;
+
+ if (tu.link_config_hactive_time + 1 /*margin*/ >= tu.resolution_line_time)
+ tu.min_hblank_violated = 1;
+ }
+
+ tu.delay_start_time_fp = 0;
+
+ if ((tu.diff_abs_fp != 0 &&
+ ((tu.diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) ||
+ (tu.even_distribution_legacy == 0) ||
+ (DP_BRUTE_FORCE == 1))) ||
+ (tu.min_hblank_violated == 1)) {
+ _dp_calc_boundary(&tu);
+
+ if (tu.boundary_moderation_en) {
+ temp1_fp = drm_fixp_from_fraction(
+ (tu.upper_boundary_count *
+ tu.valid_boundary_link +
+ tu.lower_boundary_count *
+ (tu.valid_boundary_link - 1)), 1);
+ temp2_fp = drm_fixp_from_fraction(
+ (tu.upper_boundary_count +
+ tu.lower_boundary_count), 1);
+ tu.resulting_valid_fp =
+ drm_fixp_div(temp1_fp, temp2_fp);
+
+ temp1_fp = drm_fixp_from_fraction(
+ tu.tu_size_desired, 1);
+ tu.ratio_by_tu_fp =
+ drm_fixp_mul(tu.original_ratio_fp, temp1_fp);
+
+ tu.valid_lower_boundary_link =
+ tu.valid_boundary_link - 1;
+
+ temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+ temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp);
+ temp2_fp = drm_fixp_div(temp1_fp,
+ tu.resulting_valid_fp);
+ tu.n_tus = drm_fixp2int(temp2_fp);
+
+ tu.tu_size_minus1 = tu.tu_size_desired - 1;
+ tu.even_distribution_BF = 1;
+
+ temp1_fp =
+ drm_fixp_from_fraction(tu.tu_size_desired, 1);
+ temp2_fp =
+ drm_fixp_div(tu.resulting_valid_fp, temp1_fp);
+ tu.TU_ratio_err_fp = temp2_fp - tu.original_ratio_fp;
+ }
+ }
+
+ if (tu.async_en) {
+ temp2_fp = drm_fixp_mul(LCLK_FAST_SKEW_fp, tu.lwidth_fp);
+ temp = msm_fixp2int_ceil(temp2_fp);
+
+ temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1);
+ temp2_fp = drm_fixp_mul(tu.original_ratio_fp, temp1_fp);
+ temp1_fp = drm_fixp_from_fraction(tu.bpp, 8);
+ temp2_fp = drm_fixp_div(temp1_fp, temp2_fp);
+ temp1_fp = drm_fixp_from_fraction(temp, 1);
+ temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
+ temp = drm_fixp2int(temp2_fp);
+
+ tu.delay_start_link += (int)temp;
+ }
+
+ temp1_fp = drm_fixp_from_fraction(tu.delay_start_link, 1);
+ tu.delay_start_time_fp = drm_fixp_div(temp1_fp, tu.lclk_fp);
+
+ /* OUTPUTS */
+ tu_table->valid_boundary_link = tu.valid_boundary_link;
+ tu_table->delay_start_link = tu.delay_start_link;
+ tu_table->boundary_moderation_en = tu.boundary_moderation_en;
+ tu_table->valid_lower_boundary_link = tu.valid_lower_boundary_link;
+ tu_table->upper_boundary_count = tu.upper_boundary_count;
+ tu_table->lower_boundary_count = tu.lower_boundary_count;
+ tu_table->tu_size_minus1 = tu.tu_size_minus1;
+
+ drm_dbg_dp(drm_dev, "TU: valid_boundary_link: %d\n", tu_table->valid_boundary_link);
+ drm_dbg_dp(drm_dev, "TU: delay_start_link: %d\n", tu_table->delay_start_link);
+ drm_dbg_dp(drm_dev, "TU: boundary_moderation_en: %d\n",
+ tu_table->boundary_moderation_en);
+ drm_dbg_dp(drm_dev, "TU: valid_lower_boundary_link: %d\n",
+ tu_table->valid_lower_boundary_link);
+ drm_dbg_dp(drm_dev, "TU: upper_boundary_count: %d\n",
+ tu_table->upper_boundary_count);
+ drm_dbg_dp(drm_dev, "TU: lower_boundary_count: %d\n",
+ tu_table->lower_boundary_count);
+ drm_dbg_dp(drm_dev, "TU: tu_size_minus1: %d\n", tu_table->tu_size_minus1);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_tu_calc.h b/drivers/gpu/drm/msm/dp/dp_tu_calc.h
new file mode 100644
index 000000000000..95af5b6b43b9
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_tu_calc.h
@@ -0,0 +1,40 @@
+#ifndef _DP_TU_CALC_H_
+#define _DP_TU_CALC_H_
+
+#include <linux/types.h>
+#include <drm/drm_device.h>
+
+struct msm_dp_tu_calc_input {
+ u64 lclk; /* 162, 270, 540 and 810 */
+ u64 pclk_khz; /* in KHz */
+ u64 hactive; /* active h-width */
+ u64 hporch; /* bp + fp + pulse */
+ int nlanes; /* no.of.lanes */
+ int bpp; /* bits */
+ int pixel_enc; /* 444, 420, 422 */
+ int dsc_en; /* dsc on/off */
+ int async_en; /* async mode */
+ int fec_en; /* fec */
+ int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
+ int num_of_dsc_slices; /* number of slices per line */
+};
+
+struct msm_dp_vc_tu_mapping_table {
+ u32 vic;
+ u8 lanes;
+ u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
+ u8 bpp;
+ u8 valid_boundary_link;
+ u16 delay_start_link;
+ bool boundary_moderation_en;
+ u8 valid_lower_boundary_link;
+ u8 upper_boundary_count;
+ u8 lower_boundary_count;
+ u8 tu_size_minus1;
+};
+
+void msm_dp_ctrl_calc_tu(struct drm_device *drm_dev,
+ struct msm_dp_tu_calc_input *in,
+ struct msm_dp_vc_tu_mapping_table *tu_table);
+
+#endif /* _DP_TU_CALC_H_ */
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index d8bb40ef820e..dec3a4ce7fab 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -12,11 +12,16 @@ 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);
}
+bool msm_dsi_dsc_enabled_for_mode(struct msm_dsi *msm_dsi, const struct drm_display_mode *adj_mode)
+{
+ return msm_dsi_host_dsc_enabled_for_mode(msm_dsi->host, adj_mode);
+}
+
bool msm_dsi_wide_bus_enabled(struct msm_dsi *msm_dsi)
{
return msm_dsi_host_is_wide_bus_enabled(msm_dsi->host);
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index 93c028a122f3..72fbfc43cbd8 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -121,7 +121,8 @@ 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);
+bool msm_dsi_host_dsc_enabled_for_mode(struct mipi_dsi_host *host, const struct drm_display_mode *adj_mode);
bool msm_dsi_host_is_wide_bus_enabled(struct mipi_dsi_host *host);
/* dsi phy */
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index db6da99375a1..bb36f0cc327e 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -34,8 +34,6 @@
#define DSI_RESET_TOGGLE_DELAY_MS 20
-static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc);
-
static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor)
{
u32 ver;
@@ -166,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;
@@ -1026,7 +1025,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
/* we do the calculations for dsc parameters here so that
* panel can use these parameters
*/
- ret = dsi_populate_dsc_params(msm_host, dsc);
+ ret = msm_populate_dsc_params(&msm_host->pdev->dev, dsc);
if (ret)
return;
@@ -1830,52 +1829,6 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host,
return -EINVAL;
}
-static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc)
-{
- int ret;
-
- if (dsc->bits_per_pixel & 0xf) {
- DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n");
- return -EINVAL;
- }
-
- switch (dsc->bits_per_component) {
- case 8:
- case 10:
- case 12:
- /*
- * Only 8, 10, and 12 bpc are supported for DSC 1.1 block.
- * If additional bpc values need to be supported, update
- * this quard with the appropriate DSC version verification.
- */
- break;
- default:
- DRM_DEV_ERROR(&msm_host->pdev->dev,
- "Unsupported bits_per_component value: %d\n",
- dsc->bits_per_component);
- return -EOPNOTSUPP;
- }
-
- dsc->simple_422 = 0;
- dsc->convert_rgb = 1;
- dsc->vbr_enable = 0;
-
- drm_dsc_set_const_params(dsc);
- drm_dsc_set_rc_buf_thresh(dsc);
-
- /* DPU supports only pre-SCR panels */
- ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR);
- if (ret) {
- DRM_DEV_ERROR(&msm_host->pdev->dev, "could not find DSC RC parameters\n");
- return ret;
- }
-
- dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc);
- dsc->line_buf_depth = dsc->bits_per_component + 1;
-
- return drm_dsc_compute_rc_parameters(dsc);
-}
-
static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
{
struct msm_dsi *msm_dsi = platform_get_drvdata(msm_host->pdev);
@@ -2659,9 +2612,21 @@ 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;
+ }
+ return comp_info;
+}
+
+bool msm_dsi_host_dsc_enabled_for_mode(struct mipi_dsi_host *host, const struct drm_display_mode *adj_mode)
+{
+ struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+ return !!msm_host->dsc;
}
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 6d847d593f1a..6215ff1a488d 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -70,6 +70,67 @@ enum msm_dsi_controller {
#define MSM_GPU_MAX_RINGS 4
+#define MSM_CHROMA_444 0x0
+#define MSM_CHROMA_422 0x1
+#define MSM_CHROMA_420 0x2
+
+enum msm_display_compression_type {
+ MSM_DISPLAY_COMPRESSION_NONE,
+ MSM_DISPLAY_COMPRESSION_DSC,
+};
+
+struct msm_display_dsc_info {
+ struct drm_dsc_config drm_dsc;
+ u8 scr_rev;
+
+ int initial_lines;
+ int pkt_per_line;
+ int bytes_in_slice;
+ int bytes_per_pkt;
+ int eol_byte_num;
+ int pclk_per_line;
+ int slice_last_group_size;
+ int slice_per_pkt;
+ int num_active_ss_per_enc;
+ int source_color_space;
+ int chroma_format;
+ int det_thresh_flatness;
+ u32 extra_width;
+};
+
+#define to_msm_dsc_info(dsc) container_of((dsc), struct msm_display_dsc_info, drm_dsc)
+
+#define DSC_BPP(drm_dsc) ((drm_dsc).bits_per_pixel >> 4)
+
+struct msm_compression_info {
+ enum msm_display_compression_type comp_type;
+ u32 comp_ratio;
+ u32 src_bpp;
+ u32 tgt_bpp;
+ struct msm_display_dsc_info msm_dsc_info;
+};
+
+struct msm_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 msm_drm_private {
struct drm_device *dev;
@@ -304,7 +365,8 @@ 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);
bool msm_dsi_wide_bus_enabled(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);
+bool msm_dsi_dsc_enabled_for_mode(struct msm_dsi *msm_dsi, const struct drm_display_mode *adj_mode);
const char *msm_dsi_get_te_source(struct msm_dsi *msm_dsi);
#else
static inline void __init msm_dsi_register(void)
@@ -339,11 +401,16 @@ static inline bool msm_dsi_wide_bus_enabled(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;
}
+static inline bool msm_dsi_dsc_enabled_for_mode(struct msm_dsi *msm_dsi, const struct drm_display_mode *adj_mode)
+{
+ return false;
+}
+
static inline const char *msm_dsi_get_te_source(struct msm_dsi *msm_dsi)
{
return NULL;
@@ -362,7 +429,8 @@ bool msm_dp_is_yuv_420_enabled(const struct msm_dp *dp_display,
bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display,
const struct drm_display_mode *mode);
bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
-
+struct msm_compression_info *msm_dp_get_dsc_config(struct msm_dp *msm_dp);
+bool msm_dp_dsc_enabled_for_mode(struct msm_dp *msm_dp, const struct drm_display_mode *adj_mode);
#else
static inline int __init msm_dp_register(void)
{
@@ -400,6 +468,15 @@ static inline bool msm_dp_wide_bus_available(const struct msm_dp *dp_display)
return false;
}
+static inline struct msm_compression_info *msm_dp_get_dsc_config(struct msm_dp *msm_dp)
+{
+ return NULL;
+}
+
+static inline bool msm_dp_dsc_enabled_for_mode(struct msm_dp *msm_dp, const struct drm_display_mode *adj_mode)
+{
+ return false;
+}
#endif
#ifdef CONFIG_DRM_MSM_MDP4
diff --git a/drivers/gpu/drm/msm/msm_dsc_helper.h b/drivers/gpu/drm/msm/msm_dsc_helper.h
index 63f95523b2cb..844d6d649af2 100644
--- a/drivers/gpu/drm/msm/msm_dsc_helper.h
+++ b/drivers/gpu/drm/msm/msm_dsc_helper.h
@@ -11,6 +11,7 @@
#include <linux/math.h>
#include <drm/display/drm_dsc_helper.h>
+#include "dpu_dsc_helper.h"
/**
* msm_dsc_get_bytes_per_line() - calculate bytes per line
@xzn
Copy link
Copy Markdown
Author

xzn commented May 6, 2026

Note:

Bugs remain:

  • Available DP modes enum fails sometimes (try disabling usb autosuspend maybe will alleviate this)
  • DSC doesn't work on low res (currently worked around by disabling DSC on low res) Fixed in rev 2

Fixed:

  • Resource cleanup when switching modes

Ported toward v7.0.2 kernel

Later kernel versions have changes from https://gitlab.freedesktop.org/drm/msm and will need adjustment.

Edit:

Rev3:
Less aggressive in handing out layer mixers, fixes resource starvation when connecting to many displays.

Rev4, Rev5, Rev6, Rev7, and Rev8:
Minor fix/cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment