[PATCH 33/44] drm/i915/hdmi_frl_dfm: Add support for DFM calculation with DSC

Ankit Nautiyal <[email protected]>
Newsgroups org.freedesktop.lists.dri-devel,org.freedesktop.lists.intel-gfx,org.freedesktop.lists.intel-xe
Message-ID <[email protected]>
Add helper functions for calculating FRL capacity and DFM
requirements with given compressed bpp.

v2: Fix misc issues seen while testing (Mitul)
- Utilization reuirement for DSC should return true

Signed-off-by: Ankit Nautiyal <[email protected]>
Signed-off-by: Vandita Kulkarni <[email protected]>
Signed-off-by: Mitul Golani <[email protected]>
---
 .../gpu/drm/i915/display/intel_hdmi_frl_dfm.c | 289 ++++++++++++++++++
 .../gpu/drm/i915/display/intel_hdmi_frl_dfm.h |   3 +
 2 files changed, 292 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.c b/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.c
index 2de9d3dd23ae..56ade6009983 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.c
@@ -413,3 +413,292 @@ intel_hdmi_frl_dfm_nondsc_requirement_met(struct intel_hdmi_frl_dfm *frl_dfm)
 
 	return false;
 }
+
+/* Get required no. of tribytes (estimate1) during HCBlank */
+static u32
+get_frl_hcblank_tb_est1_target(u32 hcactive_target_tb,
+			       u32 hactive, u32 hblank)
+{
+	return DIV_ROUND_UP(hcactive_target_tb * hblank, hactive);
+}
+
+/* Get required no. of tribytes during HCBlank */
+static u32
+get_frl_hcblank_tb_target(u32 hcactive_target_tb, u32 hactive,
+			  u32 hblank, u32 hcblank_audio_min,
+			  u32 cfrl_available)
+{
+	u32 hcblank_target_tb1 = get_frl_hcblank_tb_est1_target(hcactive_target_tb,
+									 hactive, hblank);
+	u32 hcblank_target_tb2 = max(hcblank_target_tb1, hcblank_audio_min);
+
+	return 4 * (min(hcblank_target_tb2,
+			(2 * cfrl_available - 3 * hcactive_target_tb) / 2) / 4);
+}
+
+static u32
+get_dsc_tribyte_time(u32 num_tribyte,
+		     u32 tribyte_rate_k)
+{
+	u32 tribyte_time, time_multiplier;
+
+	/*
+	 * tribyte_rate_k is the num of kilo tribytes in 1 sec, on an average.
+	 *
+	 * time taken for:
+	 * (tribyte_rate_k * 1000) tribytes -> 1 sec
+	 * (tribyte_rate_k * 1000) tribytes -> 10^9 nsec
+	 * 1000 tribytes                    -> (10^9 / tribyte_rate_k) nsec
+	 * 1 tribyte                        -> 10^9 / tribyte_rate_k * 1000)
+	 * 1 tribyte                        -> 10^6 / tribyte_rate_k
+	 * tribyte_time			    -> num_tribyte * 10 ^ 6 / (tribyte_rate_k)
+	 *
+	 * So, avg time for tribyte_time in nsec =
+	 *				num_tribyte * 10 ^ 6 / (tribyte_rate_k)
+	 */
+	time_multiplier = DIV_ROUND_UP(FRL_TIMING_NS_MULTIPLIER, 1000);
+	tribyte_time = num_tribyte * time_multiplier;
+
+	return DIV_ROUND_UP(tribyte_time, tribyte_rate_k);
+}
+
+/* Get time to send all tribytes in hcactive region in nsec*/
+static u32
+get_dsc_tactive_target_ns(u32 frl_lanes, u32 hcactive_target_tb,
+			  u32 ftb_avg_k, u32 min_frl_char_rate_k,
+			  u32 overhead_max)
+{
+	u32 avg_tribyte_time_ns, tribyte_time_ns;
+	u32 num_chars_hcactive;
+	u32 frl_char_rate_k;
+
+	/* Avg time to transmit all active region tribytes */
+	avg_tribyte_time_ns = get_dsc_tribyte_time(hcactive_target_tb, ftb_avg_k);
+
+	/*
+	 * 2 bytes in active region = 1 FRL characters
+	 * 1 Tribyte in active region = 3/2 FRL characters
+	 */
+	num_chars_hcactive = DIV_ROUND_UP(hcactive_target_tb * 3, 2);
+
+	/*
+	 * FRL rate = lanes * frl character rate
+	 * But actual bandwidth wil be less, due to FRL limitations so account
+	 * for the overhead involved.
+	 * FRL rate with overhead = FRL rate * (100 - overhead %) / 100
+	 */
+	frl_char_rate_k = frl_lanes * min_frl_char_rate_k;
+	frl_char_rate_k = DIV_ROUND_UP(frl_char_rate_k * (EFFICIENCY_MULTIPLIER - overhead_max),
+				       EFFICIENCY_MULTIPLIER);
+
+	/* Time to transmit all characters with FRL limitations */
+	tribyte_time_ns	= get_dsc_tribyte_time(num_chars_hcactive, frl_char_rate_k);
+	return max(avg_tribyte_time_ns, tribyte_time_ns);
+}
+
+/* Get TBdelta : borrowing in tribytes relative to avg tribyte rate */
+static u32
+get_dsc_tri_bytes_delta(u32 tactive_target_ns, u32 tblank_target_ns,
+			u32 tactive_ref_ns, u32 tblank_ref_ns,
+			u32 hcactive_target_tb, u32 ftb_avg_k,
+			u32 hactive, u32 hblank,
+			u32 line_time_ns)
+{
+	u32 tb_delta_limit;
+	u32 hcblank_target_tb1 = get_frl_hcblank_tb_est1_target(hcactive_target_tb,
+								    hactive, hblank);
+	u32 tribytes = (hcactive_target_tb + hcblank_target_tb1);
+	u32 tactive_avg_ns;
+
+	if (tblank_ref_ns < tblank_target_ns) {
+		tactive_avg_ns = mult_frac(FRL_TIMING_NS_MULTIPLIER,
+					   hcactive_target_tb, ftb_avg_k * 1000);
+		tb_delta_limit = DIV_ROUND_UP((tactive_ref_ns - tactive_avg_ns) * tribytes,
+					      line_time_ns);
+	} else {
+		u32 t_delta_ns;
+
+		if (tactive_target_ns > tactive_ref_ns)
+			t_delta_ns = tactive_target_ns - tactive_ref_ns;
+		else
+			t_delta_ns = tactive_ref_ns - tactive_target_ns;
+		tb_delta_limit = (t_delta_ns * tribytes) / line_time_ns;
+	}
+
+	return tb_delta_limit;
+}
+
+static u32
+get_dsc_tri_bytes_borrowed(u32 ftb_avg_k,
+			   u32 tactive_target_ns,
+			   u32 hcactive_target)
+{
+	u32 hactive, tb_borrowed;
+
+	hactive = DIV_ROUND_UP(tactive_target_ns * DIV_ROUND_UP(ftb_avg_k, 1000), 1000);
+	tb_borrowed = hactive - hcactive_target;
+
+	return tb_borrowed;
+}
+
+/* Compute hcactive and hcblank tribytes for given dsc bpp setting */
+static void
+compute_dsc_tribytes(struct intel_hdmi_frl_dfm *frl_dfm)
+{
+	u32 hcactive_target_tb;
+	u32 hcblank_target_tb;
+	u32 cfrl_available;
+	u32 num_slices;
+	u32 bytes_target;
+
+	/* Assert for slice width ?*/
+	if (!frl_dfm->config.slice_width)
+		return;
+
+	num_slices = DIV_ROUND_UP(frl_dfm->config.hactive, frl_dfm->config.slice_width);
+
+	/* Get required no. of tribytes during HCActive */
+	bytes_target = num_slices *
+		       DIV_ROUND_UP(frl_dfm->config.target_bpp_16 * frl_dfm->config.slice_width,
+				    8 * BPP_MULTIPLIER);
+
+	hcactive_target_tb = DIV_ROUND_UP(bytes_target, 3);
+
+	/* Get FRL Available characters */
+	cfrl_available = ((EFFICIENCY_MULTIPLIER - frl_dfm->params.overhead_max) *
+			  frl_dfm->params.cfrl_line) / EFFICIENCY_MULTIPLIER;
+
+	hcblank_target_tb =
+		get_frl_hcblank_tb_target(hcactive_target_tb,
+					  frl_dfm->config.hactive,
+					  frl_dfm->config.hblank,
+					  frl_dfm->params.hblank_audio_min,
+					  cfrl_available);
+
+	frl_dfm->params.hcactive_target = hcactive_target_tb;
+	frl_dfm->params.hcblank_target = hcblank_target_tb;
+}
+
+/* Check if audio supported with given dsc bpp and frl bandwidth */
+static bool
+audio_supported_with_dsc(struct intel_hdmi_frl_dfm *frl_dfm)
+{
+	return frl_dfm->params.hcblank_target > frl_dfm->params.hblank_audio_min;
+}
+
+/* Is DFM timing requirement is met with DSC */
+static
+bool timing_req_met_with_dsc(struct intel_hdmi_frl_dfm *frl_dfm)
+{
+	u32 ftb_avg_k;
+	u32 tactive_ref_ns, tblank_ref_ns, tactive_target_ns, tblank_target_ns;
+	u32 tb_borrowed, tb_delta, tb_worst;
+
+	/* Get the avg no of tribytes sent per sec (Kbps) */
+	ftb_avg_k = (frl_dfm->params.hcactive_target + frl_dfm->params.hcblank_target) *
+		    DIV_ROUND_UP(frl_dfm->params.pixel_clock_max_khz,
+				 frl_dfm->config.hactive + frl_dfm->config.hblank);
+
+	/* Time to send Active tribytes in nanoseconds */
+	tactive_ref_ns = DIV_ROUND_UP(frl_dfm->params.line_time_ns * frl_dfm->config.hactive,
+				      frl_dfm->config.hactive + frl_dfm->config.hblank);
+
+	/* Time to send Blanking tribytes in nanoseconds */
+	tblank_ref_ns = DIV_ROUND_UP(frl_dfm->params.line_time_ns * frl_dfm->config.hblank,
+				     frl_dfm->config.hactive + frl_dfm->config.hblank);
+
+	tactive_target_ns = get_dsc_tactive_target_ns(frl_dfm->config.lanes,
+						      frl_dfm->params.hcactive_target,
+						      ftb_avg_k,
+						      frl_dfm->params.char_rate_min_kbps,
+						      frl_dfm->params.overhead_max);
+
+	tblank_target_ns = frl_dfm->params.line_time_ns - tactive_target_ns;
+
+	/* Get no. of tri bytes borrowed with DSC enabled */
+	tb_borrowed = get_dsc_tri_bytes_borrowed(ftb_avg_k,
+						 tactive_target_ns,
+						 frl_dfm->params.hcactive_target);
+
+	tb_delta = get_dsc_tri_bytes_delta(tactive_target_ns,
+					   tblank_target_ns,
+					   tactive_ref_ns,
+					   tblank_ref_ns,
+					   frl_dfm->params.hcactive_target,
+					   ftb_avg_k,
+					   frl_dfm->config.hactive,
+					   frl_dfm->config.hblank,
+					   frl_dfm->params.line_time_ns);
+
+	tb_worst = max(tb_borrowed, tb_delta);
+	if (tb_worst > TB_BORROWED_MAX)
+		return false;
+
+	frl_dfm->params.ftb_avg_k = ftb_avg_k;
+	frl_dfm->params.tb_borrowed = tb_borrowed;
+
+	return true;
+}
+
+/* Check Utilization constraint with DSC */
+static bool
+utilization_constraints_met_with_dsc(struct intel_hdmi_frl_dfm *frl_dfm)
+{
+	u32 hcactive_target_tb = frl_dfm->params.hcactive_target;
+	u32 hcblank_target_tb = frl_dfm->params.hcblank_target;
+	u32 frl_char_per_line = frl_dfm->params.cfrl_line;
+	u32 overhead_max = frl_dfm->params.overhead_max;
+	u32 utilization_with_overhead;
+	u32 actual_frl_char_payload;
+	u32 utilization;
+
+	/*
+	 * Note:
+	 * 1 FRL characters per 2 bytes in active period
+	 * 1 FRL char per byte in Blanking period
+	 */
+	actual_frl_char_payload = DIV_ROUND_UP(3 * hcactive_target_tb, 2) +
+				  hcblank_target_tb;
+
+	utilization = (actual_frl_char_payload * EFFICIENCY_MULTIPLIER) /
+		      frl_char_per_line;
+
+	/*
+	 * Utilization with overhead = utlization% +overhead %
+	 * should be less than 100%
+	 */
+	utilization_with_overhead = utilization + overhead_max;
+	if (utilization_with_overhead  > EFFICIENCY_MULTIPLIER)
+		return false;
+
+	return true;
+}
+
+/*
+ * intel_hdmi_frl_dfm_dsc_requirement_met : Check if FRL DFM requirements are met with
+ * the given bpp.
+ * @frl_dfm: dfm structure
+ *
+ * Returns true if the frl dfm requirements are met, else returns false.
+ */
+bool intel_hdmi_frl_dfm_dsc_requirement_met(struct intel_hdmi_frl_dfm *frl_dfm)
+{
+	if (!frl_dfm->config.slice_width || !frl_dfm->config.target_bpp_16)
+		return false;
+
+	compute_max_frl_link_overhead(frl_dfm);
+	compute_link_characteristics(frl_dfm);
+	compute_audio_hblank_min(frl_dfm);
+	compute_dsc_tribytes(frl_dfm);
+
+	if (!audio_supported_with_dsc(frl_dfm))
+		return false;
+
+	if (!timing_req_met_with_dsc(frl_dfm))
+		return false;
+
+	if (!utilization_constraints_met_with_dsc(frl_dfm))
+		return false;
+
+	return true;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.h b/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.h
index 83d5b0d6370b..70a66679bdbc 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.h
@@ -112,4 +112,7 @@ struct intel_hdmi_frl_dfm {
 
 bool intel_hdmi_frl_dfm_nondsc_requirement_met(struct intel_hdmi_frl_dfm *frl_dfm);
 
+bool
+intel_hdmi_frl_dfm_dsc_requirement_met(struct intel_hdmi_frl_dfm *frl_dfm);
+
 #endif
-- 
2.50.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.