ALSA: hda - add more NID->Control mapping
authorJaroslav Kysela <perex@perex.cz>
Tue, 8 Dec 2009 15:13:32 +0000 (16:13 +0100)
committerJaroslav Kysela <perex@perex.cz>
Tue, 15 Dec 2009 08:33:04 +0000 (09:33 +0100)
This set of changes add missing NID values to some static control
elemenents. Also, it handles all "Capture Source" or "Input Source"
controls.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_via.c

index 9cfdb77..20100b1 100644 (file)
@@ -931,6 +931,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 #endif
        list_del(&codec->list);
        snd_array_free(&codec->mixers);
+       snd_array_free(&codec->nids);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
@@ -985,7 +986,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        mutex_init(&codec->control_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
+       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
+       snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
        snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        if (codec->bus->modelname) {
@@ -1706,7 +1708,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
 /**
- * snd_hda_ctl-add - Add a control element and assign to the codec
+ * snd_hda_ctl_add - Add a control element and assign to the codec
  * @codec: HD-audio codec
  * @nid: corresponding NID (optional)
  * @kctl: the control element to assign
@@ -1747,6 +1749,35 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
 /**
+ * snd_hda_add_nid - Assign a NID to a control element
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ * @index: index to kctl
+ *
+ * Add the given control element to an array inside the codec instance.
+ * This function is used when #snd_hda_ctl_add cannot be used for 1:1
+ * NID:KCTL mapping - for example "Capture Source" selector.
+ */
+int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
+                   unsigned int index, hda_nid_t nid)
+{
+       struct hda_nid_item *item;
+
+       if (nid > 0) {
+               item = snd_array_new(&codec->nids);
+               if (!item)
+                       return -ENOMEM;
+               item->kctl = kctl;
+               item->index = index;
+               item->nid = nid;
+               return 0;
+       }
+       return -EINVAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_nid);
+
+/**
  * snd_hda_ctls_clear - Clear all controls assigned to the given codec
  * @codec: HD-audio codec
  */
@@ -1757,6 +1788,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
        for (i = 0; i < codec->mixers.used; i++)
                snd_ctl_remove(codec->bus->card, items[i].kctl);
        snd_array_free(&codec->mixers);
+       snd_array_free(&codec->nids);
 }
 
 /* pseudo device locking
@@ -3476,6 +3508,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 
        for (; knew->name; knew++) {
                struct snd_kcontrol *kctl;
+               if (knew->iface == -1)  /* skip this codec private value */
+                       continue;
                kctl = snd_ctl_new1(knew, codec);
                if (!kctl)
                        return -ENOMEM;
@@ -3496,6 +3530,32 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 }
 EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
 
+/**
+ * snd_hda_add_nids - assign nids to controls from the array
+ * @codec: the HDA codec
+ * @kctl: struct snd_kcontrol
+ * @index: index to kctl
+ * @nids: the array of hda_nid_t
+ * @size: count of hda_nid_t items
+ *
+ * This helper function assigns NIDs in the given array to a control element.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl,
+                    unsigned int index, hda_nid_t *nids, unsigned int size)
+{
+       int err;
+
+       for ( ; size > 0; size--, nids++) {
+               err = snd_hda_add_nid(codec, kctl, index, *nids);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_nids);
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                                unsigned int power_state);
index 1d541b7..0d08ad5 100644 (file)
@@ -789,6 +789,7 @@ struct hda_codec {
        u32 *wcaps;
 
        struct snd_array mixers;        /* list of assigned mixer elements */
+       struct snd_array nids;          /* list of mapped mixer elements */
 
        struct hda_cache_rec amp_cache; /* cache for amp access */
        struct hda_cache_rec cmd_cache; /* cache for other commands */
index 092c6a7..5ea2128 100644 (file)
@@ -861,7 +861,8 @@ static int build_input_controls(struct hda_codec *codec)
        }
 
        /* create input MUX if multiple sources are available */
-       err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
+       err = snd_hda_ctl_add(codec, spec->adc_node->nid,
+                             snd_ctl_new1(&cap_sel, codec));
        if (err < 0)
                return err;
 
index 5778ae8..98cf3f4 100644 (file)
@@ -342,6 +342,8 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
                                const struct snd_pci_quirk *tbl);
 int snd_hda_add_new_ctls(struct hda_codec *codec,
                         struct snd_kcontrol_new *knew);
+int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl,
+                    unsigned int index, hda_nid_t *nids, unsigned int size);
 
 /*
  * unsolicited event handler
@@ -466,11 +468,14 @@ int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
 
 struct hda_nid_item {
        struct snd_kcontrol *kctl;
+       unsigned int index;
        hda_nid_t nid;
 };
 
 int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
                    struct snd_kcontrol *kctl);
+int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
+                   unsigned int index, hda_nid_t nid);
 void snd_hda_ctls_clear(struct hda_codec *codec);
 
 /*
index c9afc04..2e27d6a 100644 (file)
@@ -61,18 +61,21 @@ static const char *get_wid_type_name(unsigned int wid_value)
                return "UNKNOWN Widget";
 }
 
-static void print_nid_mixers(struct snd_info_buffer *buffer,
-                            struct hda_codec *codec, hda_nid_t nid)
+static void print_nid_array(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid,
+                           struct snd_array *array)
 {
        int i;
-       struct hda_nid_item *items = codec->mixers.list;
+       struct hda_nid_item *items = array->list, *item;
        struct snd_kcontrol *kctl;
-       for (i = 0; i < codec->mixers.used; i++) {
-               if (items[i].nid == nid) {
-                       kctl = items[i].kctl;
+       for (i = 0; i < array->used; i++) {
+               item = &items[i];
+               if (item->nid == nid) {
+                       kctl = item->kctl;
                        snd_iprintf(buffer,
                          "  Control: name=\"%s\", index=%i, device=%i\n",
-                         kctl->id.name, kctl->id.index, kctl->id.device);
+                         kctl->id.name, kctl->id.index + item->index,
+                         kctl->id.device);
                }
        }
 }
@@ -528,7 +531,8 @@ static void print_gpio(struct snd_info_buffer *buffer,
                            (data & (1<<i)) ? 1 : 0,
                            (unsol & (1<<i)) ? 1 : 0);
        /* FIXME: add GPO and GPI pin information */
-       print_nid_mixers(buffer, codec, nid);
+       print_nid_array(buffer, codec, nid, &codec->mixers);
+       print_nid_array(buffer, codec, nid, &codec->nids);
 }
 
 static void print_codec_info(struct snd_info_entry *entry,
@@ -608,7 +612,8 @@ static void print_codec_info(struct snd_info_entry *entry,
                        snd_iprintf(buffer, " CP");
                snd_iprintf(buffer, "\n");
 
-               print_nid_mixers(buffer, codec, nid);
+               print_nid_array(buffer, codec, nid, &codec->mixers);
+               print_nid_array(buffer, codec, nid, &codec->nids);
                print_nid_pcms(buffer, codec, nid);
 
                /* volume knob is a special widget that always have connection
index 447eda1..d418842 100644 (file)
@@ -174,6 +174,7 @@ static struct snd_kcontrol_new ad_beep_mixer[] = {
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
+       struct snd_kcontrol *kctl;
        unsigned int i;
        int err;
 
@@ -239,6 +240,28 @@ static int ad198x_build_controls(struct hda_codec *codec)
        }
 
        ad198x_free_kctls(codec); /* no longer needed */
+
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+       if (!kctl)
+               kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
+                                      spec->input_mux->num_items);
+               if (err < 0)
+                       return err;
+       }
+
+       /* assign IEC958 enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec,
+                       SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
+       if (kctl) {
+               err = snd_hda_add_nid(codec, kctl, 0,
+                                     spec->multiout.dig_out_nid);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -701,6 +724,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "External Amplifier",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
@@ -808,6 +832,7 @@ static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1a,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = ad1986a_hp_master_sw_put,
@@ -1608,6 +1633,7 @@ static struct snd_kcontrol_new ad1981_hp_mixers[] = {
        HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
                .name = "Master Playback Switch",
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
@@ -2121,6 +2147,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "External Amplifier",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
@@ -2242,6 +2269,7 @@ static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "IEC958 Playback Source",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
                .info = ad1988_spdif_playback_source_info,
                .get = ad1988_spdif_playback_source_get,
                .put = ad1988_spdif_playback_source_put,
@@ -3728,6 +3756,7 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x21,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = ad1884a_mobile_master_sw_put,
@@ -3756,6 +3785,7 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x21,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = ad1884a_mobile_master_sw_put,
@@ -4097,6 +4127,7 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
 /*     HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x21,
                .name = "Master Playback Switch",
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
index 4b200da..d0b8c6d 100644 (file)
@@ -759,6 +759,10 @@ static int build_input(struct hda_codec *codec)
                err = snd_hda_ctl_add(codec, 0, kctl);
                if (err < 0)
                        return err;
+               err = snd_hda_add_nids(codec, kctl, 0, spec->adc_nid,
+                                      spec->num_inputs);
+               if (err < 0)
+                       return err;
        }
        
        if (spec->num_inputs > 1 && !spec->mic_detect) {
index a45c116..cc1c223 100644 (file)
@@ -315,7 +315,8 @@ static struct hda_verb cmi9880_allout_init[] = {
 static int cmi9880_build_controls(struct hda_codec *codec)
 {
        struct cmi_spec *spec = codec->spec;
-       int err;
+       struct snd_kcontrol *kctl;
+       int i, err;
 
        err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
        if (err < 0)
@@ -340,6 +341,15 @@ static int cmi9880_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nids(codec, kctl, i, spec->adc_nids,
+                                      spec->input_mux->num_items);
+               if (err < 0)
+                       return err;
+       }
        return 0;
 }
 
index 888b631..6b0b872 100644 (file)
@@ -627,6 +627,7 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
 
 #define ALC_PIN_MODE(xname, nid, dir) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_pin_mode_info, \
          .get = alc_pin_mode_get, \
          .put = alc_pin_mode_put, \
@@ -678,6 +679,7 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
 }
 #define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_gpio_data_info, \
          .get = alc_gpio_data_get, \
          .put = alc_gpio_data_put, \
@@ -732,6 +734,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
 }
 #define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_spdif_ctrl_info, \
          .get = alc_spdif_ctrl_get, \
          .put = alc_spdif_ctrl_put, \
@@ -785,6 +788,7 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
 
 #define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_eapd_ctrl_info, \
          .get = alc_eapd_ctrl_get, \
          .put = alc_eapd_ctrl_put, \
@@ -2410,6 +2414,15 @@ static const char *alc_slave_sws[] = {
  * build control elements
  */
 
+#define NID_MAPPING            (-1)
+
+#define SUBDEV_SPEAKER_                (0 << 6)
+#define SUBDEV_HP_             (1 << 6)
+#define SUBDEV_LINE_           (2 << 6)
+#define SUBDEV_SPEAKER(x)      (SUBDEV_SPEAKER_ | ((x) & 0x3f))
+#define SUBDEV_HP(x)           (SUBDEV_HP_ | ((x) & 0x3f))
+#define SUBDEV_LINE(x)         (SUBDEV_LINE_ | ((x) & 0x3f))
+
 static void alc_free_kctls(struct hda_codec *codec);
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
@@ -2424,8 +2437,11 @@ static struct snd_kcontrol_new alc_beep_mixer[] = {
 static int alc_build_controls(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int err;
-       int i;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new *knew;
+       int i, j, err;
+       unsigned int u;
+       hda_nid_t nid;
 
        for (i = 0; i < spec->num_mixers; i++) {
                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -2494,6 +2510,73 @@ static int alc_build_controls(struct hda_codec *codec)
        }
 
        alc_free_kctls(codec); /* no longer needed */
+
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+       if (!kctl)
+               kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
+                                      spec->input_mux->num_items);
+               if (err < 0)
+                       return err;
+       }
+       if (spec->cap_mixer) {
+               const char *kname = kctl ? kctl->id.name : NULL;
+               for (knew = spec->cap_mixer; knew->name; knew++) {
+                       if (kname && strcmp(knew->name, kname) == 0)
+                               continue;
+                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+                       for (i = 0; kctl && i < kctl->count; i++) {
+                               err = snd_hda_add_nid(codec, kctl, i,
+                                                     spec->adc_nids[i]);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
+
+       /* other nid->control mapping */
+       for (i = 0; i < spec->num_mixers; i++) {
+               for (knew = spec->mixers[i]; knew->name; knew++) {
+                       if (knew->iface != NID_MAPPING)
+                               continue;
+                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+                       if (kctl == NULL)
+                               continue;
+                       u = knew->subdevice;
+                       for (j = 0; j < 4; j++, u >>= 8) {
+                               nid = u & 0x3f;
+                               if (nid == 0)
+                                       continue;
+                               switch (u & 0xc0) {
+                               case SUBDEV_SPEAKER_:
+                                       nid = spec->autocfg.speaker_pins[nid];
+                                       break;
+                               case SUBDEV_LINE_:
+                                       nid = spec->autocfg.line_out_pins[nid];
+                                       break;
+                               case SUBDEV_HP_:
+                                       nid = spec->autocfg.hp_pins[nid];
+                                       break;
+                               default:
+                                       continue;
+                               }
+                               err = snd_hda_add_nid(codec, kctl, 0, nid);
+                               if (err < 0)
+                                       return err;
+                       }
+                       u = knew->private_value;
+                       for (j = 0; j < 4; j++, u >>= 8) {
+                               nid = u & 0xff;
+                               if (nid == 0)
+                                       continue;
+                               err = snd_hda_add_nid(codec, kctl, 0, nid);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
        return 0;
 }
 
@@ -3781,6 +3864,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
 #define PIN_CTL_TEST(xname,nid) {                      \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,    \
                        .name = xname,                 \
+                       .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
                        .info = alc_test_pin_ctl_info, \
                        .get = alc_test_pin_ctl_get,   \
                        .put = alc_test_pin_ctl_put,   \
@@ -3790,6 +3874,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
 #define PIN_SRC_TEST(xname,nid) {                      \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,    \
                        .name = xname,                 \
+                       .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
                        .info = alc_test_pin_src_info, \
                        .get = alc_test_pin_src_get,   \
                        .put = alc_test_pin_src_put,   \
@@ -5080,6 +5165,7 @@ static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
@@ -5118,6 +5204,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
@@ -10188,8 +10275,14 @@ static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
                .info = snd_ctl_boolean_mono_info,              \
                .get = alc262_hp_master_sw_get,                 \
                .put = alc262_hp_master_sw_put,                 \
+       }, \
+       {                                                       \
+               .iface = NID_MAPPING,                           \
+               .name = "Master Playback Switch",               \
+               .private_value = 0x15 | (0x16 << 8) | (0x1b << 16),     \
        }
 
+
 static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
        ALC262_HP_MASTER_SWITCH,
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -10347,6 +10440,12 @@ static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
                .info = snd_ctl_boolean_mono_info,              \
                .get = alc262_hippo_master_sw_get,              \
                .put = alc262_hippo_master_sw_put,              \
+       },                                                      \
+       {                                                       \
+               .iface = NID_MAPPING,                           \
+               .name = "Master Playback Switch",               \
+               .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \
+                            (SUBDEV_SPEAKER(0) << 16), \
        }
 
 static struct snd_kcontrol_new alc262_hippo_mixer[] = {
@@ -10820,11 +10919,17 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc262_fujitsu_master_sw_put,
                .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
        },
+       {
+               .iface = NID_MAPPING,
+               .name = "Master Playback Switch",
+               .private_value = 0x1b,
+       },
        HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -10855,6 +10960,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc262_lenovo_3000_master_sw_put,
@@ -11009,6 +11115,11 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
                .get = alc_mux_enum_get,
                .put = alc262_ultra_mux_enum_put,
        },
+       {
+               .iface = NID_MAPPING,
+               .name = "Capture Source",
+               .private_value = 0x15,
+       },
        { } /* end */
 };
 
@@ -12026,6 +12137,7 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -12041,6 +12153,7 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -12058,6 +12171,7 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -13010,6 +13124,7 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -13030,6 +13145,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
index 43b436c..f419ee8 100644 (file)
@@ -122,6 +122,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol,
 #define SI3054_KCONTROL(kname,reg,mask) { \
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
        .name = kname, \
+       .subdevice = HDA_SUBDEV_NID_FLAG | reg, \
        .info = si3054_switch_info, \
        .get  = si3054_switch_get, \
        .put  = si3054_switch_put, \
index b70e26a..64995e8 100644 (file)
@@ -54,6 +54,8 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 
+#define NID_MAPPING            (-1)
+
 /* amp values */
 #define AMP_VAL_IDX_SHIFT      19
 #define AMP_VAL_IDX_MASK       (0x0f<<19)
@@ -157,6 +159,19 @@ struct via_spec {
 #endif
 };
 
+static struct via_spec * via_new_spec(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return NULL;
+
+       codec->spec = spec;
+       spec->codec = codec;
+       return spec;
+}
+
 static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
 {
        u32 vendor_id = codec->vendor_id;
@@ -448,6 +463,22 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
        return 0;
 }
 
+static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
+                                               struct snd_kcontrol_new *tmpl)
+{
+       struct snd_kcontrol_new *knew;
+
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return NULL;
+       *knew = *tmpl;
+       knew->name = kstrdup(tmpl->name, GFP_KERNEL);
+       if (!knew->name)
+               return NULL;
+       return 0;
+}
+
 static void via_free_kctls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -1088,24 +1119,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);
-       struct via_spec *spec = codec->spec;
-       hda_nid_t nid;
+       hda_nid_t nid = kcontrol->private_value;
        unsigned int pinsel;
 
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               break;
-       case VT2002P:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
        /* use !! to translate conn sel 2 for VT1718S */
        pinsel = !!snd_hda_codec_read(codec, nid, 0,
                                      AC_VERB_GET_CONNECT_SEL,
@@ -1127,29 +1143,24 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active)
        }
 }
 
+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;
+       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 = spec->hp_independent_mode
                ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
-       hda_nid_t sw3;
-
-       switch (spec->codec_type) {
-       case VT1708:
-               sw3 = 0x1b;
-               break;
-       case VT1709_10CH:
-               sw3 = 0x29;
-               break;
-       case VT1708B_8CH:
-       case VT1708S:
-               sw3 = 0x27;
-               break;
-       default:
-               sw3 = 0;
-               break;
-       }
+       hda_nid_t sw3 = side_mute_channel(spec);
 
        if (sw3)
                snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -1162,28 +1173,11 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       hda_nid_t nid = spec->autocfg.hp_pins[0];
+       hda_nid_t nid = kcontrol->private_value;
        unsigned int pinsel = ucontrol->value.enumerated.item[0];
        /* Get Independent Mode index of headphone pin widget */
        spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
                ? 1 : 0;
-
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
-               spec->multiout.num_dacs = 4;
-               break;
-       case VT2002P:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
 
        if (spec->multiout.hp_nid && spec->multiout.hp_nid
@@ -1207,18 +1201,55 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static struct snd_kcontrol_new via_hp_mixer[] = {
+static struct snd_kcontrol_new via_hp_mixer[2] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Independent HP",
-               .count = 1,
                .info = via_independent_hp_info,
                .get = via_independent_hp_get,
                .put = via_independent_hp_put,
        },
-       { } /* end */
+       {
+               .iface = NID_MAPPING,
+               .name = "Independent HP",
+       },
 };
 
+static int via_hp_build(struct via_spec *spec)
+{
+       struct snd_kcontrol_new *knew;
+       hda_nid_t nid;
+
+       knew = via_clone_control(spec, &via_hp_mixer[0]);
+       if (knew == NULL)
+               return -ENOMEM;
+
+       switch (spec->codec_type) {
+       case VT1718S:
+               nid = 0x34;
+               break;
+       case VT2002P:
+               nid = 0x35;
+               break;
+       case VT1812:
+               nid = 0x3d;
+               break;
+       default:
+               nid = spec->autocfg.hp_pins[0];
+               break;
+       }
+
+       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;
+}
+
 static void notify_aa_path_ctls(struct hda_codec *codec)
 {
        int i;
@@ -1376,7 +1407,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
-static struct snd_kcontrol_new via_smart51_mixer[] = {
+static struct snd_kcontrol_new via_smart51_mixer[2] = {
        {
         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
         .name = "Smart 5.1",
@@ -1385,9 +1416,36 @@ static struct snd_kcontrol_new via_smart51_mixer[] = {
         .get = via_smart51_get,
         .put = via_smart51_put,
         },
-       {}                      /* end */
+       {
+        .iface = NID_MAPPING,
+        .name = "Smart 5.1",
+       }
 };
 
+static int via_smart51_build(struct via_spec *spec)
+{
+       struct snd_kcontrol_new *knew;
+       int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+       hda_nid_t nid;
+       int i;
+
+       knew = via_clone_control(spec, &via_smart51_mixer[0]);
+       if (knew == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(index); i++) {
+               nid = spec->autocfg.input_pins[index[i]];
+               if (nid) {
+                       knew = via_clone_control(spec, &via_smart51_mixer[1]);
+                       if (knew == NULL)
+                               return -ENOMEM;
+                       knew->subdevice = nid;
+               }
+       }
+
+       return 0;
+}
+
 /* capture mixer elements */
 static struct snd_kcontrol_new vt1708_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -1819,8 +1877,9 @@ static struct hda_pcm_stream vt1708_pcm_digital_capture = {
 static int via_build_controls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       int err;
-       int i;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new *knew;
+       int err, i;
 
        for (i = 0; i < spec->num_mixers; i++) {
                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -1845,6 +1904,28 @@ static int via_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids,
+                                      spec->input_mux->num_items);
+               if (err < 0)
+                       return err;
+       }
+
+       /* other nid->control mapping */
+       for (i = 0; i < spec->num_mixers; i++) {
+               for (knew = spec->mixers[i]; knew->name; knew++) {
+                       if (knew->iface != NID_MAPPING)
+                               continue;
+                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+                       if (kctl == NULL)
+                               continue;
+                       err = snd_hda_add_nid(codec, kctl, 0,
+                                             knew->subdevice);
+               }
+       }
+
        /* init power states */
        set_jack_power_state(codec);
        analog_low_current_mode(codec, 1);
@@ -2481,9 +2562,9 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -2554,12 +2635,10 @@ static int patch_vt1708(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708_parse_auto_config(codec);
        if (err < 0) {
@@ -2597,7 +2676,6 @@ static int patch_vt1708(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = vt1708_loopbacks;
 #endif
-       spec->codec = codec;
        INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
        return 0;
 }
@@ -3010,9 +3088,9 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -3032,12 +3110,10 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        err = vt1709_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
@@ -3126,12 +3202,10 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        err = vt1709_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
@@ -3581,9 +3655,9 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -3605,12 +3679,10 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)
        if (get_codec_type(codec) == VT1708BCE)
                return patch_vt1708S(codec);
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708B_parse_auto_config(codec);
        if (err < 0) {
@@ -3657,12 +3729,10 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708B_parse_auto_config(codec);
        if (err < 0) {
@@ -4071,9 +4141,9 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -4103,12 +4173,10 @@ static int patch_vt1708S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708S_parse_auto_config(codec);
        if (err < 0) {
@@ -4443,7 +4511,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
        return 1;
 }
@@ -4464,12 +4532,10 @@ static int patch_vt1702(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1702_parse_auto_config(codec);
        if (err < 0) {
@@ -4865,9 +4931,9 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
 
        return 1;
 }
@@ -4888,12 +4954,10 @@ static int patch_vt1718S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1718S_parse_auto_config(codec);
        if (err < 0) {
@@ -5014,6 +5078,7 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
        {
         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
         .name = "Digital Mic Capture Switch",
+        .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
         .count = 1,
         .info = vt1716s_dmic_info,
         .get = vt1716s_dmic_get,
@@ -5361,9 +5426,9 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
 
        return 1;
 }
@@ -5384,12 +5449,10 @@ static int patch_vt1716S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1716S_parse_auto_config(codec);
        if (err < 0) {
@@ -5719,7 +5782,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
        return 1;
 }
@@ -5741,12 +5804,10 @@ static int patch_vt2002P(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt2002P_parse_auto_config(codec);
        if (err < 0) {
@@ -6070,7 +6131,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
        return 1;
 }
@@ -6092,12 +6153,10 @@ static int patch_vt1812(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1812_parse_auto_config(codec);
        if (err < 0) {