diff mbox series

[v3,03/10] ASoC: hdac_hda: add support for HDMI/DP as a HDA codec

Message ID 20190910182916.29693-4-kai.vehmanen@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series adapt SOF to use snd-hda-codec-hdmi | expand

Commit Message

Kai Vehmanen Sept. 10, 2019, 6:29 p.m. UTC
Handle all HDA codecs using same logic, including HDMI/DP.

Call to snd_hda_codec_build_controls() is delayed for HDMI/DP HDA
devices. This is needed to discover the PCM device numbers as
defined in topology.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
---
 sound/soc/codecs/hdac_hda.c | 95 ++++++++++++++++++++++++++++++++-----
 sound/soc/codecs/hdac_hda.h | 12 ++++-
 2 files changed, 94 insertions(+), 13 deletions(-)

Comments

Pierre-Louis Bossart Sept. 10, 2019, 8:36 p.m. UTC | #1
On 9/10/19 1:29 PM, Kai Vehmanen wrote:
> Handle all HDA codecs using same logic, including HDMI/DP.
> 
> Call to snd_hda_codec_build_controls() is delayed for HDMI/DP HDA
> devices. This is needed to discover the PCM device numbers as
> defined in topology.
> 
> Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
> ---
>   sound/soc/codecs/hdac_hda.c | 95 ++++++++++++++++++++++++++++++++-----
>   sound/soc/codecs/hdac_hda.h | 12 ++++-
>   2 files changed, 94 insertions(+), 13 deletions(-)
> 
> diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
> index 91242b6f8ea7..3d4362158b29 100644
> --- a/sound/soc/codecs/hdac_hda.c
> +++ b/sound/soc/codecs/hdac_hda.c
> @@ -16,11 +16,8 @@
>   #include <sound/hdaudio_ext.h>
>   #include <sound/hda_codec.h>
>   #include <sound/hda_register.h>
> -#include "hdac_hda.h"
>   
> -#define HDAC_ANALOG_DAI_ID		0
> -#define HDAC_DIGITAL_DAI_ID		1
> -#define HDAC_ALT_ANALOG_DAI_ID		2
> +#include "hdac_hda.h"
>   
>   #define STUB_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \
>   			SNDRV_PCM_FMTBIT_U8 | \
> @@ -121,7 +118,46 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = {
>   		.formats = STUB_FORMATS,
>   		.sig_bits = 24,
>   	},
> -}
> +},
> +{
> +	.id = HDAC_HDMI_0_DAI_ID,
> +	.name = "intel-hdmi-hifi1",
> +	.ops = &hdac_hda_dai_ops,
> +	.playback = {
> +		.stream_name    = "hifi1",
> +		.channels_min   = 1,
> +		.channels_max   = 16,

IIRC HDMI only deals with 8ch?

> +		.rates          = SNDRV_PCM_RATE_8000_192000,

And frequencies above 32kHz

> +		.formats        = STUB_FORMATS,
> +		.sig_bits = 24,
> +	},
> +},
> +{
> +	.id = HDAC_HDMI_1_DAI_ID,
> +	.name = "intel-hdmi-hifi2",
> +	.ops = &hdac_hda_dai_ops,
> +	.playback = {
> +		.stream_name    = "hifi2",
> +		.channels_min   = 1,
> +		.channels_max   = 16,
> +		.rates          = SNDRV_PCM_RATE_8000_192000,
> +		.formats        = STUB_FORMATS,
> +		.sig_bits = 24,
> +	},
> +},
> +{
> +	.id = HDAC_HDMI_2_DAI_ID,
> +	.name = "intel-hdmi-hifi3",
> +	.ops = &hdac_hda_dai_ops,
> +	.playback = {
> +		.stream_name    = "hifi3",
> +		.channels_min   = 1,
> +		.channels_max   = 16,
> +		.rates          = SNDRV_PCM_RATE_8000_192000,
> +		.formats        = STUB_FORMATS,
> +		.sig_bits = 24,
> +	},
> +},
>   
>   };
>   
> @@ -135,10 +171,11 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
>   
>   	hda_pvt = snd_soc_component_get_drvdata(component);
>   	pcm = &hda_pvt->pcm[dai->id];
> +
>   	if (tx_mask)
> -		pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
> +		pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
>   	else
> -		pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
> +		pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
>   
>   	return 0;
>   }
> @@ -278,6 +315,12 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
>   	struct hda_pcm *cpcm;
>   	const char *pcm_name;
>   
> +	/*
> +	 * map DAI ID to the closest matching PCM name, using the naming
> +	 * scheme used by hda-codec snd_hda_gen_build_pcms() and for
> +	 * HDMI in hda_codec patch_hdmi.c)
> +	 */
> +
>   	switch (dai->id) {
>   	case HDAC_ANALOG_DAI_ID:
>   		pcm_name = "Analog";
> @@ -288,13 +331,22 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
>   	case HDAC_ALT_ANALOG_DAI_ID:
>   		pcm_name = "Alt Analog";
>   		break;
> +	case HDAC_HDMI_0_DAI_ID:
> +		pcm_name = "HDMI 0";
> +		break;
> +	case HDAC_HDMI_1_DAI_ID:
> +		pcm_name = "HDMI 1";
> +		break;
> +	case HDAC_HDMI_2_DAI_ID:
> +		pcm_name = "HDMI 2";
> +		break;
>   	default:
>   		dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
>   		return NULL;
>   	}
>   
>   	list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
> -		if (strpbrk(cpcm->name, pcm_name))
> +		if (strstr(cpcm->name, pcm_name))
>   			return cpcm;
>   	}
>   
> @@ -302,6 +354,18 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
>   	return NULL;
>   }
>   
> +static bool is_hdmi_codec(struct hda_codec *hcodec)
> +{
> +	struct hda_pcm *cpcm;
> +
> +	list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
> +		if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
>   static int hdac_hda_codec_probe(struct snd_soc_component *component)
>   {
>   	struct hdac_hda_priv *hda_pvt =
> @@ -366,16 +430,23 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
>   		dev_dbg(&hdev->dev, "no patch file found\n");
>   	}
>   
> +	/* configure codec for 1:1 PCM:DAI mapping */
> +	hcodec->mst_no_extra_pcms = 1;
> +
>   	ret = snd_hda_codec_parse_pcms(hcodec);
>   	if (ret < 0) {
>   		dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
>   		goto error;
>   	}
>   
> -	ret = snd_hda_codec_build_controls(hcodec);
> -	if (ret < 0) {
> -		dev_err(&hdev->dev, "unable to create controls %d\n", ret);
> -		goto error;
> +	/* HDMI controls need to be created in machine drivers */
> +	if (!is_hdmi_codec(hcodec)) {
> +		ret = snd_hda_codec_build_controls(hcodec);
> +		if (ret < 0) {
> +			dev_err(&hdev->dev, "unable to create controls %d\n",
> +				ret);
> +			goto error;
> +		}
>   	}
>   
>   	hcodec->core.lazy_cache = true;
> diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
> index 6b1bd4f428e7..5d0979f6f215 100644
> --- a/sound/soc/codecs/hdac_hda.h
> +++ b/sound/soc/codecs/hdac_hda.h
> @@ -6,6 +6,16 @@
>   #ifndef __HDAC_HDA_H__
>   #define __HDAC_HDA_H__
>   
> +enum {
> +	HDAC_ANALOG_DAI_ID = 0,
> +	HDAC_DIGITAL_DAI_ID,
> +	HDAC_ALT_ANALOG_DAI_ID,
> +	HDAC_HDMI_0_DAI_ID,
> +	HDAC_HDMI_1_DAI_ID,
> +	HDAC_HDMI_2_DAI_ID,
> +	HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID,
> +};
> +
>   struct hdac_hda_pcm {
>   	int stream_tag[2];
>   	unsigned int format_val[2];
> @@ -13,7 +23,7 @@ struct hdac_hda_pcm {
>   
>   struct hdac_hda_priv {
>   	struct hda_codec codec;
> -	struct hdac_hda_pcm pcm[2];
> +	struct hdac_hda_pcm pcm[HDAC_LAST_DAI_ID];
>   };
>   
>   #define hdac_to_hda_priv(_hdac) \
>
Kai Vehmanen Sept. 12, 2019, 9:58 a.m. UTC | #2
Hi,

On Tue, 10 Sep 2019, Pierre-Louis Bossart wrote:

> On 9/10/19 1:29 PM, Kai Vehmanen wrote:
> > +	.id = HDAC_HDMI_0_DAI_ID,
> > +	.name = "intel-hdmi-hifi1",
> > +	.ops = &hdac_hda_dai_ops,
> > +	.playback = {
> > +		.stream_name    = "hifi1",
> > +		.channels_min   = 1,
> > +		.channels_max   = 16,
> 
> IIRC HDMI only deals with 8ch?

good catch. I had just copied these from hdac-hda.c. HDMI2.0 actually 
bumped this from 8 to 32 channels.

The constraints remain pretty wide and unsupported configurations will be 
caught at runtime in PCM open in patch_hdmi.c where requested channel 
count is compared to ELD information.

In comparison, the hdac-hdmi does query and add some static constraints 
with snd_hdac_query_supported_pcm(), but e.g. for channels it just sets 
min/max to 2.

> > +		.rates          = SNDRV_PCM_RATE_8000_192000,
> 
> And frequencies above 32kHz

Ack, will fix. I'll probably keep the maximum at 192kHz. HDMI2.0/DP1.4 in 
fact bumped maximum to 1536kHz, but practically that is probably for 
8x192kHz (or 32x48kHz) audio.

Br, Kai
diff mbox series

Patch

diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 91242b6f8ea7..3d4362158b29 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -16,11 +16,8 @@ 
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_codec.h>
 #include <sound/hda_register.h>
-#include "hdac_hda.h"
 
-#define HDAC_ANALOG_DAI_ID		0
-#define HDAC_DIGITAL_DAI_ID		1
-#define HDAC_ALT_ANALOG_DAI_ID		2
+#include "hdac_hda.h"
 
 #define STUB_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \
 			SNDRV_PCM_FMTBIT_U8 | \
@@ -121,7 +118,46 @@  static struct snd_soc_dai_driver hdac_hda_dais[] = {
 		.formats = STUB_FORMATS,
 		.sig_bits = 24,
 	},
-}
+},
+{
+	.id = HDAC_HDMI_0_DAI_ID,
+	.name = "intel-hdmi-hifi1",
+	.ops = &hdac_hda_dai_ops,
+	.playback = {
+		.stream_name    = "hifi1",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates          = SNDRV_PCM_RATE_8000_192000,
+		.formats        = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+},
+{
+	.id = HDAC_HDMI_1_DAI_ID,
+	.name = "intel-hdmi-hifi2",
+	.ops = &hdac_hda_dai_ops,
+	.playback = {
+		.stream_name    = "hifi2",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates          = SNDRV_PCM_RATE_8000_192000,
+		.formats        = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+},
+{
+	.id = HDAC_HDMI_2_DAI_ID,
+	.name = "intel-hdmi-hifi3",
+	.ops = &hdac_hda_dai_ops,
+	.playback = {
+		.stream_name    = "hifi3",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates          = SNDRV_PCM_RATE_8000_192000,
+		.formats        = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+},
 
 };
 
@@ -135,10 +171,11 @@  static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
 
 	hda_pvt = snd_soc_component_get_drvdata(component);
 	pcm = &hda_pvt->pcm[dai->id];
+
 	if (tx_mask)
-		pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+		pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
 	else
-		pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+		pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
 
 	return 0;
 }
@@ -278,6 +315,12 @@  static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
 	struct hda_pcm *cpcm;
 	const char *pcm_name;
 
+	/*
+	 * map DAI ID to the closest matching PCM name, using the naming
+	 * scheme used by hda-codec snd_hda_gen_build_pcms() and for
+	 * HDMI in hda_codec patch_hdmi.c)
+	 */
+
 	switch (dai->id) {
 	case HDAC_ANALOG_DAI_ID:
 		pcm_name = "Analog";
@@ -288,13 +331,22 @@  static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
 	case HDAC_ALT_ANALOG_DAI_ID:
 		pcm_name = "Alt Analog";
 		break;
+	case HDAC_HDMI_0_DAI_ID:
+		pcm_name = "HDMI 0";
+		break;
+	case HDAC_HDMI_1_DAI_ID:
+		pcm_name = "HDMI 1";
+		break;
+	case HDAC_HDMI_2_DAI_ID:
+		pcm_name = "HDMI 2";
+		break;
 	default:
 		dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
 		return NULL;
 	}
 
 	list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
-		if (strpbrk(cpcm->name, pcm_name))
+		if (strstr(cpcm->name, pcm_name))
 			return cpcm;
 	}
 
@@ -302,6 +354,18 @@  static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
 	return NULL;
 }
 
+static bool is_hdmi_codec(struct hda_codec *hcodec)
+{
+	struct hda_pcm *cpcm;
+
+	list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+		if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
+			return true;
+	}
+
+	return false;
+}
+
 static int hdac_hda_codec_probe(struct snd_soc_component *component)
 {
 	struct hdac_hda_priv *hda_pvt =
@@ -366,16 +430,23 @@  static int hdac_hda_codec_probe(struct snd_soc_component *component)
 		dev_dbg(&hdev->dev, "no patch file found\n");
 	}
 
+	/* configure codec for 1:1 PCM:DAI mapping */
+	hcodec->mst_no_extra_pcms = 1;
+
 	ret = snd_hda_codec_parse_pcms(hcodec);
 	if (ret < 0) {
 		dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
 		goto error;
 	}
 
-	ret = snd_hda_codec_build_controls(hcodec);
-	if (ret < 0) {
-		dev_err(&hdev->dev, "unable to create controls %d\n", ret);
-		goto error;
+	/* HDMI controls need to be created in machine drivers */
+	if (!is_hdmi_codec(hcodec)) {
+		ret = snd_hda_codec_build_controls(hcodec);
+		if (ret < 0) {
+			dev_err(&hdev->dev, "unable to create controls %d\n",
+				ret);
+			goto error;
+		}
 	}
 
 	hcodec->core.lazy_cache = true;
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
index 6b1bd4f428e7..5d0979f6f215 100644
--- a/sound/soc/codecs/hdac_hda.h
+++ b/sound/soc/codecs/hdac_hda.h
@@ -6,6 +6,16 @@ 
 #ifndef __HDAC_HDA_H__
 #define __HDAC_HDA_H__
 
+enum {
+	HDAC_ANALOG_DAI_ID = 0,
+	HDAC_DIGITAL_DAI_ID,
+	HDAC_ALT_ANALOG_DAI_ID,
+	HDAC_HDMI_0_DAI_ID,
+	HDAC_HDMI_1_DAI_ID,
+	HDAC_HDMI_2_DAI_ID,
+	HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID,
+};
+
 struct hdac_hda_pcm {
 	int stream_tag[2];
 	unsigned int format_val[2];
@@ -13,7 +23,7 @@  struct hdac_hda_pcm {
 
 struct hdac_hda_priv {
 	struct hda_codec codec;
-	struct hdac_hda_pcm pcm[2];
+	struct hdac_hda_pcm pcm[HDAC_LAST_DAI_ID];
 };
 
 #define hdac_to_hda_priv(_hdac) \