| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | // |
| 3 | // Copyright(c) 2019 Intel Corporation |
| 4 | |
| 5 | #include <linux/module.h> |
| 6 | #include <sound/pcm.h> |
| 7 | #include <sound/soc.h> |
| 8 | #include <sound/hda_codec.h> |
| 9 | #include <sound/hda_i915.h> |
| 10 | #include "../../codecs/hdac_hda.h" |
| 11 | |
| 12 | #include "hda_dsp_common.h" |
| 13 | |
| 14 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) |
| 15 | |
| 16 | /* |
| 17 | * Search card topology and return PCM device number |
| 18 | * matching Nth playback HDMI device (zero-based index). |
| 19 | */ |
| 20 | static struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, |
| 21 | int hdmi_idx) |
| 22 | { |
| 23 | struct snd_soc_pcm_runtime *rtd; |
| 24 | struct snd_pcm *spcm; |
| 25 | int i = 0; |
| 26 | |
| 27 | for_each_card_rtds(card, rtd) { |
| 28 | /* ignore BE PCMs */ |
| 29 | if (rtd->dai_link && rtd->dai_link->no_pcm) |
| 30 | continue; |
| 31 | |
| 32 | spcm = rtd->pcm; |
| 33 | |
| 34 | /* ignore PCMs with no playback streams */ |
| 35 | if (!spcm || !spcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) |
| 36 | continue; |
| 37 | |
| 38 | /* look for FE PCMs with name "HDMI x" */ |
| 39 | if (spcm && strstr(spcm->id, "HDMI" )) { |
| 40 | if (i == hdmi_idx) |
| 41 | return rtd->pcm; |
| 42 | ++i; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | return NULL; |
| 47 | } |
| 48 | |
| 49 | /* |
| 50 | * Search card topology and register HDMI PCM related controls |
| 51 | * to codec driver. |
| 52 | */ |
| 53 | int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, |
| 54 | struct snd_soc_component *comp) |
| 55 | { |
| 56 | struct hdac_hda_priv *hda_pvt; |
| 57 | struct hda_codec *hcodec; |
| 58 | struct snd_pcm *spcm; |
| 59 | struct hda_pcm *hpcm; |
| 60 | int err = 0, i = 0; |
| 61 | |
| 62 | if (!comp) |
| 63 | return -EINVAL; |
| 64 | |
| 65 | hda_pvt = snd_soc_component_get_drvdata(c: comp); |
| 66 | hcodec = hda_pvt->codec; |
| 67 | |
| 68 | list_for_each_entry(hpcm, &hcodec->pcm_list_head, list) { |
| 69 | spcm = hda_dsp_hdmi_pcm_handle(card, hdmi_idx: i); |
| 70 | if (spcm) { |
| 71 | hpcm->pcm = spcm; |
| 72 | hpcm->device = spcm->device; |
| 73 | dev_dbg(card->dev, |
| 74 | "mapping HDMI converter %d to PCM %d (%p)\n" , |
| 75 | i, hpcm->device, spcm); |
| 76 | } else { |
| 77 | hpcm->pcm = NULL; |
| 78 | hpcm->device = SNDRV_PCM_INVALID_DEVICE; |
| 79 | dev_warn(card->dev, |
| 80 | "%s: no PCM in topology for HDMI converter %d\n" , |
| 81 | __func__, i); |
| 82 | } |
| 83 | i++; |
| 84 | } |
| 85 | snd_hdac_display_power(bus: hcodec->core.bus, |
| 86 | HDA_CODEC_IDX_CONTROLLER, enable: true); |
| 87 | err = snd_hda_codec_build_controls(codec: hcodec); |
| 88 | if (err < 0) |
| 89 | dev_err(card->dev, "unable to create controls %d\n" , err); |
| 90 | snd_hdac_display_power(bus: hcodec->core.bus, |
| 91 | HDA_CODEC_IDX_CONTROLLER, enable: false); |
| 92 | |
| 93 | return err; |
| 94 | } |
| 95 | EXPORT_SYMBOL_NS(hda_dsp_hdmi_build_controls, "SND_SOC_INTEL_HDA_DSP_COMMON" ); |
| 96 | |
| 97 | #endif |
| 98 | |
| 99 | MODULE_DESCRIPTION("ASoC Intel HDMI helpers" ); |
| 100 | MODULE_LICENSE("GPL" ); |
| 101 | |