diff options
author | Takashi Iwai <tiwai@suse.de> | 2006-03-01 08:16:17 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-03-22 04:33:31 -0500 |
commit | 834be88d136ee82828e3ce1b34fa7a1dcf947b81 (patch) | |
tree | 03cc02d74cbc548c50616494de00f6e598a54f2c /sound | |
parent | 381e3cda682abcef2acd7828e9639f4ceb95880f (diff) |
[ALSA] hda-codec - Fix ALC262 for Fujitsu laptop
Modules: HDA Codec driver,HDA generic driver
Add 'fujitsu' model for ALC262 patch to support a FSC laptop.
The internal speaker is turned on/off with jack sensing.
Also fixed alc262 'basic' model.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 6 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 5 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 159 |
3 files changed, 155 insertions, 15 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0d1566a39965..fc91256e42eb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -729,7 +729,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, | |||
729 | /* | 729 | /* |
730 | * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. | 730 | * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. |
731 | */ | 731 | */ |
732 | static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) | 732 | int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, |
733 | int direction, int index) | ||
733 | { | 734 | { |
734 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); | 735 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); |
735 | if (! info) | 736 | if (! info) |
@@ -740,7 +741,8 @@ static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch | |||
740 | /* | 741 | /* |
741 | * update the AMP value, mask = bit mask to set, val = the value | 742 | * update the AMP value, mask = bit mask to set, val = the value |
742 | */ | 743 | */ |
743 | static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) | 744 | int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, |
745 | int direction, int idx, int mask, int val) | ||
744 | { | 746 | { |
745 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); | 747 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); |
746 | 748 | ||
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index c82d2a72d13e..548a8b698487 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h | |||
@@ -66,6 +66,11 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e | |||
66 | int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); | 66 | int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); |
67 | int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); | 67 | int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); |
68 | int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); | 68 | int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); |
69 | /* lowlevel accessor with caching; use carefully */ | ||
70 | int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, | ||
71 | int direction, int index); | ||
72 | int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, | ||
73 | int direction, int idx, int mask, int val); | ||
69 | 74 | ||
70 | /* mono switch binding multiple inputs */ | 75 | /* mono switch binding multiple inputs */ |
71 | #define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ | 76 | #define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ |
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 219ddf0b8d43..5de754a51fc7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -75,6 +75,7 @@ enum { | |||
75 | /* ALC262 models */ | 75 | /* ALC262 models */ |
76 | enum { | 76 | enum { |
77 | ALC262_BASIC, | 77 | ALC262_BASIC, |
78 | ALC262_FUJITSU, | ||
78 | ALC262_AUTO, | 79 | ALC262_AUTO, |
79 | ALC262_MODEL_LAST /* last tag */ | 80 | ALC262_MODEL_LAST /* last tag */ |
80 | }; | 81 | }; |
@@ -145,6 +146,10 @@ struct alc_spec { | |||
145 | struct snd_kcontrol_new *kctl_alloc; | 146 | struct snd_kcontrol_new *kctl_alloc; |
146 | struct hda_input_mux private_imux; | 147 | struct hda_input_mux private_imux; |
147 | hda_nid_t private_dac_nids[5]; | 148 | hda_nid_t private_dac_nids[5]; |
149 | |||
150 | /* for pin sensing */ | ||
151 | unsigned int sense_updated: 1; | ||
152 | unsigned int jack_present: 1; | ||
148 | }; | 153 | }; |
149 | 154 | ||
150 | /* | 155 | /* |
@@ -4194,19 +4199,9 @@ static struct snd_kcontrol_new alc262_base_mixer[] = { | |||
4194 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), | 4199 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), |
4195 | HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), | 4200 | HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), |
4196 | HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), | 4201 | HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), |
4197 | HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), | ||
4198 | HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), | ||
4199 | { | ||
4200 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
4201 | .name = "Capture Source", | ||
4202 | .count = 1, | ||
4203 | .info = alc882_mux_enum_info, | ||
4204 | .get = alc882_mux_enum_get, | ||
4205 | .put = alc882_mux_enum_put, | ||
4206 | }, | ||
4207 | { } /* end */ | 4202 | { } /* end */ |
4208 | }; | 4203 | }; |
4209 | 4204 | ||
4210 | #define alc262_capture_mixer alc882_capture_mixer | 4205 | #define alc262_capture_mixer alc882_capture_mixer |
4211 | #define alc262_capture_alt_mixer alc882_capture_alt_mixer | 4206 | #define alc262_capture_alt_mixer alc882_capture_alt_mixer |
4212 | 4207 | ||
@@ -4289,6 +4284,129 @@ static struct hda_verb alc262_init_verbs[] = { | |||
4289 | { } | 4284 | { } |
4290 | }; | 4285 | }; |
4291 | 4286 | ||
4287 | /* | ||
4288 | * fujitsu model | ||
4289 | * 0x14 = headphone/spdif-out, 0x15 = internal speaker | ||
4290 | */ | ||
4291 | |||
4292 | #define ALC_HP_EVENT 0x37 | ||
4293 | |||
4294 | static struct hda_verb alc262_fujitsu_unsol_verbs[] = { | ||
4295 | {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, | ||
4296 | {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
4297 | {} | ||
4298 | }; | ||
4299 | |||
4300 | static struct hda_input_mux alc262_fujitsu_capture_source = { | ||
4301 | .num_items = 2, | ||
4302 | .items = { | ||
4303 | { "Mic", 0x0 }, | ||
4304 | { "CD", 0x4 }, | ||
4305 | }, | ||
4306 | }; | ||
4307 | |||
4308 | /* mute/unmute internal speaker according to the hp jack and mute state */ | ||
4309 | static void alc262_fujitsu_automute(struct hda_codec *codec, int force) | ||
4310 | { | ||
4311 | struct alc_spec *spec = codec->spec; | ||
4312 | unsigned int mute; | ||
4313 | |||
4314 | if (force || ! spec->sense_updated) { | ||
4315 | unsigned int present; | ||
4316 | /* need to execute and sync at first */ | ||
4317 | snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); | ||
4318 | present = snd_hda_codec_read(codec, 0x14, 0, | ||
4319 | AC_VERB_GET_PIN_SENSE, 0); | ||
4320 | spec->jack_present = (present & 0x80000000) != 0; | ||
4321 | spec->sense_updated = 1; | ||
4322 | } | ||
4323 | if (spec->jack_present) { | ||
4324 | /* mute internal speaker */ | ||
4325 | snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, | ||
4326 | 0x80, 0x80); | ||
4327 | snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, | ||
4328 | 0x80, 0x80); | ||
4329 | } else { | ||
4330 | /* unmute internal speaker if necessary */ | ||
4331 | mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); | ||
4332 | snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, | ||
4333 | 0x80, mute & 0x80); | ||
4334 | mute = snd_hda_codec_amp_read(codec, 0x14, 1, HDA_OUTPUT, 0); | ||
4335 | snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, | ||
4336 | 0x80, mute & 0x80); | ||
4337 | } | ||
4338 | } | ||
4339 | |||
4340 | /* unsolicited event for HP jack sensing */ | ||
4341 | static void alc262_fujitsu_unsol_event(struct hda_codec *codec, | ||
4342 | unsigned int res) | ||
4343 | { | ||
4344 | if ((res >> 26) != ALC_HP_EVENT) | ||
4345 | return; | ||
4346 | alc262_fujitsu_automute(codec, 1); | ||
4347 | } | ||
4348 | |||
4349 | /* bind volumes of both NID 0x0c and 0x0d */ | ||
4350 | static int alc262_fujitsu_master_vol_put(struct snd_kcontrol *kcontrol, | ||
4351 | struct snd_ctl_elem_value *ucontrol) | ||
4352 | { | ||
4353 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
4354 | long *valp = ucontrol->value.integer.value; | ||
4355 | int change; | ||
4356 | |||
4357 | change = snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0, | ||
4358 | 0x7f, valp[0] & 0x7f); | ||
4359 | change |= snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0, | ||
4360 | 0x7f, valp[1] & 0x7f); | ||
4361 | snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0, | ||
4362 | 0x7f, valp[0] & 0x7f); | ||
4363 | snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0, | ||
4364 | 0x7f, valp[1] & 0x7f); | ||
4365 | return change; | ||
4366 | } | ||
4367 | |||
4368 | /* bind hp and internal speaker mute (with plug check) */ | ||
4369 | static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, | ||
4370 | struct snd_ctl_elem_value *ucontrol) | ||
4371 | { | ||
4372 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
4373 | long *valp = ucontrol->value.integer.value; | ||
4374 | int change; | ||
4375 | |||
4376 | change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, | ||
4377 | 0x80, valp[0] ? 0 : 0x80); | ||
4378 | change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, | ||
4379 | 0x80, valp[1] ? 0 : 0x80); | ||
4380 | if (change || codec->in_resume) | ||
4381 | alc262_fujitsu_automute(codec, codec->in_resume); | ||
4382 | return change; | ||
4383 | } | ||
4384 | |||
4385 | static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { | ||
4386 | { | ||
4387 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
4388 | .name = "Master Playback Volume", | ||
4389 | .info = snd_hda_mixer_amp_volume_info, | ||
4390 | .get = snd_hda_mixer_amp_volume_get, | ||
4391 | .put = alc262_fujitsu_master_vol_put, | ||
4392 | .private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), | ||
4393 | }, | ||
4394 | { | ||
4395 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
4396 | .name = "Master Playback Switch", | ||
4397 | .info = snd_hda_mixer_amp_switch_info, | ||
4398 | .get = snd_hda_mixer_amp_switch_get, | ||
4399 | .put = alc262_fujitsu_master_sw_put, | ||
4400 | .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), | ||
4401 | }, | ||
4402 | HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), | ||
4403 | HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), | ||
4404 | HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), | ||
4405 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), | ||
4406 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), | ||
4407 | { } /* end */ | ||
4408 | }; | ||
4409 | |||
4292 | /* add playback controls from the parsed DAC table */ | 4410 | /* add playback controls from the parsed DAC table */ |
4293 | static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) | 4411 | static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) |
4294 | { | 4412 | { |
@@ -4479,6 +4597,8 @@ static int alc262_auto_init(struct hda_codec *codec) | |||
4479 | */ | 4597 | */ |
4480 | static struct hda_board_config alc262_cfg_tbl[] = { | 4598 | static struct hda_board_config alc262_cfg_tbl[] = { |
4481 | { .modelname = "basic", .config = ALC262_BASIC }, | 4599 | { .modelname = "basic", .config = ALC262_BASIC }, |
4600 | { .modelname = "fujitsu", .config = ALC262_FUJITSU }, | ||
4601 | { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU }, | ||
4482 | { .modelname = "auto", .config = ALC262_AUTO }, | 4602 | { .modelname = "auto", .config = ALC262_AUTO }, |
4483 | {} | 4603 | {} |
4484 | }; | 4604 | }; |
@@ -4494,6 +4614,17 @@ static struct alc_config_preset alc262_presets[] = { | |||
4494 | .channel_mode = alc262_modes, | 4614 | .channel_mode = alc262_modes, |
4495 | .input_mux = &alc262_capture_source, | 4615 | .input_mux = &alc262_capture_source, |
4496 | }, | 4616 | }, |
4617 | [ALC262_FUJITSU] = { | ||
4618 | .mixers = { alc262_fujitsu_mixer }, | ||
4619 | .init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs }, | ||
4620 | .num_dacs = ARRAY_SIZE(alc262_dac_nids), | ||
4621 | .dac_nids = alc262_dac_nids, | ||
4622 | .hp_nid = 0x03, | ||
4623 | .dig_out_nid = ALC262_DIGOUT_NID, | ||
4624 | .num_channel_mode = ARRAY_SIZE(alc262_modes), | ||
4625 | .channel_mode = alc262_modes, | ||
4626 | .input_mux = &alc262_fujitsu_capture_source, | ||
4627 | }, | ||
4497 | }; | 4628 | }; |
4498 | 4629 | ||
4499 | static int patch_alc262(struct hda_codec *codec) | 4630 | static int patch_alc262(struct hda_codec *codec) |
@@ -4568,7 +4699,9 @@ static int patch_alc262(struct hda_codec *codec) | |||
4568 | codec->patch_ops = alc_patch_ops; | 4699 | codec->patch_ops = alc_patch_ops; |
4569 | if (board_config == ALC262_AUTO) | 4700 | if (board_config == ALC262_AUTO) |
4570 | codec->patch_ops.init = alc262_auto_init; | 4701 | codec->patch_ops.init = alc262_auto_init; |
4571 | 4702 | if (board_config == ALC262_FUJITSU) | |
4703 | codec->patch_ops.unsol_event = alc262_fujitsu_unsol_event; | ||
4704 | |||
4572 | return 0; | 4705 | return 0; |
4573 | } | 4706 | } |
4574 | 4707 | ||