aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorJonathan Woithe <jwoithe@physics.adelaide.edu.au>2006-02-09 05:53:48 -0500
committerJaroslav Kysela <perex@suse.cz>2006-03-22 04:28:42 -0500
commit4c5186ed6b25278df595edf2d355ee87b00c4426 (patch)
tree1114da4875d4925213e9c99d59dadf7673693900 /sound/pci
parentba22429d3ea3b9945735b88d4dde74711171ffab (diff)
[ALSA] hda: add PCM for 2nd ADC on ALC260
Modules: HDA Codec driver The following patch against alsa 1.0.11rc3 creates a PCM device (pcm1c) for the second ADC present on the ALC260 codec used by the hda driver. It also defines a new mixer control allowing the mode of retasking pins to be set; this means a user can (for example) designate the headphone jack to be a second input. With this patch in place it is possible to do 4 channel recording on laptops equipped with an ALC260 codec assuming both a stereo line-in jack is provided in addition to a headphone jack. Mixer controls are provided to allow the headphone jack to be switched as an input. In addition, an (input only) mode control is configured for the line-in jack to allow a bias voltage to be requested (VREF80 or VREF50) so headsets based on condensor microphones have a chance of working. This patch has been tested on a Fujitsu S7020 laptop and as such these features are currently only configured for the 'fujitsu' model. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/patch_realtek.c149
1 files changed, 119 insertions, 30 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b76755264730..c8fc6269b03c 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -132,7 +132,7 @@ struct alc_spec {
132 int num_channel_mode; 132 int num_channel_mode;
133 133
134 /* PCM information */ 134 /* PCM information */
135 struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */ 135 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
136 136
137 /* dynamic controls, init_verbs and input_mux */ 137 /* dynamic controls, init_verbs and input_mux */
138 struct auto_pin_cfg autocfg; 138 struct auto_pin_cfg autocfg;
@@ -218,56 +218,96 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
218 spec->num_channel_mode, &spec->multiout.max_channels); 218 spec->num_channel_mode, &spec->multiout.max_channels);
219} 219}
220 220
221
222/* 221/*
223 * Control of pin widget settings via the mixer. Only boolean settings are 222 * Control the mode of pin widget settings via the mixer. "pc" is used
224 * supported, so VrefEn can't be controlled using these functions as they 223 * instead of "%" to avoid consequences of accidently treating the % as
225 * stand. 224 * being part of a format specifier. Maximum allowed length of a value is
225 * 63 characters plus NULL terminator.
226 */
227static char *alc_pin_mode_names[] = {
228 "Line in", "Mic 80pc bias", "Mic 50pc bias",
229 "Line out", "Headphone out",
230};
231static unsigned char alc_pin_mode_values[] = {
232 PIN_IN, PIN_VREF80, PIN_VREF50, PIN_OUT, PIN_HP,
233};
234/* The control can present all 5 options, or it can limit the options based
235 * in the pin being assumed to be exclusively an input or an output pin.
226 */ 236 */
227static int alc_pinctl_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 237#define ALC_PIN_DIR_IN 0x00
238#define ALC_PIN_DIR_OUT 0x01
239#define ALC_PIN_DIR_INOUT 0x02
240
241/* Info about the pin modes supported by the three different pin directions.
242 * For each direction the minimum and maximum values are given.
243 */
244static signed char alc_pin_mode_dir_info[3][2] = {
245 { 0, 2 }, /* ALC_PIN_DIR_IN */
246 { 3, 4 }, /* ALC_PIN_DIR_OUT */
247 { 0, 4 }, /* ALC_PIN_DIR_INOUT */
248};
249#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
250#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
251#define alc_pin_mode_n_items(_dir) \
252 (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
253
254static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
228{ 255{
229 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 256 unsigned int item_num = uinfo->value.enumerated.item;
257 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
258
259 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
230 uinfo->count = 1; 260 uinfo->count = 1;
231 uinfo->value.integer.min = 0; 261 uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
232 uinfo->value.integer.max = 1; 262
263 if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
264 item_num = alc_pin_mode_min(dir);
265 strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
233 return 0; 266 return 0;
234} 267}
235 268
236static int alc_pinctl_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 269static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
237{ 270{
271 unsigned int i;
238 struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 272 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
239 hda_nid_t nid = kcontrol->private_value & 0xffff; 273 hda_nid_t nid = kcontrol->private_value & 0xffff;
240 long mask = (kcontrol->private_value >> 16) & 0xff; 274 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
241 long *valp = ucontrol->value.integer.value; 275 long *valp = ucontrol->value.integer.value;
276 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
242 277
243 *valp = 0; 278 /* Find enumerated value for current pinctl setting */
244 if (snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00) & mask) 279 i = alc_pin_mode_min(dir);
245 *valp = 1; 280 while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir))
281 i++;
282 *valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir);
246 return 0; 283 return 0;
247} 284}
248 285
249static int alc_pinctl_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 286static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
250{ 287{
288 signed int change;
251 struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 289 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
252 hda_nid_t nid = kcontrol->private_value & 0xffff; 290 hda_nid_t nid = kcontrol->private_value & 0xffff;
253 long mask = (kcontrol->private_value >> 16) & 0xff; 291 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
254 long *valp = ucontrol->value.integer.value; 292 long val = *ucontrol->value.integer.value;
255 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00); 293 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
256 int change = ((pinctl & mask)!=0) != *valp;
257 294
295 if (val<alc_pin_mode_min(dir) || val>alc_pin_mode_max(dir))
296 val = alc_pin_mode_min(dir);
297
298 change = pinctl != alc_pin_mode_values[val];
258 if (change) 299 if (change)
259 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL, 300 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
260 *valp?(pinctl|mask):(pinctl&~mask)); 301 alc_pin_mode_values[val]);
261 return change; 302 return change;
262} 303}
263 304
264#define ALC_PINCTL_SWITCH(xname, nid, mask) \ 305#define ALC_PIN_MODE(xname, nid, dir) \
265 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ 306 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
266 .info = alc_pinctl_switch_info, \ 307 .info = alc_pin_mode_info, \
267 .get = alc_pinctl_switch_get, \ 308 .get = alc_pin_mode_get, \
268 .put = alc_pinctl_switch_put, \ 309 .put = alc_pin_mode_put, \
269 .private_value = (nid) | (mask<<16) } 310 .private_value = nid | (dir<<16) }
270
271 311
272/* 312/*
273 * set up from the preset table 313 * set up from the preset table
@@ -1250,6 +1290,13 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = {
1250 /* NID is set in alc_build_pcms */ 1290 /* NID is set in alc_build_pcms */
1251}; 1291};
1252 1292
1293/* Used by alc_build_pcms to flag that a PCM has no playback stream */
1294static struct hda_pcm_stream alc_pcm_null_playback = {
1295 .substreams = 0,
1296 .channels_min = 0,
1297 .channels_max = 0,
1298};
1299
1253static int alc_build_pcms(struct hda_codec *codec) 1300static int alc_build_pcms(struct hda_codec *codec)
1254{ 1301{
1255 struct alc_spec *spec = codec->spec; 1302 struct alc_spec *spec = codec->spec;
@@ -1280,6 +1327,23 @@ static int alc_build_pcms(struct hda_codec *codec)
1280 } 1327 }
1281 } 1328 }
1282 1329
1330 /* If the use of more than one ADC is requested for the current
1331 * model, configure a second analog capture-only PCM.
1332 */
1333 if (spec->num_adc_nids > 1) {
1334 codec->num_pcms++;
1335 info++;
1336 info->name = spec->stream_name_analog;
1337 /* No playback stream for second PCM */
1338 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
1339 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
1340 if (spec->stream_analog_capture) {
1341 snd_assert(spec->adc_nids, return -EINVAL);
1342 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1343 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
1344 }
1345 }
1346
1283 if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1347 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1284 codec->num_pcms++; 1348 codec->num_pcms++;
1285 info++; 1349 info++;
@@ -2322,6 +2386,11 @@ static hda_nid_t alc260_hp_adc_nids[2] = {
2322 0x05, 0x04 2386 0x05, 0x04
2323}; 2387};
2324 2388
2389static hda_nid_t alc260_fujitsu_adc_nids[2] = {
2390 /* ADC0, ADC1 */
2391 0x04, 0x05
2392};
2393
2325#define ALC260_DIGOUT_NID 0x03 2394#define ALC260_DIGOUT_NID 0x03
2326#define ALC260_DIGIN_NID 0x06 2395#define ALC260_DIGIN_NID 0x06
2327 2396
@@ -2339,10 +2408,11 @@ static struct hda_input_mux alc260_capture_source = {
2339 * and the internal CD lines. 2408 * and the internal CD lines.
2340 */ 2409 */
2341static struct hda_input_mux alc260_fujitsu_capture_source = { 2410static struct hda_input_mux alc260_fujitsu_capture_source = {
2342 .num_items = 2, 2411 .num_items = 3,
2343 .items = { 2412 .items = {
2344 { "Mic/Line", 0x0 }, 2413 { "Mic/Line", 0x0 },
2345 { "CD", 0x4 }, 2414 { "CD", 0x4 },
2415 { "Headphone", 0x2 },
2346 }, 2416 },
2347}; 2417};
2348 2418
@@ -2408,11 +2478,12 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
2408static struct snd_kcontrol_new alc260_fujitsu_mixer[] = { 2478static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
2409 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), 2479 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
2410 HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT), 2480 HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
2411 ALC_PINCTL_SWITCH("Headphone Amp Switch", 0x14, PIN_HP_AMP), 2481 ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
2412 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), 2482 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
2413 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), 2483 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
2414 HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT), 2484 HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
2415 HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT), 2485 HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
2486 ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
2416 HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), 2487 HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
2417 HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), 2488 HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
2418 HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT), 2489 HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
@@ -2645,6 +2716,11 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {
2645 {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, 2716 {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
2646 {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, 2717 {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
2647 2718
2719 /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
2720 * when acting as an output.
2721 */
2722 {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
2723
2648 /* Start with mixer outputs muted */ 2724 /* Start with mixer outputs muted */
2649 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, 2725 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2650 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, 2726 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
@@ -2654,14 +2730,27 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {
2654 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2730 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2655 /* Unmute Line1 pin widget amp left and right (no equiv mixer ctrl) */ 2731 /* Unmute Line1 pin widget amp left and right (no equiv mixer ctrl) */
2656 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2732 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2733 /* Unmute Line1 pin widget input for when this pin is used as input
2734 * (no equiv mixer ctrl). Having input and output unmuted doesn't
2735 * seem to cause a problem.
2736 */
2737 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2657 /* Unmute pin widget used for Line-in (no equiv mixer ctrl) */ 2738 /* Unmute pin widget used for Line-in (no equiv mixer ctrl) */
2658 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2739 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2659 2740
2660 /* Mute capture amp left and right */ 2741 /* Mute capture amp left and right */
2661 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2742 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2662 /* Set ADC connection select to line in (on mic1 pin) */ 2743 /* Set ADC connection select to match default mixer setting - line
2744 * in (on mic1 pin)
2745 */
2663 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, 2746 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
2664 2747
2748 /* Do the same for the second ADC: mute capture input amp and
2749 * set ADC connection to line in
2750 */
2751 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2752 {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
2753
2665 /* Mute all inputs to mixer widget (even unconnected ones) */ 2754 /* Mute all inputs to mixer widget (even unconnected ones) */
2666 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ 2755 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
2667 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ 2756 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
@@ -3009,8 +3098,8 @@ static struct alc_config_preset alc260_presets[] = {
3009 .init_verbs = { alc260_fujitsu_init_verbs }, 3098 .init_verbs = { alc260_fujitsu_init_verbs },
3010 .num_dacs = ARRAY_SIZE(alc260_dac_nids), 3099 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3011 .dac_nids = alc260_dac_nids, 3100 .dac_nids = alc260_dac_nids,
3012 .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), 3101 .num_adc_nids = ARRAY_SIZE(alc260_fujitsu_adc_nids),
3013 .adc_nids = alc260_adc_nids, 3102 .adc_nids = alc260_fujitsu_adc_nids,
3014 .num_channel_mode = ARRAY_SIZE(alc260_modes), 3103 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3015 .channel_mode = alc260_modes, 3104 .channel_mode = alc260_modes,
3016 .input_mux = &alc260_fujitsu_capture_source, 3105 .input_mux = &alc260_fujitsu_capture_source,