[alsa-devel] [RFC PATCH v2 0/6] adapt SOF to use snd-hda-codec-hdmi
Hi all, here's a second RFC round for this series that adapts SOF (and one example machine driver) to use snd-hda-codec-hdmi (patch_hdmi.c) codec driver instead of hdac_hdmi (soc/codecs/hdac_hdmi.c). The primary goal is to unify the HDMI codec implementation between DSP and non-DSP HDA configurations, offer same interface to user-space and reduce maintenance load for all.
If the approach looks ok, I'll remove RFC from v3 and update the remaining machine drivers (this v2 still changes only one machine driver and thus the RFC tag).
v2 changes: - Codec selection via module parameter. Concerns were raised that a Kconfig option will not be sufficient as the patch changes user-space mixer controls. Distro kernels will want to use same kernel config for all platforms and a kconfig control would apply to all drivers.
To address this, a combination of a module parameter, and a default value set via kconfig, was added to the patchset. This allows distros to support different combinations with a single kernel build. To switch codecs for a specific machine, UCM file update can be deployed together with a change to modprobe parameter. This is somewhat complicated, so further feedback is very welcome whether this is enough.
- In machine drivers that are shared with SST and SOF, added runtime detection for which codec is used. For SST, the old hdac-hdmi is always used.
- One patch dropped from series. This patch is no longer needed (pcm handles now passed to codec driver).
Feature and testing info:
- Tested on multiple Intel platforms supported by SOF. - Tested with ALSA console tools as well as with Pulseaudio. - requires Pulseaudio 12.x or newer, see https://lists.freedesktop.org/archives/pulseaudio-discuss/2019-August/031358... - HDMI, DP, DP-MST with multi-monitor use-scenarios work ok. - New feature for SOF: ELD /proc fs works just like in DSP-less mode. - New feature for SOF: jack detection works out-of-the-box with Pulseaudio (no need for card specific UCM for HDMI) - Pre-reviews at: https://github.com/thesofproject/linux/pull/1155
Kai Vehmanen (6): ALSA: hda - add mst_no_extra_pcms flag to hda_codec ASoC: Intel: skl-hda-dsp-generic: use snd-hda-codec-hdmi ASoC: hdac_hda: add support for HDMI/DP as a HDA codec ALSA: hda/hdmi - implement mst_no_extra_pcms flag ALSA: hda/hdmi - allow control creation without a linked pcm ASoC: SOF: Intel: add support for snd-hda-codec-hdmi
include/sound/hda_codec.h | 1 + sound/pci/hda/patch_hdmi.c | 23 +++-- sound/soc/codecs/hdac_hda.c | 95 +++++++++++++++++--- sound/soc/codecs/hdac_hda.h | 12 ++- sound/soc/intel/boards/skl_hda_dsp_common.c | 28 +++++- sound/soc/intel/boards/skl_hda_dsp_common.h | 69 ++++++++++++++ sound/soc/intel/boards/skl_hda_dsp_generic.c | 7 -- sound/soc/sof/intel/Kconfig | 10 +++ sound/soc/sof/intel/hda-codec.c | 14 ++- sound/soc/sof/intel/hda.h | 6 +- 10 files changed, 231 insertions(+), 34 deletions(-)
Add a flag to hda_codec struct to control whether legacy PCM numbering should be followed in DP-MST mode. When set, no backup PCMs will be created and PCM count is limited to number of converters.
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- include/sound/hda_codec.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 9a0393cf024c..ac18f428eda6 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -254,6 +254,7 @@ struct hda_codec { unsigned int force_pin_prefix:1; /* Add location prefix */ unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ + unsigned int mst_no_extra_pcms:1; /* no backup PCMs for DP-MST */
#ifdef CONFIG_PM unsigned long power_on_acct;
On Fri, 06 Sep 2019 16:29:04 +0200, Kai Vehmanen wrote:
Add a flag to hda_codec struct to control whether legacy PCM numbering should be followed in DP-MST mode. When set, no backup PCMs will be created and PCM count is limited to number of converters.
Let's combine this and patch 4, the actual change in HDMI codec driver with the flag. It'll make the meaning of the flag clearer.
thanks,
Takashi
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com
include/sound/hda_codec.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 9a0393cf024c..ac18f428eda6 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -254,6 +254,7 @@ struct hda_codec { unsigned int force_pin_prefix:1; /* Add location prefix */ unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */
- unsigned int mst_no_extra_pcms:1; /* no backup PCMs for DP-MST */
#ifdef CONFIG_PM unsigned long power_on_acct; -- 2.17.1
Add support for using snd-hda-codec-hdmi driver for HDMI/DP instead of ASoC hdac-hdmi. This is aligned with how other HDA codecs are already handled.
When snd-hda-codec-hdmi is used, the PCM device numbers are parsed from card topology and passed to the codec driver. This needs to be done at runtime as topology changes may affect PCM device allocation.
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/intel/boards/skl_hda_dsp_common.c | 26 +++++++- sound/soc/intel/boards/skl_hda_dsp_common.h | 69 ++++++++++++++++++++ sound/soc/intel/boards/skl_hda_dsp_generic.c | 7 -- 3 files changed, 94 insertions(+), 8 deletions(-)
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index 55fd82e05e2c..23784178015b 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -14,6 +14,9 @@ #include "../../codecs/hdac_hdmi.h" #include "skl_hda_dsp_common.h"
+#include <sound/hda_codec.h> +#include "../../codecs/hdac_hda.h" + #define NAME_SIZE 32
int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device) @@ -133,17 +136,38 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card) struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_component *component = NULL; struct skl_hda_hdmi_pcm *pcm; + const char *modname; + int use_common_codec = 0; char jack_name[NAME_SIZE]; int err;
+ /* + * Detect whether this machine driver is used with SOF and + * alter behaviour based on which HDMI codec driver has + * been selected. + */ + if (!strncmp(card->name, "sof-skl_hda_card", 16)) { + for_each_card_components(card, component) { + modname = module_name(component->dev->driver->owner); + if (!strncmp(component->name, "ehdaudio0D2", 11) && + !strncmp(modname, "snd_hda_codec_hdmi", 18)) + use_common_codec = 1; + } + } + + if (use_common_codec) + return skl_hda_hdmi_build_controls(card); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + if (!pcm) + continue; + component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, &pcm->hdmi_jack, NULL, 0); - if (err) return err;
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h index daa582e513b2..5438631be565 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.h +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -14,6 +14,8 @@ #include <linux/platform_device.h> #include <sound/core.h> #include <sound/jack.h> +#include <sound/hda_codec.h> +#include "../../codecs/hdac_hda.h"
#define HDA_DSP_MAX_BE_DAI_LINKS 7
@@ -35,4 +37,71 @@ extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; int skl_hda_hdmi_jack_init(struct snd_soc_card *card); int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device);
+/* + * Search card topology and return PCM device number + * matching Nth HDMI device (zero-based index). + */ +static inline struct snd_pcm *skl_hda_hdmi_pcm_handle(struct snd_soc_card *card, + int hdmi_idx) +{ + struct snd_soc_pcm_runtime *rtd; + int i = 0; + struct snd_pcm *spcm; + + for_each_card_rtds(card, rtd) { + spcm = rtd->pcm ? + rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].pcm : 0; + if (spcm && strstr(spcm->id, "HDMI")) { + if (i == hdmi_idx) + return rtd->pcm; + ++i; + } + } + + return 0; +} + +/* + * Search card topology and register HDMI PCM related controls + * to codec driver. + */ +static inline int skl_hda_hdmi_build_controls(struct snd_soc_card *card) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + struct hdac_hda_priv *hda_pvt; + struct skl_hda_hdmi_pcm *pcm; + struct hda_codec *hcodec; + struct snd_pcm *spcm; + struct hda_pcm *hpcm; + int err = 0, i = 0; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct skl_hda_hdmi_pcm, + head); + component = pcm->codec_dai->component; + if (!component) + return -EINVAL; + + hda_pvt = snd_soc_component_get_drvdata(component); + hcodec = &hda_pvt->codec; + + list_for_each_entry(hpcm, &hcodec->pcm_list_head, list) { + spcm = skl_hda_hdmi_pcm_handle(card, i); + if (spcm) { + hpcm->pcm = spcm; + hpcm->device = spcm->device; + dev_dbg(card->dev, + "%s: mapping HDMI converter %d to PCM %d (%p)\n", + __func__, i, hpcm->device, spcm); + } + i++; + } + + err = snd_hda_codec_build_controls(hcodec); + if (err < 0) + dev_err(card->dev, "unable to create controls %d\n", err); + + return err; +} + #endif /* __SOUND_SOC_HDA_DSP_COMMON_H */ diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 9ed68eb4f058..9b6f8cc87f99 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -26,13 +26,6 @@ static const struct snd_soc_dapm_widget skl_hda_widgets[] = { };
static const struct snd_soc_dapm_route skl_hda_map[] = { - { "hifi3", NULL, "iDisp3 Tx"}, - { "iDisp3 Tx", NULL, "iDisp3_out"}, - { "hifi2", NULL, "iDisp2 Tx"}, - { "iDisp2 Tx", NULL, "iDisp2_out"}, - { "hifi1", NULL, "iDisp1 Tx"}, - { "iDisp1 Tx", NULL, "iDisp1_out"}, - { "Analog Out", NULL, "Codec Output Pin1" }, { "Digital Out", NULL, "Codec Output Pin2" }, { "Alt Analog Out", NULL, "Codec Output Pin3" },
On 9/6/19 9:29 AM, Kai Vehmanen wrote:
Add support for using snd-hda-codec-hdmi driver for HDMI/DP instead of ASoC hdac-hdmi. This is aligned with how other HDA codecs are already handled.
When snd-hda-codec-hdmi is used, the PCM device numbers are parsed from card topology and passed to the codec driver. This needs to be done at runtime as topology changes may affect PCM device allocation.
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com
sound/soc/intel/boards/skl_hda_dsp_common.c | 26 +++++++- sound/soc/intel/boards/skl_hda_dsp_common.h | 69 ++++++++++++++++++++ sound/soc/intel/boards/skl_hda_dsp_generic.c | 7 -- 3 files changed, 94 insertions(+), 8 deletions(-)
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index 55fd82e05e2c..23784178015b 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -14,6 +14,9 @@ #include "../../codecs/hdac_hdmi.h" #include "skl_hda_dsp_common.h"
+#include <sound/hda_codec.h> +#include "../../codecs/hdac_hda.h"
#define NAME_SIZE 32
int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device)
@@ -133,17 +136,38 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card) struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_component *component = NULL; struct skl_hda_hdmi_pcm *pcm;
const char *modname;
int use_common_codec = 0; char jack_name[NAME_SIZE]; int err;
/*
* Detect whether this machine driver is used with SOF and
* alter behaviour based on which HDMI codec driver has
* been selected.
*/
if (!strncmp(card->name, "sof-skl_hda_card", 16)) {
for_each_card_components(card, component) {
modname = module_name(component->dev->driver->owner);
if (!strncmp(component->name, "ehdaudio0D2", 11) &&
!strncmp(modname, "snd_hda_codec_hdmi", 18))
use_common_codec = 1;
}
}
yuk. I am not a big fan of this...
It seems that we could pass information from the SOF side to the machine driver using the mach_params argument. we already pass the codec_mask and other fields, it wouldn't be too hard to reclaim a field or extend the structure to pass the information.
- if (use_common_codec)
return skl_hda_hdmi_build_controls(card);
- list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
if (!pcm)
continue;
- component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, &pcm->hdmi_jack, NULL, 0);
- if (err) return err;
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h index daa582e513b2..5438631be565 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.h +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -14,6 +14,8 @@ #include <linux/platform_device.h> #include <sound/core.h> #include <sound/jack.h> +#include <sound/hda_codec.h> +#include "../../codecs/hdac_hda.h"
#define HDA_DSP_MAX_BE_DAI_LINKS 7
@@ -35,4 +37,71 @@ extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; int skl_hda_hdmi_jack_init(struct snd_soc_card *card); int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device);
+/*
- Search card topology and return PCM device number
- matching Nth HDMI device (zero-based index).
- */
+static inline struct snd_pcm *skl_hda_hdmi_pcm_handle(struct snd_soc_card *card,
int hdmi_idx)
+{
- struct snd_soc_pcm_runtime *rtd;
- int i = 0;
- struct snd_pcm *spcm;
- for_each_card_rtds(card, rtd) {
spcm = rtd->pcm ?
rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].pcm : 0;
if (spcm && strstr(spcm->id, "HDMI")) {
if (i == hdmi_idx)
return rtd->pcm;
++i;
}
- }
- return 0;
+}
+/*
- Search card topology and register HDMI PCM related controls
- to codec driver.
- */
+static inline int skl_hda_hdmi_build_controls(struct snd_soc_card *card) +{
- struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card);
- struct snd_soc_component *component;
- struct hdac_hda_priv *hda_pvt;
- struct skl_hda_hdmi_pcm *pcm;
- struct hda_codec *hcodec;
- struct snd_pcm *spcm;
- struct hda_pcm *hpcm;
- int err = 0, i = 0;
- pcm = list_first_entry(&ctx->hdmi_pcm_list, struct skl_hda_hdmi_pcm,
head);
- component = pcm->codec_dai->component;
- if (!component)
return -EINVAL;
- hda_pvt = snd_soc_component_get_drvdata(component);
- hcodec = &hda_pvt->codec;
- list_for_each_entry(hpcm, &hcodec->pcm_list_head, list) {
spcm = skl_hda_hdmi_pcm_handle(card, i);
if (spcm) {
hpcm->pcm = spcm;
hpcm->device = spcm->device;
dev_dbg(card->dev,
"%s: mapping HDMI converter %d to PCM %d (%p)\n",
__func__, i, hpcm->device, spcm);
}
i++;
- }
- err = snd_hda_codec_build_controls(hcodec);
- if (err < 0)
dev_err(card->dev, "unable to create controls %d\n", err);
- return err;
+}
- #endif /* __SOUND_SOC_HDA_DSP_COMMON_H */
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 9ed68eb4f058..9b6f8cc87f99 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -26,13 +26,6 @@ static const struct snd_soc_dapm_widget skl_hda_widgets[] = { };
static const struct snd_soc_dapm_route skl_hda_map[] = {
- { "hifi3", NULL, "iDisp3 Tx"},
- { "iDisp3 Tx", NULL, "iDisp3_out"},
- { "hifi2", NULL, "iDisp2 Tx"},
- { "iDisp2 Tx", NULL, "iDisp2_out"},
- { "hifi1", NULL, "iDisp1 Tx"},
- { "iDisp1 Tx", NULL, "iDisp1_out"},
- { "Analog Out", NULL, "Codec Output Pin1" }, { "Digital Out", NULL, "Codec Output Pin2" }, { "Alt Analog Out", NULL, "Codec Output Pin3" },
Hey,
On Fri, 6 Sep 2019, Pierre-Louis Bossart wrote:
- if (!strncmp(card->name, "sof-skl_hda_card", 16)) {
for_each_card_components(card, component) {
modname = module_name(component->dev->driver->owner);
if (!strncmp(component->name, "ehdaudio0D2", 11) &&
!strncmp(modname, "snd_hda_codec_hdmi", 18))
use_common_codec = 1;
}
- }
yuk. I am not a big fan of this...
It seems that we could pass information from the SOF side to the machine driver using the mach_params argument. we already pass the codec_mask and other fields, it wouldn't be too hard to reclaim a field or extend the structure to pass the information.
it is ugly, no question about it. :) My reasoning for this was to contain the ugliness within the machine drivers (especially these that are used with both SOF and SST). New machine drivers with no legacy to maintain could just skip it.
If the general concept of compiling both codec drivers in, and selecting at runtime, is acceptable, I'll check how this would look via mach_params.
Br, Kai
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 7d4940256914..1c950e089e39 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 = "HDMI 1 Playback", + .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 = "HDMI 2 Playback", + .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 = "HDMI 3 Playback", + .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) \
When mst_no_exxtra_pcms flag is set, the codec should not use backup PCMs to handle DP-MST scenarios. Instead a simple 1:1 mapping is assumed between PCMs and converters.
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/pci/hda/patch_hdmi.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bca5de78e9ad..59aaee4a40fd 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2072,15 +2072,24 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int idx; + int idx, pcm_num;
/* * for non-mst mode, pcm number is the same as before - * for DP MST mode, pcm number is (nid number + dev_num - 1) - * dev_num is the device entry number in a pin - * + * for DP MST mode without extra PCM, pcm number is same + * for DP MST mode with extra PCMs, pcm number is + * (nid number + dev_num - 1) + * dev_num is the device entry number in a pin */ - for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) { + + if (codec->mst_no_extra_pcms) + pcm_num = spec->num_nids; + else + pcm_num = spec->num_nids + spec->dev_num - 1; + + codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num); + + for (idx = 0; idx < pcm_num; idx++) { struct hda_pcm *info; struct hda_pcm_stream *pstr;
On 9/6/19 9:29 AM, Kai Vehmanen wrote:
When mst_no_exxtra_pcms flag is set, the codec should not
typo: extra
use backup PCMs to handle DP-MST scenarios. Instead a simple 1:1 mapping is assumed between PCMs and converters.
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com
sound/pci/hda/patch_hdmi.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bca5de78e9ad..59aaee4a40fd 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2072,15 +2072,24 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec;
- int idx;
int idx, pcm_num;
/*
- for non-mst mode, pcm number is the same as before
* for DP MST mode, pcm number is (nid number + dev_num - 1)
* dev_num is the device entry number in a pin
*
* for DP MST mode without extra PCM, pcm number is same
* for DP MST mode with extra PCMs, pcm number is
* (nid number + dev_num - 1)
*/* dev_num is the device entry number in a pin
- for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
- if (codec->mst_no_extra_pcms)
pcm_num = spec->num_nids;
- else
pcm_num = spec->num_nids + spec->dev_num - 1;
- codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
- for (idx = 0; idx < pcm_num; idx++) { struct hda_pcm *info; struct hda_pcm_stream *pstr;
Fix the logic in generic_hdmi_build_controls() to identify unused hda_pcm entries by searching for SNDRV_PCM_INVALID_DEVICE. This matches with logic in snd_hda_codec_build_pcms().
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/pci/hda/patch_hdmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 59aaee4a40fd..c52726e19f44 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2183,11 +2183,13 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) static int generic_hdmi_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; + struct hda_pcm *hda_pcm; int dev, err; int pin_idx, pcm_idx;
for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - if (!get_pcm_rec(spec, pcm_idx)->pcm) { + hda_pcm = get_pcm_rec(spec, pcm_idx); + if (hda_pcm->device == SNDRV_PCM_INVALID_DEVICE) { /* no PCM: mark this for skipping permanently */ set_bit(pcm_idx, &spec->pcm_bitmap); continue;
Add support to implement HDMI/DP audio by using the common snd-hda-codec-hdmi driver.
Change of codec driver affects user-space as the the two drivers expose different mixer controls. A new kernel module option "use_common_hdmi" is added to user-space to indicate which interface should be used. The default driver can be selected via a Kconfig option.
Signed-off-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/intel/Kconfig | 10 ++++++++++ sound/soc/sof/intel/hda-codec.c | 14 ++++++++++---- sound/soc/sof/intel/hda.h | 6 ++++-- 3 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index dd14ce92fe10..1cf292eb59a7 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -241,6 +241,16 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC Say Y if you want to enable HDAudio codecs with SOF. If unsure select "N".
+config SND_SOC_SOF_HDA_COMMON_HDMI_CODEC + bool "SOF common HDA HDMI codec driver" + depends on SND_SOC_SOF_HDA_LINK + depends on SND_HDA_CODEC_HDMI + help + This adds support for enabling HDMI audio by using the common + HDMI/DisplayPort codec driver. + Say Y if you want to use the common codec driver with SOF. + If unsure select "Y". + endif ## SND_SOC_SOF_HDA_COMMON
config SND_SOC_SOF_HDA_LINK_BASELINE diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index b8b37f082309..571614049f10 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -19,6 +19,11 @@ #include "../../codecs/hdac_hda.h" #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
+static bool hda_codec_use_common_hdmi = + IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_COMMON_HDMI_CODEC); +module_param_named(use_common_hdmi, hda_codec_use_common_hdmi, bool, 0444); +MODULE_PARM_DESC(use_common_hdmi, "SOF HDA use common HDMI codec driver"); + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) #define IDISP_VID_INTEL 0x80860000
@@ -74,8 +79,8 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) if (ret < 0) return ret;
- /* use legacy bus only for HDA codecs, idisp uses ext bus */ - if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) { + if (hda_codec_use_common_hdmi || + (resp & 0xFFFF0000) != IDISP_VID_INTEL) { hdev->type = HDA_DEV_LEGACY; hda_codec_load_module(&hda_priv->codec); } @@ -117,7 +122,8 @@ int hda_codec_probe_bus(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(hda_codec_probe_bus);
-#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \ + IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)
void hda_codec_i915_get(struct snd_sof_dev *sdev) { @@ -166,6 +172,6 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(hda_codec_i915_exit);
-#endif /* CONFIG_SND_SOC_HDAC_HDMI */ +#endif
MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 75b096050fa2..d2d4a04c087c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -559,7 +559,9 @@ int hda_codec_probe_bus(struct snd_sof_dev *sdev);
#endif /* CONFIG_SND_SOC_SOF_HDA */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && \ + (IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \ + IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
void hda_codec_i915_get(struct snd_sof_dev *sdev); void hda_codec_i915_put(struct snd_sof_dev *sdev); @@ -573,7 +575,7 @@ static inline void hda_codec_i915_put(struct snd_sof_dev *sdev) { } static inline int hda_codec_i915_init(struct snd_sof_dev *sdev) { return 0; } static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
-#endif /* CONFIG_SND_SOC_SOF_HDA && CONFIG_SND_SOC_HDAC_HDMI */ +#endif
/* * Trace Control.
participants (3)
-
Kai Vehmanen
-
Pierre-Louis Bossart
-
Takashi Iwai