ALSA: hda - hdmi: Refactor hdmi_eld into parsed_hdmi_eld
[deliverable/linux.git] / sound / pci / hda / patch_hdmi.c
index 807a2aa1ff384e5a54f64a6f951f8d8e44e3061b..1e381918eb826a89abf8192ac1b903830394b76b 100644 (file)
@@ -64,6 +64,9 @@ struct hdmi_spec_per_cvt {
        unsigned int maxbps;
 };
 
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS    32
+
 struct hdmi_spec_per_pin {
        hda_nid_t pin_nid;
        int num_mux_nids;
@@ -81,6 +84,7 @@ struct hdmi_spec_per_pin {
 struct hdmi_spec {
        int num_cvts;
        struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
+       hda_nid_t cvt_nids[MAX_HDMI_CVTS];
 
        int num_pins;
        struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
@@ -339,14 +343,16 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_info *uinfo)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct hdmi_spec *spec;
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_eld *eld;
        int pin_idx;
 
-       spec = codec->spec;
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 
        pin_idx = kcontrol->private_value;
-       uinfo->count = spec->pins[pin_idx].sink_eld.eld_size;
+       eld = &spec->pins[pin_idx].sink_eld;
+
+       uinfo->count = eld->eld_valid ? eld->eld_size : 0;
 
        return 0;
 }
@@ -355,14 +361,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct hdmi_spec *spec;
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_eld *eld;
        int pin_idx;
 
-       spec = codec->spec;
        pin_idx = kcontrol->private_value;
+       eld = &spec->pins[pin_idx].sink_eld;
 
-       memcpy(ucontrol->value.bytes.data,
-               spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE);
+       if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
+               snd_BUG();
+               return -EINVAL;
+       }
+
+       memset(ucontrol->value.bytes.data, 0,
+              ARRAY_SIZE(ucontrol->value.bytes.data));
+       if (eld->eld_valid)
+               memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+                      eld->eld_size);
 
        return 0;
 }
@@ -516,7 +531,7 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
         * expand ELD's notions to match the ones used by Audio InfoFrame.
         */
        for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (eld->spk_alloc & (1 << i))
+               if (eld->info.spk_alloc & (1 << i))
                        spk_mask |= eld_speaker_allocation_bits[i];
        }
 
@@ -530,7 +545,7 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
                }
        }
 
-       snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+       snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
        snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
                    ca, channels, buf);
 
@@ -714,9 +729,10 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
 
 static void hdmi_setup_channel_mapping(struct hda_codec *codec,
                                       hda_nid_t pin_nid, bool non_pcm, int ca,
-                                      int channels, unsigned char *map)
+                                      int channels, unsigned char *map,
+                                      bool chmap_set)
 {
-       if (!non_pcm && map) {
+       if (!non_pcm && chmap_set) {
                hdmi_manual_setup_channel_mapping(codec, pin_nid,
                                                  channels, map);
        } else {
@@ -870,7 +886,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
                ca = 0;
 
        memset(&ai, 0, sizeof(ai));
-       if (eld->conn_type == 0) { /* HDMI */
+       if (eld->info.conn_type == 0) { /* HDMI */
                struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
 
                hdmi_ai->type           = 0x84;
@@ -879,7 +895,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
                hdmi_ai->CC02_CT47      = channels - 1;
                hdmi_ai->CA             = ca;
                hdmi_checksum_audio_infoframe(hdmi_ai);
-       } else if (eld->conn_type == 1) { /* DisplayPort */
+       } else if (eld->info.conn_type == 1) { /* DisplayPort */
                struct dp_audio_infoframe *dp_ai = &ai.dp;
 
                dp_ai->type             = 0x84;
@@ -905,7 +921,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
                            pin_nid,
                            channels);
                hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                          channels, per_pin->chmap);
+                                          channels, per_pin->chmap,
+                                          per_pin->chmap_set);
                hdmi_stop_infoframe_trans(codec, pin_nid);
                hdmi_fill_audio_infoframe(codec, pin_nid,
                                            ai.bytes, sizeof(ai));
@@ -915,7 +932,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
                 * accordingly */
                if (per_pin->non_pcm != non_pcm)
                        hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                                  channels, per_pin->chmap);
+                                                  channels, per_pin->chmap,
+                                                  per_pin->chmap_set);
        }
 
        per_pin->non_pcm = non_pcm;
@@ -1098,10 +1116,14 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 
        /* Restrict capabilities by ELD if this isn't disabled */
        if (!static_hdmi_pcm && eld->eld_valid) {
-               snd_hdmi_eld_update_pcm_info(eld, hinfo);
+               snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
                if (hinfo->channels_min > hinfo->channels_max ||
-                   !hinfo->rates || !hinfo->formats)
+                   !hinfo->rates || !hinfo->formats) {
+                       per_cvt->assigned = 0;
+                       hinfo->nid = 0;
+                       snd_hda_spdif_ctls_unassign(codec, pin_idx);
                        return -ENODEV;
+               }
        }
 
        /* Store the updated parameters */
@@ -1155,8 +1177,6 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
        int present = snd_hda_pin_sense(codec, pin_nid);
        bool eld_valid = false;
 
-       memset(eld, 0, offsetof(struct hdmi_eld, eld_buffer));
-
        eld->monitor_present    = !!(present & AC_PINSENSE_PRESENCE);
        if (eld->monitor_present)
                eld_valid       = !!(present & AC_PINSENSE_ELDV);
@@ -1165,9 +1185,22 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
                "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
                codec->addr, pin_nid, eld->monitor_present, eld_valid);
 
+       eld->eld_valid = false;
        if (eld_valid) {
-               if (!snd_hdmi_get_eld(eld, codec, pin_nid))
-                       snd_hdmi_show_eld(eld);
+               if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer,
+                                                    &eld->eld_size) < 0)
+                       eld_valid = false;
+               else {
+                       memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld));
+                       if (snd_hdmi_parse_eld(&eld->info, eld->eld_buffer,
+                                                   eld->eld_size) < 0)
+                               eld_valid = false;
+               }
+
+               if (eld_valid) {
+                       snd_hdmi_show_eld(&eld->info);
+                       eld->eld_valid = true;
+               }
                else if (repoll) {
                        queue_delayed_work(codec->bus->workq,
                                           &per_pin->work,
@@ -1187,6 +1220,9 @@ static void hdmi_repoll_eld(struct work_struct *work)
        hdmi_present_sense(per_pin, per_pin->repoll_count);
 }
 
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
+                                            hda_nid_t nid);
+
 static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -1206,6 +1242,9 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
        if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
                return -E2BIG;
 
+       if (codec->vendor_id == 0x80862807)
+               intel_haswell_fixup_connect_list(codec, pin_nid);
+
        pin_idx = spec->num_pins;
        per_pin = &spec->pins[pin_idx];
 
@@ -1253,7 +1292,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        if (err < 0)
                return err;
 
-       spec->num_cvts++;
+       spec->cvt_nids[spec->num_cvts++] = cvt_nid;
 
        return 0;
 }
@@ -1681,30 +1720,92 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
        .unsol_event            = hdmi_unsol_event,
 };
 
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec)
+
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
+                                            hda_nid_t nid)
+{
+       struct hdmi_spec *spec = codec->spec;
+       hda_nid_t conns[4];
+       int nconns;
+
+       nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
+       if (nconns == spec->num_cvts &&
+           !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
+               return;
+
+       /* override pins connection list */
+       snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
+       snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
+}
+
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12                  0x02 /* enable DP 1.2 features */
+#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)
 {
        unsigned int vendor_param;
-       hda_nid_t list[3] = {0x2, 0x3, 0x4};
 
-       vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0);
-       if (vendor_param == -1 || vendor_param & 0x02)
+       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)
                return;
 
-       /* enable DP1.2 mode */
-       vendor_param |= 0x02;
-       snd_hda_codec_read(codec, 0x08, 0, 0x781, vendor_param);
+       vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+       vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+                               INTEL_SET_VENDOR_VERB, vendor_param);
+       if (vendor_param == -1)
+               return;
+
+       snd_hda_codec_update_widgets(codec);
+       return;
+}
 
-       vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0);
-       if (vendor_param == -1 || !(vendor_param & 0x02))
+static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
+{
+       unsigned int vendor_param;
+
+       vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+                               INTEL_GET_VENDOR_VERB, 0);
+       if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
                return;
 
-       /* override 3 pins connection list */
-       snd_hda_override_conn_list(codec, 0x05, 3, list);
-       snd_hda_override_conn_list(codec, 0x06, 3, list);
-       snd_hda_override_conn_list(codec, 0x07, 3, list);
+       /* enable DP1.2 mode */
+       vendor_param |= INTEL_EN_DP12;
+       snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
+                               INTEL_SET_VENDOR_VERB, vendor_param);
 }
 
 
+
+/* 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,
+       },
+};
+
+
 static int patch_generic_hdmi(struct hda_codec *codec)
 {
        struct hdmi_spec *spec;
@@ -1715,8 +1816,11 @@ static int patch_generic_hdmi(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       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)
-               intel_haswell_fixup_connect_list(codec);
+               intel_haswell_fixup_enable_dp12(codec);
 
        if (hdmi_parse_codec(codec) < 0) {
                codec->spec = NULL;
This page took 0.046135 seconds and 5 git commands to generate.