[alsa-devel] [PATCH v2] ALSA: hdmi: expose ELD control

Takashi Iwai tiwai at suse.de
Tue Sep 27 09:53:34 CEST 2011


At Mon, 26 Sep 2011 17:48:43 -0500,
Pierre-Louis Bossart wrote:
> 
> Applications may want to read ELD information to
> understand what codecs are supported on the HDMI
> receiver and handle the a-v delay for better lip-sync.
> 
> ELD information is exposed in a device-specific
> IFACE_PCM kcontrol. Tested both with amixer and
> PulseAudio.
> 
> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
> ---
>  sound/pci/hda/hda_eld.c    |    9 ++--
>  sound/pci/hda/hda_local.h  |    2 +
>  sound/pci/hda/patch_hdmi.c |   88 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 94 insertions(+), 5 deletions(-)
> 
> diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
> index c34f730..9bf0d8b 100644
> --- a/sound/pci/hda/hda_eld.c
> +++ b/sound/pci/hda/hda_eld.c
> @@ -327,14 +327,14 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
>  		snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
>  		size = 128;
>  	}
> -	if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
> +	if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE || size > PAGE_SIZE) {

The check of size > PAGE_SIZE is meaningless now.  Let's drop.


>  		snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
>  		return -ERANGE;
>  	}
> 
> -	buf = kmalloc(size, GFP_KERNEL);
> -	if (!buf)
> -		return -ENOMEM;
> +	/* update info */
> +	eld->eld_size = size;
> +	buf = eld->eld_buffer;
> 
>  	for (i = 0; i < size; i++) {
>  		unsigned int val = hdmi_get_eld_data(codec, nid, i);
> @@ -356,7 +356,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
>  	ret = hdmi_update_eld(eld, buf, size);
> 
>  error:
> -	kfree(buf);
>  	return ret;
>  }

Wouldn't it be better to strip the buffer data and size in the error
case?  For example, radeon driver gives often size=0.  Then it's
translated as size=128 for a workaround, then again handled as an
error by checking the version.


> diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
> index aaefa7c..04d730f 100644
> --- a/sound/pci/hda/hda_local.h
> +++ b/sound/pci/hda/hda_local.h
> @@ -621,6 +621,7 @@ struct cea_sad {
>  };
> 
>  #define ELD_FIXED_BYTES	20
> +#define ELD_MAX_SIZE    256
>  #define ELD_MAX_MNL	16
>  #define ELD_MAX_SAD	16
> 
> @@ -645,6 +646,7 @@ struct hdmi_eld {
>  	int	spk_alloc;
>  	int	sad_count;
>  	struct cea_sad sad[ELD_MAX_SAD];
> +	char    eld_buffer[ELD_MAX_SIZE];
>  #ifdef CONFIG_PROC_FS
>  	struct snd_info_entry *proc_entry;
>  #endif
> diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
> index 3f1f6ac..944e0cd 100644
> --- a/sound/pci/hda/patch_hdmi.c
> +++ b/sound/pci/hda/patch_hdmi.c
> @@ -324,6 +324,86 @@ static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)
>  	return -EINVAL;
>  }
> 
> +static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
> +			struct snd_ctl_elem_info *uinfo)
> +{
> +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct hdmi_spec *spec;
> +	struct hdmi_spec_per_pin *per_pin;
> +	struct hdmi_eld *eld;
> +	int pin_idx;
> +
> +	spec = codec->spec;
> +	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
> +
> +	pin_idx = kcontrol->private_value;
> +	snd_BUG_ON(pin_idx < 0);

snd_BUG_ON() is essentially a WARN_ON(), so you should return an error
here like:
	if (snd_BUG_ON(pin_idx < 0))
		return -EINVAL;
> +
> +	per_pin = &spec->pins[pin_idx];
> +	eld = &per_pin->sink_eld;
> +	uinfo->count = eld->eld_size;
> +
> +	return 0;
> +}
> +
> +static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
> +			struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct hdmi_spec *spec;
> +	struct hdmi_spec_per_pin *per_pin;
> +	struct hdmi_eld *eld;
> +	int pin_idx;
> +	int size;
> +
> +	spec = codec->spec;
> +	pin_idx = kcontrol->private_value;
> +
> +	snd_BUG_ON(pin_idx < 0);

Ditto.

> +	memset(ucontrol->value.bytes.data, 0, ELD_MAX_SIZE);
> +	per_pin = &spec->pins[pin_idx];
> +	eld = &per_pin->sink_eld;
> +	size = eld->eld_size;
> +
> +	memcpy(ucontrol->value.bytes.data, eld->eld_buffer, size);
> +
> +	/* reset rest of control */
> +	memset(&ucontrol->value.bytes.data[size], 0, ELD_MAX_SIZE-size);

This is superfluous.

> +	return 0;
> +}
> +
> +static struct snd_kcontrol_new eld_bytes_ctl = {
> +	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
> +	.name = "ELD",
> +	.info = hdmi_eld_ctl_info,
> +	.get = hdmi_eld_ctl_get,

Set the access field to SNDRV_CTL_ELEM_ACCESS_READ.
Also, unless we implement a notification, better to set
SNDRV_CTL_ELEM_ACCESS_VOLATILE flag, too.


thanks,

Takashi


More information about the Alsa-devel mailing list