📄 hda_codec.c
字号:
*//* * open the digital out in the exclusive mode */int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout){ mutex_lock(&codec->spdif_mutex); if (mout->dig_out_used) { mutex_unlock(&codec->spdif_mutex); return -EBUSY; /* already being used */ } mout->dig_out_used = HDA_DIG_EXCLUSIVE; mutex_unlock(&codec->spdif_mutex); return 0;}/* * release the digital out */int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout){ mutex_lock(&codec->spdif_mutex); mout->dig_out_used = 0; mutex_unlock(&codec->spdif_mutex); return 0;}/* * set up more restrictions for analog out */int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, struct snd_pcm_substream *substream){ substream->runtime->hw.channels_max = mout->max_channels; return snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);}/* * set up the i/o for analog out * when the digital out is available, copy the front out to digital out, too. */int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream){ hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; int i; mutex_lock(&codec->spdif_mutex); if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (chs == 2 && snd_hda_is_supported_format(codec, mout->dig_out_nid, format) && ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) { mout->dig_out_used = HDA_DIG_ANALOG_DUP; /* setup digital receiver */ snd_hda_codec_setup_stream(codec, mout->dig_out_nid, stream_tag, 0, format); } else { mout->dig_out_used = 0; snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); } } mutex_unlock(&codec->spdif_mutex); /* front */ snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); if (mout->hp_nid) /* headphone out will just decode front left/right (stereo) */ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); /* extra outputs copied from front */ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) if (mout->extra_out_nid[i]) snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], stream_tag, 0, format); /* surrounds */ for (i = 1; i < mout->num_dacs; i++) { if (chs >= (i + 1) * 2) /* independent out */ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2, format); else /* copy front */ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); } return 0;}/* * clean up the setting for analog out */int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout){ hda_nid_t *nids = mout->dac_nids; int i; for (i = 0; i < mout->num_dacs; i++) snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); if (mout->hp_nid) snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) if (mout->extra_out_nid[i]) snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], 0, 0, 0); mutex_lock(&codec->spdif_mutex); if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); mout->dig_out_used = 0; } mutex_unlock(&codec->spdif_mutex); return 0;}/* * Helper for automatic ping configuration */static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list){ for (; *list; list++) if (*list == nid) return 1; return 0;}/* * Parse all pin widgets and store the useful pin nids to cfg * * The number of line-outs or any primary output is stored in line_outs, * and the corresponding output pins are assigned to line_out_pins[], * in the order of front, rear, CLFE, side, ... * * If more extra outputs (speaker and headphone) are found, the pins are * assisnged to hp_pin and speaker_pins[], respectively. If no line-out jack * is detected, one of speaker of HP pins is assigned as the primary * output, i.e. to line_out_pins[0]. So, line_outs is always positive * if any analog output exists. * * The analog input pins are assigned to input_pins array. * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, * respectively. */int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg, hda_nid_t *ignore_nids){ hda_nid_t nid, nid_start; int i, j, nodes; short seq, assoc_line_out, sequences[ARRAY_SIZE(cfg->line_out_pins)]; memset(cfg, 0, sizeof(*cfg)); memset(sequences, 0, sizeof(sequences)); assoc_line_out = 0; nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); for (nid = nid_start; nid < nodes + nid_start; nid++) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; unsigned int def_conf; short assoc, loc; /* read all default configuration for pin complex */ if (wid_type != AC_WID_PIN) continue; /* ignore the given nids (e.g. pc-beep returns error) */ if (ignore_nids && is_in_nid_list(nid, ignore_nids)) continue; def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) continue; loc = get_defcfg_location(def_conf); switch (get_defcfg_device(def_conf)) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); assoc = get_defcfg_association(def_conf); if (! assoc) continue; if (! assoc_line_out) assoc_line_out = assoc; else if (assoc_line_out != assoc) continue; if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) continue; cfg->line_out_pins[cfg->line_outs] = nid; sequences[cfg->line_outs] = seq; cfg->line_outs++; break; case AC_JACK_SPEAKER: if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) continue; cfg->speaker_pins[cfg->speaker_outs] = nid; cfg->speaker_outs++; break; case AC_JACK_HP_OUT: cfg->hp_pin = nid; break; case AC_JACK_MIC_IN: if (loc == AC_JACK_LOC_FRONT) cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid; else cfg->input_pins[AUTO_PIN_MIC] = nid; break; case AC_JACK_LINE_IN: if (loc == AC_JACK_LOC_FRONT) cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid; else cfg->input_pins[AUTO_PIN_LINE] = nid; break; case AC_JACK_CD: cfg->input_pins[AUTO_PIN_CD] = nid; break; case AC_JACK_AUX: cfg->input_pins[AUTO_PIN_AUX] = nid; break; case AC_JACK_SPDIF_OUT: cfg->dig_out_pin = nid; break; case AC_JACK_SPDIF_IN: cfg->dig_in_pin = nid; break; } } /* sort by sequence */ for (i = 0; i < cfg->line_outs; i++) for (j = i + 1; j < cfg->line_outs; j++) if (sequences[i] > sequences[j]) { seq = sequences[i]; sequences[i] = sequences[j]; sequences[j] = seq; nid = cfg->line_out_pins[i]; cfg->line_out_pins[i] = cfg->line_out_pins[j]; cfg->line_out_pins[j] = nid; } /* Reorder the surround channels * ALSA sequence is front/surr/clfe/side * HDA sequence is: * 4-ch: front/surr => OK as it is * 6-ch: front/clfe/surr * 8-ch: front/clfe/side/surr */ switch (cfg->line_outs) { case 3: nid = cfg->line_out_pins[1]; cfg->line_out_pins[1] = cfg->line_out_pins[2]; cfg->line_out_pins[2] = nid; break; case 4: nid = cfg->line_out_pins[1]; cfg->line_out_pins[1] = cfg->line_out_pins[3]; cfg->line_out_pins[3] = cfg->line_out_pins[2]; cfg->line_out_pins[2] = nid; break; } /* * debug prints of the parsed results */ snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], cfg->line_out_pins[2], cfg->line_out_pins[3], cfg->line_out_pins[4]); snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", cfg->speaker_outs, cfg->speaker_pins[0], cfg->speaker_pins[1], cfg->speaker_pins[2], cfg->speaker_pins[3], cfg->speaker_pins[4]); snd_printd(" hp=0x%x, dig_out=0x%x, din_in=0x%x\n", cfg->hp_pin, cfg->dig_out_pin, cfg->dig_in_pin); snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," " cd=0x%x, aux=0x%x\n", cfg->input_pins[AUTO_PIN_MIC], cfg->input_pins[AUTO_PIN_FRONT_MIC], cfg->input_pins[AUTO_PIN_LINE], cfg->input_pins[AUTO_PIN_FRONT_LINE], cfg->input_pins[AUTO_PIN_CD], cfg->input_pins[AUTO_PIN_AUX]); /* * FIX-UP: if no line-outs are detected, try to use speaker or HP pin * as a primary output */ if (! cfg->line_outs) { if (cfg->speaker_outs) { cfg->line_outs = cfg->speaker_outs; memcpy(cfg->line_out_pins, cfg->speaker_pins, sizeof(cfg->speaker_pins)); cfg->speaker_outs = 0; memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); } else if (cfg->hp_pin) { cfg->line_outs = 1; cfg->line_out_pins[0] = cfg->hp_pin; cfg->hp_pin = 0; } } return 0;}/* labels for input pins */const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"};#ifdef CONFIG_PM/* * power management *//** * snd_hda_suspend - suspend the codecs * @bus: the HDA bus * @state: suspsend state * * Returns 0 if successful. */int snd_hda_suspend(struct hda_bus *bus, pm_message_t state){ struct list_head *p; /* FIXME: should handle power widget capabilities */ list_for_each(p, &bus->codec_list) { struct hda_codec *codec = list_entry(p, struct hda_codec, list); if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec, state); hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D3); } return 0;}/** * snd_hda_resume - resume the codecs * @bus: the HDA bus * @state: resume state * * Returns 0 if successful. */int snd_hda_resume(struct hda_bus *bus){ struct list_head *p; list_for_each(p, &bus->codec_list) { struct hda_codec *codec = list_entry(p, struct hda_codec, list); hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); if (codec->patch_ops.resume) codec->patch_ops.resume(codec); } return 0;}/** * snd_hda_resume_ctls - resume controls in the new control list * @codec: the HDA codec * @knew: the array of struct snd_kcontrol_new * * This function resumes the mixer controls in the struct snd_kcontrol_new array, * originally for snd_hda_add_new_ctls(). * The array must be terminated with an empty entry as terminator. */int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew){ struct snd_ctl_elem_value *val; val = kmalloc(sizeof(*val), GFP_KERNEL); if (! val) return -ENOMEM; codec->in_resume = 1; for (; knew->name; knew++) { int i, count; count = knew->count ? knew->count : 1; for (i = 0; i < count; i++) { memset(val, 0, sizeof(*val)); val->id.iface = knew->iface; val->id.device = knew->device; val->id.subdevice = knew->subdevice; strcpy(val->id.name, knew->name); val->id.index = knew->index ? knew->index : i; /* Assume that get callback reads only from cache, * not accessing to the real hardware */ if (snd_ctl_elem_read(codec->bus->card, val) < 0) continue; snd_ctl_elem_write(codec->bus->card, NULL, val); } } codec->in_resume = 0; kfree(val); return 0;}/** * snd_hda_resume_spdif_out - resume the digital out * @codec: the HDA codec */int snd_hda_resume_spdif_out(struct hda_codec *codec){ return snd_hda_resume_ctls(codec, dig_mixes);}/** * snd_hda_resume_spdif_in - resume the digital in * @codec: the HDA codec */int snd_hda_resume_spdif_in(struct hda_codec *codec){ return snd_hda_resume_ctls(codec, dig_in_ctls);}#endif/* * symbols exported for controller modules */EXPORT_SYMBOL(snd_hda_codec_read);EXPORT_SYMBOL(snd_hda_codec_write);EXPORT_SYMBOL(snd_hda_sequence_write);EXPORT_SYMBOL(snd_hda_get_sub_nodes);EXPORT_SYMBOL(snd_hda_queue_unsol_event);EXPORT_SYMBOL(snd_hda_bus_new);EXPORT_SYMBOL(snd_hda_codec_new);EXPORT_SYMBOL(snd_hda_codec_setup_stream);EXPORT_SYMBOL(snd_hda_calc_stream_format);EXPORT_SYMBOL(snd_hda_build_pcms);EXPORT_SYMBOL(snd_hda_build_controls);#ifdef CONFIG_PMEXPORT_SYMBOL(snd_hda_suspend);EXPORT_SYMBOL(snd_hda_resume);#endif/* * INIT part */static int __init alsa_hda_init(void){ return 0;}static void __exit alsa_hda_exit(void){}module_init(alsa_hda_init)module_exit(alsa_hda_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -