diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 651 |
1 files changed, 642 insertions, 9 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 972e7c453b3d..6361f752b5f3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -57,6 +57,12 @@ struct conexant_jack { | |||
57 | 57 | ||
58 | }; | 58 | }; |
59 | 59 | ||
60 | struct pin_dac_pair { | ||
61 | hda_nid_t pin; | ||
62 | hda_nid_t dac; | ||
63 | int type; | ||
64 | }; | ||
65 | |||
60 | struct conexant_spec { | 66 | struct conexant_spec { |
61 | 67 | ||
62 | struct snd_kcontrol_new *mixers[5]; | 68 | struct snd_kcontrol_new *mixers[5]; |
@@ -77,6 +83,7 @@ struct conexant_spec { | |||
77 | unsigned int cur_eapd; | 83 | unsigned int cur_eapd; |
78 | unsigned int hp_present; | 84 | unsigned int hp_present; |
79 | unsigned int auto_mic; | 85 | unsigned int auto_mic; |
86 | int auto_mic_ext; /* autocfg.inputs[] index for ext mic */ | ||
80 | unsigned int need_dac_fix; | 87 | unsigned int need_dac_fix; |
81 | 88 | ||
82 | /* capture */ | 89 | /* capture */ |
@@ -110,9 +117,12 @@ struct conexant_spec { | |||
110 | struct auto_pin_cfg autocfg; | 117 | struct auto_pin_cfg autocfg; |
111 | struct hda_input_mux private_imux; | 118 | struct hda_input_mux private_imux; |
112 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | 119 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; |
120 | struct pin_dac_pair dac_info[8]; | ||
121 | int dac_info_filled; | ||
113 | 122 | ||
114 | unsigned int dell_automute; | ||
115 | unsigned int port_d_mode; | 123 | unsigned int port_d_mode; |
124 | unsigned int auto_mute:1; /* used in auto-parser */ | ||
125 | unsigned int dell_automute:1; | ||
116 | unsigned int dell_vostro:1; | 126 | unsigned int dell_vostro:1; |
117 | unsigned int ideapad:1; | 127 | unsigned int ideapad:1; |
118 | unsigned int thinkpad:1; | 128 | unsigned int thinkpad:1; |
@@ -3065,7 +3075,7 @@ enum { | |||
3065 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ | 3075 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ |
3066 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ | 3076 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ |
3067 | CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ | 3077 | CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ |
3068 | CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ | 3078 | CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */ |
3069 | CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ | 3079 | CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ |
3070 | CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ | 3080 | CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ |
3071 | CXT5066_HP_LAPTOP, /* HP Laptop */ | 3081 | CXT5066_HP_LAPTOP, /* HP Laptop */ |
@@ -3076,25 +3086,26 @@ static const char *cxt5066_models[CXT5066_MODELS] = { | |||
3076 | [CXT5066_LAPTOP] = "laptop", | 3086 | [CXT5066_LAPTOP] = "laptop", |
3077 | [CXT5066_DELL_LAPTOP] = "dell-laptop", | 3087 | [CXT5066_DELL_LAPTOP] = "dell-laptop", |
3078 | [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", | 3088 | [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", |
3079 | [CXT5066_DELL_VOSTO] = "dell-vostro", | 3089 | [CXT5066_DELL_VOSTRO] = "dell-vostro", |
3080 | [CXT5066_IDEAPAD] = "ideapad", | 3090 | [CXT5066_IDEAPAD] = "ideapad", |
3081 | [CXT5066_THINKPAD] = "thinkpad", | 3091 | [CXT5066_THINKPAD] = "thinkpad", |
3082 | [CXT5066_HP_LAPTOP] = "hp-laptop", | 3092 | [CXT5066_HP_LAPTOP] = "hp-laptop", |
3083 | }; | 3093 | }; |
3084 | 3094 | ||
3085 | static struct snd_pci_quirk cxt5066_cfg_tbl[] = { | 3095 | static struct snd_pci_quirk cxt5066_cfg_tbl[] = { |
3086 | SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", | 3096 | SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD), |
3087 | CXT5066_LAPTOP), | 3097 | SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO), |
3088 | SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", | 3098 | SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", |
3089 | CXT5066_DELL_LAPTOP), | 3099 | CXT5066_DELL_LAPTOP), |
3090 | SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), | 3100 | SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO), |
3091 | SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO), | ||
3092 | SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), | ||
3093 | SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), | 3101 | SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), |
3094 | SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), | 3102 | SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), |
3095 | SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), | 3103 | SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), |
3096 | SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), | 3104 | SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), |
3097 | SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), | 3105 | SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), |
3106 | SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", | ||
3107 | CXT5066_LAPTOP), | ||
3108 | SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), | ||
3098 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), | 3109 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), |
3099 | SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), | 3110 | SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), |
3100 | SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD), | 3111 | SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD), |
@@ -3196,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
3196 | spec->capture_prepare = cxt5066_olpc_capture_prepare; | 3207 | spec->capture_prepare = cxt5066_olpc_capture_prepare; |
3197 | spec->capture_cleanup = cxt5066_olpc_capture_cleanup; | 3208 | spec->capture_cleanup = cxt5066_olpc_capture_cleanup; |
3198 | break; | 3209 | break; |
3199 | case CXT5066_DELL_VOSTO: | 3210 | case CXT5066_DELL_VOSTRO: |
3200 | codec->patch_ops.init = cxt5066_init; | 3211 | codec->patch_ops.init = cxt5066_init; |
3201 | codec->patch_ops.unsol_event = cxt5066_vostro_event; | 3212 | codec->patch_ops.unsol_event = cxt5066_vostro_event; |
3202 | spec->init_verbs[0] = cxt5066_init_verbs_vostro; | 3213 | spec->init_verbs[0] = cxt5066_init_verbs_vostro; |
@@ -3254,6 +3265,604 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
3254 | } | 3265 | } |
3255 | 3266 | ||
3256 | /* | 3267 | /* |
3268 | * Automatic parser for CX20641 & co | ||
3269 | */ | ||
3270 | |||
3271 | static hda_nid_t cx_auto_adc_nids[] = { 0x14 }; | ||
3272 | |||
3273 | /* get the connection index of @nid in the widget @mux */ | ||
3274 | static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, | ||
3275 | hda_nid_t nid) | ||
3276 | { | ||
3277 | hda_nid_t conn[HDA_MAX_NUM_INPUTS]; | ||
3278 | int i, nums; | ||
3279 | |||
3280 | nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); | ||
3281 | for (i = 0; i < nums; i++) | ||
3282 | if (conn[i] == nid) | ||
3283 | return i; | ||
3284 | return -1; | ||
3285 | } | ||
3286 | |||
3287 | /* get an unassigned DAC from the given list. | ||
3288 | * Return the nid if found and reduce the DAC list, or return zero if | ||
3289 | * not found | ||
3290 | */ | ||
3291 | static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, | ||
3292 | hda_nid_t *dacs, int *num_dacs) | ||
3293 | { | ||
3294 | int i, nums = *num_dacs; | ||
3295 | hda_nid_t ret = 0; | ||
3296 | |||
3297 | for (i = 0; i < nums; i++) { | ||
3298 | if (get_connection_index(codec, pin, dacs[i]) >= 0) { | ||
3299 | ret = dacs[i]; | ||
3300 | break; | ||
3301 | } | ||
3302 | } | ||
3303 | if (!ret) | ||
3304 | return 0; | ||
3305 | if (--nums > 0) | ||
3306 | memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); | ||
3307 | *num_dacs = nums; | ||
3308 | return ret; | ||
3309 | } | ||
3310 | |||
3311 | #define MAX_AUTO_DACS 5 | ||
3312 | |||
3313 | /* fill analog DAC list from the widget tree */ | ||
3314 | static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) | ||
3315 | { | ||
3316 | hda_nid_t nid, end_nid; | ||
3317 | int nums = 0; | ||
3318 | |||
3319 | end_nid = codec->start_nid + codec->num_nodes; | ||
3320 | for (nid = codec->start_nid; nid < end_nid; nid++) { | ||
3321 | unsigned int wcaps = get_wcaps(codec, nid); | ||
3322 | unsigned int type = get_wcaps_type(wcaps); | ||
3323 | if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { | ||
3324 | dacs[nums++] = nid; | ||
3325 | if (nums >= MAX_AUTO_DACS) | ||
3326 | break; | ||
3327 | } | ||
3328 | } | ||
3329 | return nums; | ||
3330 | } | ||
3331 | |||
3332 | /* fill pin_dac_pair list from the pin and dac list */ | ||
3333 | static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, | ||
3334 | int num_pins, hda_nid_t *dacs, int *rest, | ||
3335 | struct pin_dac_pair *filled, int type) | ||
3336 | { | ||
3337 | int i, nums; | ||
3338 | |||
3339 | nums = 0; | ||
3340 | for (i = 0; i < num_pins; i++) { | ||
3341 | filled[nums].pin = pins[i]; | ||
3342 | filled[nums].type = type; | ||
3343 | filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); | ||
3344 | nums++; | ||
3345 | } | ||
3346 | return nums; | ||
3347 | } | ||
3348 | |||
3349 | /* parse analog output paths */ | ||
3350 | static void cx_auto_parse_output(struct hda_codec *codec) | ||
3351 | { | ||
3352 | struct conexant_spec *spec = codec->spec; | ||
3353 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3354 | hda_nid_t dacs[MAX_AUTO_DACS]; | ||
3355 | int i, j, nums, rest; | ||
3356 | |||
3357 | rest = fill_cx_auto_dacs(codec, dacs); | ||
3358 | /* parse all analog output pins */ | ||
3359 | nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, | ||
3360 | dacs, &rest, spec->dac_info, | ||
3361 | AUTO_PIN_LINE_OUT); | ||
3362 | nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, | ||
3363 | dacs, &rest, spec->dac_info + nums, | ||
3364 | AUTO_PIN_HP_OUT); | ||
3365 | nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, | ||
3366 | dacs, &rest, spec->dac_info + nums, | ||
3367 | AUTO_PIN_SPEAKER_OUT); | ||
3368 | spec->dac_info_filled = nums; | ||
3369 | /* fill multiout struct */ | ||
3370 | for (i = 0; i < nums; i++) { | ||
3371 | hda_nid_t dac = spec->dac_info[i].dac; | ||
3372 | if (!dac) | ||
3373 | continue; | ||
3374 | switch (spec->dac_info[i].type) { | ||
3375 | case AUTO_PIN_LINE_OUT: | ||
3376 | spec->private_dac_nids[spec->multiout.num_dacs] = dac; | ||
3377 | spec->multiout.num_dacs++; | ||
3378 | break; | ||
3379 | case AUTO_PIN_HP_OUT: | ||
3380 | case AUTO_PIN_SPEAKER_OUT: | ||
3381 | if (!spec->multiout.hp_nid) { | ||
3382 | spec->multiout.hp_nid = dac; | ||
3383 | break; | ||
3384 | } | ||
3385 | for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) | ||
3386 | if (!spec->multiout.extra_out_nid[j]) { | ||
3387 | spec->multiout.extra_out_nid[j] = dac; | ||
3388 | break; | ||
3389 | } | ||
3390 | break; | ||
3391 | } | ||
3392 | } | ||
3393 | spec->multiout.dac_nids = spec->private_dac_nids; | ||
3394 | spec->multiout.max_channels = nums * 2; | ||
3395 | |||
3396 | if (cfg->hp_outs > 0) | ||
3397 | spec->auto_mute = 1; | ||
3398 | spec->vmaster_nid = spec->private_dac_nids[0]; | ||
3399 | } | ||
3400 | |||
3401 | /* auto-mute/unmute speaker and line outs according to headphone jack */ | ||
3402 | static void cx_auto_hp_automute(struct hda_codec *codec) | ||
3403 | { | ||
3404 | struct conexant_spec *spec = codec->spec; | ||
3405 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3406 | int i, present; | ||
3407 | |||
3408 | if (!spec->auto_mute) | ||
3409 | return; | ||
3410 | present = 0; | ||
3411 | for (i = 0; i < cfg->hp_outs; i++) { | ||
3412 | if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) { | ||
3413 | present = 1; | ||
3414 | break; | ||
3415 | } | ||
3416 | } | ||
3417 | for (i = 0; i < cfg->line_outs; i++) { | ||
3418 | snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, | ||
3419 | AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
3420 | present ? 0 : PIN_OUT); | ||
3421 | } | ||
3422 | for (i = 0; i < cfg->speaker_outs; i++) { | ||
3423 | snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, | ||
3424 | AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
3425 | present ? 0 : PIN_OUT); | ||
3426 | } | ||
3427 | } | ||
3428 | |||
3429 | /* automatic switch internal and external mic */ | ||
3430 | static void cx_auto_automic(struct hda_codec *codec) | ||
3431 | { | ||
3432 | struct conexant_spec *spec = codec->spec; | ||
3433 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3434 | struct hda_input_mux *imux = &spec->private_imux; | ||
3435 | int ext_idx = spec->auto_mic_ext; | ||
3436 | |||
3437 | if (!spec->auto_mic) | ||
3438 | return; | ||
3439 | if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) { | ||
3440 | snd_hda_codec_write(codec, spec->adc_nids[0], 0, | ||
3441 | AC_VERB_SET_CONNECT_SEL, | ||
3442 | imux->items[ext_idx].index); | ||
3443 | } else { | ||
3444 | snd_hda_codec_write(codec, spec->adc_nids[0], 0, | ||
3445 | AC_VERB_SET_CONNECT_SEL, | ||
3446 | imux->items[!ext_idx].index); | ||
3447 | } | ||
3448 | } | ||
3449 | |||
3450 | static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) | ||
3451 | { | ||
3452 | int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; | ||
3453 | switch (res >> 26) { | ||
3454 | case CONEXANT_HP_EVENT: | ||
3455 | cx_auto_hp_automute(codec); | ||
3456 | conexant_report_jack(codec, nid); | ||
3457 | break; | ||
3458 | case CONEXANT_MIC_EVENT: | ||
3459 | cx_auto_automic(codec); | ||
3460 | conexant_report_jack(codec, nid); | ||
3461 | break; | ||
3462 | } | ||
3463 | } | ||
3464 | |||
3465 | /* return true if it's an internal-mic pin */ | ||
3466 | static int is_int_mic(struct hda_codec *codec, hda_nid_t pin) | ||
3467 | { | ||
3468 | unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); | ||
3469 | return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && | ||
3470 | snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT; | ||
3471 | } | ||
3472 | |||
3473 | /* return true if it's an external-mic pin */ | ||
3474 | static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin) | ||
3475 | { | ||
3476 | unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); | ||
3477 | return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && | ||
3478 | snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL && | ||
3479 | (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT); | ||
3480 | } | ||
3481 | |||
3482 | /* check whether the pin config is suitable for auto-mic switching; | ||
3483 | * auto-mic is enabled only when one int-mic and one-ext mic exist | ||
3484 | */ | ||
3485 | static void cx_auto_check_auto_mic(struct hda_codec *codec) | ||
3486 | { | ||
3487 | struct conexant_spec *spec = codec->spec; | ||
3488 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3489 | |||
3490 | if (is_ext_mic(codec, cfg->inputs[0].pin) && | ||
3491 | is_int_mic(codec, cfg->inputs[1].pin)) { | ||
3492 | spec->auto_mic = 1; | ||
3493 | spec->auto_mic_ext = 1; | ||
3494 | return; | ||
3495 | } | ||
3496 | if (is_int_mic(codec, cfg->inputs[1].pin) && | ||
3497 | is_ext_mic(codec, cfg->inputs[0].pin)) { | ||
3498 | spec->auto_mic = 1; | ||
3499 | spec->auto_mic_ext = 0; | ||
3500 | return; | ||
3501 | } | ||
3502 | } | ||
3503 | |||
3504 | static void cx_auto_parse_input(struct hda_codec *codec) | ||
3505 | { | ||
3506 | struct conexant_spec *spec = codec->spec; | ||
3507 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3508 | struct hda_input_mux *imux; | ||
3509 | int i; | ||
3510 | |||
3511 | imux = &spec->private_imux; | ||
3512 | for (i = 0; i < cfg->num_inputs; i++) { | ||
3513 | int idx = get_connection_index(codec, spec->adc_nids[0], | ||
3514 | cfg->inputs[i].pin); | ||
3515 | if (idx >= 0) { | ||
3516 | const char *label; | ||
3517 | label = hda_get_autocfg_input_label(codec, cfg, i); | ||
3518 | snd_hda_add_imux_item(imux, label, idx, NULL); | ||
3519 | } | ||
3520 | } | ||
3521 | if (imux->num_items == 2 && cfg->num_inputs == 2) | ||
3522 | cx_auto_check_auto_mic(codec); | ||
3523 | if (imux->num_items > 1 && !spec->auto_mic) | ||
3524 | spec->input_mux = imux; | ||
3525 | } | ||
3526 | |||
3527 | /* get digital-input audio widget corresponding to the given pin */ | ||
3528 | static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) | ||
3529 | { | ||
3530 | hda_nid_t nid, end_nid; | ||
3531 | |||
3532 | end_nid = codec->start_nid + codec->num_nodes; | ||
3533 | for (nid = codec->start_nid; nid < end_nid; nid++) { | ||
3534 | unsigned int wcaps = get_wcaps(codec, nid); | ||
3535 | unsigned int type = get_wcaps_type(wcaps); | ||
3536 | if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { | ||
3537 | if (get_connection_index(codec, nid, pin) >= 0) | ||
3538 | return nid; | ||
3539 | } | ||
3540 | } | ||
3541 | return 0; | ||
3542 | } | ||
3543 | |||
3544 | static void cx_auto_parse_digital(struct hda_codec *codec) | ||
3545 | { | ||
3546 | struct conexant_spec *spec = codec->spec; | ||
3547 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3548 | hda_nid_t nid; | ||
3549 | |||
3550 | if (cfg->dig_outs && | ||
3551 | snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) | ||
3552 | spec->multiout.dig_out_nid = nid; | ||
3553 | if (cfg->dig_in_pin) | ||
3554 | spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); | ||
3555 | } | ||
3556 | |||
3557 | #ifdef CONFIG_SND_HDA_INPUT_BEEP | ||
3558 | static void cx_auto_parse_beep(struct hda_codec *codec) | ||
3559 | { | ||
3560 | struct conexant_spec *spec = codec->spec; | ||
3561 | hda_nid_t nid, end_nid; | ||
3562 | |||
3563 | end_nid = codec->start_nid + codec->num_nodes; | ||
3564 | for (nid = codec->start_nid; nid < end_nid; nid++) | ||
3565 | if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { | ||
3566 | set_beep_amp(spec, nid, 0, HDA_OUTPUT); | ||
3567 | break; | ||
3568 | } | ||
3569 | } | ||
3570 | #else | ||
3571 | #define cx_auto_parse_beep(codec) | ||
3572 | #endif | ||
3573 | |||
3574 | static int cx_auto_parse_auto_config(struct hda_codec *codec) | ||
3575 | { | ||
3576 | struct conexant_spec *spec = codec->spec; | ||
3577 | int err; | ||
3578 | |||
3579 | err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); | ||
3580 | if (err < 0) | ||
3581 | return err; | ||
3582 | |||
3583 | cx_auto_parse_output(codec); | ||
3584 | cx_auto_parse_input(codec); | ||
3585 | cx_auto_parse_digital(codec); | ||
3586 | cx_auto_parse_beep(codec); | ||
3587 | return 0; | ||
3588 | } | ||
3589 | |||
3590 | static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins, | ||
3591 | hda_nid_t *pins) | ||
3592 | { | ||
3593 | int i; | ||
3594 | for (i = 0; i < num_pins; i++) { | ||
3595 | if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) | ||
3596 | snd_hda_codec_write(codec, pins[i], 0, | ||
3597 | AC_VERB_SET_EAPD_BTLENABLE, 0x02); | ||
3598 | } | ||
3599 | } | ||
3600 | |||
3601 | static void select_connection(struct hda_codec *codec, hda_nid_t pin, | ||
3602 | hda_nid_t src) | ||
3603 | { | ||
3604 | int idx = get_connection_index(codec, pin, src); | ||
3605 | if (idx >= 0) | ||
3606 | snd_hda_codec_write(codec, pin, 0, | ||
3607 | AC_VERB_SET_CONNECT_SEL, idx); | ||
3608 | } | ||
3609 | |||
3610 | static void cx_auto_init_output(struct hda_codec *codec) | ||
3611 | { | ||
3612 | struct conexant_spec *spec = codec->spec; | ||
3613 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3614 | hda_nid_t nid; | ||
3615 | int i; | ||
3616 | |||
3617 | for (i = 0; i < spec->multiout.num_dacs; i++) | ||
3618 | snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, | ||
3619 | AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); | ||
3620 | |||
3621 | for (i = 0; i < cfg->hp_outs; i++) | ||
3622 | snd_hda_codec_write(codec, cfg->hp_pins[i], 0, | ||
3623 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); | ||
3624 | if (spec->auto_mute) { | ||
3625 | for (i = 0; i < cfg->hp_outs; i++) { | ||
3626 | snd_hda_codec_write(codec, cfg->hp_pins[i], 0, | ||
3627 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
3628 | AC_USRSP_EN | CONEXANT_HP_EVENT); | ||
3629 | } | ||
3630 | cx_auto_hp_automute(codec); | ||
3631 | } else { | ||
3632 | for (i = 0; i < cfg->line_outs; i++) | ||
3633 | snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, | ||
3634 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||
3635 | for (i = 0; i < cfg->speaker_outs; i++) | ||
3636 | snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, | ||
3637 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||
3638 | } | ||
3639 | |||
3640 | for (i = 0; i < spec->dac_info_filled; i++) { | ||
3641 | nid = spec->dac_info[i].dac; | ||
3642 | if (!nid) | ||
3643 | nid = spec->multiout.dac_nids[0]; | ||
3644 | select_connection(codec, spec->dac_info[i].pin, nid); | ||
3645 | } | ||
3646 | |||
3647 | /* turn on EAPD */ | ||
3648 | cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins); | ||
3649 | cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins); | ||
3650 | cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins); | ||
3651 | } | ||
3652 | |||
3653 | static void cx_auto_init_input(struct hda_codec *codec) | ||
3654 | { | ||
3655 | struct conexant_spec *spec = codec->spec; | ||
3656 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3657 | int i; | ||
3658 | |||
3659 | for (i = 0; i < spec->num_adc_nids; i++) | ||
3660 | snd_hda_codec_write(codec, spec->adc_nids[i], 0, | ||
3661 | AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); | ||
3662 | |||
3663 | for (i = 0; i < cfg->num_inputs; i++) { | ||
3664 | unsigned int type; | ||
3665 | if (cfg->inputs[i].type == AUTO_PIN_MIC) | ||
3666 | type = PIN_VREF80; | ||
3667 | else | ||
3668 | type = PIN_IN; | ||
3669 | snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, | ||
3670 | AC_VERB_SET_PIN_WIDGET_CONTROL, type); | ||
3671 | } | ||
3672 | |||
3673 | if (spec->auto_mic) { | ||
3674 | int ext_idx = spec->auto_mic_ext; | ||
3675 | snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0, | ||
3676 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
3677 | AC_USRSP_EN | CONEXANT_MIC_EVENT); | ||
3678 | cx_auto_automic(codec); | ||
3679 | } else { | ||
3680 | for (i = 0; i < spec->num_adc_nids; i++) { | ||
3681 | snd_hda_codec_write(codec, spec->adc_nids[i], 0, | ||
3682 | AC_VERB_SET_CONNECT_SEL, | ||
3683 | spec->private_imux.items[0].index); | ||
3684 | } | ||
3685 | } | ||
3686 | } | ||
3687 | |||
3688 | static void cx_auto_init_digital(struct hda_codec *codec) | ||
3689 | { | ||
3690 | struct conexant_spec *spec = codec->spec; | ||
3691 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3692 | |||
3693 | if (spec->multiout.dig_out_nid) | ||
3694 | snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, | ||
3695 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||
3696 | if (spec->dig_in_nid) | ||
3697 | snd_hda_codec_write(codec, cfg->dig_in_pin, 0, | ||
3698 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); | ||
3699 | } | ||
3700 | |||
3701 | static int cx_auto_init(struct hda_codec *codec) | ||
3702 | { | ||
3703 | /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ | ||
3704 | cx_auto_init_output(codec); | ||
3705 | cx_auto_init_input(codec); | ||
3706 | cx_auto_init_digital(codec); | ||
3707 | return 0; | ||
3708 | } | ||
3709 | |||
3710 | static int cx_auto_add_volume(struct hda_codec *codec, const char *basename, | ||
3711 | const char *dir, int cidx, | ||
3712 | hda_nid_t nid, int hda_dir) | ||
3713 | { | ||
3714 | static char name[32]; | ||
3715 | static struct snd_kcontrol_new knew[] = { | ||
3716 | HDA_CODEC_VOLUME(name, 0, 0, 0), | ||
3717 | HDA_CODEC_MUTE(name, 0, 0, 0), | ||
3718 | }; | ||
3719 | static char *sfx[2] = { "Volume", "Switch" }; | ||
3720 | int i, err; | ||
3721 | |||
3722 | for (i = 0; i < 2; i++) { | ||
3723 | struct snd_kcontrol *kctl; | ||
3724 | knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir); | ||
3725 | knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; | ||
3726 | knew[i].index = cidx; | ||
3727 | snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); | ||
3728 | kctl = snd_ctl_new1(&knew[i], codec); | ||
3729 | if (!kctl) | ||
3730 | return -ENOMEM; | ||
3731 | err = snd_hda_ctl_add(codec, nid, kctl); | ||
3732 | if (err < 0) | ||
3733 | return err; | ||
3734 | if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE)) | ||
3735 | break; | ||
3736 | } | ||
3737 | return 0; | ||
3738 | } | ||
3739 | |||
3740 | #define cx_auto_add_pb_volume(codec, nid, str, idx) \ | ||
3741 | cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) | ||
3742 | |||
3743 | static int cx_auto_build_output_controls(struct hda_codec *codec) | ||
3744 | { | ||
3745 | struct conexant_spec *spec = codec->spec; | ||
3746 | int i, err; | ||
3747 | int num_line = 0, num_hp = 0, num_spk = 0; | ||
3748 | static const char *texts[3] = { "Front", "Surround", "CLFE" }; | ||
3749 | |||
3750 | if (spec->dac_info_filled == 1) | ||
3751 | return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac, | ||
3752 | "Master", 0); | ||
3753 | for (i = 0; i < spec->dac_info_filled; i++) { | ||
3754 | const char *label; | ||
3755 | int idx, type; | ||
3756 | if (!spec->dac_info[i].dac) | ||
3757 | continue; | ||
3758 | type = spec->dac_info[i].type; | ||
3759 | if (type == AUTO_PIN_LINE_OUT) | ||
3760 | type = spec->autocfg.line_out_type; | ||
3761 | switch (type) { | ||
3762 | case AUTO_PIN_LINE_OUT: | ||
3763 | default: | ||
3764 | label = texts[num_line++]; | ||
3765 | idx = 0; | ||
3766 | break; | ||
3767 | case AUTO_PIN_HP_OUT: | ||
3768 | label = "Headphone"; | ||
3769 | idx = num_hp++; | ||
3770 | break; | ||
3771 | case AUTO_PIN_SPEAKER_OUT: | ||
3772 | label = "Speaker"; | ||
3773 | idx = num_spk++; | ||
3774 | break; | ||
3775 | } | ||
3776 | err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac, | ||
3777 | label, idx); | ||
3778 | if (err < 0) | ||
3779 | return err; | ||
3780 | } | ||
3781 | return 0; | ||
3782 | } | ||
3783 | |||
3784 | static int cx_auto_build_input_controls(struct hda_codec *codec) | ||
3785 | { | ||
3786 | struct conexant_spec *spec = codec->spec; | ||
3787 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3788 | static const char *prev_label; | ||
3789 | int i, err, cidx; | ||
3790 | |||
3791 | err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0], | ||
3792 | HDA_INPUT); | ||
3793 | if (err < 0) | ||
3794 | return err; | ||
3795 | prev_label = NULL; | ||
3796 | cidx = 0; | ||
3797 | for (i = 0; i < cfg->num_inputs; i++) { | ||
3798 | hda_nid_t nid = cfg->inputs[i].pin; | ||
3799 | const char *label; | ||
3800 | if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) | ||
3801 | continue; | ||
3802 | label = hda_get_autocfg_input_label(codec, cfg, i); | ||
3803 | if (label == prev_label) | ||
3804 | cidx++; | ||
3805 | else | ||
3806 | cidx = 0; | ||
3807 | prev_label = label; | ||
3808 | err = cx_auto_add_volume(codec, label, " Capture", cidx, | ||
3809 | nid, HDA_INPUT); | ||
3810 | if (err < 0) | ||
3811 | return err; | ||
3812 | } | ||
3813 | return 0; | ||
3814 | } | ||
3815 | |||
3816 | static int cx_auto_build_controls(struct hda_codec *codec) | ||
3817 | { | ||
3818 | int err; | ||
3819 | |||
3820 | err = cx_auto_build_output_controls(codec); | ||
3821 | if (err < 0) | ||
3822 | return err; | ||
3823 | err = cx_auto_build_input_controls(codec); | ||
3824 | if (err < 0) | ||
3825 | return err; | ||
3826 | return conexant_build_controls(codec); | ||
3827 | } | ||
3828 | |||
3829 | static struct hda_codec_ops cx_auto_patch_ops = { | ||
3830 | .build_controls = cx_auto_build_controls, | ||
3831 | .build_pcms = conexant_build_pcms, | ||
3832 | .init = cx_auto_init, | ||
3833 | .free = conexant_free, | ||
3834 | .unsol_event = cx_auto_unsol_event, | ||
3835 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
3836 | .suspend = conexant_suspend, | ||
3837 | #endif | ||
3838 | .reboot_notify = snd_hda_shutup_pins, | ||
3839 | }; | ||
3840 | |||
3841 | static int patch_conexant_auto(struct hda_codec *codec) | ||
3842 | { | ||
3843 | struct conexant_spec *spec; | ||
3844 | int err; | ||
3845 | |||
3846 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | ||
3847 | if (!spec) | ||
3848 | return -ENOMEM; | ||
3849 | codec->spec = spec; | ||
3850 | spec->adc_nids = cx_auto_adc_nids; | ||
3851 | spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids); | ||
3852 | spec->capsrc_nids = spec->adc_nids; | ||
3853 | err = cx_auto_parse_auto_config(codec); | ||
3854 | if (err < 0) { | ||
3855 | kfree(codec->spec); | ||
3856 | codec->spec = NULL; | ||
3857 | return err; | ||
3858 | } | ||
3859 | codec->patch_ops = cx_auto_patch_ops; | ||
3860 | if (spec->beep_amp) | ||
3861 | snd_hda_attach_beep_device(codec, spec->beep_amp); | ||
3862 | return 0; | ||
3863 | } | ||
3864 | |||
3865 | /* | ||
3257 | */ | 3866 | */ |
3258 | 3867 | ||
3259 | static struct hda_codec_preset snd_hda_preset_conexant[] = { | 3868 | static struct hda_codec_preset snd_hda_preset_conexant[] = { |
@@ -3271,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { | |||
3271 | .patch = patch_cxt5066 }, | 3880 | .patch = patch_cxt5066 }, |
3272 | { .id = 0x14f15069, .name = "CX20585", | 3881 | { .id = 0x14f15069, .name = "CX20585", |
3273 | .patch = patch_cxt5066 }, | 3882 | .patch = patch_cxt5066 }, |
3883 | { .id = 0x14f15097, .name = "CX20631", | ||
3884 | .patch = patch_conexant_auto }, | ||
3885 | { .id = 0x14f15098, .name = "CX20632", | ||
3886 | .patch = patch_conexant_auto }, | ||
3887 | { .id = 0x14f150a1, .name = "CX20641", | ||
3888 | .patch = patch_conexant_auto }, | ||
3889 | { .id = 0x14f150a2, .name = "CX20642", | ||
3890 | .patch = patch_conexant_auto }, | ||
3891 | { .id = 0x14f150ab, .name = "CX20651", | ||
3892 | .patch = patch_conexant_auto }, | ||
3893 | { .id = 0x14f150ac, .name = "CX20652", | ||
3894 | .patch = patch_conexant_auto }, | ||
3895 | { .id = 0x14f150b8, .name = "CX20664", | ||
3896 | .patch = patch_conexant_auto }, | ||
3897 | { .id = 0x14f150b9, .name = "CX20665", | ||
3898 | .patch = patch_conexant_auto }, | ||
3274 | {} /* terminator */ | 3899 | {} /* terminator */ |
3275 | }; | 3900 | }; |
3276 | 3901 | ||
@@ -3281,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066"); | |||
3281 | MODULE_ALIAS("snd-hda-codec-id:14f15067"); | 3906 | MODULE_ALIAS("snd-hda-codec-id:14f15067"); |
3282 | MODULE_ALIAS("snd-hda-codec-id:14f15068"); | 3907 | MODULE_ALIAS("snd-hda-codec-id:14f15068"); |
3283 | MODULE_ALIAS("snd-hda-codec-id:14f15069"); | 3908 | MODULE_ALIAS("snd-hda-codec-id:14f15069"); |
3909 | MODULE_ALIAS("snd-hda-codec-id:14f15097"); | ||
3910 | MODULE_ALIAS("snd-hda-codec-id:14f15098"); | ||
3911 | MODULE_ALIAS("snd-hda-codec-id:14f150a1"); | ||
3912 | MODULE_ALIAS("snd-hda-codec-id:14f150a2"); | ||
3913 | MODULE_ALIAS("snd-hda-codec-id:14f150ab"); | ||
3914 | MODULE_ALIAS("snd-hda-codec-id:14f150ac"); | ||
3915 | MODULE_ALIAS("snd-hda-codec-id:14f150b8"); | ||
3916 | MODULE_ALIAS("snd-hda-codec-id:14f150b9"); | ||
3284 | 3917 | ||
3285 | MODULE_LICENSE("GPL"); | 3918 | MODULE_LICENSE("GPL"); |
3286 | MODULE_DESCRIPTION("Conexant HD-audio codec"); | 3919 | MODULE_DESCRIPTION("Conexant HD-audio codec"); |