Skip to content

Commit

Permalink
ALSA: hda/hdmi: expose ELD control
Browse files Browse the repository at this point in the history
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; with a corresponding patch passthrough modes
are enabled automagically.

ELD control size is set to zero in case of errors or
wrong configurations. No notifications are implemented
for now, it is expected that jack detection is used to
reconfigure the audio outputs.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Pierre-Louis Bossart authored and Takashi Iwai committed Oct 3, 2011
1 parent ef940b0 commit 14bc52b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 5 deletions.
13 changes: 8 additions & 5 deletions sound/pci/hda/hda_eld.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
int size;
unsigned char *buf;

/*
* ELD size is initialized to zero in caller function. If no errors and
* ELD is valid, actual eld_size is assigned in hdmi_update_eld()
*/

if (!eld->eld_valid)
return -ENOENT;

Expand All @@ -327,14 +332,13 @@ 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) {
snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
return -ERANGE;
}

buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* set ELD buffer */
buf = eld->eld_buffer;

for (i = 0; i < size; i++) {
unsigned int val = hdmi_get_eld_data(codec, nid, i);
Expand All @@ -356,7 +360,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
ret = hdmi_update_eld(eld, buf, size);

error:
kfree(buf);
return ret;
}

Expand Down
2 changes: 2 additions & 0 deletions sound/pci/hda/hda_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
68 changes: 68 additions & 0 deletions sound/pci/hda/patch_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,66 @@ 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;
int pin_idx;

spec = codec->spec;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;

pin_idx = kcontrol->private_value;
uinfo->count = spec->pins[pin_idx].sink_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;
int pin_idx;

spec = codec->spec;
pin_idx = kcontrol->private_value;

memcpy(ucontrol->value.bytes.data,
spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE);

return 0;
}

static struct snd_kcontrol_new eld_bytes_ctl = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
.get = hdmi_eld_ctl_get,
};

static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
int device)
{
struct snd_kcontrol *kctl;
struct hdmi_spec *spec = codec->spec;
int err;

kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
if (!kctl)
return -ENOMEM;
kctl->private_value = pin_idx;
kctl->id.device = device;

err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl);
if (err < 0)
return err;

return 0;
}

#ifdef BE_PARANOID
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
int *packet_index, int *byte_index)
Expand Down Expand Up @@ -1193,6 +1253,14 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
snd_hda_spdif_ctls_unassign(codec, pin_idx);

/* add control for ELD Bytes */
err = hdmi_create_eld_ctl(codec,
pin_idx,
spec->pcm_rec[pin_idx].device);

if (err < 0)
return err;
}

return 0;
Expand Down

0 comments on commit 14bc52b

Please sign in to comment.