ALSA: hda: check validity of speaker allocation field
Nikesh Oswal [Thu, 13 Sep 2012 08:58:02 +0000 (13:58 +0530)]
Kernel 3.1 hda driver read the eld data once and if
the speaker allocation field was 0 (not set yet by
hdmi driver) then it considers it as 0xFFFF which
allows multichannel and doesn't block it. In Kernel
3.4 hda driver there is a repolling mechanism added
for ELD data, if the ELD data sanity check fails
then HDA driver re-schedules the work unit to poll
ELD data again hence the check on speaker allocation
field was removed. But the NVIDIA hda/hdmi controller
for some reason fails to update the speaker allocation
field and the read data is not valid even after
repeated attempts, hence the ASP channel mapping
happens for default 2 channel case. Adding the check
for speaker allocation field again solves the issue
and multichannel content plays fine over hda/hdmi.

Bug 1045435
Bug 1043021

Change-Id: I79fe33c0e354142f5af16c3ebbb3611a733dd88d
Signed-off-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-on: http://git-master/r/132128
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Reviewed-by: Scott Peterson <speterson@nvidia.com>
Reviewed-by: Ravindra Lokhande <rlokhande@nvidia.com>

sound/pci/hda/patch_hdmi.c

index 391fc03..51a73e1 100644 (file)
@@ -764,6 +764,9 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
        int pin_nid;
        int pin_idx;
        struct hda_jack_tbl *jack;
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       struct hdmi_eld *eld = &spec->pins[pin_idx].sink_eld;
+#endif
 
        jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
        if (!jack)
@@ -781,6 +784,19 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
                return;
 
        hdmi_present_sense(&spec->pins[pin_idx], 1);
+
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       if (codec->preset->id == 0x10de0020) {
+               /*
+                * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+                * in console or for audio devices. Assume the highest speakers
+                * configuration, to _not_ prohibit multi-channel audio playback
+                */
+               if (!eld->spk_alloc)
+                       eld->spk_alloc = 0xffff;
+       }
+#endif
+
        snd_hda_jack_report_sync(codec);
 }
 
@@ -1024,11 +1040,27 @@ static void hdmi_repoll_eld(struct work_struct *work)
 {
        struct hdmi_spec_per_pin *per_pin =
        container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       struct hda_codec *codec = per_pin->codec;
+       struct hdmi_eld *eld = &per_pin->sink_eld;
+#endif
 
        if (per_pin->repoll_count++ > 6)
                per_pin->repoll_count = 0;
 
        hdmi_present_sense(per_pin, per_pin->repoll_count);
+
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       if (codec->preset->id == 0x10de0020) {
+               /*
+                * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+                * in console or for audio devices. Assume the highest speakers
+                * configuration, to _not_ prohibit multi-channel audio playback
+                */
+               if (!eld->spk_alloc)
+                       eld->spk_alloc = 0xffff;
+       }
+#endif
 }
 
 static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)