ASoC: hdac_hdmi: Register chmap controls and ops
authorSubhransu S. Prusty <subhransu.s.prusty@intel.com>
Thu, 14 Apr 2016 04:37:34 +0000 (10:07 +0530)
committerMark Brown <broonie@kernel.org>
Thu, 28 Apr 2016 17:49:24 +0000 (18:49 +0100)
With this patch, chmap controls are created and user space can
set the channel map.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/hdac_hdmi.c

index 034593bf2cd6fcb2f65099d1bc2e85c7df0b26ad..0ed39753c871e83cefac22cef6ef7f6e4abb939a 100644 (file)
@@ -114,6 +114,19 @@ struct hdac_hdmi_priv {
        struct hdac_chmap chmap;
 };
 
+static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
+                                               int pcm_idx)
+{
+       struct hdac_hdmi_pcm *pcm;
+
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->pcm_id == pcm_idx)
+                       return pcm;
+       }
+
+       return NULL;
+}
+
 static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 {
        struct hdac_device *hdac = dev_to_hdac_dev(dev);
@@ -664,6 +677,8 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
                        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
 
                mutex_lock(&dai_map->pin->lock);
+               dai_map->pin->chmap_set = false;
+               memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap));
                dai_map->pin->channels = 0;
                mutex_unlock(&dai_map->pin->lock);
 
@@ -1386,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = {
        .pin_eld_notify = hdac_hdmi_eld_notify_cb,
 };
 
+static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
+                                               int device)
+{
+       struct snd_soc_pcm_runtime *rtd;
+
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (rtd->pcm && (rtd->pcm->device == device))
+                       return rtd->pcm;
+       }
+
+       return NULL;
+}
+
 int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
 {
        char jack_name[NAME_SIZE];
@@ -1395,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
                snd_soc_component_get_dapm(&codec->component);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm;
+       struct snd_pcm *snd_pcm;
+       int err;
 
        /*
         * this is a new PCM device, create new pcm and
@@ -1406,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
        pcm->pcm_id = device;
        pcm->cvt = hdmi->dai_map[dai->id].cvt;
 
+       snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
+       if (snd_pcm) {
+               err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
+               if (err < 0) {
+                       dev_err(&edev->hdac.dev,
+                               "chmap control add failed with err: %d for pcm: %d\n",
+                               err, device);
+                       kfree(pcm);
+                       return err;
+               }
+       }
+
        list_add_tail(&pcm->head, &hdmi->pcm_list);
 
        sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
@@ -1524,6 +1566,60 @@ static struct snd_soc_codec_driver hdmi_hda_codec = {
        .idle_bias_off  = true,
 };
 
+static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap)
+{
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+
+       /* chmap is already set to 0 in caller */
+       if (!pin)
+               return;
+
+       memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap));
+}
+
+static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+                               unsigned char *chmap, int prepared)
+{
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+
+       mutex_lock(&pin->lock);
+       pin->chmap_set = true;
+       memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap));
+       if (prepared)
+               hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid);
+       mutex_unlock(&pin->lock);
+}
+
+static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+
+       return pin ? true:false;
+}
+
+static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
+{
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+
+       if (!pin && !pin->eld.eld_valid)
+               return 0;
+
+       return pin->eld.info.spk_alloc;
+}
+
 static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 {
        struct hdac_device *codec = &edev->hdac;
@@ -1538,6 +1634,10 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 
        edev->private_data = hdmi_priv;
        snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap);
+       hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
+       hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
+       hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
+       hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
 
        dev_set_drvdata(&codec->dev, edev);