📄 patch_cmedia.c
字号:
memset(assigned, 0, sizeof(assigned)); /* check the pins we found */ for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */ if (nid >= 0x0b && nid <= 0x0e) { spec->dac_nids[i] = (nid - 0x0b) + 0x03; assigned[nid - 0x0b] = 1; } } /* left pin can be connect to any audio widget */ for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; if (nid <= 0x0e) continue; /* search for an empty channel */ for (j = 0; j < cfg->line_outs; j++) { if (! assigned[j]) { spec->dac_nids[i] = j + 0x03; assigned[j] = 1; break; } } } spec->num_dacs = cfg->line_outs; return 0;}/* create multi_init table, which is used for multichannel initialization */static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg){ struct cmi_spec *spec = codec->spec; hda_nid_t nid; int i, j, k, len; /* clear the table, only one c-media dac assumed here */ memset(spec->multi_init, 0, sizeof(spec->multi_init)); for (j = 0, i = 0; i < cfg->line_outs; i++) { hda_nid_t conn[4]; nid = cfg->line_out_pins[i]; /* set as output */ spec->multi_init[j].nid = nid; spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL; spec->multi_init[j].param = PIN_OUT; j++; if (nid > 0x0e) { /* set connection */ spec->multi_init[j].nid = nid; spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; spec->multi_init[j].param = 0; /* find the index in connect list */ len = snd_hda_get_connections(codec, nid, conn, 4); for (k = 0; k < len; k++) if (conn[k] == spec->dac_nids[i]) { spec->multi_init[j].param = k; break; } j++; } } return 0;}static int cmi9880_init(struct hda_codec *codec){ struct cmi_spec *spec = codec->spec; if (spec->board_config == CMI_ALLOUT) snd_hda_sequence_write(codec, cmi9880_allout_init); else snd_hda_sequence_write(codec, cmi9880_basic_init); if (spec->board_config == CMI_AUTO) snd_hda_sequence_write(codec, spec->multi_init); return 0;}#ifdef CONFIG_PM/* * resume */static int cmi9880_resume(struct hda_codec *codec){ struct cmi_spec *spec = codec->spec; cmi9880_init(codec); snd_hda_resume_ctls(codec, cmi9880_basic_mixer); if (spec->surr_switch) snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer); if (spec->multiout.dig_out_nid) snd_hda_resume_spdif_out(codec); if (spec->dig_in_nid) snd_hda_resume_spdif_in(codec); return 0;}#endif/* * Analog playback callbacks */static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);}static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);}static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);}/* * Digital out */static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_open(codec, &spec->multiout);}static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_close(codec, &spec->multiout);}/* * Analog capture */static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], stream_tag, 0, format); return 0;}static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, snd_pcm_substream_t *substream){ struct cmi_spec *spec = codec->spec; snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); return 0;}/* */static struct hda_pcm_stream cmi9880_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, .nid = 0x03, /* NID to query formats and rates */ .ops = { .open = cmi9880_playback_pcm_open, .prepare = cmi9880_playback_pcm_prepare, .cleanup = cmi9880_playback_pcm_cleanup },};static struct hda_pcm_stream cmi9880_pcm_analog_capture = { .substreams = 2, .channels_min = 2, .channels_max = 2, .nid = 0x08, /* NID to query formats and rates */ .ops = { .prepare = cmi9880_capture_pcm_prepare, .cleanup = cmi9880_capture_pcm_cleanup },};static struct hda_pcm_stream cmi9880_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in cmi9880_build_pcms */ .ops = { .open = cmi9880_dig_playback_pcm_open, .close = cmi9880_dig_playback_pcm_close },};static struct hda_pcm_stream cmi9880_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in cmi9880_build_pcms */};static int cmi9880_build_pcms(struct hda_codec *codec){ struct cmi_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; codec->num_pcms = 1; codec->pcm_info = info; info->name = "CMI9880"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture; if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; info++; info->name = "CMI9880 Digital"; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; } if (spec->dig_in_nid) { info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } } return 0;}static void cmi9880_free(struct hda_codec *codec){ kfree(codec->spec);}/* */static struct hda_board_config cmi9880_cfg_tbl[] = { { .modelname = "minimal", .config = CMI_MINIMAL }, { .modelname = "min_fp", .config = CMI_MIN_FP }, { .modelname = "full", .config = CMI_FULL }, { .modelname = "full_dig", .config = CMI_FULL_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x813d, .config = CMI_FULL_DIG }, /* ASUS P5AD2 */ { .modelname = "allout", .config = CMI_ALLOUT }, { .modelname = "auto", .config = CMI_AUTO }, {} /* terminator */};static struct hda_codec_ops cmi9880_patch_ops = { .build_controls = cmi9880_build_controls, .build_pcms = cmi9880_build_pcms, .init = cmi9880_init, .free = cmi9880_free,#ifdef CONFIG_PM .resume = cmi9880_resume,#endif};static int patch_cmi9880(struct hda_codec *codec){ struct cmi_spec *spec; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; codec->spec = spec; spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl); if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); spec->board_config = CMI_AUTO; /* try everything */ } /* copy default DAC NIDs */ memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids)); spec->num_dacs = 4; switch (spec->board_config) { case CMI_MINIMAL: case CMI_MIN_FP: spec->surr_switch = 1; if (spec->board_config == CMI_MINIMAL) spec->num_ch_modes = 2; else { spec->front_panel = 1; spec->num_ch_modes = 3; } spec->channel_modes = cmi9880_channel_modes; spec->multiout.max_channels = cmi9880_channel_modes[0].channels; spec->input_mux = &cmi9880_basic_mux; break; case CMI_FULL: case CMI_FULL_DIG: spec->front_panel = 1; spec->multiout.max_channels = 8; spec->input_mux = &cmi9880_basic_mux; if (spec->board_config == CMI_FULL_DIG) { spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; spec->dig_in_nid = CMI_DIG_IN_NID; } break; case CMI_ALLOUT: spec->front_panel = 1; spec->multiout.max_channels = 8; spec->no_line_in = 1; spec->input_mux = &cmi9880_no_line_mux; spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; break; case CMI_AUTO: { unsigned int port_e, port_f, port_g, port_h; unsigned int port_spdifi, port_spdifo; struct auto_pin_cfg cfg; /* collect pin default configuration */ port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); spec->front_panel = 1; if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); spec->surr_switch = 1; /* no front panel */ if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) { /* no optional rear panel */ spec->board_config = CMI_MINIMAL; spec->front_panel = 0; spec->num_ch_modes = 2; } else { spec->board_config = CMI_MIN_FP; spec->num_ch_modes = 3; } spec->channel_modes = cmi9880_channel_modes; spec->input_mux = &cmi9880_basic_mux; spec->multiout.max_channels = cmi9880_channel_modes[0].channels; } else { spec->input_mux = &cmi9880_basic_mux; port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) spec->dig_in_nid = CMI_DIG_IN_NID; spec->multiout.max_channels = 8; } snd_hda_parse_pin_def_config(codec, &cfg); if (cfg.line_outs) { spec->multiout.max_channels = cfg.line_outs * 2; cmi9880_fill_multi_dac_nids(codec, &cfg); cmi9880_fill_multi_init(codec, &cfg); } else snd_printd("patch_cmedia: cannot detect association in defcfg\n"); break; } } spec->multiout.num_dacs = spec->num_dacs; spec->multiout.dac_nids = spec->dac_nids; spec->adc_nids = cmi9880_adc_nids; codec->patch_ops = cmi9880_patch_ops; return 0;}/* * patch entries */struct hda_codec_preset snd_hda_preset_cmedia[] = { { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 }, { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 }, {} /* terminator */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -