summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/pci/hda/hda_eld.c92
-rw-r--r--sound/pci/hda/hda_local.h1
-rw-r--r--sound/pci/hda/patch_hdmi.c45
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)