ALSA: hda - Fix independent-HP handling in patch_via.c
authorTakashi Iwai <tiwai@suse.de>
Sun, 19 Jun 2011 14:24:21 +0000 (16:24 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 20 Jun 2011 14:24:08 +0000 (16:24 +0200)
Fix races in handling of HP DAC and independent streams for VIA codecs.
Also, allow the HP output path without front-DAC, and removed
unnecessary activation of HP mixer elements.

This also removes the handling of shared side/HP stream; it's anyway
implemented in a broken way, so we need to re-implement the feature
later...

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_via.c

index 14fccdc..fa5ed36 100644 (file)
@@ -133,6 +133,7 @@ struct via_spec {
        /* playback */
        struct hda_multi_out multiout;
        hda_nid_t slave_dig_outs[2];
+       hda_nid_t hp_dac_nid;
 
        struct nid_path out_path[4];
        struct nid_path hp_path;
@@ -702,64 +703,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value;
-       unsigned int pinsel;
-
-       /* use !! to translate conn sel 2 for VT1718S */
-       pinsel = !!snd_hda_codec_read(codec, nid, 0,
-                                     AC_VERB_GET_CONNECT_SEL,
-                                     0x00);
-       ucontrol->value.enumerated.item[0] = pinsel;
-
-       return 0;
-}
-
-static void activate_ctl(struct hda_codec *codec, const char *name, int active)
-{
-       struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
-       if (ctl) {
-               ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
-               ctl->vd[0].access |= active
-                       ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
-               snd_ctl_notify(codec->bus->card,
-                              SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
-       }
-}
-
-static hda_nid_t side_mute_channel(struct via_spec *spec)
-{
-       switch (spec->codec_type) {
-       case VT1708:            return 0x1b;
-       case VT1709_10CH:       return 0x29;
-       case VT1708B_8CH:       /* fall thru */
-       case VT1708S:           return 0x27;
-       case VT2002P:           return 0x19;
-       case VT1802:            return 0x15;
-       case VT1812:            return 0x15;
-       default:                return 0;
-       }
-}
-
-static int update_side_mute_status(struct hda_codec *codec)
-{
-       /* mute side channel */
        struct via_spec *spec = codec->spec;
-       unsigned int parm;
-       hda_nid_t sw3 = side_mute_channel(spec);
 
-       if (sw3) {
-               if (VT2002P_COMPATIBLE(spec))
-                       parm = spec->hp_independent_mode ?
-                              AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
-               else
-                       parm = spec->hp_independent_mode ?
-                              AMP_OUT_MUTE : AMP_OUT_UNMUTE;
-               snd_hda_codec_write(codec, sw3, 0,
-                                   AC_VERB_SET_AMP_GAIN_MUTE, parm);
-               if (spec->codec_type == VT1812)
-                       snd_hda_codec_write(codec, 0x1d, 0,
-                                           AC_VERB_SET_AMP_GAIN_MUTE, parm);
-       }
+       ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
        return 0;
 }
 
@@ -773,50 +719,19 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
        /* Get Independent Mode index of headphone pin widget */
        spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
                ? 1 : 0;
-       if (spec->codec_type == VT1718S)
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
-       else
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CONNECT_SEL, pinsel);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
 
-       if (spec->codec_type == VT1812)
-               snd_hda_codec_write(codec, 0x35, 0,
-                                   AC_VERB_SET_CONNECT_SEL, pinsel);
-       if (spec->multiout.hp_nid && spec->multiout.hp_nid
-           != spec->multiout.dac_nids[HDA_FRONT])
-               snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
-                                          0, 0, 0);
-
-       update_side_mute_status(codec);
-       /* update HP volume/swtich active state */
-       if (spec->codec_type == VT1708S
-           || spec->codec_type == VT1702
-           || spec->codec_type == VT1718S
-           || spec->codec_type == VT1716S
-           || VT2002P_COMPATIBLE(spec)) {
-               activate_ctl(codec, "Headphone Playback Volume",
-                            spec->hp_independent_mode);
-               activate_ctl(codec, "Headphone Playback Switch",
-                            spec->hp_independent_mode);
-       }
        /* update jack power state */
        set_widgets_power_state(codec);
        return 0;
 }
 
-static const struct snd_kcontrol_new via_hp_mixer[2] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Independent HP",
-               .info = via_independent_hp_info,
-               .get = via_independent_hp_get,
-               .put = via_independent_hp_put,
-       },
-       {
-               .iface = NID_MAPPING,
-               .name = "Independent HP",
-       },
+static const struct snd_kcontrol_new via_hp_mixer = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Independent HP",
+       .info = via_independent_hp_info,
+       .get = via_independent_hp_get,
+       .put = via_independent_hp_put,
 };
 
 static int via_hp_build(struct hda_codec *codec)
@@ -824,44 +739,15 @@ static int via_hp_build(struct hda_codec *codec)
        struct via_spec *spec = codec->spec;
        struct snd_kcontrol_new *knew;
        hda_nid_t nid;
-       int nums;
-       hda_nid_t conn[HDA_MAX_CONNECTIONS];
 
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               break;
-       case VT2002P:
-       case VT1802:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
-
-       if (spec->codec_type != VT1708) {
-               nums = snd_hda_get_connections(codec, nid,
-                                              conn, HDA_MAX_CONNECTIONS);
-               if (nums <= 1)
-                       return 0;
-       }
-
-       knew = via_clone_control(spec, &via_hp_mixer[0]);
+       nid = spec->autocfg.hp_pins[0];
+       knew = via_clone_control(spec, &via_hp_mixer);
        if (knew == NULL)
                return -ENOMEM;
 
        knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
        knew->private_value = nid;
 
-       knew = via_clone_control(spec, &via_hp_mixer[1]);
-       if (knew == NULL)
-               return -ENOMEM;
-       knew->subdevice = side_mute_channel(spec);
-
        return 0;
 }
 
@@ -1199,20 +1085,26 @@ static void substream_set_idle(struct hda_codec *codec,
        analog_low_current_mode(codec, idle);
 }
 
-static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
+static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
                                 struct hda_codec *codec,
                                 struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
+
+       if (!spec->hp_independent_mode)
+               spec->multiout.hp_nid = spec->hp_dac_nid;
        substream_set_idle(codec, substream);
        return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
                                             hinfo);
 }
 
-static int via_playback_pcm_close(struct hda_pcm_stream *hinfo,
+static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
                                  struct hda_codec *codec,
                                  struct snd_pcm_substream *substream)
 {
+       struct via_spec *spec = codec->spec;
+
+       spec->multiout.hp_nid = 0;
        substream_set_idle(codec, substream);
        return 0;
 }
@@ -1222,11 +1114,19 @@ static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
                                    struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
 
-       if (!mout->hp_nid || mout->hp_nid == mout->dac_nids[HDA_FRONT] ||
-           !spec->hp_independent_mode)
+       if (snd_BUG_ON(!spec->hp_dac_nid))
                return -EINVAL;
+       if (!spec->hp_independent_mode || spec->multiout.hp_nid)
+               return -EBUSY;
+       substream_set_idle(codec, substream);
+       return 0;
+}
+
+static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
+                                    struct hda_codec *codec,
+                                    struct snd_pcm_substream *substream)
+{
        substream_set_idle(codec, substream);
        return 0;
 }
@@ -1238,68 +1138,9 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
                                          struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
-       const hda_nid_t *nids = mout->dac_nids;
-       int chs = substream->runtime->channels;
-       int i;
-       struct hda_spdif_out *spdif =
-               snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid);
-
-       mutex_lock(&codec->spdif_mutex);
-       if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
-               if (chs == 2 &&
-                   snd_hda_is_supported_format(codec, mout->dig_out_nid,
-                                               format) &&
-                   !(spdif->status & IEC958_AES0_NONAUDIO)) {
-                       mout->dig_out_used = HDA_DIG_ANALOG_DUP;
-                       /* turn off SPDIF once; otherwise the IEC958 bits won't
-                        * be updated */
-                       if (spdif->ctls & AC_DIG1_ENABLE)
-                               snd_hda_codec_write(codec, mout->dig_out_nid, 0,
-                                                   AC_VERB_SET_DIGI_CONVERT_1,
-                                                   spdif->ctls &
-                                                       ~AC_DIG1_ENABLE & 0xff);
-                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                                  stream_tag, 0, format);
-                       /* turn on again (if needed) */
-                       if (spdif->ctls & AC_DIG1_ENABLE)
-                               snd_hda_codec_write(codec, mout->dig_out_nid, 0,
-                                                   AC_VERB_SET_DIGI_CONVERT_1,
-                                                   spdif->ctls & 0xff);
-               } else {
-                       mout->dig_out_used = 0;
-                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                                  0, 0, 0);
-               }
-       }
-       mutex_unlock(&codec->spdif_mutex);
-
-       /* front */
-       snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
-                                  0, format);
-
-       if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
-           && !spec->hp_independent_mode)
-               /* headphone out will just decode front left/right (stereo) */
-               snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
-                                          0, format);
-
-       /* extra outputs copied from front */
-       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-               if (mout->extra_out_nid[i])
-                       snd_hda_codec_setup_stream(codec,
-                                                  mout->extra_out_nid[i],
-                                                  stream_tag, 0, format);
-
-       /* surrounds */
-       for (i = 1; i < mout->num_dacs; i++) {
-               if (chs >= (i + 1) * 2) /* independent out */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
-                                                  i * 2, format);
-               else /* copy front */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
-                                                  0, format);
-       }
+
+       snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+                                        format, substream);
        vt1708_start_hp_work(spec);
        return 0;
 }
@@ -1311,9 +1152,9 @@ static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
                                       struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
 
-       snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
+       snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
+                                  stream_tag, 0, format);
        vt1708_start_hp_work(spec);
        return 0;
 }
@@ -1323,30 +1164,8 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                    struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
-       const hda_nid_t *nids = mout->dac_nids;
-       int i;
 
-       for (i = 0; i < mout->num_dacs; i++)
-               snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
-
-       if (mout->hp_nid && !spec->hp_independent_mode)
-               snd_hda_codec_setup_stream(codec, mout->hp_nid,
-                                          0, 0, 0);
-
-       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-               if (mout->extra_out_nid[i])
-                       snd_hda_codec_setup_stream(codec,
-                                                  mout->extra_out_nid[i],
-                                                  0, 0, 0);
-       mutex_lock(&codec->spdif_mutex);
-       if (mout->dig_out_nid &&
-           mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
-               snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                          0, 0, 0);
-               mout->dig_out_used = 0;
-       }
-       mutex_unlock(&codec->spdif_mutex);
+       snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
        vt1708_stop_hp_work(spec);
        return 0;
 }
@@ -1356,9 +1175,8 @@ static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                       struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
 
-       snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+       snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
        vt1708_stop_hp_work(spec);
        return 0;
 }
@@ -1433,8 +1251,8 @@ static const struct hda_pcm_stream via_pcm_analog_playback = {
        .channels_max = 8,
        /* NID is set in via_build_pcms */
        .ops = {
-               .open = via_playback_pcm_open,
-               .close = via_playback_pcm_close,
+               .open = via_playback_multi_pcm_open,
+               .close = via_playback_multi_pcm_close,
                .prepare = via_playback_multi_pcm_prepare,
                .cleanup = via_playback_multi_pcm_cleanup
        },
@@ -1447,7 +1265,7 @@ static const struct hda_pcm_stream via_pcm_hp_playback = {
        /* NID is set in via_build_pcms */
        .ops = {
                .open = via_playback_hp_pcm_open,
-               .close = via_playback_pcm_close,
+               .close = via_playback_hp_pcm_close,
                .prepare = via_playback_hp_pcm_prepare,
                .cleanup = via_playback_hp_pcm_cleanup
        },
@@ -1464,8 +1282,8 @@ static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
         */
        .formats = SNDRV_PCM_FMTBIT_S16_LE,
        .ops = {
-               .open = via_playback_pcm_open,
-               .close = via_playback_pcm_close,
+               .open = via_playback_multi_pcm_open,
+               .close = via_playback_multi_pcm_close,
                .prepare = via_playback_multi_pcm_prepare,
                .cleanup = via_playback_multi_pcm_cleanup
        },
@@ -1477,8 +1295,6 @@ static const struct hda_pcm_stream via_pcm_analog_capture = {
        .channels_max = 2,
        /* NID is set in via_build_pcms */
        .ops = {
-               .open = via_playback_pcm_open,
-               .close = via_playback_pcm_close,
                .prepare = via_capture_pcm_prepare,
                .cleanup = via_capture_pcm_cleanup
        },
@@ -1624,7 +1440,7 @@ static int via_build_pcms(struct hda_codec *codec)
                }
        }
 
-       if (spec->multiout.hp_nid) {
+       if (spec->hp_dac_nid) {
                codec->num_pcms++;
                info++;
                snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
@@ -1632,7 +1448,7 @@ static int via_build_pcms(struct hda_codec *codec)
                info->name = spec->stream_name_hp;
                info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
                info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
-                       spec->multiout.hp_nid;
+                       spec->hp_dac_nid;
        }
        return 0;
 }
@@ -1883,7 +1699,7 @@ static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
                if (spec->multiout.dac_nids[i] == dac)
                        return false;
        }
-       if (spec->multiout.hp_nid == dac)
+       if (spec->hp_dac_nid == dac)
                return false;
        return true;
 }
@@ -2076,24 +1892,25 @@ static void create_hp_imux(struct via_spec *spec)
 static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
 {
        struct via_spec *spec = codec->spec;
-       hda_nid_t dac = 0;
        int err;
 
        if (!pin)
                return 0;
 
-       if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
-                              &spec->hp_dep_path, 0, -1))
-               return 0;
        if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
-               dac = spec->hp_path.path[spec->hp_path.depth - 1];
-               spec->multiout.hp_nid = dac;
+               spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
                spec->hp_independent_mode_index =
                        spec->hp_path.idx[spec->hp_path.depth - 1];
                create_hp_imux(spec);
        }
 
-       err = create_ch_ctls(codec, "Headphone", pin, dac, 3);
+       if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
+                              &spec->hp_dep_path, 0, -1) &&
+           !spec->hp_dac_nid)
+               return 0;
+
+
+       err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
        if (err < 0)
                return err;
 
@@ -2364,8 +2181,11 @@ static int via_parse_auto_config(struct hda_codec *codec)
 
        spec->input_mux = &spec->private_imux[0];
 
-       if (spec->hp_mux)
-               via_hp_build(codec);
+       if (spec->hp_mux) {
+               err = via_hp_build(codec);
+               if (err < 0)
+                       return err;
+       }
 
        err = via_smart51_build(codec);
        if (err < 0)