diff options
Diffstat (limited to 'sound/pci/hda/patch_analog.c')
-rw-r--r-- | sound/pci/hda/patch_analog.c | 132 |
1 files changed, 98 insertions, 34 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 686c77491dea..26247cfe749d 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c | |||
@@ -27,7 +27,6 @@ | |||
27 | #include <sound/core.h> | 27 | #include <sound/core.h> |
28 | #include "hda_codec.h" | 28 | #include "hda_codec.h" |
29 | #include "hda_local.h" | 29 | #include "hda_local.h" |
30 | #include "hda_patch.h" | ||
31 | 30 | ||
32 | struct ad198x_spec { | 31 | struct ad198x_spec { |
33 | struct snd_kcontrol_new *mixers[5]; | 32 | struct snd_kcontrol_new *mixers[5]; |
@@ -67,8 +66,7 @@ struct ad198x_spec { | |||
67 | 66 | ||
68 | /* dynamic controls, init_verbs and input_mux */ | 67 | /* dynamic controls, init_verbs and input_mux */ |
69 | struct auto_pin_cfg autocfg; | 68 | struct auto_pin_cfg autocfg; |
70 | unsigned int num_kctl_alloc, num_kctl_used; | 69 | struct snd_array kctls; |
71 | struct snd_kcontrol_new *kctl_alloc; | ||
72 | struct hda_input_mux private_imux; | 70 | struct hda_input_mux private_imux; |
73 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | 71 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; |
74 | 72 | ||
@@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = { | |||
154 | NULL | 152 | NULL |
155 | }; | 153 | }; |
156 | 154 | ||
155 | static void ad198x_free_kctls(struct hda_codec *codec); | ||
156 | |||
157 | static int ad198x_build_controls(struct hda_codec *codec) | 157 | static int ad198x_build_controls(struct hda_codec *codec) |
158 | { | 158 | { |
159 | struct ad198x_spec *spec = codec->spec; | 159 | struct ad198x_spec *spec = codec->spec; |
@@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec) | |||
202 | return err; | 202 | return err; |
203 | } | 203 | } |
204 | 204 | ||
205 | ad198x_free_kctls(codec); /* no longer needed */ | ||
205 | return 0; | 206 | return 0; |
206 | } | 207 | } |
207 | 208 | ||
@@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec) | |||
375 | return 0; | 376 | return 0; |
376 | } | 377 | } |
377 | 378 | ||
378 | static void ad198x_free(struct hda_codec *codec) | 379 | static void ad198x_free_kctls(struct hda_codec *codec) |
379 | { | 380 | { |
380 | struct ad198x_spec *spec = codec->spec; | 381 | struct ad198x_spec *spec = codec->spec; |
381 | unsigned int i; | ||
382 | 382 | ||
383 | if (spec->kctl_alloc) { | 383 | if (spec->kctls.list) { |
384 | for (i = 0; i < spec->num_kctl_used; i++) | 384 | struct snd_kcontrol_new *kctl = spec->kctls.list; |
385 | kfree(spec->kctl_alloc[i].name); | 385 | int i; |
386 | kfree(spec->kctl_alloc); | 386 | for (i = 0; i < spec->kctls.used; i++) |
387 | kfree(kctl[i].name); | ||
387 | } | 388 | } |
389 | snd_array_free(&spec->kctls); | ||
390 | } | ||
391 | |||
392 | static void ad198x_free(struct hda_codec *codec) | ||
393 | { | ||
394 | struct ad198x_spec *spec = codec->spec; | ||
395 | |||
396 | if (!spec) | ||
397 | return; | ||
398 | |||
399 | ad198x_free_kctls(codec); | ||
388 | kfree(codec->spec); | 400 | kfree(codec->spec); |
389 | } | 401 | } |
390 | 402 | ||
@@ -629,6 +641,36 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { | |||
629 | HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), | 641 | HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), |
630 | HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), | 642 | HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), |
631 | HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), | 643 | HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), |
644 | HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), | ||
645 | HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), | ||
646 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), | ||
647 | HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), | ||
648 | HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), | ||
649 | HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), | ||
650 | HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), | ||
651 | { | ||
652 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
653 | .name = "Capture Source", | ||
654 | .info = ad198x_mux_enum_info, | ||
655 | .get = ad198x_mux_enum_get, | ||
656 | .put = ad198x_mux_enum_put, | ||
657 | }, | ||
658 | { | ||
659 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
660 | .name = "External Amplifier", | ||
661 | .info = ad198x_eapd_info, | ||
662 | .get = ad198x_eapd_get, | ||
663 | .put = ad198x_eapd_put, | ||
664 | .private_value = 0x1b | (1 << 8), /* port-D, inversed */ | ||
665 | }, | ||
666 | { } /* end */ | ||
667 | }; | ||
668 | |||
669 | static struct snd_kcontrol_new ad1986a_samsung_mixers[] = { | ||
670 | HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), | ||
671 | HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), | ||
672 | HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), | ||
673 | HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), | ||
632 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), | 674 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), |
633 | HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), | 675 | HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), |
634 | HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), | 676 | HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), |
@@ -917,6 +959,7 @@ enum { | |||
917 | AD1986A_LAPTOP_EAPD, | 959 | AD1986A_LAPTOP_EAPD, |
918 | AD1986A_LAPTOP_AUTOMUTE, | 960 | AD1986A_LAPTOP_AUTOMUTE, |
919 | AD1986A_ULTRA, | 961 | AD1986A_ULTRA, |
962 | AD1986A_SAMSUNG, | ||
920 | AD1986A_MODELS | 963 | AD1986A_MODELS |
921 | }; | 964 | }; |
922 | 965 | ||
@@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = { | |||
927 | [AD1986A_LAPTOP_EAPD] = "laptop-eapd", | 970 | [AD1986A_LAPTOP_EAPD] = "laptop-eapd", |
928 | [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", | 971 | [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", |
929 | [AD1986A_ULTRA] = "ultra", | 972 | [AD1986A_ULTRA] = "ultra", |
973 | [AD1986A_SAMSUNG] = "samsung", | ||
930 | }; | 974 | }; |
931 | 975 | ||
932 | static struct snd_pci_quirk ad1986a_cfg_tbl[] = { | 976 | static struct snd_pci_quirk ad1986a_cfg_tbl[] = { |
@@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { | |||
949 | SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), | 993 | SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), |
950 | SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), | 994 | SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), |
951 | SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), | 995 | SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), |
952 | SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), | 996 | SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG), |
953 | SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), | 997 | SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG), |
954 | SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), | 998 | SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG), |
955 | SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), | 999 | SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), |
956 | SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), | 1000 | SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), |
957 | SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), | 1001 | SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), |
@@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec) | |||
1033 | break; | 1077 | break; |
1034 | case AD1986A_LAPTOP_EAPD: | 1078 | case AD1986A_LAPTOP_EAPD: |
1035 | spec->mixers[0] = ad1986a_laptop_eapd_mixers; | 1079 | spec->mixers[0] = ad1986a_laptop_eapd_mixers; |
1080 | spec->num_init_verbs = 2; | ||
1081 | spec->init_verbs[1] = ad1986a_eapd_init_verbs; | ||
1082 | spec->multiout.max_channels = 2; | ||
1083 | spec->multiout.num_dacs = 1; | ||
1084 | spec->multiout.dac_nids = ad1986a_laptop_dac_nids; | ||
1085 | if (!is_jack_available(codec, 0x25)) | ||
1086 | spec->multiout.dig_out_nid = 0; | ||
1087 | spec->input_mux = &ad1986a_laptop_eapd_capture_source; | ||
1088 | break; | ||
1089 | case AD1986A_SAMSUNG: | ||
1090 | spec->mixers[0] = ad1986a_samsung_mixers; | ||
1036 | spec->num_init_verbs = 3; | 1091 | spec->num_init_verbs = 3; |
1037 | spec->init_verbs[1] = ad1986a_eapd_init_verbs; | 1092 | spec->init_verbs[1] = ad1986a_eapd_init_verbs; |
1038 | spec->init_verbs[2] = ad1986a_automic_verbs; | 1093 | spec->init_verbs[2] = ad1986a_automic_verbs; |
@@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = { | |||
2452 | * Automatic parse of I/O pins from the BIOS configuration | 2507 | * Automatic parse of I/O pins from the BIOS configuration |
2453 | */ | 2508 | */ |
2454 | 2509 | ||
2455 | #define NUM_CONTROL_ALLOC 32 | ||
2456 | #define NUM_VERB_ALLOC 32 | ||
2457 | |||
2458 | enum { | 2510 | enum { |
2459 | AD_CTL_WIDGET_VOL, | 2511 | AD_CTL_WIDGET_VOL, |
2460 | AD_CTL_WIDGET_MUTE, | 2512 | AD_CTL_WIDGET_MUTE, |
@@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, | |||
2472 | { | 2524 | { |
2473 | struct snd_kcontrol_new *knew; | 2525 | struct snd_kcontrol_new *knew; |
2474 | 2526 | ||
2475 | if (spec->num_kctl_used >= spec->num_kctl_alloc) { | 2527 | snd_array_init(&spec->kctls, sizeof(*knew), 32); |
2476 | int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; | 2528 | knew = snd_array_new(&spec->kctls); |
2477 | 2529 | if (!knew) | |
2478 | knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ | 2530 | return -ENOMEM; |
2479 | if (! knew) | ||
2480 | return -ENOMEM; | ||
2481 | if (spec->kctl_alloc) { | ||
2482 | memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); | ||
2483 | kfree(spec->kctl_alloc); | ||
2484 | } | ||
2485 | spec->kctl_alloc = knew; | ||
2486 | spec->num_kctl_alloc = num; | ||
2487 | } | ||
2488 | |||
2489 | knew = &spec->kctl_alloc[spec->num_kctl_used]; | ||
2490 | *knew = ad1988_control_templates[type]; | 2531 | *knew = ad1988_control_templates[type]; |
2491 | knew->name = kstrdup(name, GFP_KERNEL); | 2532 | knew->name = kstrdup(name, GFP_KERNEL); |
2492 | if (! knew->name) | 2533 | if (! knew->name) |
2493 | return -ENOMEM; | 2534 | return -ENOMEM; |
2494 | knew->private_value = val; | 2535 | knew->private_value = val; |
2495 | spec->num_kctl_used++; | ||
2496 | return 0; | 2536 | return 0; |
2497 | } | 2537 | } |
2498 | 2538 | ||
@@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) | |||
2846 | if (spec->autocfg.dig_in_pin) | 2886 | if (spec->autocfg.dig_in_pin) |
2847 | spec->dig_in_nid = AD1988_SPDIF_IN; | 2887 | spec->dig_in_nid = AD1988_SPDIF_IN; |
2848 | 2888 | ||
2849 | if (spec->kctl_alloc) | 2889 | if (spec->kctls.list) |
2850 | spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | 2890 | spec->mixers[spec->num_mixers++] = spec->kctls.list; |
2851 | 2891 | ||
2852 | spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; | 2892 | spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; |
2853 | 2893 | ||
@@ -3861,6 +3901,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = { | |||
3861 | static struct snd_pci_quirk ad1884a_cfg_tbl[] = { | 3901 | static struct snd_pci_quirk ad1884a_cfg_tbl[] = { |
3862 | SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), | 3902 | SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), |
3863 | SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), | 3903 | SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), |
3904 | SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP), | ||
3864 | SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP), | 3905 | SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP), |
3865 | SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP), | 3906 | SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP), |
3866 | SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), | 3907 | SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), |
@@ -4267,7 +4308,7 @@ static int patch_ad1882(struct hda_codec *codec) | |||
4267 | /* | 4308 | /* |
4268 | * patch entries | 4309 | * patch entries |
4269 | */ | 4310 | */ |
4270 | struct hda_codec_preset snd_hda_preset_analog[] = { | 4311 | static struct hda_codec_preset snd_hda_preset_analog[] = { |
4271 | { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, | 4312 | { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, |
4272 | { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, | 4313 | { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, |
4273 | { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, | 4314 | { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, |
@@ -4285,3 +4326,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = { | |||
4285 | { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, | 4326 | { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, |
4286 | {} /* terminator */ | 4327 | {} /* terminator */ |
4287 | }; | 4328 | }; |
4329 | |||
4330 | MODULE_ALIAS("snd-hda-codec-id:11d4*"); | ||
4331 | |||
4332 | MODULE_LICENSE("GPL"); | ||
4333 | MODULE_DESCRIPTION("Analog Devices HD-audio codec"); | ||
4334 | |||
4335 | static struct hda_codec_preset_list analog_list = { | ||
4336 | .preset = snd_hda_preset_analog, | ||
4337 | .owner = THIS_MODULE, | ||
4338 | }; | ||
4339 | |||
4340 | static int __init patch_analog_init(void) | ||
4341 | { | ||
4342 | return snd_hda_add_codec_preset(&analog_list); | ||
4343 | } | ||
4344 | |||
4345 | static void __exit patch_analog_exit(void) | ||
4346 | { | ||
4347 | snd_hda_delete_codec_preset(&analog_list); | ||
4348 | } | ||
4349 | |||
4350 | module_init(patch_analog_init) | ||
4351 | module_exit(patch_analog_exit) | ||