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.

Bspec: 67768
Signed-off-by: Vinod Govindapillai <vinod.govindapil...@intel.com>
---
 drivers/gpu/drm/i915/display/intel_audio.c | 107 +++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_audio.h |   3 +
 drivers/gpu/drm/i915/display/intel_dp.c    |  50 ++++++++++
 3 files changed, 160 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_audio.c 
b/drivers/gpu/drm/i915/display/intel_audio.c
index 6946f3b0f8c9..e84101ef9531 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.c
+++ b/drivers/gpu/drm/i915/display/intel_audio.c
@@ -705,6 +705,113 @@ static bool intel_audio_eld_valid(struct 
drm_connector_state *conn_state)
        return true;
 }
 
+static bool
+intel_audio_frequency_feasible(int line_freq_khz, int hblank_slots_lanes,
+                              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 - 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(struct drm_i915_private *i915,
+                              int line_freq_khz, int hblank_slots_lanes,
+                              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,
+                                                  avail_overhead,
+                                                  req_overhead,
+                                                  channels, freq))
+                       break;
+
+               /* "freq" not feasible! Prune it from the list */
+               pruned_sad_freq &= ~BIT(j);
+               drm_dbg_kms(&i915->drm,
+                           "Frequency[%d]: %d channels: %d not feasible\n",
+                           j, freq, channels);
+       }
+
+       return pruned_sad_freq;
+}
+
+static void intel_audio_compute_sad(struct drm_i915_private *i915,
+                                   int line_freq_khz, int hblank_slots_lanes,
+                                   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(i915, line_freq_khz,
+                                                 hblank_slots_lanes,
+                                                 avail_overhead,
+                                                 req_overhead, sad_channels,
+                                                 sad->freq);
+
+       sad->freq = sad_freq;
+}
+
+bool intel_audio_compute_eld_config(struct drm_connector_state *conn_state,
+                                   int line_freq_khz, int hblank_slots_lanes,
+                                   int avail_overhead, int req_overhead)
+{
+       struct drm_connector *connector = conn_state->connector;
+       struct drm_i915_private *i915 = to_i915(connector->dev);
+       u8 *eld;
+
+       if (!intel_audio_eld_valid(conn_state))
+               return false;
+
+       eld = connector->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(i915, line_freq_khz,
+                                       hblank_slots_lanes,
+                                       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(&i915->drm, "sad updated. Pruned freq list: 
0x%x\n", sad.freq);
+               }
+       }
+
+       return true;
+}
+
 bool intel_audio_compute_config(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 9b327b677d89..f8574fc30973 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.h
+++ b/drivers/gpu/drm/i915/display/intel_audio.h
@@ -30,5 +30,8 @@ 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 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 6347258b4a49..dde7244ccd15 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -2994,6 +2994,53 @@ 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)
+{
+       /* 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 = hblank_slots * crtc_state->lane_count * 4;
+}
+
+static bool
+intel_dp_audio_compute_bw_limits(struct intel_crtc_state *crtc_state,
+                                struct drm_connector_state *conn_state)
+{
+       struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+       int hblank_bytes_avail_overhead = 64;
+       int hblank_bytes_req_overhead = 80;
+       int hblank_slots_lanes;
+       int line_freq_khz;
+
+       intel_dp_compute_audio_bwparams(crtc_state, &line_freq_khz,
+                                       &hblank_slots_lanes);
+       drm_dbg_kms(&i915->drm,
+                   "bw params: line_freq_khz: %d hblank_slots_lanes: %d\n",
+                   line_freq_khz, hblank_slots_lanes);
+
+       return intel_audio_compute_eld_config(conn_state, line_freq_khz,
+                                             hblank_slots_lanes,
+                                             hblank_bytes_avail_overhead,
+                                             hblank_bytes_req_overhead);
+}
+
 void
 intel_dp_audio_compute_config(struct intel_encoder *encoder,
                              struct intel_crtc_state *pipe_config,
@@ -3001,6 +3048,9 @@ intel_dp_audio_compute_config(struct intel_encoder 
*encoder,
 {
        pipe_config->has_audio = intel_dp_has_audio(encoder, conn_state);
 
+       pipe_config->has_audio = pipe_config->has_audio &&
+               intel_dp_audio_compute_bw_limits(pipe_config, conn_state);
+
        pipe_config->has_audio = pipe_config->has_audio &&
                intel_audio_compute_config(pipe_config, conn_state);
 
-- 
2.34.1

Reply via email to