All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support
@ 2016-02-12  2:16 Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 01/15] ASoC: hdac_hdmi: Add hotplug notification and read ELD Subhransu S. Prusty
                   ` (14 more replies)
  0 siblings, 15 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: Jani Nikula, lgirdwood, dri-devel, patches.audio, broonie,
	Daniel Vetter, Subhransu S. Prusty

This patch series adds DP audio and hotplug notification support.

In skylake platform the DP is on a different port which is not
enabled by default. A vendor widget is programmed to enable
playback on all ports. This also enables all the converters.

Codec Dais, widgets, graph are now created dynamically based on
the nodes enumerated, thus removing the hardcoding. Mux controls
are used to establish routing stream to a specific port.

For DP audio support, infoframe needs to be different. Based on
ELD queried we either pack HDMI or DP infoframe.

Also with this series, Jack notification support is added and
Jack event is reported to userspace as is done in case of legacy
driver.

Few fixes are added to check for possible memory leak,
reconfiguring register during resume from D3 etc.

The 8th patch is adding a small macro for getting connection type
in drm header. It is reviewed by drm folks, CCed them. Please
merge it through sound trees.

changes in v6:
	- Fixed some review comments
	- Fix afg node D3 state

Cc: Jani Nikula <jani.nikula@intel.com>
Cc: David Airlie <airlied@linux.ie>
Cc: dri-devel@lists.freedesktop.org
Cc: Daniel Vetter <daniel.vetter@intel.com>

Jeeja KP (2):
  ASoC: hdac_hdmi: Add jack reporting
  ASoC: hdac_hdmi: Add PM support

Ramesh Babu (1):
  ASoC: hdac_hdmi: Fix to keep codec power active during enumeration.

Subhransu S. Prusty (12):
  ASoC: hdac_hdmi: Add hotplug notification and read ELD
  ASoC: hdac_hdmi: Apply constraints based on ELD
  ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins
  ASoC: hdac_hdmi: create dais based on number of cvts
  ASoC: hdac_hdmi: Create widget/route based on nodes enumerated
  ASoC: hdac_hdmi: Enable playback on all enumerated ports
  drm/edid: Add API to help find connection type
  ASoC: hdac_hdmi: Add infoframe support for dp audio
  ASoC: hdac_hdmi: Fix possible memory leak in hw_params
  ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy
  ASoC: hdac_hdmi: Fix to reconfigure registers in runtime resume
  ASoC: hdac_hdmi: Fix to wait for D3 before powering off codec

 include/drm/drm_edid.h       |   12 +
 sound/soc/codecs/Kconfig     |    1 +
 sound/soc/codecs/hdac_hdmi.c | 1208 +++++++++++++++++++++++++++++++++++++-----
 sound/soc/codecs/hdac_hdmi.h |    6 +
 4 files changed, 1104 insertions(+), 123 deletions(-)
 create mode 100644 sound/soc/codecs/hdac_hdmi.h

-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v6 01/15] ASoC: hdac_hdmi: Add hotplug notification and read ELD
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Add hotplug notification and read ELD" to the asoc tree Mark Brown
  2016-02-12  2:16 ` [PATCH v6 02/15] ASoC: hdac_hdmi: Apply constraints based on ELD Subhransu S. Prusty
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

This patch uses i915 component framework to register for hotplug
notification.

In the hotplug notification, driver reads pin sense and ELD by
sending PIN_SENSE and ELD verbs over HDA bus. Once it identifies
valid pin sense and valid ELD, store the ELD into the
corresponding pin map buffer.

Also read the monitor present sense during resume and ignore the
ELD notify from graphics during PM as is done in legacy hda,
commit 8ae743e82f0b ("ALSA: hda - Skip ELD notification during
system suspend")

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 217 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 211 insertions(+), 6 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 5a1ec0f..fff225d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -34,6 +34,9 @@
 
 #define HDA_MAX_CONNECTIONS     32
 
+#define ELD_MAX_SIZE    256
+#define ELD_FIXED_BYTES	20
+
 struct hdac_hdmi_cvt_params {
 	unsigned int channels_min;
 	unsigned int channels_max;
@@ -48,11 +51,22 @@ struct hdac_hdmi_cvt {
 	struct hdac_hdmi_cvt_params params;
 };
 
+struct hdac_hdmi_eld {
+	bool	monitor_present;
+	bool	eld_valid;
+	int	eld_size;
+	char    eld_buffer[ELD_MAX_SIZE];
+};
+
 struct hdac_hdmi_pin {
 	struct list_head head;
 	hda_nid_t nid;
 	int num_mux_nids;
 	hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+	struct hdac_hdmi_eld eld;
+	struct hdac_ext_device *edev;
+	int repoll_count;
+	struct delayed_work work;
 };
 
 struct hdac_hdmi_dai_pin_map {
@@ -76,6 +90,80 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 	return to_ehdac_device(hdac);
 }
 
+ /* HDMI ELD routines */
+static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
+				hda_nid_t nid, int byte_index)
+{
+	unsigned int val;
+
+	val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
+							byte_index);
+
+	dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
+					byte_index, val);
+
+	return val;
+}
+
+static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
+{
+	return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+						 AC_DIPSIZE_ELD_BUF);
+}
+
+/*
+ * This function queries the ELD size and ELD data and fills in the buffer
+ * passed by user
+ */
+static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
+			     unsigned char *buf, int *eld_size)
+{
+	int i, size, ret = 0;
+
+	/*
+	 * ELD size is initialized to zero in caller function. If no errors and
+	 * ELD is valid, actual eld_size is assigned.
+	 */
+
+	size = hdac_hdmi_get_eld_size(codec, nid);
+	if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+		dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
+		return -ERANGE;
+	}
+
+	/* set ELD buffer */
+	for (i = 0; i < size; i++) {
+		unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
+		/*
+		 * Graphics driver might be writing to ELD buffer right now.
+		 * Just abort. The caller will repoll after a while.
+		 */
+		if (!(val & AC_ELDD_ELD_VALID)) {
+			dev_err(&codec->dev,
+				"HDMI: invalid ELD data byte %d\n", i);
+			ret = -EINVAL;
+			goto error;
+		}
+		val &= AC_ELDD_ELD_DATA;
+		/*
+		 * The first byte cannot be zero. This can happen on some DVI
+		 * connections. Some Intel chips may also need some 250ms delay
+		 * to return non-zero ELD data, even when the graphics driver
+		 * correctly writes ELD content before setting ELD_valid bit.
+		 */
+		if (!val && !i) {
+			dev_err(&codec->dev, "HDMI: 0 ELD data\n");
+			ret = -EINVAL;
+			goto error;
+		}
+		buf[i] = val;
+	}
+
+	*eld_size = size;
+error:
+	return ret;
+}
+
 static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
 				hda_nid_t cvt_nid, hda_nid_t pin_nid,
 				u32 stream_tag, int format)
@@ -246,7 +334,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
 	struct hdac_hdmi_dai_pin_map *dai_map;
-	int val;
 
 	if (dai->id > 0) {
 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
@@ -255,12 +342,14 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 
 	dai_map = &hdmi->dai_map[dai->id];
 
-	val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
-					AC_VERB_GET_PIN_SENSE, 0);
-	dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
+	if ((!dai_map->pin->eld.monitor_present) ||
+			(!dai_map->pin->eld.eld_valid)) {
+
+		dev_err(&hdac->hdac.dev,
+				"Failed: montior present? %d ELD valid?: %d\n",
+				dai_map->pin->eld.monitor_present,
+				dai_map->pin->eld.eld_valid);
 
-	if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
-		dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
 		return -ENODEV;
 	}
 
@@ -410,6 +499,71 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 	return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
 }
 
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
+{
+	struct hdac_ext_device *edev = pin->edev;
+	int val;
+
+	if (!edev)
+		return;
+
+	pin->repoll_count = repoll;
+
+	pm_runtime_get_sync(&edev->hdac.dev);
+	val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
+					AC_VERB_GET_PIN_SENSE, 0);
+
+	dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
+						val, pin->nid);
+
+	pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
+	pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
+
+	if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
+
+		dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
+						__func__, pin->nid);
+		goto put_hdac_device;
+	}
+
+	if (pin->eld.monitor_present && pin->eld.eld_valid) {
+		/* TODO: use i915 component for reading ELD later */
+		if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
+				pin->eld.eld_buffer,
+				&pin->eld.eld_size) == 0) {
+
+			print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
+					pin->eld.eld_buffer, pin->eld.eld_size);
+		} else {
+			pin->eld.monitor_present = false;
+			pin->eld.eld_valid = false;
+		}
+	}
+
+	/*
+	 * Sometimes the pin_sense may present invalid monitor
+	 * present and eld_valid. If ELD data is not valid, loop few
+	 * more times to get correct pin sense and valid ELD.
+	 */
+	if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
+		schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
+
+put_hdac_device:
+	pm_runtime_put_sync(&edev->hdac.dev);
+}
+
+static void hdac_hdmi_repoll_eld(struct work_struct *work)
+{
+	struct hdac_hdmi_pin *pin =
+		container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
+
+	/* picked from legacy HDA driver */
+	if (pin->repoll_count++ > 6)
+		pin->repoll_count = 0;
+
+	hdac_hdmi_present_sense(pin, pin->repoll_count);
+}
+
 static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 {
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
@@ -424,6 +578,9 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 	list_add_tail(&pin->head, &hdmi->pin_list);
 	hdmi->num_pin++;
 
+	pin->edev = edev;
+	INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
+
 	return 0;
 }
 
@@ -482,17 +639,65 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
 	return hdac_hdmi_init_dai_map(edev);
 }
 
+static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
+{
+	struct hdac_ext_device *edev = aptr;
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pin *pin;
+	struct snd_soc_codec *codec = edev->scodec;
+
+	/* Don't know how this mapping is derived */
+	hda_nid_t pin_nid = port + 0x04;
+
+	dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
+
+	/*
+	 * skip notification during system suspend (but not in runtime PM);
+	 * the state will be updated at resume. Also since the ELD and
+	 * connection states are updated in anyway at the end of the resume,
+	 * we can skip it when received during PM process.
+	 */
+	if (snd_power_get_state(codec->component.card->snd_card) !=
+			SNDRV_CTL_POWER_D0)
+		return;
+
+	if (atomic_read(&edev->hdac.in_pm))
+		return;
+
+	list_for_each_entry(pin, &hdmi->pin_list, head) {
+		if (pin->nid == pin_nid)
+			hdac_hdmi_present_sense(pin, 1);
+	}
+}
+
+static struct i915_audio_component_audio_ops aops = {
+	.pin_eld_notify	= hdac_hdmi_eld_notify_cb,
+};
+
 static int hdmi_codec_probe(struct snd_soc_codec *codec)
 {
 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	struct snd_soc_dapm_context *dapm =
 		snd_soc_component_get_dapm(&codec->component);
+	struct hdac_hdmi_pin *pin;
+	int ret;
 
 	edev->scodec = codec;
 
 	create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
 
+	aops.audio_ptr = edev;
+	ret = snd_hdac_i915_register_notifier(&aops);
+	if (ret < 0) {
+		dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
+				ret);
+		return ret;
+	}
+
+	list_for_each_entry(pin, &hdmi->pin_list, head)
+		hdac_hdmi_present_sense(pin, 1);
+
 	/* Imp: Store the card pointer in hda_codec */
 	edev->card = dapm->card->snd_card;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 02/15] ASoC: hdac_hdmi: Apply constraints based on ELD
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 01/15] ASoC: hdac_hdmi: Add hotplug notification and read ELD Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Apply constraints based on ELD" to the asoc tree Mark Brown
  2016-02-12  2:16 ` [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins Subhransu S. Prusty
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

Uses the drm ELD core framework to apply rate and channel

Also compute the format to be set based on ELD.

Even though the channel constraint is based on ELD, infoframe
is set with stereo only. Multichannel support will be added
later.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/Kconfig     |  1 +
 sound/soc/codecs/hdac_hdmi.c | 51 +++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4383966..a817830 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -493,6 +493,7 @@ config SND_SOC_GTM601
 config SND_SOC_HDAC_HDMI
 	tristate
 	select SND_HDA_EXT_CORE
+	select SND_PCM_ELD
 	select HDMI
 
 config SND_SOC_ICS43432
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index fff225d..d22fa6b 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -22,10 +22,12 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/hdmi.h>
+#include <drm/drm_edid.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_i915.h>
+#include <sound/pcm_drm_eld.h>
 #include "../../hda/local.h"
 
 #define AMP_OUT_MUTE		0xb080
@@ -90,6 +92,45 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 	return to_ehdac_device(hdac);
 }
 
+static unsigned int sad_format(const u8 *sad)
+{
+	return ((sad[0] >> 0x3) & 0x1f);
+}
+
+static unsigned int sad_sample_bits_lpcm(const u8 *sad)
+{
+	return (sad[2] & 7);
+}
+
+static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
+						void *eld)
+{
+	u64 formats = SNDRV_PCM_FMTBIT_S16;
+	int i;
+	const u8 *sad, *eld_buf = eld;
+
+	sad = drm_eld_sad(eld_buf);
+	if (!sad)
+		goto format_constraint;
+
+	for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
+		if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
+
+			/*
+			 * the controller support 20 and 24 bits in 32 bit
+			 * container so we set S32
+			 */
+			if (sad_sample_bits_lpcm(sad) & 0x6)
+				formats |= SNDRV_PCM_FMTBIT_S32;
+		}
+	}
+
+format_constraint:
+	return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+				formats);
+
+}
+
  /* HDMI ELD routines */
 static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
 				hda_nid_t nid, int byte_index)
@@ -334,6 +375,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
 	struct hdac_hdmi_dai_pin_map *dai_map;
+	int ret;
 
 	if (dai->id > 0) {
 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
@@ -358,10 +400,13 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
 
-	snd_pcm_hw_constraint_step(substream->runtime, 0,
-				   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+	ret = hdac_hdmi_eld_limit_formats(substream->runtime,
+				dai_map->pin->eld.eld_buffer);
+	if (ret < 0)
+		return ret;
 
-	return 0;
+	return snd_pcm_hw_constraint_eld(substream->runtime,
+				dai_map->pin->eld.eld_buffer);
 }
 
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 01/15] ASoC: hdac_hdmi: Add hotplug notification and read ELD Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 02/15] ASoC: hdac_hdmi: Apply constraints based on ELD Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:10   ` Mark Brown
  2016-02-12  2:16 ` [PATCH v6 04/15] ASoC: hdac_hdmi: create dais based on number of cvts Subhransu S. Prusty
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

Skylake supports 3 pin and 3 converter widgets. But by default
only one converter and pin widget are enabled. In skylake
platform the DP port is on a different port which is not enabled
by default. To enable playback on DP port, enable all pin and
converter widget by sending a vendor VERB for a vendor widget to
set required bits.

As we are enabling the DP support enable the DP1.2 feature as well.

Enabling DP1.2 and all widget changes are copied from patch_hdmi.c.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index d22fa6b..b2e5e54 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -629,6 +629,46 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 	return 0;
 }
 
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12			0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS	0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
+{
+	unsigned int vendor_param;
+
+	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+				INTEL_GET_VENDOR_VERB, 0);
+	if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+		return;
+
+	vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+				INTEL_SET_VENDOR_VERB, vendor_param);
+	if (vendor_param == -1)
+		return;
+}
+
+static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac)
+{
+	unsigned int vendor_param;
+
+	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+				INTEL_GET_VENDOR_VERB, 0);
+	if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+		return;
+
+	/* enable DP1.2 mode */
+	vendor_param |= INTEL_EN_DP12;
+	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+				INTEL_SET_VENDOR_VERB, vendor_param);
+	if (vendor_param == -1)
+		return;
+
+}
+
 /*
  * Parse all nodes and store the cvt/pin nids in array
  * Add one time initialization for pin and cvt widgets
@@ -641,6 +681,9 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	int ret;
 
+	hdac_hdmi_skl_enable_all_pins(hdac);
+	hdac_hdmi_skl_enable_dp12(hdac);
+
 	num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
 	if (!nid || num_nodes <= 0) {
 		dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 04/15] ASoC: hdac_hdmi: create dais based on number of cvts
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (2 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 05/15] ASoC: hdac_hdmi: Create widget/route based on nodes enumerated Subhransu S. Prusty
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

After enabling all pins/cvts, Skylake HDMI codec enumerates 3
converters.  Three independent streams can be supported with 3
cvts. This patch removes the static dai creation and creates dais
based on the number of cvts queried.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 123 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 91 insertions(+), 32 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index b2e5e54..c85deae 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -30,6 +30,8 @@
 #include <sound/pcm_drm_eld.h>
 #include "../../hda/local.h"
 
+#define NAME_SIZE	32
+
 #define AMP_OUT_MUTE		0xb080
 #define AMP_OUT_UNMUTE		0xb000
 #define PIN_OUT			(AC_PINCTL_OUT_EN)
@@ -669,11 +671,82 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac)
 
 }
 
+static struct snd_soc_dai_ops hdmi_dai_ops = {
+	.startup = hdac_hdmi_pcm_open,
+	.shutdown = hdac_hdmi_pcm_close,
+	.hw_params = hdac_hdmi_set_hw_params,
+	.prepare = hdac_hdmi_playback_prepare,
+	.hw_free = hdac_hdmi_playback_cleanup,
+};
+
+/*
+ * Each converter can support a stream independently. So a dai is created
+ * based on the number of converter queried.
+ */
+static int hdac_hdmi_create_dais(struct hdac_device *hdac,
+		struct snd_soc_dai_driver **dais,
+		struct hdac_hdmi_priv *hdmi, int num_dais)
+{
+	struct snd_soc_dai_driver *hdmi_dais;
+	struct hdac_hdmi_cvt *cvt;
+	char name[NAME_SIZE], dai_name[NAME_SIZE];
+	int i = 0;
+	u32 rates, bps;
+	unsigned int rate_max = 384000, rate_min = 8000;
+	u64 formats;
+	int ret;
+
+	hdmi_dais = devm_kzalloc(&hdac->dev,
+			(sizeof(*hdmi_dais) * num_dais),
+			GFP_KERNEL);
+	if (!hdmi_dais)
+		return -ENOMEM;
+
+	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+		ret = snd_hdac_query_supported_pcm(hdac, cvt->nid,
+					&rates,	&formats, &bps);
+		if (ret)
+			return ret;
+
+		sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
+		hdmi_dais[i].name = devm_kstrdup(&hdac->dev,
+					dai_name, GFP_KERNEL);
+
+		if (!hdmi_dais[i].name)
+			return -ENOMEM;
+
+		snprintf(name, sizeof(name), "hifi%d", i+1);
+		hdmi_dais[i].playback.stream_name =
+				devm_kstrdup(&hdac->dev, name, GFP_KERNEL);
+		if (!hdmi_dais[i].playback.stream_name)
+			return -ENOMEM;
+
+		/*
+		 * Set caps based on capability queried from the converter.
+		 * It will be constrained runtime based on ELD queried.
+		 */
+		hdmi_dais[i].playback.formats = formats;
+		hdmi_dais[i].playback.rates = rates;
+		hdmi_dais[i].playback.rate_max = rate_max;
+		hdmi_dais[i].playback.rate_min = rate_min;
+		hdmi_dais[i].playback.channels_min = 2;
+		hdmi_dais[i].playback.channels_max = 2;
+		hdmi_dais[i].ops = &hdmi_dai_ops;
+
+		i++;
+	}
+
+	*dais = hdmi_dais;
+
+	return 0;
+}
+
 /*
  * Parse all nodes and store the cvt/pin nids in array
  * Add one time initialization for pin and cvt widgets
  */
-static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
+static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
+		struct snd_soc_dai_driver **dais, int *num_dais)
 {
 	hda_nid_t nid;
 	int i, num_nodes;
@@ -724,6 +797,15 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
 	if (!hdmi->num_pin || !hdmi->num_cvt)
 		return -EIO;
 
+	ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt);
+	if (ret) {
+		dev_err(&hdac->dev, "Failed to create dais with err: %d\n",
+							ret);
+		return ret;
+	}
+
+	*num_dais = hdmi->num_cvt;
+
 	return hdac_hdmi_init_dai_map(edev);
 }
 
@@ -814,38 +896,12 @@ static struct snd_soc_codec_driver hdmi_hda_codec = {
 	.idle_bias_off	= true,
 };
 
-static struct snd_soc_dai_ops hdmi_dai_ops = {
-	.startup = hdac_hdmi_pcm_open,
-	.shutdown = hdac_hdmi_pcm_close,
-	.hw_params = hdac_hdmi_set_hw_params,
-	.prepare = hdac_hdmi_playback_prepare,
-	.hw_free = hdac_hdmi_playback_cleanup,
-};
-
-static struct snd_soc_dai_driver hdmi_dais[] = {
-	{	.name = "intel-hdmi-hif1",
-		.playback = {
-			.stream_name = "hif1",
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = SNDRV_PCM_RATE_32000 |
-				SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-				SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-				SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
-			.formats = SNDRV_PCM_FMTBIT_S16_LE |
-				SNDRV_PCM_FMTBIT_S20_3LE |
-				SNDRV_PCM_FMTBIT_S24_LE |
-				SNDRV_PCM_FMTBIT_S32_LE,
-
-		},
-		.ops = &hdmi_dai_ops,
-	},
-};
-
 static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 {
 	struct hdac_device *codec = &edev->hdac;
 	struct hdac_hdmi_priv *hdmi_priv;
+	struct snd_soc_dai_driver *hdmi_dais = NULL;
+	int num_dais = 0;
 	int ret = 0;
 
 	hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
@@ -859,13 +915,16 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 	INIT_LIST_HEAD(&hdmi_priv->pin_list);
 	INIT_LIST_HEAD(&hdmi_priv->cvt_list);
 
-	ret = hdac_hdmi_parse_and_map_nid(edev);
-	if (ret < 0)
+	ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
+	if (ret < 0) {
+		dev_err(&codec->dev,
+			"Failed in parse and map nid with err: %d\n", ret);
 		return ret;
+	}
 
 	/* ASoC specific initialization */
 	return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
-			hdmi_dais, ARRAY_SIZE(hdmi_dais));
+			hdmi_dais, num_dais);
 }
 
 static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 05/15] ASoC: hdac_hdmi: Create widget/route based on nodes enumerated
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (3 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 04/15] ASoC: hdac_hdmi: create dais based on number of cvts Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 06/15] ASoC: hdac_hdmi: Add jack reporting Subhransu S. Prusty
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

In skylake, HDMI codec enumerates 3 pins and 3 cvts. Stream can
be routed from any cvt to any pin based on the connection list
queried from the pin.

This patch removes the static modelling of widget/route and
creates it dynamically based on the codec widgets enumerated.

Mux widgets are added to represent the map between a cvt and pin.
Ideally the mux widgets should be created based on the connection
list queried from the pin widget. But due to HW behavior, if an
external display is not connected on a port, querying the
connection list returns zero. So create mux widgets to map all
the cvt to all pins. At runtime, playback support on a pin can be
verified based on the connection list query.

Few function in driver have additional arguments now to support
this.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 241 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 219 insertions(+), 22 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index c85deae..6fb44c4 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -446,46 +446,241 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
 	return err;
 }
 
-static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
-				enum snd_soc_dapm_type id,
-				const char *wname, const char *stream)
+static int hdac_hdmi_fill_widget_info(struct device *dev,
+				struct snd_soc_dapm_widget *w,
+				enum snd_soc_dapm_type id, void *priv,
+				const char *wname, const char *stream,
+				struct snd_kcontrol_new *wc, int numkc)
 {
 	w->id = id;
-	w->name = wname;
+	w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
+	if (!w->name)
+		return -ENOMEM;
+
 	w->sname = stream;
 	w->reg = SND_SOC_NOPM;
 	w->shift = 0;
-	w->kcontrol_news = NULL;
-	w->num_kcontrols = 0;
-	w->priv = NULL;
+	w->kcontrol_news = wc;
+	w->num_kcontrols = numkc;
+	w->priv = priv;
+
+	return 0;
 }
 
 static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
-		const char *sink, const char *control, const char *src)
+		const char *sink, const char *control, const char *src,
+		int (*handler)(struct snd_soc_dapm_widget *src,
+			struct snd_soc_dapm_widget *sink))
 {
 	route->sink = sink;
 	route->source = src;
 	route->control = control;
-	route->connected = NULL;
+	route->connected = handler;
 }
 
-static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
-					struct hdac_hdmi_dai_pin_map *dai_map)
+/*
+ * Ideally the Mux inputs should be based on the num_muxs enumerated, but
+ * the display driver seem to be programming the connection list for the pin
+ * widget runtime.
+ *
+ * So programming all the possible inputs for the mux, the user has to take
+ * care of selecting the right one and leaving all other inputs selected to
+ * "NONE"
+ */
+static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
+				struct hdac_hdmi_pin *pin,
+				struct snd_soc_dapm_widget *widget,
+				const char *widget_name)
+{
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct snd_kcontrol_new *kc;
+	struct hdac_hdmi_cvt *cvt;
+	struct soc_enum *se;
+	char kc_name[NAME_SIZE];
+	char mux_items[NAME_SIZE];
+	/* To hold inputs to the Pin mux */
+	char *items[HDA_MAX_CONNECTIONS];
+	int i = 0;
+	int num_items = hdmi->num_cvt + 1;
+
+	kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL);
+	if (!kc)
+		return -ENOMEM;
+
+	se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL);
+	if (!se)
+		return -ENOMEM;
+
+	sprintf(kc_name, "Pin %d Input", pin->nid);
+	kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
+	if (!kc->name)
+		return -ENOMEM;
+
+	kc->private_value = (long)se;
+	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	kc->access = 0;
+	kc->info = snd_soc_info_enum_double;
+	kc->put = snd_soc_dapm_put_enum_double;
+	kc->get = snd_soc_dapm_get_enum_double;
+
+	se->reg = SND_SOC_NOPM;
+
+	/* enum texts: ["NONE", "cvt #", "cvt #", ...] */
+	se->items = num_items;
+	se->mask = roundup_pow_of_two(se->items) - 1;
+
+	sprintf(mux_items, "NONE");
+	items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
+	if (!items[i])
+		return -ENOMEM;
+
+	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+		i++;
+		sprintf(mux_items, "cvt %d", cvt->nid);
+		items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
+		if (!items[i])
+			return -ENOMEM;
+	}
+
+	se->texts = devm_kmemdup(&edev->hdac.dev, items,
+			(num_items  * sizeof(char *)), GFP_KERNEL);
+	if (!se->texts)
+		return -ENOMEM;
+
+	return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
+			snd_soc_dapm_mux, &pin->nid, widget_name,
+			NULL, kc, 1);
+}
+
+/* Add cvt <- input <- mux route map */
+static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
+			struct snd_soc_dapm_widget *widgets,
+			struct snd_soc_dapm_route *route, int rindex)
+{
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	const struct snd_kcontrol_new *kc;
+	struct soc_enum *se;
+	int mux_index = hdmi->num_cvt + hdmi->num_pin;
+	int i, j;
+
+	for (i = 0; i < hdmi->num_pin; i++) {
+		kc = widgets[mux_index].kcontrol_news;
+		se = (struct soc_enum *)kc->private_value;
+		for (j = 0; j < hdmi->num_cvt; j++) {
+			hdac_hdmi_fill_route(&route[rindex],
+					widgets[mux_index].name,
+					se->texts[j + 1],
+					widgets[j].name, NULL);
+
+			rindex++;
+		}
+
+		mux_index++;
+	}
+}
+
+/*
+ * Widgets are added in the below sequence
+ *	Converter widgets for num converters enumerated
+ *	Pin widgets for num pins enumerated
+ *	Pin mux widgets to represent connenction list of pin widget
+ *
+ * Total widgets elements = num_cvt + num_pin + num_pin;
+ *
+ * Routes are added as below:
+ *	pin mux -> pin (based on num_pins)
+ *	cvt -> "Input sel control" -> pin_mux
+ *
+ * Total route elements:
+ *	num_pins + (pin_muxes * num_cvt)
+ */
+static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
 {
-	struct snd_soc_dapm_route route[1];
-	struct snd_soc_dapm_widget widgets[2] = { {0} };
+	struct snd_soc_dapm_widget *widgets;
+	struct snd_soc_dapm_route *route;
+	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
+	char widget_name[NAME_SIZE];
+	struct hdac_hdmi_cvt *cvt;
+	struct hdac_hdmi_pin *pin;
+	int ret, i = 0, num_routes = 0;
 
-	memset(&route, 0, sizeof(route));
+	if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
+		return -EINVAL;
 
-	hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
-			"hif1 Output", NULL);
-	hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
-			"Coverter 1", "hif1");
+	widgets = devm_kzalloc(dapm->dev,
+		(sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
+		GFP_KERNEL);
 
-	hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
+	if (!widgets)
+		return -ENOMEM;
+
+	/* DAPM widgets to represent each converter widget */
+	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+		sprintf(widget_name, "Converter %d", cvt->nid);
+		ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+			snd_soc_dapm_aif_in, &cvt->nid,
+			widget_name, dai_drv[i].playback.stream_name, NULL, 0);
+		if (ret < 0)
+			return ret;
+		i++;
+	}
+
+	list_for_each_entry(pin, &hdmi->pin_list, head) {
+		sprintf(widget_name, "hif%d Output", pin->nid);
+		ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+				snd_soc_dapm_output, &pin->nid,
+				widget_name, NULL, NULL, 0);
+		if (ret < 0)
+			return ret;
+		i++;
+	}
+
+	/* DAPM widgets to represent the connection list to pin widget */
+	list_for_each_entry(pin, &hdmi->pin_list, head) {
+		sprintf(widget_name, "Pin %d Mux", pin->nid);
+		ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
+							widget_name);
+		if (ret < 0)
+			return ret;
+		i++;
+
+		/* For cvt to pin_mux mapping */
+		num_routes += hdmi->num_cvt;
+
+		/* For pin_mux to pin mapping */
+		num_routes++;
+	}
+
+	route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
+							GFP_KERNEL);
+	if (!route)
+		return -ENOMEM;
+
+	i = 0;
+	/* Add pin <- NULL <- mux route map */
+	list_for_each_entry(pin, &hdmi->pin_list, head) {
+		int sink_index = i + hdmi->num_cvt;
+		int src_index = sink_index + hdmi->num_pin;
+
+		hdac_hdmi_fill_route(&route[i],
+				widgets[sink_index].name, NULL,
+				widgets[src_index].name, NULL);
+		i++;
+
+	}
+
+	hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
+
+	snd_soc_dapm_new_controls(dapm, widgets,
+		((2 * hdmi->num_pin) + hdmi->num_cvt));
+
+	snd_soc_dapm_add_routes(dapm, route, num_routes);
+	snd_soc_dapm_new_widgets(dapm->card);
+
+	return 0;
 
-	snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
-	snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
 }
 
 static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
@@ -855,7 +1050,9 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
 
 	edev->scodec = codec;
 
-	create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
+	ret = create_fill_widget_route_map(dapm);
+	if (ret < 0)
+		return ret;
 
 	aops.audio_ptr = edev;
 	ret = snd_hdac_i915_register_notifier(&aops);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 06/15] ASoC: hdac_hdmi: Add jack reporting
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (4 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 05/15] ASoC: hdac_hdmi: Create widget/route based on nodes enumerated Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Add jack reporting" to the asoc tree Mark Brown
  2016-02-12  2:16 ` [PATCH v6 07/15] ASoC: hdac_hdmi: Enable playback on all enumerated ports Subhransu S. Prusty
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Jeeja KP, Vinod Koul,
	Subhransu S. Prusty

From: Jeeja KP <jeeja.kp@intel.com>

Jack is created based on pcm devices enumerated, so we will
create Jack as "HDMI/DP, pcm=x Jack". This style is expected by
current usermode like PulseAudio and CRAS.

This patch exports an API which can be used to register Jack
based on PCM. This API also establishes the map between PCM and
cvt. Further cvt to pin mapping is established with the help of
usermode selection based on the board topology.

During device probe as the PCMs may not be registered, initial
pin sense don't report jack events. So, first time jack reporting
is done during user selection of mux control.

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 160 ++++++++++++++++++++++++++++++++++++++++++-
 sound/soc/codecs/hdac_hdmi.h |   6 ++
 2 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 sound/soc/codecs/hdac_hdmi.h

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 6fb44c4..8391486 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -24,11 +24,13 @@
 #include <linux/hdmi.h>
 #include <drm/drm_edid.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_i915.h>
 #include <sound/pcm_drm_eld.h>
 #include "../../hda/local.h"
+#include "hdac_hdmi.h"
 
 #define NAME_SIZE	32
 
@@ -52,6 +54,7 @@ struct hdac_hdmi_cvt_params {
 struct hdac_hdmi_cvt {
 	struct list_head head;
 	hda_nid_t nid;
+	const char *name;
 	struct hdac_hdmi_cvt_params params;
 };
 
@@ -73,6 +76,14 @@ struct hdac_hdmi_pin {
 	struct delayed_work work;
 };
 
+struct hdac_hdmi_pcm {
+	struct list_head head;
+	int pcm_id;
+	struct hdac_hdmi_pin *pin;
+	struct hdac_hdmi_cvt *cvt;
+	struct snd_jack *jack;
+};
+
 struct hdac_hdmi_dai_pin_map {
 	int dai_id;
 	struct hdac_hdmi_pin *pin;
@@ -83,8 +94,10 @@ struct hdac_hdmi_priv {
 	struct hdac_hdmi_dai_pin_map dai_map[3];
 	struct list_head pin_list;
 	struct list_head cvt_list;
+	struct list_head pcm_list;
 	int num_pin;
 	int num_cvt;
+	struct mutex pin_mutex;
 };
 
 static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
@@ -478,6 +491,67 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
 	route->connected = handler;
 }
 
+static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
+					struct hdac_hdmi_pin *pin)
+{
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm = NULL;
+
+	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+		if (pcm->pin == pin)
+			return pcm;
+	}
+
+	return NULL;
+}
+
+/*
+ * Based on user selection, map the PINs with the PCMs.
+ */
+static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int ret;
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct hdac_hdmi_pin *pin = w->priv;
+	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm = NULL;
+	const char *cvt_name =  e->texts[ucontrol->value.enumerated.item[0]];
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&hdmi->pin_mutex);
+	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+		if (pcm->pin == pin)
+			pcm->pin = NULL;
+
+		/*
+		 * Jack status is not reported during device probe as the
+		 * PCMs are not registered by then. So report it here.
+		 */
+		if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
+			pcm->pin = pin;
+			if (pin->eld.monitor_present && pin->eld.eld_valid) {
+				dev_dbg(&edev->hdac.dev,
+					"jack report for pcm=%d\n",
+					pcm->pcm_id);
+
+				snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+			}
+			mutex_unlock(&hdmi->pin_mutex);
+			return ret;
+		}
+	}
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return ret;
+}
+
 /*
  * Ideally the Mux inputs should be based on the num_muxs enumerated, but
  * the display driver seem to be programming the connection list for the pin
@@ -520,7 +594,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
 	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 	kc->access = 0;
 	kc->info = snd_soc_info_enum_double;
-	kc->put = snd_soc_dapm_put_enum_double;
+	kc->put = hdac_hdmi_set_pin_mux;
 	kc->get = snd_soc_dapm_get_enum_double;
 
 	se->reg = SND_SOC_NOPM;
@@ -548,8 +622,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
 		return -ENOMEM;
 
 	return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
-			snd_soc_dapm_mux, &pin->nid, widget_name,
-			NULL, kc, 1);
+			snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
 }
 
 /* Add cvt <- input <- mux route map */
@@ -728,12 +801,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 {
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	struct hdac_hdmi_cvt *cvt;
+	char name[NAME_SIZE];
 
 	cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
 	if (!cvt)
 		return -ENOMEM;
 
 	cvt->nid = nid;
+	sprintf(name, "cvt %d", cvt->nid);
+	cvt->name = kstrdup(name, GFP_KERNEL);
 
 	list_add_tail(&cvt->head, &hdmi->cvt_list);
 	hdmi->num_cvt++;
@@ -744,6 +820,8 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 {
 	struct hdac_ext_device *edev = pin->edev;
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm;
 	int val;
 
 	if (!edev)
@@ -758,13 +836,31 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 	dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
 						val, pin->nid);
 
+
+	mutex_lock(&hdmi->pin_mutex);
 	pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
 	pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
 
+	pcm = hdac_hdmi_get_pcm(edev, pin);
+
 	if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
 
 		dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
 						__func__, pin->nid);
+
+		/*
+		 * PCMs are not registered during device probe, so don't
+		 * report jack here. It will be done in usermode mux
+		 * control select.
+		 */
+		if (pcm) {
+			dev_dbg(&edev->hdac.dev,
+				"jack report for pcm=%d\n", pcm->pcm_id);
+
+			snd_jack_report(pcm->jack, 0);
+		}
+
+		mutex_unlock(&hdmi->pin_mutex);
 		goto put_hdac_device;
 	}
 
@@ -774,14 +870,32 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 				pin->eld.eld_buffer,
 				&pin->eld.eld_size) == 0) {
 
+			if (pcm) {
+				dev_dbg(&edev->hdac.dev,
+					"jack report for pcm=%d\n",
+					pcm->pcm_id);
+
+				snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+			}
+
 			print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
 					pin->eld.eld_buffer, pin->eld.eld_size);
 		} else {
 			pin->eld.monitor_present = false;
 			pin->eld.eld_valid = false;
+
+			if (pcm) {
+				dev_dbg(&edev->hdac.dev,
+					"jack report for pcm=%d\n",
+					pcm->pcm_id);
+
+				snd_jack_report(pcm->jack, 0);
+			}
 		}
 	}
 
+	mutex_unlock(&hdmi->pin_mutex);
+
 	/*
 	 * Sometimes the pin_sense may present invalid monitor
 	 * present and eld_valid. If ELD data is not valid, loop few
@@ -1039,6 +1153,35 @@ static struct i915_audio_component_audio_ops aops = {
 	.pin_eld_notify	= hdac_hdmi_eld_notify_cb,
 };
 
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
+{
+	char jack_name[NAME_SIZE];
+	struct snd_soc_codec *codec = dai->codec;
+	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_component_get_dapm(&codec->component);
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm;
+
+	/*
+	 * this is a new PCM device, create new pcm and
+	 * add to the pcm list
+	 */
+	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+	if (!pcm)
+		return -ENOMEM;
+	pcm->pcm_id = device;
+	pcm->cvt = hdmi->dai_map[dai->id].cvt;
+
+	list_add_tail(&pcm->head, &hdmi->pcm_list);
+
+	sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
+
+	return snd_jack_new(dapm->card->snd_card, jack_name,
+		SND_JACK_AVOUT,	&pcm->jack, true, false);
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
+
 static int hdmi_codec_probe(struct snd_soc_codec *codec)
 {
 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
@@ -1111,6 +1254,8 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 
 	INIT_LIST_HEAD(&hdmi_priv->pin_list);
 	INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+	INIT_LIST_HEAD(&hdmi_priv->pcm_list);
+	mutex_init(&hdmi_priv->pin_mutex);
 
 	ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
 	if (ret < 0) {
@@ -1129,11 +1274,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	struct hdac_hdmi_pin *pin, *pin_next;
 	struct hdac_hdmi_cvt *cvt, *cvt_next;
+	struct hdac_hdmi_pcm *pcm, *pcm_next;
 
 	snd_soc_unregister_codec(&edev->hdac.dev);
 
+	list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
+		pcm->cvt = NULL;
+		pcm->pin = NULL;
+		list_del(&pcm->head);
+		kfree(pcm);
+	}
+
 	list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
 		list_del(&cvt->head);
+		kfree(cvt->name);
 		kfree(cvt);
 	}
 
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
new file mode 100644
index 0000000..8dfd1e0
--- /dev/null
+++ b/sound/soc/codecs/hdac_hdmi.h
@@ -0,0 +1,6 @@
+#ifndef __HDAC_HDMI_H__
+#define __HDAC_HDMI_H__
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
+
+#endif /* __HDAC_HDMI_H__ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 07/15] ASoC: hdac_hdmi: Enable playback on all enumerated ports
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (5 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 06/15] ASoC: hdac_hdmi: Add jack reporting Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Enable playback on all enumerated ports" to the asoc tree Mark Brown
  2016-02-12  2:16 ` [PATCH v6 08/15] drm/edid: Add API to help find connection type Subhransu S. Prusty
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Jeeja KP, Vinod Koul,
	Subhransu S. Prusty

Last patch added infrastructure to render over all the ports,
PCM<->cvt<- pin mapping and user selection of controls. But we
still have restriction of playback on the default port alone, so
remove that.

This patch removes the hardcoding of cvt<->pin map from the dai.
Cvt and pin for a dai are now derived from the already stored pcm
list of device opened.  We query connection list of a pin from
codec to validate the cvt<->pin map.

If connection list returns zero, then monitor is not connected so
fail playback.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 174 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 132 insertions(+), 42 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 8391486..dcd1cb6 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -40,6 +40,8 @@
 
 #define HDA_MAX_CONNECTIONS     32
 
+#define HDA_MAX_CVTS		3
+
 #define ELD_MAX_SIZE    256
 #define ELD_FIXED_BYTES	20
 
@@ -91,7 +93,7 @@ struct hdac_hdmi_dai_pin_map {
 };
 
 struct hdac_hdmi_priv {
-	struct hdac_hdmi_dai_pin_map dai_map[3];
+	struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
 	struct list_head pin_list;
 	struct list_head cvt_list;
 	struct list_head pcm_list;
@@ -384,44 +386,136 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
+		struct hdac_hdmi_dai_pin_map *dai_map)
+{
+	int mux_idx;
+	struct hdac_hdmi_pin *pin = dai_map->pin;
+
+	for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
+		if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
+			snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+					AC_VERB_SET_CONNECT_SEL, mux_idx);
+			break;
+		}
+	}
+
+	if (mux_idx == pin->num_mux_nids)
+		return -EIO;
+
+	/* Enable out path for this pin widget */
+	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+
+	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+	return 0;
+}
+
+static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
+					struct hdac_hdmi_pin *pin)
+{
+	if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
+		dev_warn(&hdac->hdac.dev,
+			"HDMI: pin %d wcaps %#x does not support connection list\n",
+			pin->nid, get_wcaps(&hdac->hdac, pin->nid));
+		return -EINVAL;
+	}
+
+	pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
+			pin->mux_nids, HDA_MAX_CONNECTIONS);
+	if (pin->num_mux_nids == 0)
+		dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
+								pin->nid);
+
+	dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
+			pin->num_mux_nids, pin->nid);
+
+	return pin->num_mux_nids;
+}
+
+/*
+ * Query pcm list and return pin widget to which stream is routed.
+ *
+ * Also query connection list of the pin, to validate the cvt to pin map.
+ *
+ * Same stream rendering to multiple pins simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first pin
+ * connected.
+ */
+static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
+			struct hdac_ext_device *edev,
+			struct hdac_hdmi_priv *hdmi,
+			struct hdac_hdmi_cvt *cvt)
+{
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_pin *pin = NULL;
+	int ret, i;
+
+	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+		if (pcm->cvt == cvt) {
+			pin = pcm->pin;
+			break;
+		}
+	}
+
+	if (pin) {
+		ret = hdac_hdmi_query_pin_connlist(edev, pin);
+		if (ret < 0)
+			return NULL;
+
+		for (i = 0; i < pin->num_mux_nids; i++) {
+			if (pin->mux_nids[i] == cvt->nid)
+				return pin;
+		}
+	}
+
+	return NULL;
+}
+
 static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
 	struct hdac_hdmi_dai_pin_map *dai_map;
+	struct hdac_hdmi_cvt *cvt;
+	struct hdac_hdmi_pin *pin;
 	int ret;
 
-	if (dai->id > 0) {
-		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
-		return -ENODEV;
-	}
-
 	dai_map = &hdmi->dai_map[dai->id];
 
-	if ((!dai_map->pin->eld.monitor_present) ||
-			(!dai_map->pin->eld.eld_valid)) {
+	cvt = dai_map->cvt;
+	pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
+	if (!pin)
+		return -EIO;
+
+	if ((!pin->eld.monitor_present) ||
+			(!pin->eld.eld_valid)) {
 
 		dev_err(&hdac->hdac.dev,
-				"Failed: montior present? %d ELD valid?: %d\n",
-				dai_map->pin->eld.monitor_present,
-				dai_map->pin->eld.eld_valid);
+			"Failed: montior present? %d ELD valid?: %d for pin: %d\n",
+			pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
 
 		return -ENODEV;
 	}
 
-	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+	dai_map->pin = pin;
 
-	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
-			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	ret = hdac_hdmi_enable_pin(hdac, dai_map);
+	if (ret < 0)
+		return ret;
 
 	ret = hdac_hdmi_eld_limit_formats(substream->runtime,
-				dai_map->pin->eld.eld_buffer);
+				pin->eld.eld_buffer);
 	if (ret < 0)
 		return ret;
 
 	return snd_pcm_hw_constraint_eld(substream->runtime,
-				dai_map->pin->eld.eld_buffer);
+				pin->eld.eld_buffer);
 }
 
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
@@ -437,6 +531,8 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 
 	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+	dai_map->pin = NULL;
 }
 
 static int
@@ -759,40 +855,34 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
 static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
 {
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
-	struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
+	struct hdac_hdmi_dai_pin_map *dai_map;
 	struct hdac_hdmi_cvt *cvt;
-	struct hdac_hdmi_pin *pin;
+	int dai_id = 0;
 
-	if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
+	if (list_empty(&hdmi->cvt_list))
 		return -EINVAL;
 
-	/*
-	 * Currently on board only 1 pin and 1 converter is enabled for
-	 * simplification, more will be added eventually
-	 * So using fixed map for dai_id:pin:cvt
-	 */
-	cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
-	pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
-
-	dai_map->dai_id = 0;
-	dai_map->pin = pin;
+	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+		dai_map = &hdmi->dai_map[dai_id];
+		dai_map->dai_id = dai_id;
+		dai_map->cvt = cvt;
 
-	dai_map->cvt = cvt;
+		/* Enable transmission */
+		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+				AC_VERB_SET_DIGI_CONVERT_1, 1);
 
-	/* Enable out path for this pin widget */
-	snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
-			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		/* Category Code (CC) to zero */
+		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+				AC_VERB_SET_DIGI_CONVERT_2, 0);
 
-	/* Enable transmission */
-	snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-			AC_VERB_SET_DIGI_CONVERT_1, 1);
+		dai_id++;
 
-	/* Category Code (CC) to zero */
-	snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-			AC_VERB_SET_DIGI_CONVERT_2, 0);
-
-	snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
-			AC_VERB_SET_CONNECT_SEL, 0);
+		if (dai_id == HDA_MAX_CVTS) {
+			dev_warn(&edev->hdac.dev,
+				"Max dais supported: %d\n", dai_id);
+			break;
+		}
+	}
 
 	return 0;
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 08/15] drm/edid: Add API to help find connection type
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (6 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 07/15] ASoC: hdac_hdmi: Enable playback on all enumerated ports Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:25   ` Applied "drm/edid: Add API to help find connection type" to the asoc tree Mark Brown
  2016-02-12  2:16 ` [PATCH v6 09/15] ASoC: hdac_hdmi: Add infoframe support for dp audio Subhransu S. Prusty
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: lgirdwood, dri-devel, patches.audio, broonie, Daniel Vetter,
	Vinod Koul, Subhransu S. Prusty

To fill the audio infoframe it is required to identify the
connection type as DP or HDMI. This patch adds an API which
parses ELD and returns the display type connected.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Cc: David Airlie <airlied@linux.ie>
Cc: dri-devel@lists.freedesktop.org
Cc: Daniel Vetter <daniel.vetter@intel.com>
---
 include/drm/drm_edid.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 2af9769..dec6221 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld)
 	return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
 }
 
+/**
+ * drm_eld_get_conn_type - Get device type hdmi/dp connected
+ * @eld: pointer to an ELD memory structure
+ *
+ * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to
+ * identify the display type connected.
+ */
+static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
+{
+	return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
+}
+
 struct edid *drm_do_get_edid(struct drm_connector *connector,
 	int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
 			      size_t len),
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 09/15] ASoC: hdac_hdmi: Add infoframe support for dp audio
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (7 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 08/15] drm/edid: Add API to help find connection type Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params Subhransu S. Prusty
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

For DP audio support, infoframe needs to be different. Based on
get_conn_type result we pack either HDMI or DP infoframe.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 74 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 64 insertions(+), 10 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index dcd1cb6..36f1200 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -253,27 +253,75 @@ hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
 				AC_VERB_SET_HDMI_DIP_INDEX, val);
 }
 
+struct dp_audio_infoframe {
+	u8 type; /* 0x84 */
+	u8 len;  /* 0x1b */
+	u8 ver;  /* 0x11 << 2 */
+
+	u8 CC02_CT47;	/* match with HDMI infoframe from this on */
+	u8 SS01_SF24;
+	u8 CXT04;
+	u8 CA;
+	u8 LFEPBL01_LSV36_DM_INH7;
+};
+
 static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
 				hda_nid_t cvt_nid, hda_nid_t pin_nid)
 {
 	uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
 	struct hdmi_audio_infoframe frame;
-	u8 *dip = (u8 *)&frame;
+	struct dp_audio_infoframe dp_ai;
+	struct hdac_hdmi_priv *hdmi = hdac->private_data;
+	struct hdac_hdmi_pin *pin;
+	u8 *dip;
 	int ret;
 	int i;
+	const u8 *eld_buf;
+	u8 conn_type;
+	int channels = 2;
 
-	hdmi_audio_infoframe_init(&frame);
+	list_for_each_entry(pin, &hdmi->pin_list, head) {
+		if (pin->nid == pin_nid)
+			break;
+	}
 
-	/* Default stereo for now */
-	frame.channels = 2;
+	eld_buf = pin->eld.eld_buffer;
+	conn_type = drm_eld_get_conn_type(eld_buf);
 
 	/* setup channel count */
 	snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-			    AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
+			    AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
 
-	ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
-	if (ret < 0)
-		return ret;
+	switch (conn_type) {
+	case DRM_ELD_CONN_TYPE_HDMI:
+		hdmi_audio_infoframe_init(&frame);
+
+		/* Default stereo for now */
+		frame.channels = channels;
+
+		ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+		if (ret < 0)
+			return ret;
+
+		dip = (u8 *)&frame;
+		break;
+
+	case DRM_ELD_CONN_TYPE_DP:
+		memset(&dp_ai, 0, sizeof(dp_ai));
+		dp_ai.type	= 0x84;
+		dp_ai.len	= 0x1b;
+		dp_ai.ver	= 0x11 << 2;
+		dp_ai.CC02_CT47	= channels - 1;
+		dp_ai.CA	= 0;
+
+		dip = (u8 *)&dp_ai;
+		break;
+
+	default:
+		dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n",
+						conn_type);
+		return -EIO;
+	}
 
 	/* stop infoframe transmission */
 	hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
@@ -283,9 +331,15 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
 
 	/*  Fill infoframe. Index auto-incremented */
 	hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
-	for (i = 0; i < sizeof(frame); i++)
-		snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+	if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
+		for (i = 0; i < sizeof(frame); i++)
+			snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
 				AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
+	} else {
+		for (i = 0; i < sizeof(dp_ai); i++)
+			snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+				AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
+	}
 
 	/* Start infoframe */
 	hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (8 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 09/15] ASoC: hdac_hdmi: Add infoframe support for dp audio Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:57   ` Mark Brown
  2016-02-12  2:16 ` [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy Subhransu S. Prusty
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

It's possible for hw_params to be called two times. So add NULL
check to prevent memory leak.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 36f1200..40db292 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -405,9 +405,14 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
 		return -ENODEV;
 	}
 
-	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-	if (!dd)
-		return -ENOMEM;
+	dd = (struct hdac_ext_dma_params *)
+		snd_soc_dai_get_dma_data(dai, substream);
+	if (!dd) {
+		dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+		if (!dd)
+			return -ENOMEM;
+	}
+
 	dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
 			params_channels(hparams), params_format(hparams),
 			24, 0);
@@ -433,9 +438,11 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
 				AC_VERB_SET_STREAM_FORMAT, 0);
 
 	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
-	snd_soc_dai_set_dma_data(dai, substream, NULL);
 
-	kfree(dd);
+	if (dd) {
+		snd_soc_dai_set_dma_data(dai, substream, NULL);
+		kfree(dd);
+	}
 
 	return 0;
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (9 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-15 20:55   ` Mark Brown
  2016-02-12  2:16 ` [PATCH v6 12/15] ASoC: hdac_hdmi: Add PM support Subhransu S. Prusty
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

In dai startup, driver was checking for ELD and would fail if no
monitor is connected. This causes userland like PA, CRAS to be
unhappy as they scan the device list at bootup.

So move the ELD check to hw_params and fail, if valid ELD is not
found.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 48 ++++++++++++++++++++++++++++++++------------
 1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 40db292..b030d3e 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -398,10 +398,20 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
 {
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
+	struct hdac_hdmi_priv *hdmi = hdac->private_data;
+	struct hdac_hdmi_dai_pin_map *dai_map;
+	struct hdac_hdmi_pin *pin;
 	struct hdac_ext_dma_params *dd;
 
-	if (dai->id > 0) {
-		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
+	dai_map = &hdmi->dai_map[dai->id];
+	pin = dai_map->pin;
+
+	if (!pin)
+		return -ENODEV;
+
+	if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) {
+		dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n",
+								pin->nid);
 		return -ENODEV;
 	}
 
@@ -432,11 +442,6 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
 
 	dai_map = &hdmi->dai_map[dai->id];
 
-	snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-				AC_VERB_SET_CHANNEL_STREAMID, 0);
-	snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-				AC_VERB_SET_STREAM_FORMAT, 0);
-
 	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
 
 	if (dd) {
@@ -537,6 +542,11 @@ static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
 	return NULL;
 }
 
+/*
+ * This tries to get a valid pin and set the HW constraints based on the
+ * ELD. Even if a valid pin is not found return success so that device open
+ * doesn't fail.
+ */
 static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
@@ -551,17 +561,22 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 
 	cvt = dai_map->cvt;
 	pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
+
+	/*
+	 * To make PA and other userland happy.
+	 * userland scans devices so returning error does not help.
+	 */
 	if (!pin)
-		return -EIO;
+		return 0;
 
 	if ((!pin->eld.monitor_present) ||
 			(!pin->eld.eld_valid)) {
 
-		dev_err(&hdac->hdac.dev,
+		dev_warn(&hdac->hdac.dev,
 			"Failed: montior present? %d ELD valid?: %d for pin: %d\n",
 			pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
 
-		return -ENODEV;
+		return 0;
 	}
 
 	dai_map->pin = pin;
@@ -588,12 +603,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 
 	dai_map = &hdmi->dai_map[dai->id];
 
-	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
+	if (dai_map->pin && dai_map->pin->eld.monitor_present) {
+		snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
+				AC_VERB_SET_CHANNEL_STREAMID, 0);
+		snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
+				AC_VERB_SET_STREAM_FORMAT, 0);
+
+		hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
 
-	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
+		snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
 
-	dai_map->pin = NULL;
+		dai_map->pin = NULL;
+	}
 }
 
 static int
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 12/15] ASoC: hdac_hdmi: Add PM support
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (10 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 13/15] ASoC: hdac_hdmi: Fix to keep codec power active during enumeration Subhransu S. Prusty
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Jeeja KP, Vinod Koul,
	Subhransu S. Prusty

From: Jeeja KP <jeeja.kp@intel.com>

The codec registers are reset during S3. So need to reconfigure
all pins and DP1.2 feature again after resume from S3. Also
reprogram the required registers if the S3 was triggered during
playback.

In suspended state ELD notify callback is not processed, So add
ELD check for all pins as well.

Also turn the codec power domain oFF which is kept ON during
controller resequencing and codec reenumeration.

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 61 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index b030d3e..9b7e058 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -594,6 +594,26 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 				pin->eld.eld_buffer);
 }
 
+static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+		struct snd_soc_dai *dai)
+{
+	struct hdac_hdmi_dai_pin_map *dai_map;
+	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
+	struct hdac_hdmi_priv *hdmi = hdac->private_data;
+	int ret;
+
+	dai_map = &hdmi->dai_map[dai->id];
+	if (cmd == SNDRV_PCM_TRIGGER_RESUME) {
+		ret = hdac_hdmi_enable_pin(hdac, dai_map);
+		if (ret < 0)
+			return ret;
+
+		return hdac_hdmi_playback_prepare(substream, dai);
+	}
+
+	return 0;
+}
+
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -1158,6 +1178,7 @@ static struct snd_soc_dai_ops hdmi_dai_ops = {
 	.shutdown = hdac_hdmi_pcm_close,
 	.hw_params = hdac_hdmi_set_hw_params,
 	.prepare = hdac_hdmi_playback_prepare,
+	.trigger = hdac_hdmi_trigger,
 	.hw_free = hdac_hdmi_playback_cleanup,
 };
 
@@ -1403,9 +1424,49 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int hdmi_codec_resume(struct snd_soc_codec *codec)
+{
+	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pin *pin;
+	struct hdac_device *hdac = &edev->hdac;
+	struct hdac_bus *bus = hdac->bus;
+	int err;
+
+	hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+	hdac_hdmi_skl_enable_dp12(&edev->hdac);
+
+	/* Power up afg */
+	if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
+		snd_hdac_codec_write(hdac, hdac->afg, 0,
+			AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+	/*
+	 * As the ELD notify callback request is not entertained while the
+	 * device is in suspend state. Need to manually check detection of
+	 * all pins here.
+	 */
+	list_for_each_entry(pin, &hdmi->pin_list, head)
+		hdac_hdmi_present_sense(pin, 1);
+
+	/* Codec power is turned ON during controller resume */
+	err = snd_hdac_display_power(bus, false);
+	if (err < 0) {
+		dev_err(bus->dev, "Cannot turn on display power on i915\n");
+		return err;
+	}
+
+	return 0;
+}
+#else
+#define hdmi_codec_resume NULL
+#endif
+
 static struct snd_soc_codec_driver hdmi_hda_codec = {
 	.probe		= hdmi_codec_probe,
 	.remove		= hdmi_codec_remove,
+	.resume		= hdmi_codec_resume,
 	.idle_bias_off	= true,
 };
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 13/15] ASoC: hdac_hdmi: Fix to keep codec power active during enumeration.
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (11 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 12/15] ASoC: hdac_hdmi: Add PM support Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 14/15] ASoC: hdac_hdmi: Fix to reconfigure registers in runtime resume Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 15/15] ASoC: hdac_hdmi: Fix to wait for D3 before powering off codec Subhransu S. Prusty
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, Ramesh Babu, patches.audio, broonie,
	Vinod Koul, Subhransu S. Prusty

From: Ramesh Babu <ramesh.babu@intel.com>

Codec power is turned OFF in the first explicit call to
pm_runtime_suspend, thus keeping the refcount balanced. For any
other communication with codec, the power is turned ON/OFF in
runtime PM handlers.

Signed-off-by: Ramesh Babu <ramesh.babu@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 9b7e058..8371b76 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1491,6 +1491,18 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 	INIT_LIST_HEAD(&hdmi_priv->pcm_list);
 	mutex_init(&hdmi_priv->pin_mutex);
 
+	/*
+	 * Turned off in the runtime_suspend during the first explicit
+	 * pm_runtime_suspend call.
+	 */
+	ret = snd_hdac_display_power(edev->hdac.bus, true);
+	if (ret < 0) {
+		dev_err(&edev->hdac.dev,
+			"Cannot turn on display power on i915 err: %d\n",
+			ret);
+		return ret;
+	}
+
 	ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
 	if (ret < 0) {
 		dev_err(&codec->dev,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 14/15] ASoC: hdac_hdmi: Fix to reconfigure registers in runtime resume
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (12 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 13/15] ASoC: hdac_hdmi: Fix to keep codec power active during enumeration Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  2016-02-12  2:16 ` [PATCH v6 15/15] ASoC: hdac_hdmi: Fix to wait for D3 before powering off codec Subhransu S. Prusty
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

The registers seeem to be reset during the D3 to D0 transition. So
reconfigure them.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 8371b76..0baf504 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -374,11 +374,6 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
 	struct hdac_ext_dma_params *dd;
 	int ret;
 
-	if (dai->id > 0) {
-		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
-		return -ENODEV;
-	}
-
 	dai_map = &hdmi->dai_map[dai->id];
 
 	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
@@ -452,6 +447,18 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
+		struct hdac_hdmi_dai_pin_map *dai_map)
+{
+	/* Enable transmission */
+	snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
+			AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+	/* Category Code (CC) to zero */
+	snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
+			AC_VERB_SET_DIGI_CONVERT_2, 0);
+}
+
 static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
 		struct hdac_hdmi_dai_pin_map *dai_map)
 {
@@ -581,6 +588,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 
 	dai_map->pin = pin;
 
+	hdac_hdmi_enable_cvt(hdac, dai_map);
 	ret = hdac_hdmi_enable_pin(hdac, dai_map);
 	if (ret < 0)
 		return ret;
@@ -970,14 +978,6 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
 		dai_map->dai_id = dai_id;
 		dai_map->cvt = cvt;
 
-		/* Enable transmission */
-		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-				AC_VERB_SET_DIGI_CONVERT_1, 1);
-
-		/* Category Code (CC) to zero */
-		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-				AC_VERB_SET_DIGI_CONVERT_2, 0);
-
 		dai_id++;
 
 		if (dai_id == HDA_MAX_CVTS) {
@@ -1592,6 +1592,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
 		return err;
 	}
 
+	hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+	hdac_hdmi_skl_enable_dp12(&edev->hdac);
+
 	/* Power up afg */
 	if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
 		snd_hdac_codec_write(hdac, hdac->afg, 0,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v6 15/15] ASoC: hdac_hdmi: Fix to wait for D3 before powering off codec
  2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
                   ` (13 preceding siblings ...)
  2016-02-12  2:16 ` [PATCH v6 14/15] ASoC: hdac_hdmi: Fix to reconfigure registers in runtime resume Subhransu S. Prusty
@ 2016-02-12  2:16 ` Subhransu S. Prusty
  14 siblings, 0 replies; 29+ messages in thread
From: Subhransu S. Prusty @ 2016-02-12  2:16 UTC (permalink / raw)
  To: alsa-devel
  Cc: tiwai, lgirdwood, patches.audio, broonie, Vinod Koul,
	Subhransu S. Prusty

Powering off codec immediately after sending D3 verb may not set
the node to D3 state. So wait for a confirmation response before
shutting down codec.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 0baf504..a4cf0b9 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1551,6 +1551,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
 	struct hdac_ext_device *edev = to_hda_ext_device(dev);
 	struct hdac_device *hdac = &edev->hdac;
 	struct hdac_bus *bus = hdac->bus;
+	unsigned long timeout;
 	int err;
 
 	dev_dbg(dev, "Enter: %s\n", __func__);
@@ -1560,10 +1561,19 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
 		return 0;
 
 	/* Power down afg */
-	if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
+	if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) {
 		snd_hdac_codec_write(hdac, hdac->afg, 0,
 			AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 
+		/* Wait till power state is set to D3 */
+		timeout = jiffies + msecs_to_jiffies(1000);
+		while(!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)
+				&& time_before(jiffies, timeout)) {
+
+			msleep(50);
+		}
+	}
+
 	err = snd_hdac_display_power(bus, false);
 	if (err < 0) {
 		dev_err(bus->dev, "Cannot turn on display power on i915\n");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins
  2016-02-12  2:16 ` [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins Subhransu S. Prusty
@ 2016-02-15 20:10   ` Mark Brown
  2016-02-15 22:31     ` Takashi Iwai
  0 siblings, 1 reply; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:10 UTC (permalink / raw)
  To: Subhransu S. Prusty
  Cc: tiwai, patches.audio, alsa-devel, Vinod Koul, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 635 bytes --]

On Fri, Feb 12, 2016 at 07:46:03AM +0530, Subhransu S. Prusty wrote:

> +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
> +{
> +	unsigned int vendor_param;
> +
> +	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
> +				INTEL_GET_VENDOR_VERB, 0);
> +	if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
> +		return;
> +
> +	vendor_param |= INTEL_EN_ALL_PIN_CVTS;
> +	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
> +				INTEL_SET_VENDOR_VERB, vendor_param);
> +	if (vendor_param == -1)
> +		return;
> +}

So to enable the pins we do a read?  That seems...  innovative.  :/

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Applied "drm/edid: Add API to help find connection type" to the asoc tree
  2016-02-12  2:16 ` [PATCH v6 08/15] drm/edid: Add API to help find connection type Subhransu S. Prusty
@ 2016-02-15 20:25   ` Mark Brown
  0 siblings, 0 replies; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:25 UTC (permalink / raw)
  To: Subhransu S. Prusty, Vinod Koul, David Airlie, dri-devel,
	Daniel Vetter, Mark Brown
  Cc: alsa-devel

The patch

   drm/edid: Add API to help find connection type

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

From 1aa8ec255370d3e6f60dfa4a8cdd5e1357b1ab78 Mon Sep 17 00:00:00 2001
From: "Subhransu S. Prusty" <subhransu.s.prusty@intel.com>
Date: Fri, 12 Feb 2016 07:46:08 +0530
Subject: [PATCH] drm/edid: Add API to help find connection type

To fill the audio infoframe it is required to identify the
connection type as DP or HDMI. This patch adds an API which
parses ELD and returns the display type connected.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Cc: David Airlie <airlied@linux.ie>
Cc: dri-devel@lists.freedesktop.org
Cc: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 include/drm/drm_edid.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 2af9769..dec6221 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld)
 	return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
 }
 
+/**
+ * drm_eld_get_conn_type - Get device type hdmi/dp connected
+ * @eld: pointer to an ELD memory structure
+ *
+ * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to
+ * identify the display type connected.
+ */
+static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
+{
+	return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
+}
+
 struct edid *drm_do_get_edid(struct drm_connector *connector,
 	int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
 			      size_t len),
-- 
2.7.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy
  2016-02-12  2:16 ` [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy Subhransu S. Prusty
@ 2016-02-15 20:55   ` Mark Brown
  2016-02-16  3:36     ` Vinod Koul
  0 siblings, 1 reply; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:55 UTC (permalink / raw)
  To: Subhransu S. Prusty
  Cc: tiwai, patches.audio, alsa-devel, Vinod Koul, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 374 bytes --]

On Fri, Feb 12, 2016 at 07:46:11AM +0530, Subhransu S. Prusty wrote:
> In dai startup, driver was checking for ELD and would fail if no
> monitor is connected. This causes userland like PA, CRAS to be
> unhappy as they scan the device list at bootup.
> 
> So move the ELD check to hw_params and fail, if valid ELD is not
> found.

Should this not be sent as a fix?

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params
  2016-02-12  2:16 ` [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params Subhransu S. Prusty
@ 2016-02-15 20:57   ` Mark Brown
  2016-02-16  2:58     ` Vinod Koul
  0 siblings, 1 reply; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:57 UTC (permalink / raw)
  To: Subhransu S. Prusty
  Cc: tiwai, patches.audio, alsa-devel, Vinod Koul, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 440 bytes --]

On Fri, Feb 12, 2016 at 07:46:10AM +0530, Subhransu S. Prusty wrote:
> It's possible for hw_params to be called two times. So add NULL
> check to prevent memory leak.

Another fix?

> -	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
> -	if (!dd)
> -		return -ENOMEM;
> +	dd = (struct hdac_ext_dma_params *)
> +		snd_soc_dai_get_dma_data(dai, substream);

If you need to cast away from void * that suggests a problem and it'd
look a lot neater too.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Applied "ASoC: hdac_hdmi: Enable playback on all enumerated ports" to the asoc tree
  2016-02-12  2:16 ` [PATCH v6 07/15] ASoC: hdac_hdmi: Enable playback on all enumerated ports Subhransu S. Prusty
@ 2016-02-15 20:58   ` Mark Brown
  0 siblings, 0 replies; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:58 UTC (permalink / raw)
  To: Subhransu S. Prusty, Jeeja KP, Vinod Koul, Mark Brown; +Cc: alsa-devel

The patch

   ASoC: hdac_hdmi: Enable playback on all enumerated ports

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 148569fddbc6d3ce97236c4344068b1293e51838 Mon Sep 17 00:00:00 2001
From: "Subhransu S. Prusty" <subhransu.s.prusty@intel.com>
Date: Fri, 12 Feb 2016 07:46:07 +0530
Subject: [PATCH] ASoC: hdac_hdmi: Enable playback on all enumerated ports

Last patch added infrastructure to render over all the ports,
PCM<->cvt<- pin mapping and user selection of controls. But we
still have restriction of playback on the default port alone, so
remove that.

This patch removes the hardcoding of cvt<->pin map from the dai.
Cvt and pin for a dai are now derived from the already stored pcm
list of device opened.  We query connection list of a pin from
codec to validate the cvt<->pin map.

If connection list returns zero, then monitor is not connected so
fail playback.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Jeeja KP <jeeja.kp@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 | 174 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 132 insertions(+), 42 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 8391486..dcd1cb6 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -40,6 +40,8 @@
 
 #define HDA_MAX_CONNECTIONS     32
 
+#define HDA_MAX_CVTS		3
+
 #define ELD_MAX_SIZE    256
 #define ELD_FIXED_BYTES	20
 
@@ -91,7 +93,7 @@ struct hdac_hdmi_dai_pin_map {
 };
 
 struct hdac_hdmi_priv {
-	struct hdac_hdmi_dai_pin_map dai_map[3];
+	struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
 	struct list_head pin_list;
 	struct list_head cvt_list;
 	struct list_head pcm_list;
@@ -384,44 +386,136 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
+		struct hdac_hdmi_dai_pin_map *dai_map)
+{
+	int mux_idx;
+	struct hdac_hdmi_pin *pin = dai_map->pin;
+
+	for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
+		if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
+			snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+					AC_VERB_SET_CONNECT_SEL, mux_idx);
+			break;
+		}
+	}
+
+	if (mux_idx == pin->num_mux_nids)
+		return -EIO;
+
+	/* Enable out path for this pin widget */
+	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+
+	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+	return 0;
+}
+
+static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
+					struct hdac_hdmi_pin *pin)
+{
+	if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
+		dev_warn(&hdac->hdac.dev,
+			"HDMI: pin %d wcaps %#x does not support connection list\n",
+			pin->nid, get_wcaps(&hdac->hdac, pin->nid));
+		return -EINVAL;
+	}
+
+	pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
+			pin->mux_nids, HDA_MAX_CONNECTIONS);
+	if (pin->num_mux_nids == 0)
+		dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
+								pin->nid);
+
+	dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
+			pin->num_mux_nids, pin->nid);
+
+	return pin->num_mux_nids;
+}
+
+/*
+ * Query pcm list and return pin widget to which stream is routed.
+ *
+ * Also query connection list of the pin, to validate the cvt to pin map.
+ *
+ * Same stream rendering to multiple pins simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first pin
+ * connected.
+ */
+static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
+			struct hdac_ext_device *edev,
+			struct hdac_hdmi_priv *hdmi,
+			struct hdac_hdmi_cvt *cvt)
+{
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_pin *pin = NULL;
+	int ret, i;
+
+	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+		if (pcm->cvt == cvt) {
+			pin = pcm->pin;
+			break;
+		}
+	}
+
+	if (pin) {
+		ret = hdac_hdmi_query_pin_connlist(edev, pin);
+		if (ret < 0)
+			return NULL;
+
+		for (i = 0; i < pin->num_mux_nids; i++) {
+			if (pin->mux_nids[i] == cvt->nid)
+				return pin;
+		}
+	}
+
+	return NULL;
+}
+
 static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
 	struct hdac_hdmi_dai_pin_map *dai_map;
+	struct hdac_hdmi_cvt *cvt;
+	struct hdac_hdmi_pin *pin;
 	int ret;
 
-	if (dai->id > 0) {
-		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
-		return -ENODEV;
-	}
-
 	dai_map = &hdmi->dai_map[dai->id];
 
-	if ((!dai_map->pin->eld.monitor_present) ||
-			(!dai_map->pin->eld.eld_valid)) {
+	cvt = dai_map->cvt;
+	pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
+	if (!pin)
+		return -EIO;
+
+	if ((!pin->eld.monitor_present) ||
+			(!pin->eld.eld_valid)) {
 
 		dev_err(&hdac->hdac.dev,
-				"Failed: montior present? %d ELD valid?: %d\n",
-				dai_map->pin->eld.monitor_present,
-				dai_map->pin->eld.eld_valid);
+			"Failed: montior present? %d ELD valid?: %d for pin: %d\n",
+			pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
 
 		return -ENODEV;
 	}
 
-	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+	dai_map->pin = pin;
 
-	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
-			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	ret = hdac_hdmi_enable_pin(hdac, dai_map);
+	if (ret < 0)
+		return ret;
 
 	ret = hdac_hdmi_eld_limit_formats(substream->runtime,
-				dai_map->pin->eld.eld_buffer);
+				pin->eld.eld_buffer);
 	if (ret < 0)
 		return ret;
 
 	return snd_pcm_hw_constraint_eld(substream->runtime,
-				dai_map->pin->eld.eld_buffer);
+				pin->eld.eld_buffer);
 }
 
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
@@ -437,6 +531,8 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 
 	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+	dai_map->pin = NULL;
 }
 
 static int
@@ -759,40 +855,34 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
 static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
 {
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
-	struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
+	struct hdac_hdmi_dai_pin_map *dai_map;
 	struct hdac_hdmi_cvt *cvt;
-	struct hdac_hdmi_pin *pin;
+	int dai_id = 0;
 
-	if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
+	if (list_empty(&hdmi->cvt_list))
 		return -EINVAL;
 
-	/*
-	 * Currently on board only 1 pin and 1 converter is enabled for
-	 * simplification, more will be added eventually
-	 * So using fixed map for dai_id:pin:cvt
-	 */
-	cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
-	pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
-
-	dai_map->dai_id = 0;
-	dai_map->pin = pin;
+	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+		dai_map = &hdmi->dai_map[dai_id];
+		dai_map->dai_id = dai_id;
+		dai_map->cvt = cvt;
 
-	dai_map->cvt = cvt;
+		/* Enable transmission */
+		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+				AC_VERB_SET_DIGI_CONVERT_1, 1);
 
-	/* Enable out path for this pin widget */
-	snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
-			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		/* Category Code (CC) to zero */
+		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+				AC_VERB_SET_DIGI_CONVERT_2, 0);
 
-	/* Enable transmission */
-	snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-			AC_VERB_SET_DIGI_CONVERT_1, 1);
+		dai_id++;
 
-	/* Category Code (CC) to zero */
-	snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-			AC_VERB_SET_DIGI_CONVERT_2, 0);
-
-	snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
-			AC_VERB_SET_CONNECT_SEL, 0);
+		if (dai_id == HDA_MAX_CVTS) {
+			dev_warn(&edev->hdac.dev,
+				"Max dais supported: %d\n", dai_id);
+			break;
+		}
+	}
 
 	return 0;
 }
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Applied "ASoC: hdac_hdmi: Add jack reporting" to the asoc tree
  2016-02-12  2:16 ` [PATCH v6 06/15] ASoC: hdac_hdmi: Add jack reporting Subhransu S. Prusty
@ 2016-02-15 20:58   ` Mark Brown
  0 siblings, 0 replies; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:58 UTC (permalink / raw)
  To: Jeeja KP, Subhransu S. Prusty, Vinod Koul, Mark Brown; +Cc: alsa-devel

The patch

   ASoC: hdac_hdmi: Add jack reporting

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 4a3478debf36c0aa0cf0860daec245b13cd4448f Mon Sep 17 00:00:00 2001
From: Jeeja KP <jeeja.kp@intel.com>
Date: Fri, 12 Feb 2016 07:46:06 +0530
Subject: [PATCH] ASoC: hdac_hdmi: Add jack reporting

Jack is created based on pcm devices enumerated, so we will
create Jack as "HDMI/DP, pcm=x Jack". This style is expected by
current usermode like PulseAudio and CRAS.

This patch exports an API which can be used to register Jack
based on PCM. This API also establishes the map between PCM and
cvt. Further cvt to pin mapping is established with the help of
usermode selection based on the board topology.

During device probe as the PCMs may not be registered, initial
pin sense don't report jack events. So, first time jack reporting
is done during user selection of mux control.

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
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 | 160 ++++++++++++++++++++++++++++++++++++++++++-
 sound/soc/codecs/hdac_hdmi.h |   6 ++
 2 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 sound/soc/codecs/hdac_hdmi.h

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 6fb44c4..8391486 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -24,11 +24,13 @@
 #include <linux/hdmi.h>
 #include <drm/drm_edid.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_i915.h>
 #include <sound/pcm_drm_eld.h>
 #include "../../hda/local.h"
+#include "hdac_hdmi.h"
 
 #define NAME_SIZE	32
 
@@ -52,6 +54,7 @@ struct hdac_hdmi_cvt_params {
 struct hdac_hdmi_cvt {
 	struct list_head head;
 	hda_nid_t nid;
+	const char *name;
 	struct hdac_hdmi_cvt_params params;
 };
 
@@ -73,6 +76,14 @@ struct hdac_hdmi_pin {
 	struct delayed_work work;
 };
 
+struct hdac_hdmi_pcm {
+	struct list_head head;
+	int pcm_id;
+	struct hdac_hdmi_pin *pin;
+	struct hdac_hdmi_cvt *cvt;
+	struct snd_jack *jack;
+};
+
 struct hdac_hdmi_dai_pin_map {
 	int dai_id;
 	struct hdac_hdmi_pin *pin;
@@ -83,8 +94,10 @@ struct hdac_hdmi_priv {
 	struct hdac_hdmi_dai_pin_map dai_map[3];
 	struct list_head pin_list;
 	struct list_head cvt_list;
+	struct list_head pcm_list;
 	int num_pin;
 	int num_cvt;
+	struct mutex pin_mutex;
 };
 
 static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
@@ -478,6 +491,67 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
 	route->connected = handler;
 }
 
+static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
+					struct hdac_hdmi_pin *pin)
+{
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm = NULL;
+
+	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+		if (pcm->pin == pin)
+			return pcm;
+	}
+
+	return NULL;
+}
+
+/*
+ * Based on user selection, map the PINs with the PCMs.
+ */
+static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int ret;
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct hdac_hdmi_pin *pin = w->priv;
+	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm = NULL;
+	const char *cvt_name =  e->texts[ucontrol->value.enumerated.item[0]];
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&hdmi->pin_mutex);
+	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+		if (pcm->pin == pin)
+			pcm->pin = NULL;
+
+		/*
+		 * Jack status is not reported during device probe as the
+		 * PCMs are not registered by then. So report it here.
+		 */
+		if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
+			pcm->pin = pin;
+			if (pin->eld.monitor_present && pin->eld.eld_valid) {
+				dev_dbg(&edev->hdac.dev,
+					"jack report for pcm=%d\n",
+					pcm->pcm_id);
+
+				snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+			}
+			mutex_unlock(&hdmi->pin_mutex);
+			return ret;
+		}
+	}
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return ret;
+}
+
 /*
  * Ideally the Mux inputs should be based on the num_muxs enumerated, but
  * the display driver seem to be programming the connection list for the pin
@@ -520,7 +594,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
 	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 	kc->access = 0;
 	kc->info = snd_soc_info_enum_double;
-	kc->put = snd_soc_dapm_put_enum_double;
+	kc->put = hdac_hdmi_set_pin_mux;
 	kc->get = snd_soc_dapm_get_enum_double;
 
 	se->reg = SND_SOC_NOPM;
@@ -548,8 +622,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
 		return -ENOMEM;
 
 	return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
-			snd_soc_dapm_mux, &pin->nid, widget_name,
-			NULL, kc, 1);
+			snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
 }
 
 /* Add cvt <- input <- mux route map */
@@ -728,12 +801,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 {
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	struct hdac_hdmi_cvt *cvt;
+	char name[NAME_SIZE];
 
 	cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
 	if (!cvt)
 		return -ENOMEM;
 
 	cvt->nid = nid;
+	sprintf(name, "cvt %d", cvt->nid);
+	cvt->name = kstrdup(name, GFP_KERNEL);
 
 	list_add_tail(&cvt->head, &hdmi->cvt_list);
 	hdmi->num_cvt++;
@@ -744,6 +820,8 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 {
 	struct hdac_ext_device *edev = pin->edev;
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm;
 	int val;
 
 	if (!edev)
@@ -758,13 +836,31 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 	dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
 						val, pin->nid);
 
+
+	mutex_lock(&hdmi->pin_mutex);
 	pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
 	pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
 
+	pcm = hdac_hdmi_get_pcm(edev, pin);
+
 	if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
 
 		dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
 						__func__, pin->nid);
+
+		/*
+		 * PCMs are not registered during device probe, so don't
+		 * report jack here. It will be done in usermode mux
+		 * control select.
+		 */
+		if (pcm) {
+			dev_dbg(&edev->hdac.dev,
+				"jack report for pcm=%d\n", pcm->pcm_id);
+
+			snd_jack_report(pcm->jack, 0);
+		}
+
+		mutex_unlock(&hdmi->pin_mutex);
 		goto put_hdac_device;
 	}
 
@@ -774,14 +870,32 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 				pin->eld.eld_buffer,
 				&pin->eld.eld_size) == 0) {
 
+			if (pcm) {
+				dev_dbg(&edev->hdac.dev,
+					"jack report for pcm=%d\n",
+					pcm->pcm_id);
+
+				snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+			}
+
 			print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
 					pin->eld.eld_buffer, pin->eld.eld_size);
 		} else {
 			pin->eld.monitor_present = false;
 			pin->eld.eld_valid = false;
+
+			if (pcm) {
+				dev_dbg(&edev->hdac.dev,
+					"jack report for pcm=%d\n",
+					pcm->pcm_id);
+
+				snd_jack_report(pcm->jack, 0);
+			}
 		}
 	}
 
+	mutex_unlock(&hdmi->pin_mutex);
+
 	/*
 	 * Sometimes the pin_sense may present invalid monitor
 	 * present and eld_valid. If ELD data is not valid, loop few
@@ -1039,6 +1153,35 @@ static struct i915_audio_component_audio_ops aops = {
 	.pin_eld_notify	= hdac_hdmi_eld_notify_cb,
 };
 
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
+{
+	char jack_name[NAME_SIZE];
+	struct snd_soc_codec *codec = dai->codec;
+	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_component_get_dapm(&codec->component);
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pcm *pcm;
+
+	/*
+	 * this is a new PCM device, create new pcm and
+	 * add to the pcm list
+	 */
+	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+	if (!pcm)
+		return -ENOMEM;
+	pcm->pcm_id = device;
+	pcm->cvt = hdmi->dai_map[dai->id].cvt;
+
+	list_add_tail(&pcm->head, &hdmi->pcm_list);
+
+	sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
+
+	return snd_jack_new(dapm->card->snd_card, jack_name,
+		SND_JACK_AVOUT,	&pcm->jack, true, false);
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
+
 static int hdmi_codec_probe(struct snd_soc_codec *codec)
 {
 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
@@ -1111,6 +1254,8 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 
 	INIT_LIST_HEAD(&hdmi_priv->pin_list);
 	INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+	INIT_LIST_HEAD(&hdmi_priv->pcm_list);
+	mutex_init(&hdmi_priv->pin_mutex);
 
 	ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
 	if (ret < 0) {
@@ -1129,11 +1274,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	struct hdac_hdmi_pin *pin, *pin_next;
 	struct hdac_hdmi_cvt *cvt, *cvt_next;
+	struct hdac_hdmi_pcm *pcm, *pcm_next;
 
 	snd_soc_unregister_codec(&edev->hdac.dev);
 
+	list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
+		pcm->cvt = NULL;
+		pcm->pin = NULL;
+		list_del(&pcm->head);
+		kfree(pcm);
+	}
+
 	list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
 		list_del(&cvt->head);
+		kfree(cvt->name);
 		kfree(cvt);
 	}
 
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
new file mode 100644
index 0000000..8dfd1e0
--- /dev/null
+++ b/sound/soc/codecs/hdac_hdmi.h
@@ -0,0 +1,6 @@
+#ifndef __HDAC_HDMI_H__
+#define __HDAC_HDMI_H__
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
+
+#endif /* __HDAC_HDMI_H__ */
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Applied "ASoC: hdac_hdmi: Apply constraints based on ELD" to the asoc tree
  2016-02-12  2:16 ` [PATCH v6 02/15] ASoC: hdac_hdmi: Apply constraints based on ELD Subhransu S. Prusty
@ 2016-02-15 20:58   ` Mark Brown
  0 siblings, 0 replies; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:58 UTC (permalink / raw)
  To: Subhransu S. Prusty, Vinod Koul, Mark Brown; +Cc: alsa-devel

The patch

   ASoC: hdac_hdmi: Apply constraints based on ELD

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 2428bca305e082885e08e7f3399db616daf3a51a Mon Sep 17 00:00:00 2001
From: "Subhransu S. Prusty" <subhransu.s.prusty@intel.com>
Date: Fri, 12 Feb 2016 07:46:02 +0530
Subject: [PATCH] ASoC: hdac_hdmi: Apply constraints based on ELD

Uses the drm ELD core framework to apply rate and channel

Also compute the format to be set based on ELD.

Even though the channel constraint is based on ELD, infoframe
is set with stereo only. Multichannel support will be added
later.

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/Kconfig     |  1 +
 sound/soc/codecs/hdac_hdmi.c | 51 +++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 50693c8..8c86239c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -490,6 +490,7 @@ config SND_SOC_GTM601
 config SND_SOC_HDAC_HDMI
 	tristate
 	select SND_HDA_EXT_CORE
+	select SND_PCM_ELD
 	select HDMI
 
 config SND_SOC_ICS43432
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index fff225d..d22fa6b 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -22,10 +22,12 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/hdmi.h>
+#include <drm/drm_edid.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_i915.h>
+#include <sound/pcm_drm_eld.h>
 #include "../../hda/local.h"
 
 #define AMP_OUT_MUTE		0xb080
@@ -90,6 +92,45 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 	return to_ehdac_device(hdac);
 }
 
+static unsigned int sad_format(const u8 *sad)
+{
+	return ((sad[0] >> 0x3) & 0x1f);
+}
+
+static unsigned int sad_sample_bits_lpcm(const u8 *sad)
+{
+	return (sad[2] & 7);
+}
+
+static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
+						void *eld)
+{
+	u64 formats = SNDRV_PCM_FMTBIT_S16;
+	int i;
+	const u8 *sad, *eld_buf = eld;
+
+	sad = drm_eld_sad(eld_buf);
+	if (!sad)
+		goto format_constraint;
+
+	for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
+		if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
+
+			/*
+			 * the controller support 20 and 24 bits in 32 bit
+			 * container so we set S32
+			 */
+			if (sad_sample_bits_lpcm(sad) & 0x6)
+				formats |= SNDRV_PCM_FMTBIT_S32;
+		}
+	}
+
+format_constraint:
+	return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+				formats);
+
+}
+
  /* HDMI ELD routines */
 static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
 				hda_nid_t nid, int byte_index)
@@ -334,6 +375,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
 	struct hdac_hdmi_dai_pin_map *dai_map;
+	int ret;
 
 	if (dai->id > 0) {
 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
@@ -358,10 +400,13 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 	snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
 			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
 
-	snd_pcm_hw_constraint_step(substream->runtime, 0,
-				   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+	ret = hdac_hdmi_eld_limit_formats(substream->runtime,
+				dai_map->pin->eld.eld_buffer);
+	if (ret < 0)
+		return ret;
 
-	return 0;
+	return snd_pcm_hw_constraint_eld(substream->runtime,
+				dai_map->pin->eld.eld_buffer);
 }
 
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Applied "ASoC: hdac_hdmi: Add hotplug notification and read ELD" to the asoc tree
  2016-02-12  2:16 ` [PATCH v6 01/15] ASoC: hdac_hdmi: Add hotplug notification and read ELD Subhransu S. Prusty
@ 2016-02-15 20:58   ` Mark Brown
  0 siblings, 0 replies; 29+ messages in thread
From: Mark Brown @ 2016-02-15 20:58 UTC (permalink / raw)
  To: Subhransu S. Prusty, Vinod Koul, Mark Brown; +Cc: alsa-devel

The patch

   ASoC: hdac_hdmi: Add hotplug notification and read ELD

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From b8a54545b00cbf2aff743578c5c69aafdcde1d64 Mon Sep 17 00:00:00 2001
From: "Subhransu S. Prusty" <subhransu.s.prusty@intel.com>
Date: Fri, 12 Feb 2016 07:46:01 +0530
Subject: [PATCH] ASoC: hdac_hdmi: Add hotplug notification and read ELD

This patch uses i915 component framework to register for hotplug
notification.

In the hotplug notification, driver reads pin sense and ELD by
sending PIN_SENSE and ELD verbs over HDA bus. Once it identifies
valid pin sense and valid ELD, store the ELD into the
corresponding pin map buffer.

Also read the monitor present sense during resume and ignore the
ELD notify from graphics during PM as is done in legacy hda,
commit 8ae743e82f0b ("ALSA: hda - Skip ELD notification during
system suspend")

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 | 217 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 211 insertions(+), 6 deletions(-)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 5a1ec0f..fff225d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -34,6 +34,9 @@
 
 #define HDA_MAX_CONNECTIONS     32
 
+#define ELD_MAX_SIZE    256
+#define ELD_FIXED_BYTES	20
+
 struct hdac_hdmi_cvt_params {
 	unsigned int channels_min;
 	unsigned int channels_max;
@@ -48,11 +51,22 @@ struct hdac_hdmi_cvt {
 	struct hdac_hdmi_cvt_params params;
 };
 
+struct hdac_hdmi_eld {
+	bool	monitor_present;
+	bool	eld_valid;
+	int	eld_size;
+	char    eld_buffer[ELD_MAX_SIZE];
+};
+
 struct hdac_hdmi_pin {
 	struct list_head head;
 	hda_nid_t nid;
 	int num_mux_nids;
 	hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+	struct hdac_hdmi_eld eld;
+	struct hdac_ext_device *edev;
+	int repoll_count;
+	struct delayed_work work;
 };
 
 struct hdac_hdmi_dai_pin_map {
@@ -76,6 +90,80 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 	return to_ehdac_device(hdac);
 }
 
+ /* HDMI ELD routines */
+static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
+				hda_nid_t nid, int byte_index)
+{
+	unsigned int val;
+
+	val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
+							byte_index);
+
+	dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
+					byte_index, val);
+
+	return val;
+}
+
+static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
+{
+	return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+						 AC_DIPSIZE_ELD_BUF);
+}
+
+/*
+ * This function queries the ELD size and ELD data and fills in the buffer
+ * passed by user
+ */
+static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
+			     unsigned char *buf, int *eld_size)
+{
+	int i, size, ret = 0;
+
+	/*
+	 * ELD size is initialized to zero in caller function. If no errors and
+	 * ELD is valid, actual eld_size is assigned.
+	 */
+
+	size = hdac_hdmi_get_eld_size(codec, nid);
+	if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+		dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
+		return -ERANGE;
+	}
+
+	/* set ELD buffer */
+	for (i = 0; i < size; i++) {
+		unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
+		/*
+		 * Graphics driver might be writing to ELD buffer right now.
+		 * Just abort. The caller will repoll after a while.
+		 */
+		if (!(val & AC_ELDD_ELD_VALID)) {
+			dev_err(&codec->dev,
+				"HDMI: invalid ELD data byte %d\n", i);
+			ret = -EINVAL;
+			goto error;
+		}
+		val &= AC_ELDD_ELD_DATA;
+		/*
+		 * The first byte cannot be zero. This can happen on some DVI
+		 * connections. Some Intel chips may also need some 250ms delay
+		 * to return non-zero ELD data, even when the graphics driver
+		 * correctly writes ELD content before setting ELD_valid bit.
+		 */
+		if (!val && !i) {
+			dev_err(&codec->dev, "HDMI: 0 ELD data\n");
+			ret = -EINVAL;
+			goto error;
+		}
+		buf[i] = val;
+	}
+
+	*eld_size = size;
+error:
+	return ret;
+}
+
 static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
 				hda_nid_t cvt_nid, hda_nid_t pin_nid,
 				u32 stream_tag, int format)
@@ -246,7 +334,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
 	struct hdac_hdmi_priv *hdmi = hdac->private_data;
 	struct hdac_hdmi_dai_pin_map *dai_map;
-	int val;
 
 	if (dai->id > 0) {
 		dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
@@ -255,12 +342,14 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 
 	dai_map = &hdmi->dai_map[dai->id];
 
-	val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
-					AC_VERB_GET_PIN_SENSE, 0);
-	dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
+	if ((!dai_map->pin->eld.monitor_present) ||
+			(!dai_map->pin->eld.eld_valid)) {
+
+		dev_err(&hdac->hdac.dev,
+				"Failed: montior present? %d ELD valid?: %d\n",
+				dai_map->pin->eld.monitor_present,
+				dai_map->pin->eld.eld_valid);
 
-	if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
-		dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
 		return -ENODEV;
 	}
 
@@ -410,6 +499,71 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 	return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
 }
 
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
+{
+	struct hdac_ext_device *edev = pin->edev;
+	int val;
+
+	if (!edev)
+		return;
+
+	pin->repoll_count = repoll;
+
+	pm_runtime_get_sync(&edev->hdac.dev);
+	val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
+					AC_VERB_GET_PIN_SENSE, 0);
+
+	dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
+						val, pin->nid);
+
+	pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
+	pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
+
+	if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
+
+		dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
+						__func__, pin->nid);
+		goto put_hdac_device;
+	}
+
+	if (pin->eld.monitor_present && pin->eld.eld_valid) {
+		/* TODO: use i915 component for reading ELD later */
+		if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
+				pin->eld.eld_buffer,
+				&pin->eld.eld_size) == 0) {
+
+			print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
+					pin->eld.eld_buffer, pin->eld.eld_size);
+		} else {
+			pin->eld.monitor_present = false;
+			pin->eld.eld_valid = false;
+		}
+	}
+
+	/*
+	 * Sometimes the pin_sense may present invalid monitor
+	 * present and eld_valid. If ELD data is not valid, loop few
+	 * more times to get correct pin sense and valid ELD.
+	 */
+	if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
+		schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
+
+put_hdac_device:
+	pm_runtime_put_sync(&edev->hdac.dev);
+}
+
+static void hdac_hdmi_repoll_eld(struct work_struct *work)
+{
+	struct hdac_hdmi_pin *pin =
+		container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
+
+	/* picked from legacy HDA driver */
+	if (pin->repoll_count++ > 6)
+		pin->repoll_count = 0;
+
+	hdac_hdmi_present_sense(pin, pin->repoll_count);
+}
+
 static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 {
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
@@ -424,6 +578,9 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 	list_add_tail(&pin->head, &hdmi->pin_list);
 	hdmi->num_pin++;
 
+	pin->edev = edev;
+	INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
+
 	return 0;
 }
 
@@ -482,17 +639,65 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
 	return hdac_hdmi_init_dai_map(edev);
 }
 
+static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
+{
+	struct hdac_ext_device *edev = aptr;
+	struct hdac_hdmi_priv *hdmi = edev->private_data;
+	struct hdac_hdmi_pin *pin;
+	struct snd_soc_codec *codec = edev->scodec;
+
+	/* Don't know how this mapping is derived */
+	hda_nid_t pin_nid = port + 0x04;
+
+	dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
+
+	/*
+	 * skip notification during system suspend (but not in runtime PM);
+	 * the state will be updated at resume. Also since the ELD and
+	 * connection states are updated in anyway at the end of the resume,
+	 * we can skip it when received during PM process.
+	 */
+	if (snd_power_get_state(codec->component.card->snd_card) !=
+			SNDRV_CTL_POWER_D0)
+		return;
+
+	if (atomic_read(&edev->hdac.in_pm))
+		return;
+
+	list_for_each_entry(pin, &hdmi->pin_list, head) {
+		if (pin->nid == pin_nid)
+			hdac_hdmi_present_sense(pin, 1);
+	}
+}
+
+static struct i915_audio_component_audio_ops aops = {
+	.pin_eld_notify	= hdac_hdmi_eld_notify_cb,
+};
+
 static int hdmi_codec_probe(struct snd_soc_codec *codec)
 {
 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
 	struct hdac_hdmi_priv *hdmi = edev->private_data;
 	struct snd_soc_dapm_context *dapm =
 		snd_soc_component_get_dapm(&codec->component);
+	struct hdac_hdmi_pin *pin;
+	int ret;
 
 	edev->scodec = codec;
 
 	create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
 
+	aops.audio_ptr = edev;
+	ret = snd_hdac_i915_register_notifier(&aops);
+	if (ret < 0) {
+		dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
+				ret);
+		return ret;
+	}
+
+	list_for_each_entry(pin, &hdmi->pin_list, head)
+		hdac_hdmi_present_sense(pin, 1);
+
 	/* Imp: Store the card pointer in hda_codec */
 	edev->card = dapm->card->snd_card;
 
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins
  2016-02-15 20:10   ` Mark Brown
@ 2016-02-15 22:31     ` Takashi Iwai
  2016-02-16  2:00       ` Mark Brown
  0 siblings, 1 reply; 29+ messages in thread
From: Takashi Iwai @ 2016-02-15 22:31 UTC (permalink / raw)
  To: Mark Brown
  Cc: patches.audio, Vinod Koul, alsa-devel, Subhransu S. Prusty, lgirdwood

On Mon, 15 Feb 2016 21:10:49 +0100,
Mark Brown wrote:
> 
> On Fri, Feb 12, 2016 at 07:46:03AM +0530, Subhransu S. Prusty wrote:
> 
> > +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
> > +{
> > +	unsigned int vendor_param;
> > +
> > +	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
> > +				INTEL_GET_VENDOR_VERB, 0);
> > +	if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
> > +		return;
> > +
> > +	vendor_param |= INTEL_EN_ALL_PIN_CVTS;
> > +	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
> > +				INTEL_SET_VENDOR_VERB, vendor_param);
> > +	if (vendor_param == -1)
> > +		return;
> > +}
> 
> So to enable the pins we do a read?  That seems...  innovative.  :/

It's a weird nature of HD-audio verb handling.  While *_write() just
sends the verb asynchronously, *_read() sends the verb, does sync and
read-back the return value.  But both read and write may handle the
same verb.


Takashi

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins
  2016-02-15 22:31     ` Takashi Iwai
@ 2016-02-16  2:00       ` Mark Brown
  2016-02-16  8:16         ` Takashi Iwai
  0 siblings, 1 reply; 29+ messages in thread
From: Mark Brown @ 2016-02-16  2:00 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: patches.audio, Vinod Koul, alsa-devel, Subhransu S. Prusty, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 815 bytes --]

On Mon, Feb 15, 2016 at 11:31:48PM +0100, Takashi Iwai wrote:
> Mark Brown wrote:
> > On Fri, Feb 12, 2016 at 07:46:03AM +0530, Subhransu S. Prusty wrote:

> > > +	vendor_param |= INTEL_EN_ALL_PIN_CVTS;
> > > +	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
> > > +				INTEL_SET_VENDOR_VERB, vendor_param);
> > > +	if (vendor_param == -1)
> > > +		return;
> > > +}

> > So to enable the pins we do a read?  That seems...  innovative.  :/

> It's a weird nature of HD-audio verb handling.  While *_write() just
> sends the verb asynchronously, *_read() sends the verb, does sync and
> read-back the return value.  But both read and write may handle the
> same verb.

The above one seems especially odd, we do the read and then essentially
ignore the value (the difference in handling is nonexistant).

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params
  2016-02-15 20:57   ` Mark Brown
@ 2016-02-16  2:58     ` Vinod Koul
  0 siblings, 0 replies; 29+ messages in thread
From: Vinod Koul @ 2016-02-16  2:58 UTC (permalink / raw)
  To: Mark Brown
  Cc: tiwai, patches.audio, alsa-devel, Subhransu S. Prusty, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 783 bytes --]

On Mon, Feb 15, 2016 at 08:57:26PM +0000, Mark Brown wrote:
> On Fri, Feb 12, 2016 at 07:46:10AM +0530, Subhransu S. Prusty wrote:
> > It's possible for hw_params to be called two times. So add NULL
> > check to prevent memory leak.
> 
> Another fix?

Yes but can go in 4.6 as HDMI codec does not get created in 4.5 code. That
is why I didn't send this one in last fixes series

> 
> > -	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
> > -	if (!dd)
> > -		return -ENOMEM;
> > +	dd = (struct hdac_ext_dma_params *)
> > +		snd_soc_dai_get_dma_data(dai, substream);
> 
> If you need to cast away from void * that suggests a problem and it'd
> look a lot neater too.

I suspect there is no need for cast, we seem to ahve a habit for that, let
us check that

-- 
~Vinod

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy
  2016-02-15 20:55   ` Mark Brown
@ 2016-02-16  3:36     ` Vinod Koul
  0 siblings, 0 replies; 29+ messages in thread
From: Vinod Koul @ 2016-02-16  3:36 UTC (permalink / raw)
  To: Mark Brown
  Cc: tiwai, patches.audio, alsa-devel, Subhransu S. Prusty, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 754 bytes --]

On Mon, Feb 15, 2016 at 08:55:21PM +0000, Mark Brown wrote:
> On Fri, Feb 12, 2016 at 07:46:11AM +0530, Subhransu S. Prusty wrote:
> > In dai startup, driver was checking for ELD and would fail if no
> > monitor is connected. This causes userland like PA, CRAS to be
> > unhappy as they scan the device list at bootup.
> > 
> > So move the ELD check to hw_params and fail, if valid ELD is not
> > found.
> 
> Should this not be sent as a fix?

Same reason here as well. In 4.5 since this series is missing we do not
enumerate the codecs and machine does not have these dailinks.

Btw SKL driver does the enumeration on HDA links and creates codecs, that
patch along with machine will be last one after this series.

Thanks
-- 
~Vinod

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins
  2016-02-16  2:00       ` Mark Brown
@ 2016-02-16  8:16         ` Takashi Iwai
  0 siblings, 0 replies; 29+ messages in thread
From: Takashi Iwai @ 2016-02-16  8:16 UTC (permalink / raw)
  To: Mark Brown
  Cc: patches.audio, Vinod Koul, alsa-devel, Subhransu S. Prusty, lgirdwood

On Tue, 16 Feb 2016 03:00:26 +0100,
Mark Brown wrote:
> 
> On Mon, Feb 15, 2016 at 11:31:48PM +0100, Takashi Iwai wrote:
> > Mark Brown wrote:
> > > On Fri, Feb 12, 2016 at 07:46:03AM +0530, Subhransu S. Prusty wrote:
> 
> > > > +	vendor_param |= INTEL_EN_ALL_PIN_CVTS;
> > > > +	vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
> > > > +				INTEL_SET_VENDOR_VERB, vendor_param);
> > > > +	if (vendor_param == -1)
> > > > +		return;
> > > > +}
> 
> > > So to enable the pins we do a read?  That seems...  innovative.  :/
> 
> > It's a weird nature of HD-audio verb handling.  While *_write() just
> > sends the verb asynchronously, *_read() sends the verb, does sync and
> > read-back the return value.  But both read and write may handle the
> > same verb.
> 
> The above one seems especially odd, we do the read and then essentially
> ignore the value (the difference in handling is nonexistant).

There is a difference -- it does sync.

I don't know whether the sync is mandatory in this case, though.


Takashi

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2016-02-16  8:16 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-12  2:16 [PATCH v6 00/15] ASoC: hdac_hdmi: Add DP & notification support Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 01/15] ASoC: hdac_hdmi: Add hotplug notification and read ELD Subhransu S. Prusty
2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Add hotplug notification and read ELD" to the asoc tree Mark Brown
2016-02-12  2:16 ` [PATCH v6 02/15] ASoC: hdac_hdmi: Apply constraints based on ELD Subhransu S. Prusty
2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Apply constraints based on ELD" to the asoc tree Mark Brown
2016-02-12  2:16 ` [PATCH v6 03/15] ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins Subhransu S. Prusty
2016-02-15 20:10   ` Mark Brown
2016-02-15 22:31     ` Takashi Iwai
2016-02-16  2:00       ` Mark Brown
2016-02-16  8:16         ` Takashi Iwai
2016-02-12  2:16 ` [PATCH v6 04/15] ASoC: hdac_hdmi: create dais based on number of cvts Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 05/15] ASoC: hdac_hdmi: Create widget/route based on nodes enumerated Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 06/15] ASoC: hdac_hdmi: Add jack reporting Subhransu S. Prusty
2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Add jack reporting" to the asoc tree Mark Brown
2016-02-12  2:16 ` [PATCH v6 07/15] ASoC: hdac_hdmi: Enable playback on all enumerated ports Subhransu S. Prusty
2016-02-15 20:58   ` Applied "ASoC: hdac_hdmi: Enable playback on all enumerated ports" to the asoc tree Mark Brown
2016-02-12  2:16 ` [PATCH v6 08/15] drm/edid: Add API to help find connection type Subhransu S. Prusty
2016-02-15 20:25   ` Applied "drm/edid: Add API to help find connection type" to the asoc tree Mark Brown
2016-02-12  2:16 ` [PATCH v6 09/15] ASoC: hdac_hdmi: Add infoframe support for dp audio Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 10/15] ASoC: hdac_hdmi: Fix possible memory leak in hw_params Subhransu S. Prusty
2016-02-15 20:57   ` Mark Brown
2016-02-16  2:58     ` Vinod Koul
2016-02-12  2:16 ` [PATCH v6 11/15] ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy Subhransu S. Prusty
2016-02-15 20:55   ` Mark Brown
2016-02-16  3:36     ` Vinod Koul
2016-02-12  2:16 ` [PATCH v6 12/15] ASoC: hdac_hdmi: Add PM support Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 13/15] ASoC: hdac_hdmi: Fix to keep codec power active during enumeration Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 14/15] ASoC: hdac_hdmi: Fix to reconfigure registers in runtime resume Subhransu S. Prusty
2016-02-12  2:16 ` [PATCH v6 15/15] ASoC: hdac_hdmi: Fix to wait for D3 before powering off codec Subhransu S. Prusty

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.