All of lore.kernel.org
 help / color / mirror / Atom feed
From: libin.yang@linux.intel.com
To: alsa-devel@alsa-project.org, tiwai@suse.de
Cc: libin.yang@intel.com, mengdong.lin@intel.com,
	Libin Yang <libin.yang@linux.intel.com>
Subject: [PATCH v4 2/5] ALSA: hda - hdmi playback without monitor in dynamic pcm bind mode
Date: Wed, 16 Dec 2015 13:42:42 +0800	[thread overview]
Message-ID: <1450244565-77041-3-git-send-email-libin.yang@linux.intel.com> (raw)
In-Reply-To: <1450244565-77041-1-git-send-email-libin.yang@linux.intel.com>

From: Libin Yang <libin.yang@linux.intel.com>

Pulseaudio requires open pcm successfully when probing.

This patch handles playback without monitor in dynamic pcm assignment
mode. It tries to open/prepare/close pcm successfully even there is
no pin bound to the PCM. On the meantime, it will try to find a proper
converter for the PCM.

As pcm is This patch introduces a pcm_lock in struct hdmi_spec.
This lock is used to protect:
1. the variables in struct hdmi_spec;
2. other variables shared for dynamic pcm assignment mode
3. device entry selection. As each device entry is represented by
   a separate struct struct hdmi_spec_per_pin, the lock in per_pin
   is not enough. Please see details below.

MST audio device entry operation:
1. select device entry on the pin
2. operate on the pin nid

Signed-off-by: Libin Yang <libin.yang@linux.intel.com>
---
 sound/pci/hda/patch_hdmi.c | 171 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 157 insertions(+), 14 deletions(-)

diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index f762c7d..04450f9 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -137,6 +137,7 @@ struct hdmi_spec {
 	int num_pins;
 	struct snd_array pins; /* struct hdmi_spec_per_pin */
 	struct hda_pcm *pcm_rec[16];
+	struct mutex pcm_lock;
 	unsigned int channels_max; /* max over all cvts */
 
 	struct hdmi_eld temp_eld;
@@ -1339,6 +1340,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
 	return 0;
 }
 
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
 static int hdmi_choose_cvt(struct hda_codec *codec,
 			int pin_idx, int *cvt_id, int *mux_id)
 {
@@ -1347,7 +1353,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 	struct hdmi_spec_per_cvt *per_cvt = NULL;
 	int cvt_idx, mux_idx = 0;
 
-	per_pin = get_pin(spec, pin_idx);
+	/* pin_idx < 0 means no pin will be bound to the converter */
+	if (pin_idx < 0)
+		per_pin = NULL;
+	else
+		per_pin = get_pin(spec, pin_idx);
 
 	/* Dynamically assign converter to stream */
 	for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1356,6 +1366,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 		/* Must not already be assigned */
 		if (per_cvt->assigned)
 			continue;
+		if (per_pin == NULL)
+			break;
 		/* Must be in pin's mux's list of converters */
 		for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
 			if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
@@ -1368,9 +1380,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 
 	/* No free converters */
 	if (cvt_idx == spec->num_cvts)
-		return -ENODEV;
+		return -EBUSY;
 
-	per_pin->mux_idx = mux_idx;
+	if (per_pin != NULL)
+		per_pin->mux_idx = mux_idx;
 
 	if (cvt_id)
 		*cvt_id = cvt_idx;
@@ -1396,6 +1409,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
 					    mux_idx);
 }
 
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+			hda_nid_t cvt_nid)
+{
+	int i;
+
+	for (i = 0; i < spec->num_cvts; i++)
+		if (spec->cvt_nids[i] == cvt_nid)
+			return i;
+	return -EINVAL;
+}
+
 /* Intel HDMI workaround to fix audio routing issue:
  * For some Intel display codecs, pins share the same connection list.
  * So a conveter can be selected by multiple pins and playback on any of these
@@ -1447,6 +1474,69 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
 	}
 }
 
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+			hda_nid_t pin_nid, hda_nid_t cvt_nid)
+{
+	int mux_idx;
+	struct hdmi_spec *spec = codec->spec;
+
+	if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
+		return;
+
+	/* On Intel platform, the mapping of converter nid to
+	 * mux index of the pins are always the same.
+	 * The pin nid may be 0, this means all pins will not
+	 * share the converter.
+	 */
+	mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+	if (mux_idx >= 0)
+		intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM
+ * in dyn_pcm_assign mode.
+ */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+			 struct hda_codec *codec,
+			 struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int cvt_idx;
+	struct hdmi_spec_per_cvt *per_cvt = NULL;
+	int err;
+
+	err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+	if (err)
+		return err;
+
+	per_cvt = get_cvt(spec, cvt_idx);
+	per_cvt->assigned = 1;
+	hinfo->nid = per_cvt->cvt_nid;
+
+	intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+
+	/* todo: setup spdif ctls assign */
+
+	/* Initially set the converter's capabilities */
+	hinfo->channels_min = per_cvt->channels_min;
+	hinfo->channels_max = per_cvt->channels_max;
+	hinfo->rates = per_cvt->rates;
+	hinfo->formats = per_cvt->formats;
+	hinfo->maxbps = per_cvt->maxbps;
+
+	/* Store the updated parameters */
+	runtime->hw.channels_min = hinfo->channels_min;
+	runtime->hw.channels_max = hinfo->channels_max;
+	runtime->hw.formats = hinfo->formats;
+	runtime->hw.rates = hinfo->rates;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+	return 0;
+}
+
 /*
  * HDA PCM callbacks
  */
@@ -1463,19 +1553,36 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 	int err;
 
 	/* Validate hinfo */
+	mutex_lock(&spec->pcm_lock);
 	pin_idx = hinfo_to_pin_index(codec, hinfo);
-	if (snd_BUG_ON(pin_idx < 0))
-		return -EINVAL;
-	per_pin = get_pin(spec, pin_idx);
-	eld = &per_pin->sink_eld;
+	if (!spec->dyn_pcm_assign) {
+		if (snd_BUG_ON(pin_idx < 0)) {
+			mutex_unlock(&spec->pcm_lock);
+			return -EINVAL;
+		}
+	} else {
+		/* no pin is assigned to the PCM
+		 * PA need pcm open successfully when probe
+		 */
+		if (pin_idx < 0) {
+			err = hdmi_pcm_open_no_pin(hinfo, codec, substream);
+			mutex_unlock(&spec->pcm_lock);
+			return err;
+		}
+	}
 
 	err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
-	if (err < 0)
+	if (err < 0) {
+		mutex_unlock(&spec->pcm_lock);
 		return err;
+	}
 
 	per_cvt = get_cvt(spec, cvt_idx);
 	/* Claim converter */
 	per_cvt->assigned = 1;
+
+
+	per_pin = get_pin(spec, pin_idx);
 	per_pin->cvt_nid = per_cvt->cvt_nid;
 	hinfo->nid = per_cvt->cvt_nid;
 
@@ -1496,6 +1603,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 	hinfo->formats = per_cvt->formats;
 	hinfo->maxbps = per_cvt->maxbps;
 
+	eld = &per_pin->sink_eld;
 	/* Restrict capabilities by ELD if this isn't disabled */
 	if (!static_hdmi_pcm && eld->eld_valid) {
 		snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
@@ -1504,10 +1612,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 			per_cvt->assigned = 0;
 			hinfo->nid = 0;
 			snd_hda_spdif_ctls_unassign(codec, pin_idx);
+			mutex_unlock(&spec->pcm_lock);
 			return -ENODEV;
 		}
 	}
 
+	mutex_unlock(&spec->pcm_lock);
 	/* Store the updated parameters */
 	runtime->hw.channels_min = hinfo->channels_min;
 	runtime->hw.channels_max = hinfo->channels_max;
@@ -1852,13 +1962,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 {
 	hda_nid_t cvt_nid = hinfo->nid;
 	struct hdmi_spec *spec = codec->spec;
-	int pin_idx = hinfo_to_pin_index(codec, hinfo);
-	struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-	hda_nid_t pin_nid = per_pin->pin_nid;
+	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;
+	int err;
+
+	mutex_lock(&spec->pcm_lock);
+	pin_idx = hinfo_to_pin_index(codec, hinfo);
+	if (spec->dyn_pcm_assign && pin_idx < 0) {
+		/* when dyn_pcm_assign and pcm is not bound to a pin
+		 * skip pin setup and return 0 to make audio playback
+		 * be ongoing
+		 */
+		intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+		snd_hda_codec_setup_stream(codec, cvt_nid,
+					stream_tag, 0, format);
+		mutex_unlock(&spec->pcm_lock);
+		return 0;
+	}
 
+	if (snd_BUG_ON(pin_idx < 0)) {
+		mutex_unlock(&spec->pcm_lock);
+		return -EINVAL;
+	}
+	per_pin = get_pin(spec, pin_idx);
+	pin_nid = per_pin->pin_nid;
 	if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
 		/* Verify pin:cvt selections to avoid silent audio after S3.
 		 * After S3, the audio driver restores pin:cvt selections
@@ -1883,7 +2014,6 @@ 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,
 					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@@ -1892,7 +2022,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 				    pinctl | PIN_OUT);
 	}
 
-	return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+	err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
+				 stream_tag, format);
+	mutex_unlock(&spec->pcm_lock);
+	return err;
 }
 
 static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1923,9 +2056,17 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
 		per_cvt->assigned = 0;
 		hinfo->nid = 0;
 
+		mutex_lock(&spec->pcm_lock);
 		pin_idx = hinfo_to_pin_index(codec, hinfo);
-		if (snd_BUG_ON(pin_idx < 0))
+		if (spec->dyn_pcm_assign && pin_idx < 0) {
+			mutex_unlock(&spec->pcm_lock);
+			return 0;
+		}
+
+		if (snd_BUG_ON(pin_idx < 0)) {
+			mutex_unlock(&spec->pcm_lock);
 			return -EINVAL;
+		}
 		per_pin = get_pin(spec, pin_idx);
 
 		if (spec->dyn_pin_out) {
@@ -1945,6 +2086,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
 		per_pin->setup = false;
 		per_pin->channels = 0;
 		mutex_unlock(&per_pin->lock);
+		mutex_unlock(&spec->pcm_lock);
 	}
 
 	return 0;
@@ -2459,6 +2601,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
 		return -ENOMEM;
 
 	spec->ops = generic_standard_hdmi_ops;
+	mutex_init(&spec->pcm_lock);
 	codec->spec = spec;
 	hdmi_array_init(spec, 4);
 
-- 
1.9.1

  parent reply	other threads:[~2015-12-16  5:45 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-16  5:42 [PATCH v4 0/5] ALSA: hda - hdmi support dynamic pcm assignment libin.yang
2015-12-16  5:42 ` [PATCH v4 1/5] ALSA: hda - hdmi begin to support dynamic PCM assignment libin.yang
2015-12-16  5:42 ` libin.yang [this message]
2015-12-16  5:42 ` [PATCH v4 3/5] ALSA: hda - hdmi operate spdif based on pcm libin.yang
2015-12-16  5:42 ` [PATCH v4 4/5] ALSA: hda - hdmi dynamically bind PCM to pin when monitor hotplug libin.yang
2015-12-16  7:43   ` Takashi Iwai
2015-12-16  7:47     ` Libin Yang
2015-12-16  5:42 ` [PATCH v4 5/5] ALSA: hda - hdmi setup pin when monitor hotplug in pcm dynamic assignment mode libin.yang
2015-12-16  7:47 ` [PATCH v4 0/5] ALSA: hda - hdmi support dynamic pcm assignment Takashi Iwai
2015-12-16  7:54   ` Libin Yang
2015-12-16  7:59     ` Takashi Iwai
2015-12-16  7:59       ` Libin Yang
2015-12-18  2:53   ` Yang, Libin
2015-12-18 10:48     ` Takashi Iwai
2015-12-18 12:05       ` Takashi Iwai
2015-12-21  0:51         ` Libin Yang
2015-12-21  8:35           ` Takashi Iwai
2015-12-21  8:54             ` Libin Yang
2015-12-21  9:02               ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1450244565-77041-3-git-send-email-libin.yang@linux.intel.com \
    --to=libin.yang@linux.intel.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=libin.yang@intel.com \
    --cc=mengdong.lin@intel.com \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.