[PATCH 33/34] imx-drm: dw-hdmi-audio: parse ELD from HDMI driver
Russell King
rmk+kernel at arm.linux.org.uk
Tue Feb 18 15:12:25 EST 2014
Parse the ELD (EDID like data) stored from the HDMI driver to restrict
the sample rates and channels which are available to ALSA. This causes
the ALSA device to reflect the capabilities of the overall audio path,
not just what is supported at the HDMI source interface level.
Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
drivers/staging/imx-drm/dw-hdmi-audio.c | 51 +++++++++++++++++++++++++++++++++
drivers/staging/imx-drm/imx-hdmi.c | 8 ++++++
drivers/staging/imx-drm/imx-hdmi.h | 1 +
3 files changed, 60 insertions(+)
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index a17714dfa86d..7bff8c95beec 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -273,6 +273,56 @@ static struct snd_pcm_hardware dw_hdmi_hw = {
.fifo_size = 0,
};
+static unsigned rates_mask[] = {
+ SNDRV_PCM_RATE_32000,
+ SNDRV_PCM_RATE_44100,
+ SNDRV_PCM_RATE_48000,
+ SNDRV_PCM_RATE_88200,
+ SNDRV_PCM_RATE_96000,
+ SNDRV_PCM_RATE_176400,
+ SNDRV_PCM_RATE_192000,
+};
+
+static void dw_hdmi_parse_eld(struct snd_dw_hdmi *dw,
+ struct snd_pcm_runtime *runtime)
+{
+ uint8_t *sad, *eld = imx_hdmi_get_eld(dw->hdmi);
+ unsigned eld_ver, mnl, sad_count, rates, rate_mask, i;
+ unsigned max_channels;
+
+ eld_ver = eld[0] >> 3;
+ if (eld_ver != 2 && eld_ver != 31)
+ return;
+
+ mnl = eld[4] & 0x1f;
+ if (mnl > 16)
+ return;
+
+ sad_count = eld[5] >> 4;
+ sad = eld + 20 + mnl;
+
+ /* Start from the basic audio settings */
+ max_channels = 2;
+ rates = 7;
+ while (sad_count > 0) {
+ switch (sad[0] & 0x78) {
+ case 0x08: /* PCM */
+ max_channels = max(max_channels, (sad[0] & 7) + 1u);
+ rates |= sad[1];
+ break;
+ }
+ sad += 3;
+ sad_count -= 1;
+ }
+
+ for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++)
+ if (rates & 1 << i)
+ rate_mask |= rates_mask[i];
+
+ runtime->hw.rates &= rate_mask;
+ runtime->hw.channels_max = min(runtime->hw.channels_max, max_channels);
+}
+
static int dw_hdmi_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -301,6 +351,7 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
dw->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
runtime->hw = dw_hdmi_hw;
+ dw_hdmi_parse_eld(dw, runtime);
snd_pcm_limit_hw_rates(runtime);
return 0;
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 5755368f7454..df20f94c33e2 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -371,6 +371,12 @@ void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate)
}
EXPORT_SYMBOL(imx_hdmi_set_sample_rate);
+uint8_t *imx_hdmi_get_eld(struct imx_hdmi *hdmi)
+{
+ return hdmi->connector.eld;
+}
+EXPORT_SYMBOL(imx_hdmi_get_eld);
+
/*
* this submodule is responsible for the video data synchronization.
* for example, for RGB 4:4:4 input, the data map is defined as
@@ -1402,6 +1408,8 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
+ /* Store the ELD */
+ drm_edid_to_eld(connector, edid);
kfree(edid);
} else {
dev_dbg(hdmi->dev, "failed to get edid\n");
diff --git a/drivers/staging/imx-drm/imx-hdmi.h b/drivers/staging/imx-drm/imx-hdmi.h
index 8029febdbabe..5baaa9cba943 100644
--- a/drivers/staging/imx-drm/imx-hdmi.h
+++ b/drivers/staging/imx-drm/imx-hdmi.h
@@ -1032,5 +1032,6 @@ enum {
struct imx_hdmi;
void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate);
+uint8_t *imx_hdmi_get_eld(struct imx_hdmi *hdmi);
#endif /* __IMX_HDMI_H__ */
--
1.8.3.1
More information about the linux-arm-kernel
mailing list