aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-05-18 12:21:41 -0400
committerJaroslav Kysela <perex@suse.cz>2007-07-20 05:11:18 -0400
commit2bac647c45ed9b397ace9109e6935c1364690f8a (patch)
treed7faf326e72b7ee0ddd8a5446de40bdb7ae9f207 /sound
parent198de43d758ca2700e2b52b49c0b189b4931466c (diff)
[ALSA] hda-codec - Add AD1884 / AD1984 codec support
Added the support of AD1884 and AD1984 codec chips. Also experimental quirks for Thinkpad T61/X61 laptops with AD1984. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/patch_analog.c345
1 files changed, 342 insertions, 3 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 0e1a879663fa..30248cd562c5 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1,7 +1,8 @@
1/* 1/*
2 * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988 2 * HD audio interface patch for AD1884, AD1981HD, AD1983, AD1984, AD1986A,
3 * AD1988
3 * 4 *
4 * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de> 5 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
5 * 6 *
6 * This driver is free software; you can redistribute it and/or modify 7 * This driver is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
@@ -61,7 +62,7 @@ struct ad198x_spec {
61 int num_channel_mode; 62 int num_channel_mode;
62 63
63 /* PCM information */ 64 /* PCM information */
64 struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */ 65 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
65 66
66 struct mutex amp_mutex; /* PCM volume/mute control mutex */ 67 struct mutex amp_mutex; /* PCM volume/mute control mutex */
67 unsigned int spdif_route; 68 unsigned int spdif_route;
@@ -2775,11 +2776,349 @@ static int patch_ad1988(struct hda_codec *codec)
2775 2776
2776 2777
2777/* 2778/*
2779 * AD1884 / AD1984
2780 *
2781 * port-B - front line/mic-in
2782 * port-E - aux in/out
2783 * port-F - aux in/out
2784 * port-C - rear line/mic-in
2785 * port-D - rear line/hp-out
2786 * port-A - front line/hp-out
2787 *
2788 * AD1984 = AD1884 + two digital mic-ins
2789 *
2790 * FIXME:
2791 * For simplicity, we share the single DAC for both HP and line-outs
2792 * right now. The inidividual playbacks could be easily implemented,
2793 * but no build-up framework is given, so far.
2794 */
2795
2796static hda_nid_t ad1884_dac_nids[1] = {
2797 0x04,
2798};
2799
2800static hda_nid_t ad1884_adc_nids[2] = {
2801 0x08, 0x09,
2802};
2803
2804static hda_nid_t ad1884_capsrc_nids[2] = {
2805 0x0c, 0x0d,
2806};
2807
2808#define AD1884_SPDIF_OUT 0x02
2809
2810static struct hda_input_mux ad1884_capture_source = {
2811 .num_items = 4,
2812 .items = {
2813 { "Front Mic", 0x0 },
2814 { "Mic", 0x1 },
2815 { "CD", 0x2 },
2816 { "Mix", 0x3 },
2817 },
2818};
2819
2820static struct snd_kcontrol_new ad1884_base_mixers[] = {
2821 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2822 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
2823 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
2824 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
2825 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
2826 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
2827 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
2828 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
2829 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
2830 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
2831 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
2832 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
2833 /*
2834 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
2835 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
2836 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
2837 HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
2838 */
2839 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
2840 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
2841 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2842 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2843 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2844 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2845 {
2846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2847 /* The multiple "Capture Source" controls confuse alsamixer
2848 * So call somewhat different..
2849 * FIXME: the controls appear in the "playback" view!
2850 */
2851 /* .name = "Capture Source", */
2852 .name = "Input Source",
2853 .count = 2,
2854 .info = ad198x_mux_enum_info,
2855 .get = ad198x_mux_enum_get,
2856 .put = ad198x_mux_enum_put,
2857 },
2858 /* SPDIF controls */
2859 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2860 {
2861 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2862 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
2863 /* identical with ad1983 */
2864 .info = ad1983_spdif_route_info,
2865 .get = ad1983_spdif_route_get,
2866 .put = ad1983_spdif_route_put,
2867 },
2868 { } /* end */
2869};
2870
2871static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
2872 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
2873 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
2874 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
2875 HDA_OUTPUT),
2876 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
2877 HDA_OUTPUT),
2878 { } /* end */
2879};
2880
2881/*
2882 * initialization verbs
2883 */
2884static struct hda_verb ad1884_init_verbs[] = {
2885 /* DACs; mute as default */
2886 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2887 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2888 /* Port-A (HP) mixer */
2889 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2890 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2891 /* Port-A pin */
2892 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2893 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2894 /* HP selector - select DAC2 */
2895 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
2896 /* Port-D (Line-out) mixer */
2897 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2898 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2899 /* Port-D pin */
2900 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2901 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2902 /* Mono-out mixer */
2903 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2904 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2905 /* Mono-out pin */
2906 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2907 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2908 /* Mono selector */
2909 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2910 /* Port-B (front mic) pin */
2911 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2912 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2913 /* Port-C (rear mic) pin */
2914 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2915 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2916 /* Analog mixer; mute as default */
2917 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2918 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2919 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2920 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2921 /* Analog Mix output amp */
2922 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
2923 /* SPDIF output selector */
2924 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2925 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
2926 { } /* end */
2927};
2928
2929static int patch_ad1884(struct hda_codec *codec)
2930{
2931 struct ad198x_spec *spec;
2932
2933 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2934 if (spec == NULL)
2935 return -ENOMEM;
2936
2937 mutex_init(&spec->amp_mutex);
2938 codec->spec = spec;
2939
2940 spec->multiout.max_channels = 2;
2941 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
2942 spec->multiout.dac_nids = ad1884_dac_nids;
2943 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
2944 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
2945 spec->adc_nids = ad1884_adc_nids;
2946 spec->capsrc_nids = ad1884_capsrc_nids;
2947 spec->input_mux = &ad1884_capture_source;
2948 spec->num_mixers = 1;
2949 spec->mixers[0] = ad1884_base_mixers;
2950 spec->num_init_verbs = 1;
2951 spec->init_verbs[0] = ad1884_init_verbs;
2952 spec->spdif_route = 0;
2953
2954 codec->patch_ops = ad198x_patch_ops;
2955
2956 return 0;
2957}
2958
2959/*
2960 * Lenovo Thinkpad T61/X61
2961 */
2962static struct hda_input_mux ad1984_thinkpad_capture_source = {
2963 .num_items = 4,
2964 .items = {
2965 { "Mic", 0x0 },
2966 { "Internal Mic", 0x1 },
2967 { "Mix", 0x3 },
2968 },
2969};
2970
2971static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
2972 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2973 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
2974 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
2975 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
2976 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
2977 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
2978 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
2979 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
2980 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
2981 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
2982 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
2983 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
2984 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
2985 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
2986 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_INPUT),
2987 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2988 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2989 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2990 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2991 {
2992 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2993 /* The multiple "Capture Source" controls confuse alsamixer
2994 * So call somewhat different..
2995 * FIXME: the controls appear in the "playback" view!
2996 */
2997 /* .name = "Capture Source", */
2998 .name = "Input Source",
2999 .count = 2,
3000 .info = ad198x_mux_enum_info,
3001 .get = ad198x_mux_enum_get,
3002 .put = ad198x_mux_enum_put,
3003 },
3004 { } /* end */
3005};
3006
3007/* additional verbs */
3008static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3009 /* Port-E (docking station mic) pin */
3010 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3011 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3012 /* docking mic boost */
3013 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3014 /* Analog mixer - docking mic; mute as default */
3015 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3016 { } /* end */
3017};
3018
3019/* Digial MIC ADC NID 0x05 + 0x06 */
3020static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3021 struct hda_codec *codec,
3022 unsigned int stream_tag,
3023 unsigned int format,
3024 struct snd_pcm_substream *substream)
3025{
3026 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3027 stream_tag, 0, format);
3028 return 0;
3029}
3030
3031static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3032 struct hda_codec *codec,
3033 struct snd_pcm_substream *substream)
3034{
3035 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3036 0, 0, 0);
3037 return 0;
3038}
3039
3040static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3041 .substreams = 2,
3042 .channels_min = 2,
3043 .channels_max = 2,
3044 .nid = 0x05,
3045 .ops = {
3046 .prepare = ad1984_pcm_dmic_prepare,
3047 .cleanup = ad1984_pcm_dmic_cleanup
3048 },
3049};
3050
3051static int ad1984_build_pcms(struct hda_codec *codec)
3052{
3053 struct ad198x_spec *spec = codec->spec;
3054 struct hda_pcm *info;
3055 int err;
3056
3057 err = ad198x_build_pcms(codec);
3058 if (err < 0)
3059 return err;
3060
3061 info = spec->pcm_rec + codec->num_pcms;
3062 codec->num_pcms++;
3063 info->name = "AD1984 Digital Mic";
3064 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3065 return 0;
3066}
3067
3068/* models */
3069enum {
3070 AD1984_BASIC,
3071 AD1984_THINKPAD,
3072 AD1984_MODELS
3073};
3074
3075static const char *ad1984_models[AD1984_MODELS] = {
3076 [AD1984_BASIC] = "basic",
3077 [AD1984_THINKPAD] = "thinkpad",
3078};
3079
3080static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3081 /* Lenovo Thinkpad T61/X61 */
3082 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
3083 {}
3084};
3085
3086static int patch_ad1984(struct hda_codec *codec)
3087{
3088 struct ad198x_spec *spec;
3089 int board_config, err;
3090
3091 err = patch_ad1884(codec);
3092 if (err < 0)
3093 return err;
3094 spec = codec->spec;
3095 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3096 ad1984_models, ad1984_cfg_tbl);
3097 switch (board_config) {
3098 case AD1984_BASIC:
3099 /* additional digital mics */
3100 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3101 codec->patch_ops.build_pcms = ad1984_build_pcms;
3102 break;
3103 case AD1984_THINKPAD:
3104 spec->multiout.dig_out_nid = 0;
3105 spec->input_mux = &ad1984_thinkpad_capture_source;
3106 spec->mixers[0] = ad1984_thinkpad_mixers;
3107 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3108 break;
3109 }
3110 return 0;
3111}
3112
3113
3114/*
2778 * patch entries 3115 * patch entries
2779 */ 3116 */
2780struct hda_codec_preset snd_hda_preset_analog[] = { 3117struct hda_codec_preset snd_hda_preset_analog[] = {
3118 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
2781 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, 3119 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
2782 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, 3120 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
3121 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
2783 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, 3122 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
2784 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, 3123 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
2785 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, 3124 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },