ALSA: hda - Add "Mute-LED Mode" enum control
Takashi Iwai [Mon, 12 Mar 2012 15:59:58 +0000 (16:59 +0100)]
Create snd_hda_add_vmaster_hook() and snd_hda_sync_vmaster_hook()
helper functions to handle the mute-LED in vmaster hook more
commonly.  In the former function, a new enum control "Mute-LED Mode"
is added.  This provides user to choose whether the mute-LED should be
turned on/off explicitly or to follow the master-mute status.

Reviewed-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

sound/pci/hda/hda_codec.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c

index b79ee34..b981ea9 100644 (file)
@@ -2450,6 +2450,100 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 }
 EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster);
 
+/*
+ * mute-LED control using vmaster
+ */
+static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       static const char * const texts[] = {
+               "Off", "On", "Follow Master"
+       };
+       unsigned int index;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       index = uinfo->value.enumerated.item;
+       if (index >= 3)
+               index = 2;
+       strcpy(uinfo->value.enumerated.name, texts[index]);
+       return 0;
+}
+
+static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = hook->mute_mode;
+       return 0;
+}
+
+static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
+       unsigned int old_mode = hook->mute_mode;
+
+       hook->mute_mode = ucontrol->value.enumerated.item[0];
+       if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER)
+               hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
+       if (old_mode == hook->mute_mode)
+               return 0;
+       snd_hda_sync_vmaster_hook(hook);
+       return 1;
+}
+
+static struct snd_kcontrol_new vmaster_mute_mode = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Mute-LED Mode",
+       .info = vmaster_mute_mode_info,
+       .get = vmaster_mute_mode_get,
+       .put = vmaster_mute_mode_put,
+};
+
+/*
+ * Add a mute-LED hook with the given vmaster switch kctl
+ * "Mute-LED Mode" control is automatically created and associated with
+ * the given hook.
+ */
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+                            struct hda_vmaster_mute_hook *hook)
+{
+       struct snd_kcontrol *kctl;
+
+       if (!hook->hook || !hook->sw_kctl)
+               return 0;
+       snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec);
+       hook->codec = codec;
+       hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
+       kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
+       if (!kctl)
+               return -ENOMEM;
+       return snd_hda_ctl_add(codec, 0, kctl);
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook);
+
+/*
+ * Call the hook with the current value for synchronization
+ * Should be called in init callback
+ */
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
+{
+       if (!hook->hook || !hook->codec)
+               return;
+       switch (hook->mute_mode) {
+       case HDA_VMUTE_FOLLOW_MASTER:
+               snd_ctl_sync_vmaster_hook(hook->sw_kctl);
+               break;
+       default:
+               hook->hook(hook->codec, hook->mute_mode);
+               break;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook);
+
+
 /**
  * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
  *
index c3ee4ed..3f82ab6 100644 (file)
@@ -147,6 +147,27 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
 int snd_hda_codec_reset(struct hda_codec *codec);
 
+enum {
+       HDA_VMUTE_OFF,
+       HDA_VMUTE_ON,
+       HDA_VMUTE_FOLLOW_MASTER,
+};
+
+struct hda_vmaster_mute_hook {
+       /* below two fields must be filled by the caller of
+        * snd_hda_add_vmaster_hook() beforehand
+        */
+       struct snd_kcontrol *sw_kctl;
+       void (*hook)(void *, int);
+       /* below are initialized automatically */
+       unsigned int mute_mode; /* HDA_VMUTE_XXX */
+       struct hda_codec *codec;
+};
+
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+                            struct hda_vmaster_mute_hook *hook);
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
+
 /* amp value bits */
 #define HDA_AMP_MUTE   0x80
 #define HDA_AMP_UNMUTE 0x00
index f1c9aed..a21a485 100644 (file)
@@ -70,8 +70,7 @@ struct conexant_spec {
        const struct snd_kcontrol_new *mixers[5];
        int num_mixers;
        hda_nid_t vmaster_nid;
-       struct snd_kcontrol *vmaster_sw_kctl;
-       void (*vmaster_hook)(struct snd_kcontrol *, int);
+       struct hda_vmaster_mute_hook vmaster_mute;
 
        const struct hda_verb *init_verbs[5];   /* initialization verbs
                                                 * don't forget NULL
@@ -518,7 +517,7 @@ static int conexant_build_controls(struct hda_codec *codec)
                err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
                                            NULL, slave_pfxs,
                                            "Playback Switch", true,
-                                           &spec->vmaster_sw_kctl);
+                                           &spec->vmaster_mute.sw_kctl);
                if (err < 0)
                        return err;
        }
@@ -4101,7 +4100,7 @@ static int cx_auto_init(struct hda_codec *codec)
        cx_auto_init_input(codec);
        cx_auto_init_digital(codec);
        snd_hda_jack_report_sync(codec);
-       snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
        return 0;
 }
 
@@ -4347,10 +4346,10 @@ static int cx_auto_build_controls(struct hda_codec *codec)
        err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
-       if (spec->vmaster_hook && spec->vmaster_sw_kctl) {
-               snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
-                                        spec->vmaster_hook, codec);
-               snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+       if (spec->vmaster_mute.hook && spec->vmaster_mute.sw_kctl) {
+               err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
+               if (err < 0)
+                       return err;
        }
        return 0;
 }
@@ -4481,7 +4480,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
        /* NOTE: this should be applied via fixup once when the generic
         *       fixup code is merged to hda_codec.c
         */
-       spec->vmaster_hook = cx_auto_vmaster_hook;
+       spec->vmaster_mute.hook = cx_auto_vmaster_hook;
 
        err = cx_auto_search_adcs(codec);
        if (err < 0)
index 9015472..b69d2fe 100644 (file)
@@ -198,7 +198,7 @@ struct alc_spec {
 
        /* for virtual master */
        hda_nid_t vmaster_nid;
-       struct snd_kcontrol *vmaster_sw_kctl;
+       struct hda_vmaster_mute_hook vmaster_mute;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_loopback_check loopback;
        int num_loopbacks;
@@ -1960,7 +1960,7 @@ static int __alc_build_controls(struct hda_codec *codec)
                err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
                                            NULL, alc_slave_pfxs,
                                            "Playback Switch",
-                                           true, &spec->vmaster_sw_kctl);
+                                           true, &spec->vmaster_mute.sw_kctl);
                if (err < 0)
                        return err;
        }
@@ -5894,13 +5894,11 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
        struct alc_spec *spec = codec->spec;
        switch (action) {
        case ALC_FIXUP_ACT_BUILD:
-               if (!spec->vmaster_sw_kctl)
-                       return;
-               snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
-                                        alc269_fixup_mic2_mute_hook, codec);
+               spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
+               snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
                /* fallthru */
        case ALC_FIXUP_ACT_INIT:
-               snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+               snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
                break;
        }
 }
index 6e92649..cd04e29 100644 (file)
@@ -311,7 +311,7 @@ struct sigmatel_spec {
        unsigned auto_dmic_cnt;
        hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
 
-       struct snd_kcontrol *vmaster_sw_kctl;
+       struct hda_vmaster_mute_hook vmaster_mute;
 };
 
 static const hda_nid_t stac9200_adc_nids[1] = {
@@ -1160,14 +1160,15 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
                                    NULL, slave_pfxs,
                                    "Playback Switch", true,
-                                   &spec->vmaster_sw_kctl);
+                                   &spec->vmaster_mute.sw_kctl);
        if (err < 0)
                return err;
 
        if (spec->gpio_led) {
-               snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
-                                        stac92xx_vmaster_hook, codec);
-               snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+               spec->vmaster_mute.hook = stac92xx_vmaster_hook;
+               err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
+               if (err < 0)
+                       return err;
        }
 
        if (spec->aloopback_ctl &&
@@ -4432,7 +4433,7 @@ static int stac92xx_init(struct hda_codec *codec)
        snd_hda_jack_report_sync(codec);
 
        /* sync mute LED */
-       snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
        if (spec->dac_list)
                stac92xx_power_down(codec);
        return 0;