]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - sound/pci/hda/patch_analog.c
ALSA: hda - Add missing terminator in slave dig-out array
[linux-2.6.git] / sound / pci / hda / patch_analog.c
index 686c77491dea7cffb5333562c2986f7640ac21e0..7006d62ca6c2f64cde1ee7289df014f77cd4d25a 100644 (file)
@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 struct ad198x_spec {
        struct snd_kcontrol_new *mixers[5];
@@ -67,8 +66,7 @@ struct ad198x_spec {
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
+       struct snd_array kctls;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
@@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = {
        NULL
 };
 
+static void ad198x_free_kctls(struct hda_codec *codec);
+
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       ad198x_free_kctls(codec); /* no longer needed */
        return 0;
 }
 
@@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static void ad198x_free(struct hda_codec *codec)
+static void ad198x_free_kctls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
-       unsigned int i;
 
-       if (spec->kctl_alloc) {
-               for (i = 0; i < spec->num_kctl_used; i++)
-                       kfree(spec->kctl_alloc[i].name);
-               kfree(spec->kctl_alloc);
+       if (spec->kctls.list) {
+               struct snd_kcontrol_new *kctl = spec->kctls.list;
+               int i;
+               for (i = 0; i < spec->kctls.used; i++)
+                       kfree(kctl[i].name);
        }
+       snd_array_free(&spec->kctls);
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+
+       if (!spec)
+               return;
+
+       ad198x_free_kctls(codec);
        kfree(codec->spec);
 }
 
@@ -625,6 +637,36 @@ static struct hda_input_mux ad1986a_automic_capture_source = {
 };
 
 static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
+       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+       HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "External Amplifier",
+               .info = ad198x_eapd_info,
+               .get = ad198x_eapd_get,
+               .put = ad198x_eapd_put,
+               .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+       },
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
        HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
        HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
        HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
@@ -917,6 +959,7 @@ enum {
        AD1986A_LAPTOP_EAPD,
        AD1986A_LAPTOP_AUTOMUTE,
        AD1986A_ULTRA,
+       AD1986A_SAMSUNG,
        AD1986A_MODELS
 };
 
@@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
        [AD1986A_LAPTOP_EAPD]   = "laptop-eapd",
        [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
        [AD1986A_ULTRA]         = "ultra",
+       [AD1986A_SAMSUNG]       = "samsung",
 };
 
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
@@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
        SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
-       SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
+       SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
+       SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
        SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
        SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
        SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
@@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec)
                break;
        case AD1986A_LAPTOP_EAPD:
                spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+               spec->num_init_verbs = 2;
+               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+               spec->multiout.max_channels = 2;
+               spec->multiout.num_dacs = 1;
+               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+               if (!is_jack_available(codec, 0x25))
+                       spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+               break;
+       case AD1986A_SAMSUNG:
+               spec->mixers[0] = ad1986a_samsung_mixers;
                spec->num_init_verbs = 3;
                spec->init_verbs[1] = ad1986a_eapd_init_verbs;
                spec->init_verbs[2] = ad1986a_automic_verbs;
@@ -1830,8 +1885,8 @@ static hda_nid_t ad1988_capsrc_nids[3] = {
 #define AD1988_SPDIF_OUT_HDMI  0x0b
 #define AD1988_SPDIF_IN                0x07
 
-static hda_nid_t ad1989b_slave_dig_outs[2] = {
-       AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI
+static hda_nid_t ad1989b_slave_dig_outs[] = {
+       AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
 };
 
 static struct hda_input_mux ad1988_6stack_capture_source = {
@@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = {
  * Automatic parse of I/O pins from the BIOS configuration
  */
 
-#define NUM_CONTROL_ALLOC      32
-#define NUM_VERB_ALLOC         32
-
 enum {
        AD_CTL_WIDGET_VOL,
        AD_CTL_WIDGET_MUTE,
@@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
 {
        struct snd_kcontrol_new *knew;
 
-       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
-               if (! knew)
-                       return -ENOMEM;
-               if (spec->kctl_alloc) {
-                       memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
-                       kfree(spec->kctl_alloc);
-               }
-               spec->kctl_alloc = knew;
-               spec->num_kctl_alloc = num;
-       }
-
-       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return -ENOMEM;
        *knew = ad1988_control_templates[type];
        knew->name = kstrdup(name, GFP_KERNEL);
        if (! knew->name)
                return -ENOMEM;
        knew->private_value = val;
-       spec->num_kctl_used++;
        return 0;
 }
 
@@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = AD1988_SPDIF_IN;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
 
@@ -3860,7 +3900,9 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
 
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
+       SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
+       SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
@@ -4221,13 +4263,13 @@ static int patch_ad1882(struct hda_codec *codec)
        spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
        spec->adc_nids = ad1882_adc_nids;
        spec->capsrc_nids = ad1882_capsrc_nids;
-       if (codec->vendor_id == 0x11d1882)
+       if (codec->vendor_id == 0x11d41882)
                spec->input_mux = &ad1882_capture_source;
        else
                spec->input_mux = &ad1882a_capture_source;
        spec->num_mixers = 2;
        spec->mixers[0] = ad1882_base_mixers;
-       if (codec->vendor_id == 0x11d1882)
+       if (codec->vendor_id == 0x11d41882)
                spec->mixers[1] = ad1882_loopback_mixers;
        else
                spec->mixers[1] = ad1882a_loopback_mixers;
@@ -4267,7 +4309,7 @@ static int patch_ad1882(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_analog[] = {
+static struct hda_codec_preset snd_hda_preset_analog[] = {
        { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
        { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
        { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
@@ -4285,3 +4327,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
        { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:11d4*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Analog Devices HD-audio codec");
+
+static struct hda_codec_preset_list analog_list = {
+       .preset = snd_hda_preset_analog,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_analog_init(void)
+{
+       return snd_hda_add_codec_preset(&analog_list);
+}
+
+static void __exit patch_analog_exit(void)
+{
+       snd_hda_delete_codec_preset(&analog_list);
+}
+
+module_init(patch_analog_init)
+module_exit(patch_analog_exit)