From: Takashi Iwai Date: Wed, 15 Sep 2010 08:07:08 +0000 (+0200) Subject: ALSA: hda - Preliminary support for new Conexant audio codecs X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=f2e5731dfd3ba08b023d0626d36ccf78f54ab5e7;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git ALSA: hda - Preliminary support for new Conexant audio codecs This patch adds the preliminary support for new Conexant audio codecs with 14f1:5097, 14f1:5098, 14f1:50a1, 14f1:50a2, 14f1:50ab, 14f1:50ac, 14f1:50b8 and 14f1:50b9. Unlike other Conexant parsers, this is designed to be mostly automatic, parsing from BIOS pin configurations. Signed-off-by: Takashi Iwai --- diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 972e7c453b3d..7eee0ff65ac9 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -57,6 +57,12 @@ struct conexant_jack { }; +struct pin_dac_pair { + hda_nid_t pin; + hda_nid_t dac; + int type; +}; + struct conexant_spec { struct snd_kcontrol_new *mixers[5]; @@ -77,6 +83,7 @@ struct conexant_spec { unsigned int cur_eapd; unsigned int hp_present; unsigned int auto_mic; + int auto_mic_ext; /* autocfg.inputs[] index for ext mic */ unsigned int need_dac_fix; /* capture */ @@ -110,9 +117,12 @@ struct conexant_spec { struct auto_pin_cfg autocfg; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + struct pin_dac_pair dac_info[8]; + int dac_info_filled; - unsigned int dell_automute; unsigned int port_d_mode; + unsigned int auto_mute:1; /* used in auto-parser */ + unsigned int dell_automute:1; unsigned int dell_vostro:1; unsigned int ideapad:1; unsigned int thinkpad:1; @@ -3253,6 +3263,610 @@ static int patch_cxt5066(struct hda_codec *codec) return 0; } +/* + * Automatic parser for CX20641 & co + */ + +static hda_nid_t cx_auto_adc_nids[] = { 0x14 }; + +/* get the connection index of @nid in the widget @mux */ +static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + return -1; +} + +/* get an unassigned DAC from the given list. + * Return the nid if found and reduce the DAC list, or return zero if + * not found + */ +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t *dacs, int *num_dacs) +{ + int i, nums = *num_dacs; + hda_nid_t ret = 0; + + for (i = 0; i < nums; i++) { + if (get_connection_index(codec, pin, dacs[i]) >= 0) { + ret = dacs[i]; + break; + } + } + if (!ret) + return 0; + if (--nums > 0) + memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); + *num_dacs = nums; + return ret; +} + +#define MAX_AUTO_DACS 5 + +/* fill analog DAC list from the widget tree */ +static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) +{ + hda_nid_t nid, end_nid; + int nums = 0; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { + dacs[nums++] = nid; + if (nums >= MAX_AUTO_DACS) + break; + } + } + return nums; +} + +/* fill pin_dac_pair list from the pin and dac list */ +static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, + int num_pins, hda_nid_t *dacs, int *rest, + struct pin_dac_pair *filled, int type) +{ + int i, nums; + + nums = 0; + for (i = 0; i < num_pins; i++) { + filled[nums].pin = pins[i]; + filled[nums].type = type; + filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); + nums++; + } + return nums; +} + +/* parse analog output paths */ +static void cx_auto_parse_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t dacs[MAX_AUTO_DACS]; + int i, j, nums, rest; + + rest = fill_cx_auto_dacs(codec, dacs); + /* parse all analog output pins */ + nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, + dacs, &rest, spec->dac_info, + AUTO_PIN_LINE_OUT); + nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, + dacs, &rest, spec->dac_info + nums, + AUTO_PIN_HP_OUT); + nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, + dacs, &rest, spec->dac_info + nums, + AUTO_PIN_SPEAKER_OUT); + spec->dac_info_filled = nums; + /* fill multiout struct */ + for (i = 0; i < nums; i++) { + hda_nid_t dac = spec->dac_info[i].dac; + if (!dac) + continue; + switch (spec->dac_info[i].type) { + case AUTO_PIN_LINE_OUT: + spec->private_dac_nids[spec->multiout.num_dacs] = dac; + spec->multiout.num_dacs++; + break; + case AUTO_PIN_HP_OUT: + case AUTO_PIN_SPEAKER_OUT: + if (!spec->multiout.hp_nid) { + spec->multiout.hp_nid = dac; + break; + } + for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) + if (!spec->multiout.extra_out_nid[j]) { + spec->multiout.extra_out_nid[j] = dac; + break; + } + break; + } + } + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.max_channels = nums * 2; + + if (cfg->hp_outs > 0) + spec->auto_mute = 1; + spec->vmaster_nid = spec->private_dac_nids[0]; +} + +/* auto-mute/unmute speaker and line outs according to headphone jack */ +static void cx_auto_hp_automute(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, present; + + if (!spec->auto_mute) + return; + present = 0; + for (i = 0; i < cfg->hp_outs; i++) { + if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) { + present = 1; + break; + } + } + for (i = 0; i < cfg->line_outs; i++) { + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); + } + for (i = 0; i < cfg->speaker_outs; i++) { + snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); + } +} + +/* automatic switch internal and external mic */ +static void cx_auto_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux = &spec->private_imux; + int ext_idx = spec->auto_mic_ext; + + if (!spec->auto_mic) + return; + if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) { + snd_hda_codec_write(codec, spec->adc_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[ext_idx].index); + } else { + snd_hda_codec_write(codec, spec->adc_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[!ext_idx].index); + } +} + +static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) +{ + int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cx_auto_hp_automute(codec); + conexant_report_jack(codec, nid); + break; + case CONEXANT_MIC_EVENT: + cx_auto_automic(codec); + conexant_report_jack(codec, nid); + break; + } +} + +static int is_int_mic_conn(unsigned int def_conf) +{ + unsigned int loc = get_defcfg_location(def_conf); + return get_defcfg_connect(def_conf) == AC_JACK_PORT_FIXED || + (loc & 0x30) == AC_JACK_LOC_INTERNAL; +} + +/* return true if it's an internal-mic pin */ +static int is_int_mic(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && + is_int_mic_conn(def_conf); +} + +/* return true if it's an external-mic pin */ +static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && + !is_int_mic_conn(def_conf); +} + +/* check whether the pin config is suitable for auto-mic switching; + * auto-mic is enabled only when one int-mic and one-ext mic exist + */ +static void cx_auto_check_auto_mic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (is_ext_mic(codec, cfg->inputs[0].pin) && + is_int_mic(codec, cfg->inputs[1].pin)) { + spec->auto_mic = 1; + spec->auto_mic_ext = 1; + return; + } + if (is_int_mic(codec, cfg->inputs[1].pin) && + is_ext_mic(codec, cfg->inputs[0].pin)) { + spec->auto_mic = 1; + spec->auto_mic_ext = 0; + return; + } +} + +static void cx_auto_parse_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux; + int i; + + imux = &spec->private_imux; + for (i = 0; i < cfg->num_inputs; i++) { + int idx = get_connection_index(codec, spec->adc_nids[0], + cfg->inputs[i].pin); + if (idx >= 0) { + const char *label; + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, idx, NULL); + } + } + if (imux->num_items == 2 && cfg->num_inputs == 2) + cx_auto_check_auto_mic(codec); + if (imux->num_items > 1 && !spec->auto_mic) + spec->input_mux = imux; +} + +/* get digital-input audio widget corresponding to the given pin */ +static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { + if (get_connection_index(codec, nid, pin) >= 0) + return nid; + } + } + return 0; +} + +static void cx_auto_parse_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (cfg->dig_outs && + snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) + spec->multiout.dig_out_nid = nid; + if (cfg->dig_in_pin) + spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); +} + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static void cx_auto_parse_beep(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); + break; + } +} +#else +#define cx_auto_parse_beep(codec) +#endif + +static int cx_auto_parse_auto_config(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + cx_auto_parse_output(codec); + cx_auto_parse_input(codec); + cx_auto_parse_digital(codec); + cx_auto_parse_beep(codec); + return 0; +} + +static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins, + hda_nid_t *pins) +{ + int i; + for (i = 0; i < num_pins; i++) { + if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } +} + +static void select_connection(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t src) +{ + int idx = get_connection_index(codec, pin, src); + if (idx >= 0) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, idx); +} + +static void cx_auto_init_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) + snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + for (i = 0; i < cfg->hp_outs; i++) + snd_hda_codec_write(codec, cfg->hp_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (spec->auto_mute) { + for (i = 0; i < cfg->hp_outs; i++) { + snd_hda_codec_write(codec, cfg->hp_pins[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | CONEXANT_HP_EVENT); + } + cx_auto_hp_automute(codec); + } else { + for (i = 0; i < cfg->line_outs; i++) + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + for (i = 0; i < cfg->speaker_outs; i++) + snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + } + + for (i = 0; i < spec->dac_info_filled; i++) { + nid = spec->dac_info[i].dac; + if (!nid) + nid = spec->multiout.dac_nids[0]; + select_connection(codec, spec->dac_info[i].pin, nid); + } + + /* turn on EAPD */ + cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins); + cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins); + cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins); +} + +static void cx_auto_init_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < spec->num_adc_nids; i++) + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); + + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int type; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + type = PIN_VREF80; + else + type = PIN_IN; + snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, type); + } + + if (spec->auto_mic) { + int ext_idx = spec->auto_mic_ext; + snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | CONEXANT_MIC_EVENT); + cx_auto_automic(codec); + } else { + for (i = 0; i < spec->num_adc_nids; i++) { + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + spec->private_imux.items[0].index); + } + } +} + +static void cx_auto_init_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (spec->multiout.dig_out_nid) + snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (spec->dig_in_nid) + snd_hda_codec_write(codec, cfg->dig_in_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); +} + +static int cx_auto_init(struct hda_codec *codec) +{ + /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ + cx_auto_init_output(codec); + cx_auto_init_input(codec); + cx_auto_init_digital(codec); + return 0; +} + +static int cx_auto_add_volume(struct hda_codec *codec, const char *basename, + const char *dir, int cidx, + hda_nid_t nid, int hda_dir) +{ + static char name[32]; + static struct snd_kcontrol_new knew[] = { + HDA_CODEC_VOLUME(name, 0, 0, 0), + HDA_CODEC_MUTE(name, 0, 0, 0), + }; + static char *sfx[2] = { "Volume", "Switch" }; + int i, err; + + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir); + knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; + knew[i].index = cidx; + snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); + kctl = snd_ctl_new1(&knew[i], codec); + if (!kctl) + return -ENOMEM; + err = snd_hda_ctl_add(codec, nid, kctl); + if (err < 0) + return err; + if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE)) + break; + } + return 0; +} + +#define cx_auto_add_pb_volume(codec, nid, str, idx) \ + cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) + +static int cx_auto_build_output_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int i, err; + int num_line = 0, num_hp = 0, num_spk = 0; + static const char *texts[3] = { "Front", "Surround", "CLFE" }; + + if (spec->dac_info_filled == 1) + return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac, + "Master", 0); + for (i = 0; i < spec->dac_info_filled; i++) { + const char *label; + int idx, type; + if (!spec->dac_info[i].dac) + continue; + type = spec->dac_info[i].type; + if (type == AUTO_PIN_LINE_OUT) + type = spec->autocfg.line_out_type; + switch (type) { + case AUTO_PIN_LINE_OUT: + default: + label = texts[num_line++]; + idx = 0; + break; + case AUTO_PIN_HP_OUT: + label = "Headphone"; + idx = num_hp++; + break; + case AUTO_PIN_SPEAKER_OUT: + label = "Speaker"; + idx = num_spk++; + break; + } + err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac, + label, idx); + if (err < 0) + return err; + } + return 0; +} + +static int cx_auto_build_input_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + static const char *prev_label; + int i, err, cidx; + + err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0], + HDA_INPUT); + if (err < 0) + return err; + prev_label = NULL; + cidx = 0; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + const char *label; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + if (label == prev_label) + cidx++; + else + cidx = 0; + prev_label = label; + err = cx_auto_add_volume(codec, label, " Capture", cidx, + nid, HDA_INPUT); + if (err < 0) + return err; + } + return 0; +} + +static int cx_auto_build_controls(struct hda_codec *codec) +{ + int err; + + err = cx_auto_build_output_controls(codec); + if (err < 0) + return err; + err = cx_auto_build_input_controls(codec); + if (err < 0) + return err; + return conexant_build_controls(codec); +} + +static struct hda_codec_ops cx_auto_patch_ops = { + .build_controls = cx_auto_build_controls, + .build_pcms = conexant_build_pcms, + .init = cx_auto_init, + .free = conexant_free, + .unsol_event = cx_auto_unsol_event, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .suspend = conexant_suspend, +#endif + .reboot_notify = snd_hda_shutup_pins, +}; + +static int patch_conexant_auto(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + spec->adc_nids = cx_auto_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids); + spec->capsrc_nids = spec->adc_nids; + err = cx_auto_parse_auto_config(codec); + if (err < 0) { + kfree(codec->spec); + codec->spec = NULL; + return err; + } + codec->patch_ops = cx_auto_patch_ops; + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; +} + /* */ @@ -3271,6 +3885,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5066 }, { .id = 0x14f15069, .name = "CX20585", .patch = patch_cxt5066 }, + { .id = 0x14f15097, .name = "CX20631", + .patch = patch_conexant_auto }, + { .id = 0x14f15098, .name = "CX20632", + .patch = patch_conexant_auto }, + { .id = 0x14f150a1, .name = "CX20641", + .patch = patch_conexant_auto }, + { .id = 0x14f150a2, .name = "CX20642", + .patch = patch_conexant_auto }, + { .id = 0x14f150ab, .name = "CX20651", + .patch = patch_conexant_auto }, + { .id = 0x14f150ac, .name = "CX20652", + .patch = patch_conexant_auto }, + { .id = 0x14f150b8, .name = "CX20664", + .patch = patch_conexant_auto }, + { .id = 0x14f150b9, .name = "CX20665", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -3281,6 +3911,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_ALIAS("snd-hda-codec-id:14f15068"); MODULE_ALIAS("snd-hda-codec-id:14f15069"); +MODULE_ALIAS("snd-hda-codec-id:14f15097"); +MODULE_ALIAS("snd-hda-codec-id:14f15098"); +MODULE_ALIAS("snd-hda-codec-id:14f150a1"); +MODULE_ALIAS("snd-hda-codec-id:14f150a2"); +MODULE_ALIAS("snd-hda-codec-id:14f150ab"); +MODULE_ALIAS("snd-hda-codec-id:14f150ac"); +MODULE_ALIAS("snd-hda-codec-id:14f150b8"); +MODULE_ALIAS("snd-hda-codec-id:14f150b9"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec");