ALSA: hda - Add workaround for conflicting IEC958 controls
authorTakashi Iwai <tiwai@suse.de>
Fri, 12 Oct 2012 15:24:51 +0000 (17:24 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 17 Oct 2012 06:42:00 +0000 (08:42 +0200)
When both an SPDIF and an HDMI device are created on the same card
instance, multiple IEC958 controls are created with indices=0, 1, ...
But the alsa-lib configuration can't know which index corresponds
actually to which PCM device, and both the SPDIF and the HDMI
configurations point to the first IEC958 control wrongly.

This patch introduces a (hackish and ugly) workaround: the IEC958
controls for the SPDIF device are re-labeled with device=1 when HDMI
coexists.  The device=1 corresponds to the actual PCM device for
SPDIF, so it's anyway a better representation.  In future, HDMI
controls should be moved with the corresponding PCM device number,
too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_local.h
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c

index ee958a7d1647bf7dea321b2866fc148f2379bc9b..2da78751951305957bc5e3f761248ab1906abf87 100644 (file)
@@ -2166,12 +2166,12 @@ EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
 
 /* find a mixer control element with the given name */
 static struct snd_kcontrol *
-_snd_hda_find_mixer_ctl(struct hda_codec *codec,
-                       const char *name, int idx)
+find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
 {
        struct snd_ctl_elem_id id;
        memset(&id, 0, sizeof(id));
        id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       id.device = dev;
        id.index = idx;
        if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
                return NULL;
@@ -2189,15 +2189,16 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
 struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
                                            const char *name)
 {
-       return _snd_hda_find_mixer_ctl(codec, name, 0);
+       return find_mixer_ctl(codec, name, 0, 0);
 }
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
-static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name)
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
+                                   int dev)
 {
        int idx;
        for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */
-               if (!_snd_hda_find_mixer_ctl(codec, name, idx))
+               if (!find_mixer_ctl(codec, name, dev, idx))
                        return idx;
        }
        return -EBUSY;
@@ -3148,26 +3149,48 @@ static struct snd_kcontrol_new dig_mixes[] = {
 };
 
 /**
- * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
  * @codec: the HDA codec
- * @nid: audio out widget NID
- *
- * Creates controls related with the SPDIF output.
- * Called from each patch supporting the SPDIF out.
+ * @associated_nid: NID that new ctls associated with
+ * @cvt_nid: converter NID
+ * @type: HDA_PCM_TYPE_*
+ * Creates controls related with the digital output.
+ * Called from each patch supporting the digital out.
  *
  * Returns 0 if successful, or a negative error code.
  */
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
-                                 hda_nid_t associated_nid,
-                                 hda_nid_t cvt_nid)
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+                               hda_nid_t associated_nid,
+                               hda_nid_t cvt_nid,
+                               int type)
 {
        int err;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_new *dig_mix;
-       int idx;
+       int idx, dev = 0;
+       const int spdif_pcm_dev = 1;
        struct hda_spdif_out *spdif;
 
-       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch");
+       if (codec->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+           type == HDA_PCM_TYPE_SPDIF) {
+               dev = spdif_pcm_dev;
+       } else if (codec->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
+                  type == HDA_PCM_TYPE_HDMI) {
+               for (idx = 0; idx < codec->spdif_out.used; idx++) {
+                       spdif = snd_array_elem(&codec->spdif_out, idx);
+                       for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+                               kctl = find_mixer_ctl(codec, dig_mix->name, 0, idx);
+                               if (!kctl)
+                                       break;
+                               kctl->id.device = spdif_pcm_dev;
+                       }
+               }
+               codec->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
+       }
+       if (!codec->primary_dig_out_type)
+               codec->primary_dig_out_type = type;
+
+       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", dev);
        if (idx < 0) {
                printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
                return -EBUSY;
@@ -3177,6 +3200,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
                kctl = snd_ctl_new1(dig_mix, codec);
                if (!kctl)
                        return -ENOMEM;
+               kctl->id.device = dev;
                kctl->id.index = idx;
                kctl->private_value = codec->spdif_out.used - 1;
                err = snd_hda_ctl_add(codec, associated_nid, kctl);
@@ -3189,7 +3213,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
        spdif->status = convert_to_spdif_status(spdif->ctls);
        return 0;
 }
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
+EXPORT_SYMBOL_HDA(snd_hda_create_dig_out_ctls);
 
 /* get the hda_spdif_out entry from the given NID
  * call within spdif_mutex lock
@@ -3364,7 +3388,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
        struct snd_kcontrol_new *dig_mix;
        int idx;
 
-       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch");
+       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
        if (idx < 0) {
                printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
                return -EBUSY;
@@ -4472,7 +4496,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
                                addr = codec->addr;
                        else if (!idx && !knew->index) {
                                idx = find_empty_mixer_ctl_idx(codec,
-                                                              knew->name);
+                                                              knew->name, 0);
                                if (idx <= 0)
                                        return err;
                        } else
index 10a03b049bedb00e68dcb789ae62cc3a0a2a83fe..62d4229c7b95c80c3e655615fa5a8c864f60ebeb 100644 (file)
@@ -836,6 +836,7 @@ struct hda_codec {
        struct mutex hash_mutex;
        struct snd_array spdif_out;
        unsigned int spdif_in_enable;   /* SPDIF input enable? */
+       int primary_dig_out_type;       /* primary digital out PCM type */
        const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
        struct snd_array init_pins;     /* initial (BIOS) pin configurations */
        struct snd_array driver_pins;   /* pin configs set by codec parser */
index 09dbdc37f781592b45c8e8c03a80b0f133195996..8c43198b7f560ccc47d33ffe3755aa77f2f8e95c 100644 (file)
@@ -240,9 +240,11 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 /*
  * SPDIF I/O
  */
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
-                                 hda_nid_t associated_nid,
-                                 hda_nid_t cvt_nid);
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+                               hda_nid_t associated_nid,
+                               hda_nid_t cvt_nid, int type);
+#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
+       snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
 int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
 
 /*
index 61a71131711c734556249b17fc61fc51f1218c1a..a7f8790ae885cc71a595af5042f8b2f3e047197a 100644 (file)
@@ -873,8 +873,9 @@ static int build_digital_output(struct hda_codec *codec)
        if (!spec->multiout.dig_out_nid)
                return 0;
 
-       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid,
-                                           spec->multiout.dig_out_nid);
+       err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid,
+                                         spec->multiout.dig_out_nid,
+                                         spec->pcm_rec[1].pcm_type);
        if (err < 0)
                return err;
        err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
index 71555cc54db1686519b8bd20f168d4fe5babbcad..39ca1005995d71a9863348a5e29f368741dda99d 100644 (file)
@@ -1589,9 +1589,10 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
 
-               err = snd_hda_create_spdif_out_ctls(codec,
-                                                   per_pin->pin_nid,
-                                                   per_pin->mux_nids[0]);
+               err = snd_hda_create_dig_out_ctls(codec,
+                                                 per_pin->pin_nid,
+                                                 per_pin->mux_nids[0],
+                                                 HDA_PCM_TYPE_HDMI);
                if (err < 0)
                        return err;
                snd_hda_spdif_ctls_unassign(codec, pin_idx);
index 8253b4eeb6a1bea0687675e8ac66ca41b9a84758..2d2bb6629988b05048dbc214ee177d48fbc946a6 100644 (file)
@@ -1836,9 +1836,10 @@ static int __alc_build_controls(struct hda_codec *codec)
                        return err;
        }
        if (spec->multiout.dig_out_nid) {
-               err = snd_hda_create_spdif_out_ctls(codec,
-                                                   spec->multiout.dig_out_nid,
-                                                   spec->multiout.dig_out_nid);
+               err = snd_hda_create_dig_out_ctls(codec,
+                                                 spec->multiout.dig_out_nid,
+                                                 spec->multiout.dig_out_nid,
+                                                 spec->pcm_rec[1].pcm_type);
                if (err < 0)
                        return err;
                if (!spec->no_analog) {
index 770013ff556f6be0500b14039a8e60df2fa74b2a..62141650211fcdf572a476925ec942fc68d0c020 100644 (file)
@@ -1136,9 +1136,10 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        }
 
        if (spec->multiout.dig_out_nid) {
-               err = snd_hda_create_spdif_out_ctls(codec,
-                                                   spec->multiout.dig_out_nid,
-                                                   spec->multiout.dig_out_nid);
+               err = snd_hda_create_dig_out_ctls(codec,
+                                                 spec->multiout.dig_out_nid,
+                                                 spec->multiout.dig_out_nid,
+                                                 spec->autocfg.dig_out_type[0]);
                if (err < 0)
                        return err;
                err = snd_hda_create_spdif_share_sw(codec,