ALSA: hda - Use snd_hda_check_power_state() in patch_hdmi.c
[linux-2.6-microblaze.git] / sound / pci / hda / patch_hdmi.c
index 32930e6..49ef8f8 100644 (file)
@@ -1018,13 +1018,18 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
                hdmi_non_intrinsic_event(codec, res);
 }
 
-static void haswell_verify_pin_D0(struct hda_codec *codec, hda_nid_t nid)
+static void haswell_verify_pin_D0(struct hda_codec *codec,
+               hda_nid_t cvt_nid, hda_nid_t nid)
 {
        int pwr, lamp, ramp;
 
-       pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
-       pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
-       if (pwr != AC_PWRST_D0) {
+       /* For Haswell, the converter 1/2 may keep in D3 state after bootup,
+        * thus pins could only choose converter 0 for use. Make sure the
+        * converters are in correct power state */
+       if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0))
+               snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+       if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) {
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
                                    AC_PWRST_D0);
                msleep(40);
@@ -1068,7 +1073,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
        int new_pinctl = 0;
 
        if (codec->vendor_id == 0x80862807)
-               haswell_verify_pin_D0(codec, pin_nid);
+               haswell_verify_pin_D0(codec, cvt_nid, pin_nid);
 
        if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
@@ -1101,26 +1106,15 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
        return 0;
 }
 
-/*
- * HDA PCM callbacks
- */
-static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
-                        struct hda_codec *codec,
-                        struct snd_pcm_substream *substream)
+static int hdmi_choose_cvt(struct hda_codec *codec,
+                       int pin_idx, int *cvt_id, int *mux_id)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int pin_idx, cvt_idx, mux_idx = 0;
        struct hdmi_spec_per_pin *per_pin;
-       struct hdmi_eld *eld;
        struct hdmi_spec_per_cvt *per_cvt = NULL;
+       int cvt_idx, mux_idx = 0;
 
-       /* Validate hinfo */
-       pin_idx = hinfo_to_pin_index(spec, hinfo);
-       if (snd_BUG_ON(pin_idx < 0))
-               return -EINVAL;
        per_pin = get_pin(spec, pin_idx);
-       eld = &per_pin->sink_eld;
 
        /* Dynamically assign converter to stream */
        for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1138,17 +1132,89 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                        continue;
                break;
        }
+
        /* No free converters */
        if (cvt_idx == spec->num_cvts)
                return -ENODEV;
 
+       if (cvt_id)
+               *cvt_id = cvt_idx;
+       if (mux_id)
+               *mux_id = mux_idx;
+
+       return 0;
+}
+
+static void haswell_config_cvts(struct hda_codec *codec,
+                       int pin_id, int mux_id)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin;
+       int pin_idx, mux_idx;
+       int curr;
+       int err;
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               per_pin = get_pin(spec, pin_idx);
+
+               if (pin_idx == pin_id)
+                       continue;
+
+               curr = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+                                         AC_VERB_GET_CONNECT_SEL, 0);
+
+               /* Choose another unused converter */
+               if (curr == mux_id) {
+                       err = hdmi_choose_cvt(codec, pin_idx, NULL, &mux_idx);
+                       if (err < 0)
+                               return;
+                       snd_printdd("HDMI: choose converter %d for pin %d\n", mux_idx, pin_idx);
+                       snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+                                           AC_VERB_SET_CONNECT_SEL,
+                                           mux_idx);
+               }
+       }
+}
+
+/*
+ * HDA PCM callbacks
+ */
+static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
+                        struct hda_codec *codec,
+                        struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int pin_idx, cvt_idx, mux_idx = 0;
+       struct hdmi_spec_per_pin *per_pin;
+       struct hdmi_eld *eld;
+       struct hdmi_spec_per_cvt *per_cvt = NULL;
+       int err;
+
+       /* Validate hinfo */
+       pin_idx = hinfo_to_pin_index(spec, hinfo);
+       if (snd_BUG_ON(pin_idx < 0))
+               return -EINVAL;
+       per_pin = get_pin(spec, pin_idx);
+       eld = &per_pin->sink_eld;
+
+       err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
+       if (err < 0)
+               return err;
+
+       per_cvt = get_cvt(spec, cvt_idx);
        /* Claim converter */
        per_cvt->assigned = 1;
        hinfo->nid = per_cvt->cvt_nid;
 
-       snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+       snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
                            AC_VERB_SET_CONNECT_SEL,
                            mux_idx);
+
+       /* configure unused pins to choose other converters */
+       if (codec->vendor_id == 0x80862807)
+               haswell_config_cvts(codec, pin_idx, mux_idx);
+
        snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
 
        /* Initially set the converter's capabilities */
@@ -1832,12 +1898,10 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
 #define INTEL_EN_ALL_PIN_CVTS  0x01 /* enable 2nd & 3rd pins and convertors */
 
 static void intel_haswell_enable_all_pins(struct hda_codec *codec,
-                                       const struct hda_fixup *fix, int action)
+                                         bool update_tree)
 {
        unsigned int vendor_param;
 
-       if (action != HDA_FIXUP_ACT_PRE_PROBE)
-               return;
        vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
                                INTEL_GET_VENDOR_VERB, 0);
        if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
@@ -1849,8 +1913,8 @@ static void intel_haswell_enable_all_pins(struct hda_codec *codec,
        if (vendor_param == -1)
                return;
 
-       snd_hda_codec_update_widgets(codec);
-       return;
+       if (update_tree)
+               snd_hda_codec_update_widgets(codec);
 }
 
 static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
@@ -1868,30 +1932,20 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
                                INTEL_SET_VENDOR_VERB, vendor_param);
 }
 
+/* Haswell needs to re-issue the vendor-specific verbs before turning to D0.
+ * Otherwise you may get severe h/w communication errors.
+ */
+static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+                               unsigned int power_state)
+{
+       if (power_state == AC_PWRST_D0) {
+               intel_haswell_enable_all_pins(codec, false);
+               intel_haswell_fixup_enable_dp12(codec);
+       }
 
-
-/* available models for fixup */
-enum {
-       INTEL_HASWELL,
-};
-
-static const struct hda_model_fixup hdmi_models[] = {
-       {.id = INTEL_HASWELL, .name = "Haswell"},
-       {}
-};
-
-static const struct snd_pci_quirk hdmi_fixup_tbl[] = {
-       SND_PCI_QUIRK(0x8086, 0x2010, "Haswell", INTEL_HASWELL),
-       {} /* terminator */
-};
-
-static const struct hda_fixup hdmi_fixups[] = {
-       [INTEL_HASWELL] = {
-               .type = HDA_FIXUP_FUNC,
-               .v.func = intel_haswell_enable_all_pins,
-       },
-};
-
+       snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
+       snd_hda_codec_set_power_to_all(codec, fg, power_state);
+}
 
 static int patch_generic_hdmi(struct hda_codec *codec)
 {
@@ -1904,11 +1958,10 @@ static int patch_generic_hdmi(struct hda_codec *codec)
        codec->spec = spec;
        hdmi_array_init(spec, 4);
 
-       snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups);
-       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
-       if (codec->vendor_id == 0x80862807)
+       if (codec->vendor_id == 0x80862807) {
+               intel_haswell_enable_all_pins(codec, true);
                intel_haswell_fixup_enable_dp12(codec);
+       }
 
        if (hdmi_parse_codec(codec) < 0) {
                codec->spec = NULL;
@@ -1916,6 +1969,9 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                return -EINVAL;
        }
        codec->patch_ops = generic_hdmi_patch_ops;
+       if (codec->vendor_id == 0x80862807)
+               codec->patch_ops.set_power_state = haswell_set_power_state;
+
        generic_hdmi_init_per_pins(codec);
 
        init_channel_allocations();