drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding
authorAnkit Nautiyal <ankit.k.nautiyal@intel.com>
Fri, 18 Dec 2020 10:37:22 +0000 (16:07 +0530)
committerJani Nikula <jani.nikula@intel.com>
Tue, 22 Dec 2020 15:57:58 +0000 (17:57 +0200)
When a source supporting DSC1.1 is connected to DSC1.2 HDMI2.1 sink
via DP HDMI2.1 PCON, the PCON can be configured to decode the
DSC1.1 compressed stream and encode to DSC1.2. It then sends the
DSC1.2 compressed stream to the HDMI2.1 sink.

This patch configures the PCON for DSC1.1 to DSC1.2 encoding, based
on the PCON's DSC encoder capablities and HDMI2.1 sink's DSC decoder
capabilities.

v2: Addressed review comments from Uma Shankar:
-fixed the error in packing pps parameter values
-added check for pcon in the pcon related function
-appended display in commit message

v3: Only consider non-zero DSC FRL b/w for determining max FRL b/w
supported by sink.

Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
Reviewed-by: Uma Shankar <uma.shankar@intel.com>
[Jani: Fixed checkpatch BRACES, LINE_SPACING, PARENTHESIS_ALIGNMENT.]
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201218103723.30844-15-ankit.k.nautiyal@intel.com
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_dp.h

index fd39d3966914f8efc0c3a27a2757a491e3ef62c1..d1f742ac52b7da0b6b3a2a3443147d3e0f944ddd 100644 (file)
@@ -3652,6 +3652,7 @@ static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state,
        intel_dp_sink_set_fec_ready(intel_dp, crtc_state);
 
        intel_dp_check_frl_training(intel_dp);
+       intel_dp_pcon_dsc_configure(intel_dp, crtc_state);
 
        /*
         * 7.i Follow DisplayPort specification training sequence (see notes for
index 0ff2481bb0376f1c2aabb0d38e032e640ddfbef8..ccc4d21c8b900b8f2d8aaa4e4713b01b357da2e8 100644 (file)
@@ -4030,9 +4030,22 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp)
 {
        struct intel_connector *intel_connector = intel_dp->attached_connector;
        struct drm_connector *connector = &intel_connector->base;
+       int max_frl_rate;
+       int max_lanes, rate_per_lane;
+       int max_dsc_lanes, dsc_rate_per_lane;
 
-       return (connector->display_info.hdmi.max_frl_rate_per_lane *
-               connector->display_info.hdmi.max_lanes);
+       max_lanes = connector->display_info.hdmi.max_lanes;
+       rate_per_lane = connector->display_info.hdmi.max_frl_rate_per_lane;
+       max_frl_rate = max_lanes * rate_per_lane;
+
+       if (connector->display_info.hdmi.dsc_cap.v_1p2) {
+               max_dsc_lanes = connector->display_info.hdmi.dsc_cap.max_lanes;
+               dsc_rate_per_lane = connector->display_info.hdmi.dsc_cap.max_frl_rate_per_lane;
+               if (max_dsc_lanes && dsc_rate_per_lane)
+                       max_frl_rate = min(max_frl_rate, max_dsc_lanes * dsc_rate_per_lane);
+       }
+
+       return max_frl_rate;
 }
 
 static int intel_dp_pcon_start_frl_training(struct intel_dp *intel_dp)
@@ -4140,6 +4153,103 @@ void intel_dp_check_frl_training(struct intel_dp *intel_dp)
        }
 }
 
+static int
+intel_dp_pcon_dsc_enc_slice_height(const struct intel_crtc_state *crtc_state)
+{
+       int vactive = crtc_state->hw.adjusted_mode.vdisplay;
+
+       return intel_hdmi_dsc_get_slice_height(vactive);
+}
+
+static int
+intel_dp_pcon_dsc_enc_slices(struct intel_dp *intel_dp,
+                            const struct intel_crtc_state *crtc_state)
+{
+       struct intel_connector *intel_connector = intel_dp->attached_connector;
+       struct drm_connector *connector = &intel_connector->base;
+       int hdmi_throughput = connector->display_info.hdmi.dsc_cap.clk_per_slice;
+       int hdmi_max_slices = connector->display_info.hdmi.dsc_cap.max_slices;
+       int pcon_max_slices = drm_dp_pcon_dsc_max_slices(intel_dp->pcon_dsc_dpcd);
+       int pcon_max_slice_width = drm_dp_pcon_dsc_max_slice_width(intel_dp->pcon_dsc_dpcd);
+
+       return intel_hdmi_dsc_get_num_slices(crtc_state, pcon_max_slices,
+                                            pcon_max_slice_width,
+                                            hdmi_max_slices, hdmi_throughput);
+}
+
+static int
+intel_dp_pcon_dsc_enc_bpp(struct intel_dp *intel_dp,
+                         const struct intel_crtc_state *crtc_state,
+                         int num_slices, int slice_width)
+{
+       struct intel_connector *intel_connector = intel_dp->attached_connector;
+       struct drm_connector *connector = &intel_connector->base;
+       int output_format = crtc_state->output_format;
+       bool hdmi_all_bpp = connector->display_info.hdmi.dsc_cap.all_bpp;
+       int pcon_fractional_bpp = drm_dp_pcon_dsc_bpp_incr(intel_dp->pcon_dsc_dpcd);
+       int hdmi_max_chunk_bytes =
+               connector->display_info.hdmi.dsc_cap.total_chunk_kbytes * 1024;
+
+       return intel_hdmi_dsc_get_bpp(pcon_fractional_bpp, slice_width,
+                                     num_slices, output_format, hdmi_all_bpp,
+                                     hdmi_max_chunk_bytes);
+}
+
+void
+intel_dp_pcon_dsc_configure(struct intel_dp *intel_dp,
+                           const struct intel_crtc_state *crtc_state)
+{
+       u8 pps_param[6];
+       int slice_height;
+       int slice_width;
+       int num_slices;
+       int bits_per_pixel;
+       int ret;
+       struct intel_connector *intel_connector = intel_dp->attached_connector;
+       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+       struct drm_connector *connector;
+       bool hdmi_is_dsc_1_2;
+
+       if (!intel_dp_is_hdmi_2_1_sink(intel_dp))
+               return;
+
+       if (!intel_connector)
+               return;
+       connector = &intel_connector->base;
+       hdmi_is_dsc_1_2 = connector->display_info.hdmi.dsc_cap.v_1p2;
+
+       if (!drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) ||
+           !hdmi_is_dsc_1_2)
+               return;
+
+       slice_height = intel_dp_pcon_dsc_enc_slice_height(crtc_state);
+       if (!slice_height)
+               return;
+
+       num_slices = intel_dp_pcon_dsc_enc_slices(intel_dp, crtc_state);
+       if (!num_slices)
+               return;
+
+       slice_width = DIV_ROUND_UP(crtc_state->hw.adjusted_mode.hdisplay,
+                                  num_slices);
+
+       bits_per_pixel = intel_dp_pcon_dsc_enc_bpp(intel_dp, crtc_state,
+                                                  num_slices, slice_width);
+       if (!bits_per_pixel)
+               return;
+
+       pps_param[0] = slice_height & 0xFF;
+       pps_param[1] = slice_height >> 8;
+       pps_param[2] = slice_width & 0xFF;
+       pps_param[3] = slice_width >> 8;
+       pps_param[4] = bits_per_pixel & 0xFF;
+       pps_param[5] = (bits_per_pixel >> 8) & 0x3;
+
+       ret = drm_dp_pcon_pps_override_param(&intel_dp->aux, pps_param);
+       if (ret < 0)
+               drm_dbg_kms(&i915->drm, "Failed to set pcon DSC\n");
+}
+
 static void
 g4x_set_link_train(struct intel_dp *intel_dp,
                   const struct intel_crtc_state *crtc_state,
@@ -4271,6 +4381,7 @@ static void intel_enable_dp(struct intel_atomic_state *state,
        intel_dp_set_power(intel_dp, DP_SET_POWER_D0);
        intel_dp_configure_protocol_converter(intel_dp);
        intel_dp_check_frl_training(intel_dp);
+       intel_dp_pcon_dsc_configure(intel_dp, pipe_config);
        intel_dp_start_link_train(intel_dp, pipe_config);
        intel_dp_stop_link_train(intel_dp, pipe_config);
 
@@ -6215,6 +6326,7 @@ int intel_dp_retrain_link(struct intel_encoder *encoder,
                        continue;
 
                intel_dp_check_frl_training(intel_dp);
+               intel_dp_pcon_dsc_configure(intel_dp, crtc_state);
                intel_dp_start_link_train(intel_dp, crtc_state);
                intel_dp_stop_link_train(intel_dp, crtc_state);
                break;
index 58b76a20459c55477f7131be753a916e047a2656..1bfde4f89019f1324786b2f25fd3d311d8fa459f 100644 (file)
@@ -145,5 +145,7 @@ void intel_dp_sync_state(struct intel_encoder *encoder,
                         const struct intel_crtc_state *crtc_state);
 
 void intel_dp_check_frl_training(struct intel_dp *intel_dp);
+void intel_dp_pcon_dsc_configure(struct intel_dp *intel_dp,
+                                const struct intel_crtc_state *crtc_state);
 
 #endif /* __INTEL_DP_H__ */