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

Reply via email to