ALSA: hda - Add DP-MST support for non-acomp codecs
authorNikhil Mahale <nmahale@nvidia.com>
Tue, 19 Nov 2019 08:47:09 +0000 (14:17 +0530)
committerTakashi Iwai <tiwai@suse.de>
Tue, 19 Nov 2019 10:51:10 +0000 (11:51 +0100)
This patch make it possible for non-acomp codecs to set
dyn_pcm_assign/dp_mst and get DP-MST audio support.

Document change notification HDA040-A for the Intel High Definition
Audio 1.0a specification introduces a Device Select verb for Digital
Display Pin Widgets that are multi-stream capable. This verb selects
a Device Entry that is used by subsequent Pin Widget verbs.
Once the Device Entry is selected, all subsequent Pin Widget verbs
controlling the sink device will be directed to the selected Device
Entry until the Device Select verb is updated with a new value.

These Pin Widget verbs include:

  * Connection Select
  * Get Connection List Entry
  * Amplifier Gain/Mute
  * Power State
  * Pin Widget Control
  * ELD Data
  * DIP-Size
  * DIP-Index
  * DIP-Data
  * DIP-XmitCtrl
  * Content Protection Control
  * ASP Channel Mapping

This patch adds calls to snd_hda_set_dev_select() to direct each of
these Pin Widget control verbs to the correct Device Entry.

snd_hda_get_connections() does not return per-device connection
list, therefore make use snd_hda_get_raw_connections() instead of
snd_hda_get_connections().

Signed-off-by: Nikhil Mahale <nmahale@nvidia.com>
Reviewed-by: Aaron Plattner <aplattner@nvidia.com>
Link: https://lore.kernel.org/r/20191119084710.29267-4-nmahale@nvidia.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_hdmi.c

index 37b4345afdde26b9832823c6de9a9f47fbe9cdb0..0a3045d492975fe0643fa8b0c8fa29d9467df30f 100644 (file)
@@ -80,16 +80,19 @@ struct hdmi_spec_per_pin {
 /* operations used by generic code that can be overridden by patches */
 struct hdmi_ops {
        int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
-                          unsigned char *buf, int *eld_size);
+                          int dev_id, unsigned char *buf, int *eld_size);
 
        void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
+                                   int dev_id,
                                    int ca, int active_channels, int conn_type);
 
        /* enable/disable HBR (HD passthrough) */
-       int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr);
+       int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid,
+                            int dev_id, bool hbr);
 
        int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
-                           hda_nid_t pin_nid, u32 stream_tag, int format);
+                           hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+                           int format);
 
        void (*pin_cvt_fixup)(struct hda_codec *codec,
                              struct hdmi_spec_per_pin *per_pin,
@@ -636,8 +639,16 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
        return true;
 }
 
+static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+                           int dev_id, unsigned char *buf, int *eld_size)
+{
+       snd_hda_set_dev_select(codec, nid, dev_id);
+
+       return snd_hdmi_get_eld(codec, nid, buf, eld_size);
+}
+
 static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
-                                    hda_nid_t pin_nid,
+                                    hda_nid_t pin_nid, int dev_id,
                                     int ca, int active_channels,
                                     int conn_type)
 {
@@ -667,6 +678,8 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
                return;
        }
 
+       snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
        /*
         * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
         * sizeof(*dp_ai) to avoid partial match/update problems when
@@ -692,6 +705,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
        struct hdmi_spec *spec = codec->spec;
        struct hdac_chmap *chmap = &spec->chmap;
        hda_nid_t pin_nid = per_pin->pin_nid;
+       int dev_id = per_pin->dev_id;
        int channels = per_pin->channels;
        int active_channels;
        struct hdmi_eld *eld;
@@ -700,6 +714,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
        if (!channels)
                return;
 
+       snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
        /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
        if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
                snd_hda_codec_write(codec, pin_nid, 0,
@@ -725,8 +741,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                pin_nid, non_pcm, ca, channels,
                                per_pin->chmap, per_pin->chmap_set);
 
-       spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
-                                     eld->info.conn_type);
+       spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id,
+                                     ca, active_channels, eld->info.conn_type);
 
        per_pin->non_pcm = non_pcm;
 }
@@ -868,11 +884,12 @@ static void haswell_verify_D0(struct hda_codec *codec,
        ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
 
 static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
-                             bool hbr)
+                             int dev_id, bool hbr)
 {
        int pinctl, new_pinctl;
 
        if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+               snd_hda_set_dev_select(codec, pin_nid, dev_id);
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 
@@ -902,13 +919,15 @@ static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
 }
 
 static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
-                             hda_nid_t pin_nid, u32 stream_tag, int format)
+                             hda_nid_t pin_nid, int dev_id,
+                             u32 stream_tag, int format)
 {
        struct hdmi_spec *spec = codec->spec;
        unsigned int param;
        int err;
 
-       err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
+       err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id,
+                                     is_hbr_format(format));
 
        if (err) {
                codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
@@ -1282,6 +1301,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        hda_nid_t pin_nid = per_pin->pin_nid;
+       int dev_id = per_pin->dev_id;
 
        if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
                codec_warn(codec,
@@ -1290,10 +1310,12 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
                return -EINVAL;
        }
 
+       snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
        /* all the device entries on the same pin have the same conn list */
-       per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
-                                                       per_pin->mux_nids,
-                                                       HDA_MAX_CONNECTIONS);
+       per_pin->num_mux_nids =
+               snd_hda_get_raw_connections(codec, pin_nid, per_pin->mux_nids,
+                                           HDA_MAX_CONNECTIONS);
 
        return 0;
 }
@@ -1501,6 +1523,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld = &spec->temp_eld;
        hda_nid_t pin_nid = per_pin->pin_nid;
+       int dev_id = per_pin->dev_id;
        /*
         * Always execute a GetPinSense verb here, even when called from
         * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
@@ -1513,7 +1536,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
        bool ret;
        bool do_repoll = false;
 
-       present = snd_hda_jack_pin_sense(codec, pin_nid, per_pin->dev_id);
+       present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
 
        mutex_lock(&per_pin->lock);
        eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
@@ -1527,8 +1550,8 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
                codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
 
        if (eld->eld_valid) {
-               if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer,
-                                                    &eld->eld_size) < 0)
+               if (spec->ops.pin_get_eld(codec, pin_nid, dev_id,
+                                         eld->eld_buffer, &eld->eld_size) < 0)
                        eld->eld_valid = false;
                else {
                        if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
@@ -1865,7 +1888,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        struct hdmi_spec *spec = codec->spec;
        int pin_idx;
        struct hdmi_spec_per_pin *per_pin;
-       hda_nid_t pin_nid;
        struct snd_pcm_runtime *runtime = substream->runtime;
        bool non_pcm;
        int pinctl, stripe;
@@ -1889,7 +1911,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                goto unlock;
        }
        per_pin = get_pin(spec, pin_idx);
-       pin_nid = per_pin->pin_nid;
 
        /* Verify pin:cvt selections to avoid silent audio after S3.
         * After S3, the audio driver restores pin:cvt selections
@@ -1904,8 +1925,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
        /* Todo: add DP1.2 MST audio support later */
        if (codec_has_acomp(codec))
-               snd_hdac_sync_audio_rate(&codec->core, pin_nid, per_pin->dev_id,
-                                        runtime->rate);
+               snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+                                        per_pin->dev_id, runtime->rate);
 
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
        mutex_lock(&per_pin->lock);
@@ -1923,16 +1944,18 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
        mutex_unlock(&per_pin->lock);
        if (spec->dyn_pin_out) {
-               pinctl = snd_hda_codec_read(codec, pin_nid, 0,
+               snd_hda_set_dev_select(codec, per_pin->pin_nid,
+                                      per_pin->dev_id);
+               pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               snd_hda_codec_write(codec, pin_nid, 0,
+               snd_hda_codec_write(codec, per_pin->pin_nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
                                    pinctl | PIN_OUT);
        }
 
        /* snd_hda_set_dev_select() has been called before */
-       err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
-                                stream_tag, format);
+       err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid,
+                                    per_pin->dev_id, stream_tag, format);
  unlock:
        mutex_unlock(&spec->pcm_lock);
        return err;
@@ -1984,6 +2007,8 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_pin = get_pin(spec, pin_idx);
 
                if (spec->dyn_pin_out) {
+                       snd_hda_set_dev_select(codec, per_pin->pin_nid,
+                                              per_pin->dev_id);
                        pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
                                        AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                        snd_hda_codec_write(codec, per_pin->pin_nid, 0,
@@ -2370,7 +2395,7 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
 };
 
 static const struct hdmi_ops generic_standard_hdmi_ops = {
-       .pin_get_eld                            = snd_hdmi_get_eld,
+       .pin_get_eld                            = hdmi_pin_get_eld,
        .pin_setup_infoframe                    = hdmi_pin_setup_infoframe,
        .pin_hbr_setup                          = hdmi_pin_hbr_setup,
        .setup_stream                           = hdmi_setup_stream,
@@ -2568,7 +2593,8 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
        hda_nid_t conns[4];
        int nconns;
 
-       nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
+       nconns = snd_hda_get_raw_connections(codec, nid, conns,
+                                            ARRAY_SIZE(conns));
        if (nconns == spec->num_cvts &&
            !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
                return;
@@ -2744,10 +2770,12 @@ static void register_i915_notifier(struct hda_codec *codec)
 
 /* setup_stream ops override for HSW+ */
 static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
-                                hda_nid_t pin_nid, u32 stream_tag, int format)
+                                hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+                                int format)
 {
        haswell_verify_D0(codec, cvt_nid, pin_nid);
-       return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+       return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+                                stream_tag, format);
 }
 
 /* pin_cvt_fixup ops override for HSW+ and VLV+ */
@@ -3713,16 +3741,19 @@ static int patch_tegra_hdmi(struct hda_codec *codec)
 #define ATI_HBR_ENABLE 0x10
 
 static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
-                          unsigned char *buf, int *eld_size)
+                              int dev_id, unsigned char *buf, int *eld_size)
 {
+       WARN_ON(dev_id != 0);
        /* call hda_eld.c ATI/AMD-specific function */
        return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
                                    is_amdhdmi_rev3_or_later(codec));
 }
 
-static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec,
+                                       hda_nid_t pin_nid, int dev_id, int ca,
                                        int active_channels, int conn_type)
 {
+       WARN_ON(dev_id != 0);
        snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
 }
 
@@ -3913,10 +3944,12 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
 }
 
 static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
-                                bool hbr)
+                                int dev_id, bool hbr)
 {
        int hbr_ctl, hbr_ctl_new;
 
+       WARN_ON(dev_id != 0);
+
        hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
        if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
                if (hbr)
@@ -3942,9 +3975,9 @@ static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
 }
 
 static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
-                               hda_nid_t pin_nid, u32 stream_tag, int format)
+                               hda_nid_t pin_nid, int dev_id,
+                               u32 stream_tag, int format)
 {
-
        if (is_amdhdmi_rev3_or_later(codec)) {
                int ramp_rate = 180; /* default as per AMD spec */
                /* disable ramp-up/down for non-pcm as per AMD spec */
@@ -3954,7 +3987,8 @@ static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
                snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
        }
 
-       return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+       return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+                                stream_tag, format);
 }