[PATCH 3/3] drm/i915/display: Configure and initialize HDMI audio capabilities

Mitul Golani mitulkumar.ajitkumar.golani at intel.com
Wed Sep 6 15:52:51 UTC 2023


Initialize the source audio capabilities in the `intel_crtc_state`
structure, setting them to their maximum supported values for
`max_channel` and `max_rate`. This initialization enables the
calculation of audio source capabilities concerning the available
mode bandwidth. These capabilities encompass parameters such as
supported rate and channel configurations.

Additionally, introduce a wrapper function for computing
Short Audio Descriptors (SADs). The wrapper function incorporates
logic for determining supported rates and channels according to the
capabilities of the audio source. It returns a set of SADs that are
compatible with the audio source's capabilities.

--v1:
- Refactor `max_channel` and `max_rate` to this commit as they are
being initialized here.
- Remove the call for `intel_audio_compute_eld` to avoid any regression
while merging. Instead, call it in a different commit when it is defined.
- Use `int` instead of `unsigned int` for `max_channel` and
`max_frequency`.
- Update commit message and header.

--v2:
- Use signed instead of unsigned variables.
- Avoid using magic numbers and give them proper names.

--v3:
- Move defines to intel_audio.c.
- Use consistent naming convention for rate and channel.
- Declare `num_of_channel` and `aud_rate` separately.
- Declare the index value outside of the for loop.
- Move Bandwidth calculation to intel_Audio.c as it is common for both
  DP and HDMI. Also use `static`.

--v10:
- Merged patch 2 and 3 to deduplicate function calls.
- Instead of using Calibrate and calculated functions separately,
  removed code duplication and merged functions. [Nikula, Jani]
- Remove magic value for SAD Channel mask. [Nikula, Jani]
- Corrected rate values based on HDMI Spec [Nikula, Jani]
- Update drm function to extract SAD from ELD [Nikula, Jani]

Signed-off-by: Mitul Golani <mitulkumar.ajitkumar.golani at intel.com>
---
 drivers/gpu/drm/i915/display/intel_audio.c    | 126 ++++++++++++++++++
 .../drm/i915/display/intel_display_types.h    |   6 +
 2 files changed, 132 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c
index 94342da8c515..38438ef9c649 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.c
+++ b/drivers/gpu/drm/i915/display/intel_audio.c
@@ -64,6 +64,11 @@
  * struct &i915_audio_component_audio_ops @audio_ops is called from i915 driver.
  */
 
+#define AUDIO_SAMPLE_CONTAINER_SIZE	32
+#define MAX_CHANNEL_COUNT		8
+#define ELD_SAD_CHANNELS_MASK		0x7
+#define NUM_SAMPLE_FREQUENCIES	7
+
 struct intel_audio_funcs {
 	void (*audio_codec_enable)(struct intel_encoder *encoder,
 				   const struct intel_crtc_state *crtc_state,
@@ -770,6 +775,124 @@ void intel_audio_sdp_split_update(const struct intel_crtc_state *crtc_state)
 			     crtc_state->sdp_split_enable ? AUD_ENABLE_SDP_SPLIT : 0);
 }
 
+static int sad_to_channels(const u8 *sad)
+{
+	return 1 + (sad[0] & ELD_SAD_CHANNELS_MASK);
+}
+
+static void calculate_max_rate_and_channel(struct intel_crtc_state *pipe_config,
+					   int channel_count, int *rate,
+					   int available_blank_bandwidth)
+{
+	int index;
+
+	for (index = 0; index < NUM_SAMPLE_FREQUENCIES; index++) {
+		int audio_req_bandwidth = channel_count *
+			rate[index] * AUDIO_SAMPLE_CONTAINER_SIZE;
+
+		if (audio_req_bandwidth < available_blank_bandwidth) {
+			pipe_config->audio.max_rate = rate[index];
+			pipe_config->audio.max_channel_count = channel_count;
+			return;
+		}
+	}
+}
+
+static void calc_and_calibrate_audio_config_params(struct intel_crtc_state *pipe_config,
+						   int channel, int *rate,
+						   bool calibration_required)
+{
+	struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
+	int available_blank_bandwidth, vblank, hblank;
+
+	hblank = adjusted_mode->htotal - adjusted_mode->hdisplay;
+	vblank = adjusted_mode->vtotal - adjusted_mode->vdisplay;
+	available_blank_bandwidth = hblank * vblank *
+			drm_mode_vrefresh(adjusted_mode) * pipe_config->pipe_bpp;
+
+	/*
+	 * Expected calibration of channels and respective rates,
+	 * based on MAX_CHANNEL_COUNT. First calculate channel and
+	 * rate based on maximum that source can compute, later
+	 * with respect to sink's maximum channel capacity, calibrate
+	 * supported rates.
+	 */
+	if (calibration_required) {
+		calculate_max_rate_and_channel(pipe_config, channel,
+					       rate, available_blank_bandwidth);
+	} else {
+		for (int channel_count = channel; channel_count > 0; channel_count--) {
+			calculate_max_rate_and_channel(pipe_config, channel_count,
+						       rate, available_blank_bandwidth);
+
+			// Check if a suitable configuration is found and exit the loop
+			if (pipe_config->audio.max_rate != 0)
+				break;
+		}
+	}
+}
+
+static int get_supported_freq_mask(struct intel_crtc_state *crtc_state, int *rate)
+{
+	int mask = 0, index;
+
+	for (index = NUM_SAMPLE_FREQUENCIES - 1; index >= 0; index--) {
+		if (rate[index] > crtc_state->audio.max_rate)
+			break;
+		mask |= 1 << index;
+	}
+
+	return mask;
+}
+
+static int intel_audio_compute_eld(struct intel_crtc_state *crtc_state)
+{
+	struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+	//Audio Sample Frequency Based on DP 2.1 spec Table 2-112
+	int rate[NUM_SAMPLE_FREQUENCIES] = {192000, 176400, 96000, 88200, 48000, 44100, 32000};
+	u8 *eld, *sad;
+	int index, mask = 0;
+
+	eld = crtc_state->eld;
+	if (!eld)
+		return -EINVAL;
+
+	sad = drm_extract_sad_from_eld(eld);
+	if (!sad)
+		return -ENODATA;
+
+	calc_and_calibrate_audio_config_params(crtc_state, MAX_CHANNEL_COUNT,
+					       rate, false);
+
+	mask = get_supported_freq_mask(crtc_state, rate);
+	for (index = 0; index < drm_eld_sad_count(eld); index++, sad += 3) {
+		/*
+		 * Respect source restricitions. Limit capabilities to a subset that is
+		 * supported both by the source and the sink.
+		 */
+		if (sad_to_channels(sad) >= crtc_state->audio.max_channel_count) {
+			sad[0] &= ~ELD_SAD_CHANNELS_MASK;
+			sad[0] |= crtc_state->audio.max_channel_count - 1;
+			drm_dbg_kms(&i915->drm, "Channel count is limited to %d\n",
+				    crtc_state->audio.max_channel_count - 1);
+		} else {
+			/*
+			 * calibrate rate when, sink supported channel
+			 * count is slight less than max supported
+			 * channel count.
+			 */
+			calc_and_calibrate_audio_config_params(crtc_state,
+							       sad_to_channels(sad), rate,
+							       true);
+			mask = get_supported_freq_mask(crtc_state, rate);
+		}
+
+		sad[1] &= mask;
+	}
+
+	return 0;
+}
+
 bool intel_audio_compute_config(struct intel_encoder *encoder,
 				struct intel_crtc_state *crtc_state,
 				struct drm_connector_state *conn_state)
@@ -791,6 +914,9 @@ bool intel_audio_compute_config(struct intel_encoder *encoder,
 
 	crtc_state->eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2;
 
+	if (intel_audio_compute_eld(crtc_state))
+		return false;
+
 	return true;
 }
 
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 539bca0fdebb..652d5f610188 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1131,6 +1131,12 @@ struct intel_crtc_state {
 
 	struct {
 		bool has_audio;
+
+		/* Audio rate in Hz */
+		int max_rate;
+
+		/* Number of audio channels */
+		int max_channel_count;
 	} audio;
 
 	/*
-- 
2.25.1



More information about the Intel-gfx-trybot mailing list