diff options
author | Sumit Bhattacharya <sumitb@nvidia.com> | 2012-02-08 18:15:50 +0530 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-03-02 17:39:23 -0800 |
commit | 1de7d65987afa175e552e300a2ef435bada02f08 (patch) | |
tree | bb9778db486113f8ae66f90cd76180ea0893d67c | |
parent | 27fb4ae633b456346593c60e7dc77c4c85a625a7 (diff) |
ALSA: HDA: Prevent delay in opening hdmi pcm
When monitor is plugged in instead of reading the complete ELD buffer
only read the relavant bytes required to update pcm info. Go through
the complete ELD buffer once LPCM sad ELD information is updated in
ELD structure. This is required to reduce the delay in getting a valid
PCM information which in turn delays opening of HDMI PCM stream.
Also if a valid LPCM SAD ELD information is not available when
hdmi_pcm_open is called then instead of looping inside hdmi_pcm_open
return error to unblock other operations. User space should retry to
open HDA PCM device after some time.
Bug 931930
Bug 913739
Bug 906076
Change-Id: Iaaef3f0e361ae406c92605b056bd4dff9c2b7856
Signed-off-by: Sumit Bhattacharya <sumitb@nvidia.com>
Reviewed-on: http://git-master/r/83143
Reviewed-by: Scott Peterson <speterson@nvidia.com>
-rw-r--r-- | sound/pci/hda/hda_eld.c | 92 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 45 |
3 files changed, 109 insertions, 29 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 615dcb0af1b7..f3d204f01ab2 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -157,6 +157,22 @@ static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, return val; } +static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid, + int byte_index) +{ + unsigned int val; + + val = hdmi_get_eld_data(codec, nid, byte_index); + + if ((val & AC_ELDD_ELD_VALID) == 0) { + snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n", + byte_index); + val = 0; + } + + return val & AC_ELDD_ELD_DATA; +} + #define GRAB_BITS(buf, byte, lowbit, bits) \ ({ \ BUILD_BUG_ON(lowbit > 7); \ @@ -304,6 +320,79 @@ out_fail: return -EINVAL; } +#define GET_BITS(val, lowbit, bits) \ +({ \ + BUILD_BUG_ON(lowbit > 7); \ + BUILD_BUG_ON(bits > 8); \ + BUILD_BUG_ON(bits <= 0); \ + \ + (val >> (lowbit)) & ((1 << (bits)) - 1); \ +}) + +/* update ELD information relevant for getting PCM info */ +static int hdmi_update_lpcm_sad_eld (struct hda_codec *codec, hda_nid_t nid, + struct hdmi_eld *e, int size) +{ + int i, j; + u32 val, sad_base; + struct cea_sad *a; + + val = hdmi_get_eld_byte(codec, nid, 0); + e->eld_ver = GET_BITS(val, 3, 5); + if (e->eld_ver != ELD_VER_CEA_861D && + e->eld_ver != ELD_VER_PARTIAL) { + snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n", + e->eld_ver); + goto out_fail; + } + + val = hdmi_get_eld_byte(codec, nid, 4); + sad_base = GET_BITS(val, 0, 5); + sad_base += ELD_FIXED_BYTES; + + val = hdmi_get_eld_byte(codec, nid, 5); + e->sad_count = GET_BITS(val, 4, 4); + + for (i = 0; i < e->sad_count; i++, sad_base += 3) { + if ((sad_base + 3) > size) { + snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i); + goto out_fail; + } + a = &e->sad[i]; + + val = hdmi_get_eld_byte(codec, nid, sad_base); + a->format = GET_BITS(val, 3, 4); + a->channels = GET_BITS(val, 0, 3); + a->channels++; + + a->rates = 0; + a->sample_bits = 0; + a->max_bitrate = 0; + + if (a->format != AUDIO_CODING_TYPE_LPCM) + continue; + + val = hdmi_get_eld_byte(codec, nid, sad_base + 1); + val = GET_BITS(val, 0, 7); + for (j = 0; j < 7; j++) + if (val & (1 << j)) + a->rates |= cea_sampling_frequencies[j + 1]; + + val = hdmi_get_eld_byte(codec, nid, sad_base + 2); + val = GET_BITS(val, 0, 3); + for (j = 0; j < 3; j++) + if (val & (1 << j)) + a->sample_bits |= cea_sample_sizes[j + 1]; + } + + e->lpcm_sad_ready = 1; + return 0; + +out_fail: + e->eld_ver = 0; + return -EINVAL; +} + int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, @@ -329,6 +418,9 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, return -ERANGE; } + if (!eld->lpcm_sad_ready) + hdmi_update_lpcm_sad_eld(codec, nid, eld, size); + buf = kmalloc(size, GFP_KERNEL); if (!buf) return -ENOMEM; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index f52ebe8ba0f5..d95a48491a36 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -621,6 +621,7 @@ struct cea_sad { struct hdmi_eld { bool monitor_present; bool eld_valid; + bool lpcm_sad_ready; int eld_size; int baseline_len; int eld_ver; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b03efbc17132..493d368572ae 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -829,6 +829,21 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_pin = &spec->pins[pin_idx]; eld = &per_pin->sink_eld; +#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA + if ((codec->preset->id == 0x10de0020) && + (!eld->monitor_present || !eld->lpcm_sad_ready)) { + if (!eld->monitor_present) { + if (tegra_hdmi_setup_hda_presence() < 0) { + snd_printk(KERN_WARNING + "HDMI: No HDMI device connected\n"); + return -ENODEV; + } + } + if (!eld->lpcm_sad_ready) + return -EAGAIN; + } +#endif + /* Dynamically assign converter to stream */ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { per_cvt = &spec->cvts[cvt_idx]; @@ -870,36 +885,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, hinfo->formats = per_cvt->formats; hinfo->maxbps = per_cvt->maxbps; -#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA - if ((codec->preset->id == 0x10de0020) && - (!eld->eld_valid || !eld->sad_count)) { - int err = 0; - unsigned long timeout; - - if (!eld->eld_valid) { - err = tegra_hdmi_setup_hda_presence(); - if (err < 0) { - snd_printk(KERN_WARNING - "HDMI: No HDMI device connected\n"); - return -ENODEV; - } - } - - timeout = jiffies + msecs_to_jiffies(5000); - for (;;) { - if (eld->eld_valid && eld->sad_count) - break; - - if (time_after(jiffies, timeout)) - break; - - mdelay(10); - } - } -#endif - /* Restrict capabilities by ELD if this isn't disabled */ - if (!static_hdmi_pcm && eld->eld_valid) { + if (!static_hdmi_pcm && (eld->eld_valid || eld->lpcm_sad_ready)) { snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || !hinfo->rates || !hinfo->formats) |