From: Vandita Kulkarni <[email protected]>

For HDMI FRL, the transcoder Link M/N carries the ratio of the average
tribyte rate (ftb_avg) to the FRL div18 clock.

Compute link_m/link_n from ftb_avg and the required FRL rate, reduced
by their gcd. As PIPE_LINK_M1/N1 are 24-bit wide, scale both down
preserving the ratio until they fit.

Program the Link M/N after FRL link training and along with the DFM
registers, just before enabling the transcoder.

Read it back in the FRL readout for the state checker.

Bspec: 68848
Signed-off-by: Vandita Kulkarni <[email protected]>
Signed-off-by: Ankit Nautiyal <[email protected]>
---
 .../gpu/drm/i915/display/intel_hdmi_frl_dfm.c | 64 +++++++++++++++++++
 1 file changed, 64 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 36ac13b2539b..58fc55cb09e3 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi_frl_dfm.c
@@ -3,6 +3,7 @@
  * Copyright © 2022 Intel Corp
  */
 
+#include <linux/gcd.h>
 #include <linux/kernel.h>
 
 #include <drm/drm_connector.h>
@@ -709,6 +710,28 @@ bool intel_hdmi_frl_dfm_dsc_requirement_met(struct 
intel_hdmi_frl_dfm *frl_dfm)
        return true;
 }
 
+static void frl_set_m_n(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_display *display = to_intel_display(crtc_state);
+       enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+
+       intel_de_write(display, PIPE_LINK_M1(display, cpu_transcoder),
+                      crtc_state->frl.link_m);
+       intel_de_write(display, PIPE_LINK_N1(display, cpu_transcoder),
+                      crtc_state->frl.link_n);
+}
+
+static void frl_get_m_n(struct intel_crtc_state *crtc_state)
+{
+       struct intel_display *display = to_intel_display(crtc_state);
+       enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+
+       crtc_state->frl.link_m = intel_de_read(display, PIPE_LINK_M1(display, 
cpu_transcoder)) &
+                                DATA_LINK_M_N_MASK;
+       crtc_state->frl.link_n = intel_de_read(display, PIPE_LINK_N1(display, 
cpu_transcoder)) &
+                                DATA_LINK_M_N_MASK;
+}
+
 void intel_hdmi_frl_dfm_write(const struct intel_crtc_state *crtc_state)
 {
        struct intel_display *display = to_intel_display(crtc_state);
@@ -719,6 +742,8 @@ void intel_hdmi_frl_dfm_write(const struct intel_crtc_state 
*crtc_state)
        if (!crtc_state->frl.enable)
                return;
 
+       frl_set_m_n(crtc_state);
+
        reg = TRANS_HDMI_FRL_DFMWRCTL(display, cpu_trans);
        val = TB_ACTUAL_OFFSET(crtc_state->frl.tb_actual);
        intel_de_rmw(display, reg, TB_ACTUAL_OFFSET_MASK, val);
@@ -737,6 +762,8 @@ void intel_hdmi_frl_dfm_read(struct intel_crtc_state 
*crtc_state)
        if (!crtc_state->frl.enable)
                return;
 
+       frl_get_m_n(crtc_state);
+
        val = intel_de_read(display, TRANS_HDMI_FRL_DFMWRCTL(display, 
cpu_trans));
        crtc_state->frl.tb_actual =
                REG_FIELD_GET(TB_ACTUAL_OFFSET_MASK, val);
@@ -761,6 +788,38 @@ get_drm_color_format(enum intel_output_format 
output_format)
        }
 }
 
+static void
+compute_frl_mn(struct intel_crtc_state *crtc_state, u32 ftb_avg_k)
+{
+       u64 ftb_avg, div_18_clk, gcd_val;
+       u32 link_m, link_n;
+
+       ftb_avg = ftb_avg_k * 1000;
+       div_18_clk = mult_frac(1000000000, crtc_state->frl.required_rate, 18);
+       gcd_val = gcd(ftb_avg, div_18_clk);
+
+       link_m = DIV_ROUND_UP_ULL(ftb_avg, gcd_val);
+       link_n = DIV_ROUND_UP_ULL(div_18_clk, gcd_val);
+
+       /*
+        * PIPE_LINK_M1/N1 are 24-bit (DATA_LINK_M_N_MASK). Scale both
+        * down preserving the ratio until they fit the register width.
+        *
+        * #TODO check if intel_reduce_m_n_ratio() can be exported.
+        */
+       while (link_m > DATA_LINK_M_N_MASK ||
+              link_n > DATA_LINK_M_N_MASK) {
+               link_m >>= 1;
+               link_n >>= 1;
+       }
+
+       crtc_state->frl.link_m = link_m;
+       crtc_state->frl.link_n = link_n;
+
+       /* Frl div 18 stored in Khz */
+       crtc_state->frl.div18 = DIV_ROUND_UP_ULL(div_18_clk, 1000);
+}
+
 int intel_hdmi_frl_dfm_compute_config(struct intel_encoder *encoder,
                                      struct intel_crtc_state *crtc_state)
 {
@@ -824,5 +883,10 @@ int intel_hdmi_frl_dfm_compute_config(struct intel_encoder 
*encoder,
        else
                crtc_state->frl.tb_threshold_min = 492;
 
+       compute_frl_mn(crtc_state, frl_dfm.params.ftb_avg_k);
+       drm_dbg_kms(display->drm, "FRL Clock: link_m = %dHz, link_n = %dHz, 
div18 = %dKHz\n",
+                   crtc_state->frl.link_m, crtc_state->frl.link_n,
+                   crtc_state->frl.div18);
+
        return 0;
 }
-- 
2.50.1

Reply via email to