Lines 1-7
Link Here
|
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 |
Lines 61-67
struct ad198x_spec {
Link Here
|
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; |
Lines 2775-2785
static int patch_ad1988(struct hda_codec
Link Here
|
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 |
|
2796 |
static hda_nid_t ad1884_dac_nids[1] = { |
2797 |
0x04, |
2798 |
}; |
2799 |
|
2800 |
static hda_nid_t ad1884_adc_nids[2] = { |
2801 |
0x08, 0x09, |
2802 |
}; |
2803 |
|
2804 |
static hda_nid_t ad1884_capsrc_nids[2] = { |
2805 |
0x0c, 0x0d, |
2806 |
}; |
2807 |
|
2808 |
#define AD1884_SPDIF_OUT 0x02 |
2809 |
|
2810 |
static 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 |
|
2820 |
static 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 |
|
2871 |
static 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_INPUT), |
2876 |
HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0, |
2877 |
HDA_INPUT), |
2878 |
{ } /* end */ |
2879 |
}; |
2880 |
|
2881 |
/* |
2882 |
* initialization verbs |
2883 |
*/ |
2884 |
static struct hda_verb ad1884_init_verbs[] = { |
2885 |
/* DACs; mute as default */ |
2886 |
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, |
2887 |
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, |
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 |
|
2929 |
static 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 |
*/ |
2962 |
static struct hda_input_mux ad1984_thinkpad_capture_source = { |
2963 |
.num_items = 3, |
2964 |
.items = { |
2965 |
{ "Mic", 0x0 }, |
2966 |
{ "Internal Mic", 0x1 }, |
2967 |
{ "Mix", 0x3 }, |
2968 |
}, |
2969 |
}; |
2970 |
|
2971 |
static 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 Boost", 0x14, 0x0, HDA_INPUT), |
2983 |
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), |
2984 |
HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT), |
2985 |
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), |
2986 |
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, 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 */ |
3008 |
static 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 |
/* enable EAPD bit */ |
3017 |
{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, |
3018 |
{ } /* end */ |
3019 |
}; |
3020 |
|
3021 |
/* Digial MIC ADC NID 0x05 + 0x06 */ |
3022 |
static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo, |
3023 |
struct hda_codec *codec, |
3024 |
unsigned int stream_tag, |
3025 |
unsigned int format, |
3026 |
struct snd_pcm_substream *substream) |
3027 |
{ |
3028 |
snd_hda_codec_setup_stream(codec, 0x05 + substream->number, |
3029 |
stream_tag, 0, format); |
3030 |
return 0; |
3031 |
} |
3032 |
|
3033 |
static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, |
3034 |
struct hda_codec *codec, |
3035 |
struct snd_pcm_substream *substream) |
3036 |
{ |
3037 |
snd_hda_codec_setup_stream(codec, 0x05 + substream->number, |
3038 |
0, 0, 0); |
3039 |
return 0; |
3040 |
} |
3041 |
|
3042 |
static struct hda_pcm_stream ad1984_pcm_dmic_capture = { |
3043 |
.substreams = 2, |
3044 |
.channels_min = 2, |
3045 |
.channels_max = 2, |
3046 |
.nid = 0x05, |
3047 |
.ops = { |
3048 |
.prepare = ad1984_pcm_dmic_prepare, |
3049 |
.cleanup = ad1984_pcm_dmic_cleanup |
3050 |
}, |
3051 |
}; |
3052 |
|
3053 |
static int ad1984_build_pcms(struct hda_codec *codec) |
3054 |
{ |
3055 |
struct ad198x_spec *spec = codec->spec; |
3056 |
struct hda_pcm *info; |
3057 |
int err; |
3058 |
|
3059 |
err = ad198x_build_pcms(codec); |
3060 |
if (err < 0) |
3061 |
return err; |
3062 |
|
3063 |
info = spec->pcm_rec + codec->num_pcms; |
3064 |
codec->num_pcms++; |
3065 |
info->name = "AD1984 Digital Mic"; |
3066 |
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture; |
3067 |
return 0; |
3068 |
} |
3069 |
|
3070 |
/* models */ |
3071 |
enum { |
3072 |
AD1984_BASIC, |
3073 |
AD1984_THINKPAD, |
3074 |
AD1984_MODELS |
3075 |
}; |
3076 |
|
3077 |
static const char *ad1984_models[AD1984_MODELS] = { |
3078 |
[AD1984_BASIC] = "basic", |
3079 |
[AD1984_THINKPAD] = "thinkpad", |
3080 |
}; |
3081 |
|
3082 |
static struct snd_pci_quirk ad1984_cfg_tbl[] = { |
3083 |
/* Lenovo Thinkpad T61/X61 */ |
3084 |
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), |
3085 |
{} |
3086 |
}; |
3087 |
|
3088 |
static int patch_ad1984(struct hda_codec *codec) |
3089 |
{ |
3090 |
struct ad198x_spec *spec; |
3091 |
int board_config, err; |
3092 |
|
3093 |
err = patch_ad1884(codec); |
3094 |
if (err < 0) |
3095 |
return err; |
3096 |
spec = codec->spec; |
3097 |
board_config = snd_hda_check_board_config(codec, AD1984_MODELS, |
3098 |
ad1984_models, ad1984_cfg_tbl); |
3099 |
switch (board_config) { |
3100 |
case AD1984_BASIC: |
3101 |
/* additional digital mics */ |
3102 |
spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers; |
3103 |
codec->patch_ops.build_pcms = ad1984_build_pcms; |
3104 |
break; |
3105 |
case AD1984_THINKPAD: |
3106 |
spec->multiout.dig_out_nid = 0; |
3107 |
spec->input_mux = &ad1984_thinkpad_capture_source; |
3108 |
spec->mixers[0] = ad1984_thinkpad_mixers; |
3109 |
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; |
3110 |
break; |
3111 |
} |
3112 |
return 0; |
3113 |
} |
3114 |
|
3115 |
|
3116 |
/* |
2778 |
* patch entries |
3117 |
* patch entries |
2779 |
*/ |
3118 |
*/ |
2780 |
struct hda_codec_preset snd_hda_preset_analog[] = { |
3119 |
struct hda_codec_preset snd_hda_preset_analog[] = { |
|
|
3120 |
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, |
2781 |
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, |
3121 |
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, |
2782 |
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, |
3122 |
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, |
|
|
3123 |
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, |
2783 |
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, |
3124 |
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, |
2784 |
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, |
3125 |
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, |
2785 |
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, |
3126 |
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, |