linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ASoC: hdac_hdmi: support 'ELD' mixer
@ 2020-08-14  8:34 Brent Lu
  2020-08-16  7:50 ` Takashi Iwai
  2020-08-18  0:44 ` [PATCH v2] " Brent Lu
  0 siblings, 2 replies; 10+ messages in thread
From: Brent Lu @ 2020-08-14  8:34 UTC (permalink / raw)
  To: alsa-devel
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Pierre-Louis Bossart, Brent Lu, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

Add an binary mixer 'ELD' to each HDMI PCM device so user space
could read the ELD data of external HDMI display.

Signed-off-by: Brent Lu <brent.lu@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 139 +++++++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index f26b77faed59..83656bbf22a7 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -9,6 +9,7 @@
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
+
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/module.h>
@@ -107,6 +108,7 @@ struct hdac_hdmi_pcm {
 	unsigned char chmap[8]; /* ALSA API channel-map */
 	struct mutex lock;
 	int jack_event;
+	struct snd_kcontrol *eld_ctl;
 };
 
 struct hdac_hdmi_dai_port_map {
@@ -1248,6 +1250,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 	struct hdac_hdmi_pcm *pcm;
 	int size = 0;
 	int port_id = -1;
+	bool eld_valid, eld_changed;
 
 	if (!hdmi)
 		return;
@@ -1273,6 +1276,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 			size = -EINVAL;
 	}
 
+	eld_valid = port->eld.eld_valid;
+
 	if (size > 0) {
 		port->eld.eld_valid = true;
 		port->eld.eld_size = size;
@@ -1281,6 +1286,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 		port->eld.eld_size = 0;
 	}
 
+	eld_changed = (eld_valid != port->eld.eld_valid);
+
 	pcm = hdac_hdmi_get_pcm(hdev, port);
 
 	if (!port->eld.monitor_present || !port->eld.eld_valid) {
@@ -1313,6 +1320,12 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 
 	}
 	mutex_unlock(&hdmi->pin_mutex);
+
+	if (eld_changed && pcm)
+		snd_ctl_notify(hdmi->card,
+			       SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO,
+			       &pcm->eld_ctl->id);
 }
 
 static int hdac_hdmi_add_ports(struct hdac_device *hdev,
@@ -1411,6 +1424,122 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
 
 }
 
+static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_port *port;
+	struct hdac_hdmi_eld *eld;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 0;
+
+	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+	if (!pcm) {
+		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+			kcontrol->id.device);
+		return 0;
+	}
+
+	if (list_empty(&pcm->port_list)) {
+		dev_dbg(component->dev, "%s: empty port list, device %d\n",
+			__func__, kcontrol->id.device);
+		return 0;
+	}
+
+	mutex_lock(&hdmi->pin_mutex);
+
+	list_for_each_entry(port, &pcm->port_list, head) {
+		eld = &port->eld;
+
+		if (eld->eld_valid) {
+			uinfo->count = eld->eld_size;
+			break;
+		}
+	}
+
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return 0;
+}
+
+static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_port *port;
+	struct hdac_hdmi_eld *eld;
+
+	memset(ucontrol->value.bytes.data, 0, ARRAY_SIZE(ucontrol->value.bytes.data));
+
+	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+	if (!pcm) {
+		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+			kcontrol->id.device);
+		return 0;
+	}
+
+	if (list_empty(&pcm->port_list)) {
+		dev_dbg(component->dev, "%s: empty port list, device %d\n",
+			__func__, kcontrol->id.device);
+		return 0;
+	}
+
+	mutex_lock(&hdmi->pin_mutex);
+
+	list_for_each_entry(port, &pcm->port_list, head) {
+		eld = &port->eld;
+
+		if (!eld->eld_valid)
+			continue;
+
+		if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+		    eld->eld_size > ELD_MAX_SIZE) {
+			mutex_unlock(&hdmi->pin_mutex);
+
+			dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
+				__func__, kcontrol->id.device, eld->eld_size);
+			snd_BUG();
+			return -EINVAL;
+		}
+
+		memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+		       eld->eld_size);
+		break;
+	}
+
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return 0;
+}
+
+static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_new hdmi_eld_ctl = {
+		.access	= SNDRV_CTL_ELEM_ACCESS_READ |
+			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
+		.name	= "ELD",
+		.info	= hdac_hdmi_eld_ctl_info,
+		.get	= hdac_hdmi_eld_ctl_get,
+		.device	= pcm->pcm_id,
+	};
+
+	/* add ELD ctl with the device number corresponding to the PCM stream */
+	kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
+	if (!kctl)
+		return -ENOMEM;
+
+	pcm->eld_ctl = kctl;
+
+	return snd_ctl_add(component->card->snd_card, kctl);
+}
+
 static const struct snd_soc_dai_ops hdmi_dai_ops = {
 	.startup = hdac_hdmi_pcm_open,
 	.shutdown = hdac_hdmi_pcm_close,
@@ -1784,6 +1913,16 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
 		}
 	}
 
+	/* add control for ELD Bytes */
+	err = hdac_hdmi_create_eld_ctl(component, pcm);
+	if (err < 0) {
+		dev_err(&hdev->dev,
+			"eld control add failed with err: %d for pcm: %d\n",
+			err, device);
+		kfree(pcm);
+		return err;
+	}
+
 	list_add_tail(&pcm->head, &hdmi->pcm_list);
 
 	return 0;
-- 
2.17.1


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

* Re: [PATCH] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-14  8:34 [PATCH] ASoC: hdac_hdmi: support 'ELD' mixer Brent Lu
@ 2020-08-16  7:50 ` Takashi Iwai
  2020-08-18  0:53   ` Lu, Brent
  2020-08-18  0:44 ` [PATCH v2] " Brent Lu
  1 sibling, 1 reply; 10+ messages in thread
From: Takashi Iwai @ 2020-08-16  7:50 UTC (permalink / raw)
  To: Brent Lu
  Cc: alsa-devel, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

On Fri, 14 Aug 2020 10:34:36 +0200,
Brent Lu wrote:
> @@ -1784,6 +1913,16 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
>  		}
>  	}
>  
> +	/* add control for ELD Bytes */
> +	err = hdac_hdmi_create_eld_ctl(component, pcm);
> +	if (err < 0) {
> +		dev_err(&hdev->dev,
> +			"eld control add failed with err: %d for pcm: %d\n",
> +			err, device);
> +		kfree(pcm);

pcm is allocated vida devm_kzalloc(), hence you shoudn't free it
explicitly.


thanks,

Takashi

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

* [PATCH v2] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-14  8:34 [PATCH] ASoC: hdac_hdmi: support 'ELD' mixer Brent Lu
  2020-08-16  7:50 ` Takashi Iwai
@ 2020-08-18  0:44 ` Brent Lu
  2020-08-18 11:45   ` Kai Vehmanen
  2020-08-18 14:36   ` [PATCH v3] " Brent Lu
  1 sibling, 2 replies; 10+ messages in thread
From: Brent Lu @ 2020-08-18  0:44 UTC (permalink / raw)
  To: alsa-devel
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Pierre-Louis Bossart, Brent Lu, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

Add an binary mixer 'ELD' to each HDMI PCM device so user space
could read the ELD data of external HDMI display.

Signed-off-by: Brent Lu <brent.lu@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 138 +++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index f26b77faed59..869d1547ae5d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -9,6 +9,7 @@
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
+
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/module.h>
@@ -107,6 +108,7 @@ struct hdac_hdmi_pcm {
 	unsigned char chmap[8]; /* ALSA API channel-map */
 	struct mutex lock;
 	int jack_event;
+	struct snd_kcontrol *eld_ctl;
 };
 
 struct hdac_hdmi_dai_port_map {
@@ -1248,6 +1250,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 	struct hdac_hdmi_pcm *pcm;
 	int size = 0;
 	int port_id = -1;
+	bool eld_valid, eld_changed;
 
 	if (!hdmi)
 		return;
@@ -1273,6 +1276,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 			size = -EINVAL;
 	}
 
+	eld_valid = port->eld.eld_valid;
+
 	if (size > 0) {
 		port->eld.eld_valid = true;
 		port->eld.eld_size = size;
@@ -1281,6 +1286,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 		port->eld.eld_size = 0;
 	}
 
+	eld_changed = (eld_valid != port->eld.eld_valid);
+
 	pcm = hdac_hdmi_get_pcm(hdev, port);
 
 	if (!port->eld.monitor_present || !port->eld.eld_valid) {
@@ -1313,6 +1320,12 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 
 	}
 	mutex_unlock(&hdmi->pin_mutex);
+
+	if (eld_changed && pcm)
+		snd_ctl_notify(hdmi->card,
+			       SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO,
+			       &pcm->eld_ctl->id);
 }
 
 static int hdac_hdmi_add_ports(struct hdac_device *hdev,
@@ -1411,6 +1424,122 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
 
 }
 
+static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_port *port;
+	struct hdac_hdmi_eld *eld;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 0;
+
+	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+	if (!pcm) {
+		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+			kcontrol->id.device);
+		return 0;
+	}
+
+	if (list_empty(&pcm->port_list)) {
+		dev_dbg(component->dev, "%s: empty port list, device %d\n",
+			__func__, kcontrol->id.device);
+		return 0;
+	}
+
+	mutex_lock(&hdmi->pin_mutex);
+
+	list_for_each_entry(port, &pcm->port_list, head) {
+		eld = &port->eld;
+
+		if (eld->eld_valid) {
+			uinfo->count = eld->eld_size;
+			break;
+		}
+	}
+
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return 0;
+}
+
+static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_port *port;
+	struct hdac_hdmi_eld *eld;
+
+	memset(ucontrol->value.bytes.data, 0, ARRAY_SIZE(ucontrol->value.bytes.data));
+
+	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+	if (!pcm) {
+		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+			kcontrol->id.device);
+		return 0;
+	}
+
+	if (list_empty(&pcm->port_list)) {
+		dev_dbg(component->dev, "%s: empty port list, device %d\n",
+			__func__, kcontrol->id.device);
+		return 0;
+	}
+
+	mutex_lock(&hdmi->pin_mutex);
+
+	list_for_each_entry(port, &pcm->port_list, head) {
+		eld = &port->eld;
+
+		if (!eld->eld_valid)
+			continue;
+
+		if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+		    eld->eld_size > ELD_MAX_SIZE) {
+			mutex_unlock(&hdmi->pin_mutex);
+
+			dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
+				__func__, kcontrol->id.device, eld->eld_size);
+			snd_BUG();
+			return -EINVAL;
+		}
+
+		memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+		       eld->eld_size);
+		break;
+	}
+
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return 0;
+}
+
+static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_new hdmi_eld_ctl = {
+		.access	= SNDRV_CTL_ELEM_ACCESS_READ |
+			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
+		.name	= "ELD",
+		.info	= hdac_hdmi_eld_ctl_info,
+		.get	= hdac_hdmi_eld_ctl_get,
+		.device	= pcm->pcm_id,
+	};
+
+	/* add ELD ctl with the device number corresponding to the PCM stream */
+	kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
+	if (!kctl)
+		return -ENOMEM;
+
+	pcm->eld_ctl = kctl;
+
+	return snd_ctl_add(component->card->snd_card, kctl);
+}
+
 static const struct snd_soc_dai_ops hdmi_dai_ops = {
 	.startup = hdac_hdmi_pcm_open,
 	.shutdown = hdac_hdmi_pcm_close,
@@ -1784,6 +1913,15 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
 		}
 	}
 
+	/* add control for ELD Bytes */
+	err = hdac_hdmi_create_eld_ctl(component, pcm);
+	if (err < 0) {
+		dev_err(&hdev->dev,
+			"eld control add failed with err: %d for pcm: %d\n",
+			err, device);
+		return err;
+	}
+
 	list_add_tail(&pcm->head, &hdmi->pcm_list);
 
 	return 0;
-- 
2.17.1


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

* RE: [PATCH] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-16  7:50 ` Takashi Iwai
@ 2020-08-18  0:53   ` Lu, Brent
  0 siblings, 0 replies; 10+ messages in thread
From: Lu, Brent @ 2020-08-18  0:53 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: alsa-devel, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

> > +	/* add control for ELD Bytes */
> > +	err = hdac_hdmi_create_eld_ctl(component, pcm);
> > +	if (err < 0) {
> > +		dev_err(&hdev->dev,
> > +			"eld control add failed with err: %d for pcm: %d\n",
> > +			err, device);
> > +		kfree(pcm);
> 
> pcm is allocated vida devm_kzalloc(), hence you shoudn't free it explicitly.
Will fix it in v2. Thanks.

Brent
> 
> 
> thanks,
> 
> Takashi

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

* Re: [PATCH v2] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-18  0:44 ` [PATCH v2] " Brent Lu
@ 2020-08-18 11:45   ` Kai Vehmanen
  2020-08-18 14:49     ` Lu, Brent
  2020-08-18 14:36   ` [PATCH v3] " Brent Lu
  1 sibling, 1 reply; 10+ messages in thread
From: Kai Vehmanen @ 2020-08-18 11:45 UTC (permalink / raw)
  To: Brent Lu
  Cc: alsa-devel, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

Hey,

a general comment first. We are trying to move development to patch_hdmi, 
but given we still have platforms using hdac_hdmi, this patch seems like a 
useful addition.

On Tue, 18 Aug 2020, Brent Lu wrote:

> Add an binary mixer 'ELD' to each HDMI PCM device so user space
> could read the ELD data of external HDMI display.

Minor spelling fixes:
"Add a binary mixer"
"so user space can read"

> +static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
> +			    struct snd_ctl_elem_value *ucontrol)
> +{
[...]
> +	list_for_each_entry(port, &pcm->port_list, head) {
> +		eld = &port->eld;
> +
[...]
> +		memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
> +		       eld->eld_size);
> +		break;
> +	}

This is a bit iffy part. If same PCM is connected to multiple receivers, 
you return ELD data for the first one and ignore the rest. OTOH, this is 
inline with comment in hdac_hdmi_get_port_from_cvt() in that this 
pcm-to-many routing is not really supported by the driver now. But
jack status reporting is done a port basis, not per PCM/CVTs, so this is 
not fully aligned.

Hmm. Given the proposed patch is aligned with the user-space interface 
exposed by patch_hdmi.c, I'm ok to go with this. Can you add an explicit 
comment to explain what is happening above?

Br, Kai

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

* [PATCH v3] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-18  0:44 ` [PATCH v2] " Brent Lu
  2020-08-18 11:45   ` Kai Vehmanen
@ 2020-08-18 14:36   ` Brent Lu
  2020-08-18 15:02     ` Mark Brown
  1 sibling, 1 reply; 10+ messages in thread
From: Brent Lu @ 2020-08-18 14:36 UTC (permalink / raw)
  To: alsa-devel
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Pierre-Louis Bossart, Brent Lu, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

Add a binary mixer 'ELD' to each HDMI PCM device so user-space can
read the ELD data of external HDMI display.

If same PCM is connected to multiple receivers, the mixer returns the
ELD data for the first one found in the port list and ignore the rest.
Thie behavior seems iffy but is aligned with the user-space interface
exposed by patch_hdmi.c. Also the pcm-to-many routing is nor really
supported by the driver now.

Signed-off-by: Brent Lu <brent.lu@intel.com>
---
 sound/soc/codecs/hdac_hdmi.c | 138 +++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index f26b77faed59..869d1547ae5d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -9,6 +9,7 @@
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
+
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/module.h>
@@ -107,6 +108,7 @@ struct hdac_hdmi_pcm {
 	unsigned char chmap[8]; /* ALSA API channel-map */
 	struct mutex lock;
 	int jack_event;
+	struct snd_kcontrol *eld_ctl;
 };
 
 struct hdac_hdmi_dai_port_map {
@@ -1248,6 +1250,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 	struct hdac_hdmi_pcm *pcm;
 	int size = 0;
 	int port_id = -1;
+	bool eld_valid, eld_changed;
 
 	if (!hdmi)
 		return;
@@ -1273,6 +1276,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 			size = -EINVAL;
 	}
 
+	eld_valid = port->eld.eld_valid;
+
 	if (size > 0) {
 		port->eld.eld_valid = true;
 		port->eld.eld_size = size;
@@ -1281,6 +1286,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 		port->eld.eld_size = 0;
 	}
 
+	eld_changed = (eld_valid != port->eld.eld_valid);
+
 	pcm = hdac_hdmi_get_pcm(hdev, port);
 
 	if (!port->eld.monitor_present || !port->eld.eld_valid) {
@@ -1313,6 +1320,12 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
 
 	}
 	mutex_unlock(&hdmi->pin_mutex);
+
+	if (eld_changed && pcm)
+		snd_ctl_notify(hdmi->card,
+			       SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO,
+			       &pcm->eld_ctl->id);
 }
 
 static int hdac_hdmi_add_ports(struct hdac_device *hdev,
@@ -1411,6 +1424,122 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
 
 }
 
+static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_port *port;
+	struct hdac_hdmi_eld *eld;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 0;
+
+	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+	if (!pcm) {
+		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+			kcontrol->id.device);
+		return 0;
+	}
+
+	if (list_empty(&pcm->port_list)) {
+		dev_dbg(component->dev, "%s: empty port list, device %d\n",
+			__func__, kcontrol->id.device);
+		return 0;
+	}
+
+	mutex_lock(&hdmi->pin_mutex);
+
+	list_for_each_entry(port, &pcm->port_list, head) {
+		eld = &port->eld;
+
+		if (eld->eld_valid) {
+			uinfo->count = eld->eld_size;
+			break;
+		}
+	}
+
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return 0;
+}
+
+static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+	struct hdac_hdmi_pcm *pcm;
+	struct hdac_hdmi_port *port;
+	struct hdac_hdmi_eld *eld;
+
+	memset(ucontrol->value.bytes.data, 0, ARRAY_SIZE(ucontrol->value.bytes.data));
+
+	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+	if (!pcm) {
+		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+			kcontrol->id.device);
+		return 0;
+	}
+
+	if (list_empty(&pcm->port_list)) {
+		dev_dbg(component->dev, "%s: empty port list, device %d\n",
+			__func__, kcontrol->id.device);
+		return 0;
+	}
+
+	mutex_lock(&hdmi->pin_mutex);
+
+	list_for_each_entry(port, &pcm->port_list, head) {
+		eld = &port->eld;
+
+		if (!eld->eld_valid)
+			continue;
+
+		if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+		    eld->eld_size > ELD_MAX_SIZE) {
+			mutex_unlock(&hdmi->pin_mutex);
+
+			dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
+				__func__, kcontrol->id.device, eld->eld_size);
+			snd_BUG();
+			return -EINVAL;
+		}
+
+		memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+		       eld->eld_size);
+		break;
+	}
+
+	mutex_unlock(&hdmi->pin_mutex);
+
+	return 0;
+}
+
+static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_new hdmi_eld_ctl = {
+		.access	= SNDRV_CTL_ELEM_ACCESS_READ |
+			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
+		.name	= "ELD",
+		.info	= hdac_hdmi_eld_ctl_info,
+		.get	= hdac_hdmi_eld_ctl_get,
+		.device	= pcm->pcm_id,
+	};
+
+	/* add ELD ctl with the device number corresponding to the PCM stream */
+	kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
+	if (!kctl)
+		return -ENOMEM;
+
+	pcm->eld_ctl = kctl;
+
+	return snd_ctl_add(component->card->snd_card, kctl);
+}
+
 static const struct snd_soc_dai_ops hdmi_dai_ops = {
 	.startup = hdac_hdmi_pcm_open,
 	.shutdown = hdac_hdmi_pcm_close,
@@ -1784,6 +1913,15 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
 		}
 	}
 
+	/* add control for ELD Bytes */
+	err = hdac_hdmi_create_eld_ctl(component, pcm);
+	if (err < 0) {
+		dev_err(&hdev->dev,
+			"eld control add failed with err: %d for pcm: %d\n",
+			err, device);
+		return err;
+	}
+
 	list_add_tail(&pcm->head, &hdmi->pcm_list);
 
 	return 0;
-- 
2.17.1


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

* RE: [PATCH v2] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-18 11:45   ` Kai Vehmanen
@ 2020-08-18 14:49     ` Lu, Brent
  0 siblings, 0 replies; 10+ messages in thread
From: Lu, Brent @ 2020-08-18 14:49 UTC (permalink / raw)
  To: Kai Vehmanen
  Cc: alsa-devel, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, linux-kernel

> 
> This is a bit iffy part. If same PCM is connected to multiple receivers, you
> return ELD data for the first one and ignore the rest. OTOH, this is inline with
> comment in hdac_hdmi_get_port_from_cvt() in that this pcm-to-many
> routing is not really supported by the driver now. But jack status reporting is
> done a port basis, not per PCM/CVTs, so this is not fully aligned.
> 
> Hmm. Given the proposed patch is aligned with the user-space interface
> exposed by patch_hdmi.c, I'm ok to go with this. Can you add an explicit
> comment to explain what is happening above?
> 
> Br, Kai

Fix it in v3. Thank you for the review.


Regards,
Brent

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

* Re: [PATCH v3] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-18 14:36   ` [PATCH v3] " Brent Lu
@ 2020-08-18 15:02     ` Mark Brown
  2020-08-18 15:22       ` Lu, Brent
  0 siblings, 1 reply; 10+ messages in thread
From: Mark Brown @ 2020-08-18 15:02 UTC (permalink / raw)
  To: Brent Lu
  Cc: alsa-devel, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 291 bytes --]

On Tue, Aug 18, 2020 at 10:36:32PM +0800, Brent Lu wrote:
> Add a binary mixer 'ELD' to each HDMI PCM device so user-space can
> read the ELD data of external HDMI display.

Please don't send new patches in the middle of existing threads, it
makes it hard to keep track fo what is going on.

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

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

* RE: [PATCH v3] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-18 15:02     ` Mark Brown
@ 2020-08-18 15:22       ` Lu, Brent
  2020-08-18 17:10         ` Mark Brown
  0 siblings, 1 reply; 10+ messages in thread
From: Lu, Brent @ 2020-08-18 15:22 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

> 
> Please don't send new patches in the middle of existing threads, it makes it
> hard to keep track fo what is going on.

Sorry for the problem. Does it mean I should avoid using " --in-reply-to=" when
sending new patch set?


Regards,
Brent

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

* Re: [PATCH v3] ASoC: hdac_hdmi: support 'ELD' mixer
  2020-08-18 15:22       ` Lu, Brent
@ 2020-08-18 17:10         ` Mark Brown
  0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2020-08-18 17:10 UTC (permalink / raw)
  To: Lu, Brent
  Cc: alsa-devel, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Pierre-Louis Bossart, Amadeusz Sławiński,
	Guennadi Liakhovetski, Kai Vehmanen, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 307 bytes --]

On Tue, Aug 18, 2020 at 03:22:03PM +0000, Lu, Brent wrote:

> > Please don't send new patches in the middle of existing threads, it makes it
> > hard to keep track fo what is going on.

> Sorry for the problem. Does it mean I should avoid using " --in-reply-to=" when
> sending new patch set?

Yes, please.

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

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

end of thread, other threads:[~2020-08-18 17:10 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-14  8:34 [PATCH] ASoC: hdac_hdmi: support 'ELD' mixer Brent Lu
2020-08-16  7:50 ` Takashi Iwai
2020-08-18  0:53   ` Lu, Brent
2020-08-18  0:44 ` [PATCH v2] " Brent Lu
2020-08-18 11:45   ` Kai Vehmanen
2020-08-18 14:49     ` Lu, Brent
2020-08-18 14:36   ` [PATCH v3] " Brent Lu
2020-08-18 15:02     ` Mark Brown
2020-08-18 15:22       ` Lu, Brent
2020-08-18 17:10         ` Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).