Calculate the audio bw requirements and check the supported sad
audio frequencies are feasible with selected pipe configuration.
If not feasible, prune the audio frequencies from sad list.

v2: clarity in variable names, use #define for constants (Kai)
    Squash DP2.0 check as part of this path (Kai)
    Keep passing intel_encoder for compute config calls
    Better clarity in debug statements
    Added DP2.0 in patch subject

Bspec: 67768
Signed-off-by: Vinod Govindapillai <vinod.govindapil...@intel.com>
Reviewed-by: Kai Vehmanen <kai.vehma...@linux.intel.com>
---
 drivers/gpu/drm/i915/display/intel_audio.c | 111 +++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_audio.h |   4 +
 drivers/gpu/drm/i915/display/intel_dp.c    |  67 ++++++++++++-
 3 files changed, 180 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_audio.c 
b/drivers/gpu/drm/i915/display/intel_audio.c
index e8c2cbe34523..b4eef6d60ca1 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.c
+++ b/drivers/gpu/drm/i915/display/intel_audio.c
@@ -705,6 +705,117 @@ static bool intel_audio_eld_valid(struct intel_encoder 
*encoder,
        return true;
 }
 
+static bool
+intel_audio_frequency_feasible(int line_freq_khz,
+                              int hblank_slots_lanes_bytes,
+                              int avail_overhead, int req_overhead,
+                              int channels, int aud_frequency)
+{
+       int aud_samples_per_line =
+               DIV_ROUND_UP(aud_frequency, line_freq_khz) + 1;
+       int lines_per_audio_sample =
+               max(1, line_freq_khz / aud_frequency);
+       int hblank_bytes_available =
+               (hblank_slots_lanes_bytes - avail_overhead) * 
lines_per_audio_sample;
+       int hblank_bytes_required;
+
+       if (channels > 2)
+               hblank_bytes_required =
+                       DIV_ROUND_UP(aud_samples_per_line * 10 + 2, 4) * 16 + 
req_overhead;
+       else
+               hblank_bytes_required =
+                       (DIV_ROUND_UP(DIV_ROUND_UP(aud_samples_per_line, 2) * 5 
+ 2, 4) + 2) * 16 + req_overhead;
+
+       return hblank_bytes_available > hblank_bytes_required;
+}
+
+static u8
+intel_audio_get_pruned_audfreq(int line_freq_khz,
+                              int hblank_slots_lanes_bytes,
+                              int avail_overhead, int req_overhead,
+                              int channels, u8 in_sad_freq)
+{
+       const unsigned int freq_list_khz[] = { 32, 44, 48, 88, 96, 176, 192 };
+       u8 pruned_sad_freq = in_sad_freq;
+
+       for (int j = ARRAY_SIZE(freq_list_khz) - 1; j >= 0; j--) {
+               int freq = pruned_sad_freq & BIT(j) ? freq_list_khz[j] : 0;
+
+               if (!freq)
+                       continue;
+
+               /* If "freq" is ok, then values below are also ok */
+               if (intel_audio_frequency_feasible(line_freq_khz,
+                                                  hblank_slots_lanes_bytes,
+                                                  avail_overhead,
+                                                  req_overhead,
+                                                  channels, freq))
+                       break;
+
+               /* "freq" not feasible! Prune it from the list */
+               pruned_sad_freq &= ~BIT(j);
+       }
+
+       return pruned_sad_freq;
+}
+
+static void intel_audio_compute_sad(struct intel_encoder *encoder,
+                                   int line_freq_khz,
+                                   int hblank_slots_lanes_bytes,
+                                   int avail_overhead, int req_overhead,
+                                   struct cea_sad *sad)
+{
+       u8 sad_channels = sad->channels + 1;
+       u8 sad_freq;
+
+       sad_freq = intel_audio_get_pruned_audfreq(line_freq_khz,
+                                                 hblank_slots_lanes_bytes,
+                                                 avail_overhead,
+                                                 req_overhead, sad_channels,
+                                                 sad->freq);
+
+       sad->freq = sad_freq;
+}
+
+bool intel_audio_compute_eld_config(struct intel_encoder *encoder,
+                                   struct drm_connector_state *conn_state,
+                                   int line_freq_khz,
+                                   int hblank_slots_lanes_bytes,
+                                   int avail_overhead, int req_overhead)
+{
+       struct intel_display *display = to_intel_display(encoder);
+       struct intel_connector *connector = 
to_intel_connector(conn_state->connector);
+       u8 *eld;
+
+       if (!intel_audio_eld_valid(encoder, conn_state))
+               return false;
+
+       eld = connector->base.eld;
+       for (int i = 0; i < drm_eld_sad_count(eld); i++) {
+               struct cea_sad sad;
+               u8 sad_freq;
+
+               if (drm_eld_sad_get(eld, i, &sad))
+                       continue;
+
+               sad_freq = sad.freq;
+               intel_audio_compute_sad(encoder, line_freq_khz,
+                                       hblank_slots_lanes_bytes,
+                                       avail_overhead, req_overhead, &sad);
+
+               /* Update the eld with new sad data if any changes in the list 
*/
+               if (sad_freq != sad.freq) {
+                       drm_eld_sad_set(eld, i, &sad);
+                       drm_dbg_kms(display->drm,
+                                   "[CONNECTOR:%d:%s] SAD updated. Freq: 
0x%x(0x%x)\n",
+                                   connector->base.base.id, 
connector->base.name,
+                                   sad.freq, sad_freq);
+               }
+       }
+
+       return true;
+}
+
 bool intel_audio_compute_config(struct intel_encoder *encoder,
                                struct intel_crtc_state *crtc_state,
                                struct drm_connector_state *conn_state)
diff --git a/drivers/gpu/drm/i915/display/intel_audio.h 
b/drivers/gpu/drm/i915/display/intel_audio.h
index 1bafc155434a..8e851beba46c 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.h
+++ b/drivers/gpu/drm/i915/display/intel_audio.h
@@ -32,5 +32,9 @@ void intel_audio_init(struct drm_i915_private *dev_priv);
 void intel_audio_register(struct drm_i915_private *i915);
 void intel_audio_deinit(struct drm_i915_private *dev_priv);
 void intel_audio_sdp_split_update(const struct intel_crtc_state *crtc_state);
+bool intel_audio_compute_eld_config(struct intel_encoder *encoder,
+                                   struct drm_connector_state *conn_state,
+                                   int line_freq_khz, int hblank_slots_lanes,
+                                   int avail_overhead, int req_overhead);
 
 #endif /* __INTEL_AUDIO_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c 
b/drivers/gpu/drm/i915/display/intel_dp.c
index 888bcc549ddb..efcba6e5c452 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -106,6 +106,10 @@
 /* DP DSC FEC Overhead factor in ppm = 1/(0.972261) = 1.028530 */
 #define DP_DSC_FEC_OVERHEAD_FACTOR             1028530
 
+/* DP Audio bw params calculations. Bspec: 67768 */
+#define DP_AUDIO_BW_HBLANK_OVERHEAD_AVAIL      64
+#define DP_AUDIO_BW_HBLANK_OVERHEAD_REQ                80
+
 /* Constants for DP DSC configurations */
 static const u8 valid_dsc_bpp[] = {6, 8, 10, 12, 15};
 
@@ -3033,13 +3037,72 @@ intel_dp_compute_output_format(struct intel_encoder 
*encoder,
        return ret;
 }
 
+static void
+intel_dp_compute_audio_bwparams(struct intel_crtc_state *crtc_state,
+                               int *line_freq_khz,
+                               int *hblank_slots_lanes_bytes)
+{
+       /* Calculation steps based on Bspec: 67768 */
+       struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+       int link_rate_mhz = DIV_ROUND_UP(crtc_state->port_clock, 1000);
+       int pixel_clk_mhz = DIV_ROUND_UP(adjusted_mode->crtc_clock, 1000);
+       int htotal = adjusted_mode->crtc_htotal;
+       int hblank_pixels =
+               adjusted_mode->crtc_hblank_end - 
adjusted_mode->crtc_hblank_start;
+       int mtp_clks_per_slot = DIV_ROUND_UP(4, crtc_state->lane_count);
+       int mtp_size_clks = 64 * mtp_clks_per_slot;
+       int link_clk_mhz = DIV_ROUND_UP(link_rate_mhz, 32);
+       int mtp_size_ns = DIV_ROUND_UP(mtp_size_clks * 1000, link_clk_mhz);
+       int hblank_size_ns = DIV_ROUND_UP(hblank_pixels * 1000, pixel_clk_mhz);
+       int mtps_in_hblank = DIV_ROUND_UP(hblank_size_ns, mtp_size_ns);
+       u32 temp = div_u64(mul_u32_u32(mtp_size_clks, 
crtc_state->dp_m_n.data_m),
+                               crtc_state->dp_m_n.data_n);
+       int hblank_slots = mtps_in_hblank * temp;
+
+       *line_freq_khz = DIV_ROUND_UP(pixel_clk_mhz, htotal) * 1000;
+       *hblank_slots_lanes_bytes = hblank_slots * crtc_state->lane_count * 4;
+}
+
+static bool
+intel_dp_audio_compute_bw_limits(struct intel_encoder *encoder,
+                                struct intel_crtc_state *crtc_state,
+                                struct drm_connector_state *conn_state)
+{
+       struct intel_display *display = to_intel_display(encoder);
+       struct intel_connector *connector = 
to_intel_connector(conn_state->connector);
+       int hblank_bytes_avail_overhead = DP_AUDIO_BW_HBLANK_OVERHEAD_AVAIL;
+       int hblank_bytes_req_overhead = DP_AUDIO_BW_HBLANK_OVERHEAD_REQ;
+       int hblank_slots_lanes_bytes;
+       int line_freq_khz;
+
+       intel_dp_compute_audio_bwparams(crtc_state, &line_freq_khz,
+                                       &hblank_slots_lanes_bytes);
+       drm_dbg_kms(display->drm,
+                   "[CONNECTOR:%d:%s][ENCODER:%d:%s] Bw limits params: 
line_freq: %d khz hblank_slots: %d bytes\n",
+                   connector->base.base.id, connector->base.name,
+                   encoder->base.base.id, encoder->base.name,
+                   line_freq_khz, hblank_slots_lanes_bytes);
+
+       return intel_audio_compute_eld_config(encoder, conn_state,
+                                             line_freq_khz,
+                                             hblank_slots_lanes_bytes,
+                                             hblank_bytes_avail_overhead,
+                                             hblank_bytes_req_overhead);
+}
+
 void
 intel_dp_audio_compute_config(struct intel_encoder *encoder,
                              struct intel_crtc_state *pipe_config,
                              struct drm_connector_state *conn_state)
 {
-       pipe_config->has_audio =
-               intel_dp_has_audio(encoder, conn_state) &&
+       pipe_config->has_audio = intel_dp_has_audio(encoder, conn_state);
+
+       if (intel_dp_is_uhbr(pipe_config))
+               pipe_config->has_audio = pipe_config->has_audio &&
+                       intel_dp_audio_compute_bw_limits(encoder, pipe_config,
+                                                        conn_state);
+
+       pipe_config->has_audio = pipe_config->has_audio &&
                intel_audio_compute_config(encoder, pipe_config, conn_state);
 
        pipe_config->sdp_split_enable = pipe_config->has_audio &&
-- 
2.34.1

Reply via email to