ALSA: hda - Apply AMP fix in hdmi_setup_audio_infoframe() generically
[cascardo/linux.git] / sound / pci / hda / patch_hdmi.c
index 49ee4e5..fcd207d 100644 (file)
@@ -152,15 +152,18 @@ struct hdmi_spec {
        struct hda_pcm_stream pcm_playback;
 
        /* i915/powerwell (Haswell+/Valleyview+) specific */
+       bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
        struct i915_audio_component_audio_ops i915_audio_ops;
-       bool i915_bound; /* was i915 bound in this driver? */
 
        struct hdac_chmap chmap;
 };
 
 #ifdef CONFIG_SND_HDA_I915
-#define codec_has_acomp(codec) \
-       ((codec)->bus->core.audio_component != NULL)
+static inline bool codec_has_acomp(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       return spec->use_acomp_notifier;
+}
 #else
 #define codec_has_acomp(codec) false
 #endif
@@ -680,7 +683,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
        if (!channels)
                return;
 
-       if (is_haswell_plus(codec))
+       /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
+       if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
                snd_hda_codec_write(codec, pin_nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
                                            AMP_OUT_UNMUTE);
@@ -1354,6 +1358,7 @@ static void update_eld(struct hda_codec *codec,
                           eld->eld_size) != 0)
                        eld_changed = true;
 
+       pin_eld->monitor_present = eld->monitor_present;
        pin_eld->eld_valid = eld->eld_valid;
        pin_eld->eld_size = eld->eld_size;
        if (eld->eld_valid)
@@ -1479,11 +1484,10 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
        int size;
 
        mutex_lock(&per_pin->lock);
+       eld->monitor_present = false;
        size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
                                      &eld->monitor_present, eld->eld_buffer,
                                      ELD_MAX_SIZE);
-       if (size < 0)
-               goto unlock;
        if (size > 0) {
                size = min(size, ELD_MAX_SIZE);
                if (snd_hdmi_parse_eld(codec, &eld->info,
@@ -1736,7 +1740,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
        /* Todo: add DP1.2 MST audio support later */
-       snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
+       if (codec_has_acomp(codec))
+               snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
 
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
        mutex_lock(&per_pin->lock);
@@ -2069,6 +2074,18 @@ static void hdmi_array_free(struct hdmi_spec *spec)
        snd_array_free(&spec->cvts);
 }
 
+static void generic_spec_free(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       if (spec) {
+               hdmi_array_free(spec);
+               kfree(spec);
+               codec->spec = NULL;
+       }
+       codec->dp_mst = false;
+}
+
 static void generic_hdmi_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -2093,10 +2110,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
                        spec->pcm_rec[pcm_idx].jack = NULL;
        }
 
-       if (spec->i915_bound)
-               snd_hdac_i915_exit(&codec->bus->core);
-       hdmi_array_free(spec);
-       kfree(spec);
+       generic_spec_free(codec);
 }
 
 #ifdef CONFIG_PM
@@ -2134,6 +2148,54 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
        .setup_stream                           = hdmi_setup_stream,
 };
 
+/* allocate codec->spec and assign/initialize generic parser ops */
+static int alloc_generic_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+
+       spec->ops = generic_standard_hdmi_ops;
+       mutex_init(&spec->pcm_lock);
+       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+       spec->chmap.ops.get_chmap = hdmi_get_chmap;
+       spec->chmap.ops.set_chmap = hdmi_set_chmap;
+       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+
+       codec->spec = spec;
+       hdmi_array_init(spec, 4);
+
+       codec->patch_ops = generic_hdmi_patch_ops;
+
+       return 0;
+}
+
+/* generic HDMI parser */
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+       int err;
+
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
+       }
+
+       generic_hdmi_init_per_pins(codec);
+       return 0;
+}
+
+/*
+ * Intel codec parsers and helpers
+ */
+
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
                                             hda_nid_t nid)
 {
@@ -2229,80 +2291,96 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
        check_presence_and_report(codec, pin_nid);
 }
 
-static int patch_generic_hdmi(struct hda_codec *codec)
+/* register i915 component pin_eld_notify callback */
+static void register_i915_notifier(struct hda_codec *codec)
 {
-       struct hdmi_spec *spec;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
+       struct hdmi_spec *spec = codec->spec;
 
-       spec->ops = generic_standard_hdmi_ops;
-       mutex_init(&spec->pcm_lock);
-       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+       spec->use_acomp_notifier = true;
+       spec->i915_audio_ops.audio_ptr = codec;
+       /* intel_audio_codec_enable() or intel_audio_codec_disable()
+        * will call pin_eld_notify with using audio_ptr pointer
+        * We need make sure audio_ptr is really setup
+        */
+       wmb();
+       spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
+       snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+}
 
-       spec->chmap.ops.get_chmap = hdmi_get_chmap;
-       spec->chmap.ops.set_chmap = hdmi_set_chmap;
-       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
 
-       codec->spec = spec;
-       hdmi_array_init(spec, 4);
+       /* HSW+ requires i915 binding */
+       if (!codec->bus->core.audio_component) {
+               codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+               return -ENODEV;
+       }
 
-       /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
-       if (!codec_has_acomp(codec) &&
-           (codec->core.vendor_id >> 16) == 0x8086 &&
-           is_haswell_plus(codec))
-               if (!snd_hdac_i915_init(&codec->bus->core))
-                       spec->i915_bound = true;
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
 
-       if (is_haswell_plus(codec)) {
-               intel_haswell_enable_all_pins(codec, true);
-               intel_haswell_fixup_enable_dp12(codec);
-       }
+       intel_haswell_enable_all_pins(codec, true);
+       intel_haswell_fixup_enable_dp12(codec);
 
-       /* For Valleyview/Cherryview, only the display codec is in the display
-        * power well and can use link_power ops to request/release the power.
-        * For Haswell/Broadwell, the controller is also in the power well and
+       /* For Haswell/Broadwell, the controller is also in the power well and
         * can cover the codec power request, and so need not set this flag.
-        * For previous platforms, there is no such power well feature.
         */
-       if (is_valleyview_plus(codec) || is_skylake(codec) ||
-                       is_broxton(codec))
+       if (!is_haswell(codec) && !is_broadwell(codec))
                codec->core.link_power_control = 1;
 
-       if (hdmi_parse_codec(codec) < 0) {
-               if (spec->i915_bound)
-                       snd_hdac_i915_exit(&codec->bus->core);
-               codec->spec = NULL;
-               kfree(spec);
-               return -EINVAL;
+       codec->patch_ops.set_power_state = haswell_set_power_state;
+       codec->dp_mst = true;
+       codec->depop_delay = 0;
+       codec->auto_runtime_pm = 1;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
        }
-       codec->patch_ops = generic_hdmi_patch_ops;
-       if (is_haswell_plus(codec)) {
-               codec->patch_ops.set_power_state = haswell_set_power_state;
-               codec->dp_mst = true;
+
+       generic_hdmi_init_per_pins(codec);
+       register_i915_notifier(codec);
+       return 0;
+}
+
+/* Intel Baytrail and Braswell; without get_eld notifier */
+static int patch_i915_byt_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
+
+       /* requires i915 binding */
+       if (!codec->bus->core.audio_component) {
+               codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+               return -ENODEV;
        }
 
-       /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
-       if (is_haswell_plus(codec) || is_valleyview_plus(codec))
-               codec->auto_runtime_pm = 1;
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
 
-       generic_hdmi_init_per_pins(codec);
+       /* For Valleyview/Cherryview, only the display codec is in the display
+        * power well and can use link_power ops to request/release the power.
+        */
+       codec->core.link_power_control = 1;
 
+       codec->depop_delay = 0;
+       codec->auto_runtime_pm = 1;
 
-       if (codec_has_acomp(codec)) {
-               codec->depop_delay = 0;
-               spec->i915_audio_ops.audio_ptr = codec;
-               /* intel_audio_codec_enable() or intel_audio_codec_disable()
-                * will call pin_eld_notify with using audio_ptr pointer
-                * We need make sure audio_ptr is really setup
-                */
-               wmb();
-               spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
-               snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
        }
 
-       WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
+       generic_hdmi_init_per_pins(codec);
        return 0;
 }
 
@@ -3481,14 +3559,14 @@ HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI",   patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI",   patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI",        patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",   patch_i915_hsw_hdmi),
 HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_i915_byt_hdmi),
 HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI",  patch_generic_hdmi),
 /* special ID for generic HDMI */
 HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),