Merge tag 'modules-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / sound / pci / hda / hda_generic.c
index 4b1524a..8e77cbb 100644 (file)
@@ -133,6 +133,9 @@ static void parse_user_hints(struct hda_codec *codec)
        val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
        if (val >= 0)
                spec->line_in_auto_switch = !!val;
+       val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp");
+       if (val >= 0)
+               spec->auto_mute_via_amp = !!val;
        val = snd_hda_get_bool_hint(codec, "need_dac_fix");
        if (val >= 0)
                spec->need_dac_fix = !!val;
@@ -808,6 +811,9 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
  * Helper functions for creating mixer ctl elements
  */
 
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol);
+
 enum {
        HDA_CTL_WIDGET_VOL,
        HDA_CTL_WIDGET_MUTE,
@@ -815,7 +821,15 @@ enum {
 };
 static const struct snd_kcontrol_new control_templates[] = {
        HDA_CODEC_VOLUME(NULL, 0, 0, 0),
-       HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       /* only the put callback is replaced for handling the special mute */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
+               .info = snd_hda_mixer_amp_switch_info,
+               .get = snd_hda_mixer_amp_switch_get,
+               .put = hda_gen_mixer_mute_put, /* replaced */
+               .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+       },
        HDA_BIND_MUTE(NULL, 0, 0, 0),
 };
 
@@ -840,7 +854,7 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
                                const char *pfx, const char *dir,
                                const char *sfx, int cidx, unsigned long val)
 {
-       char name[32];
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
        snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
        if (!add_control(spec, type, name, cidx, val))
                return -ENOMEM;
@@ -922,6 +936,23 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
        return add_sw_ctl(codec, pfx, cidx, chs, path);
 }
 
+/* playback mute control with the software mute bit check */
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gen_spec *spec = codec->spec;
+
+       if (spec->auto_mute_via_amp) {
+               hda_nid_t nid = get_amp_nid(kcontrol);
+               bool enabled = !((spec->mute_bits >> nid) & 1);
+               ucontrol->value.integer.value[0] &= enabled;
+               ucontrol->value.integer.value[1] &= enabled;
+       }
+
+       return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+
 /* any ctl assigned to the path with the given index? */
 static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
 {
@@ -1900,7 +1931,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
 
        for (i = 0; i < num_pins; i++) {
                const char *name;
-               char tmp[44];
+               char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
                int err, idx = 0;
 
                if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
@@ -2453,7 +2484,7 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
                }
                if (get_out_jack_num_items(codec, pin) > 1) {
                        struct snd_kcontrol_new *knew;
-                       char name[44];
+                       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
                        get_jack_mode_name(codec, pin, name, sizeof(name));
                        knew = snd_hda_gen_add_kctl(spec, name,
                                                    &out_jack_mode_enum);
@@ -2585,7 +2616,7 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
 {
        struct hda_gen_spec *spec = codec->spec;
        struct snd_kcontrol_new *knew;
-       char name[44];
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
        unsigned int defcfg;
 
        if (pin == spec->hp_mic_pin)
@@ -3285,7 +3316,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
                              bool inv_dmic)
 {
        struct hda_gen_spec *spec = codec->spec;
-       char tmpname[44];
+       char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
        int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
        const char *sfx = is_switch ? "Switch" : "Volume";
        unsigned int chs = inv_dmic ? 1 : 3;
@@ -3547,7 +3578,7 @@ static int parse_mic_boost(struct hda_codec *codec)
                struct nid_path *path;
                unsigned int val;
                int idx;
-               char boost_label[44];
+               char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
 
                idx = imux->items[i].index;
                if (idx >= imux->num_items)
@@ -3719,6 +3750,16 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
                unsigned int val, oldval;
                if (!nid)
                        break;
+
+               if (spec->auto_mute_via_amp) {
+                       if (mute)
+                               spec->mute_bits |= (1ULL << nid);
+                       else
+                               spec->mute_bits &= ~(1ULL << nid);
+                       set_pin_eapd(codec, nid, !mute);
+                       continue;
+               }
+
                oldval = snd_hda_codec_get_pin_target(codec, nid);
                if (oldval & PIN_IN)
                        continue; /* no mute for inputs */
@@ -3786,6 +3827,10 @@ static void call_update_outputs(struct hda_codec *codec)
                spec->automute_hook(codec);
        else
                snd_hda_gen_update_outputs(codec);
+
+       /* sync the whole vmaster slaves to reflect the new auto-mute status */
+       if (spec->auto_mute_via_amp && !codec->bus->shutdown)
+               snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false);
 }
 
 /* standard HP-automute helper */