diff options
-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) |