📄 hda_codec.c
字号:
* The driver needs to call ops.open in its open callback. Similarly, * ops.close is supposed to be called in the close callback. * ops.prepare should be called in the prepare or hw_params callback * with the proper parameters for set up. * ops.cleanup should be called in hw_free for clean up of streams. * * This function returns 0 if successfull, or a negative error code. */int snd_hda_build_pcms(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); unsigned int pcm, s; int err; if (! codec->patch_ops.build_pcms) continue; err = codec->patch_ops.build_pcms(codec); if (err < 0) return err; for (pcm = 0; pcm < codec->num_pcms; pcm++) { for (s = 0; s < 2; s++) { struct hda_pcm_stream *info; info = &codec->pcm_info[pcm].stream[s]; if (! info->substreams) continue; err = set_pcm_default_values(codec, info); if (err < 0) return err; } } } return 0;}/** * snd_hda_check_board_config - compare the current codec with the config table * @codec: the HDA codec * @tbl: configuration table, terminated by null entries * * Compares the modelname or PCI subsystem id of the current codec with the * given configuration table. If a matching entry is found, returns its * config value (supposed to be 0 or positive). * * If no entries are matching, the function returns a negative value. */int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl){ const struct hda_board_config *c; if (codec->bus->modelname) { for (c = tbl; c->modelname || c->pci_subvendor; c++) { if (c->modelname && ! strcmp(codec->bus->modelname, c->modelname)) { snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname); return c->config; } } } if (codec->bus->pci) { u16 subsystem_vendor, subsystem_device; pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device); for (c = tbl; c->modelname || c->pci_subvendor; c++) { if (c->pci_subvendor == subsystem_vendor && (! c->pci_subdevice /* all match */|| (c->pci_subdevice == subsystem_device))) { snd_printdd(KERN_INFO "hda_codec: PCI %x:%x, codec config %d is selected\n", subsystem_vendor, subsystem_device, c->config); return c->config; } } } return -1;}/** * snd_hda_add_new_ctls - create controls from the array * @codec: the HDA codec * @knew: the array of snd_kcontrol_new_t * * This helper function creates and add new controls in the given array. * The array must be terminated with an empty entry as terminator. * * Returns 0 if successful, or a negative error code. */int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew){ int err; for (; knew->name; knew++) { err = snd_ctl_add(codec->bus->card, snd_ctl_new1(knew, codec)); if (err < 0) return err; } return 0;}/* * input MUX helper */int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo){ unsigned int index; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = imux->num_items; index = uinfo->value.enumerated.item; if (index >= imux->num_items) index = imux->num_items - 1; strcpy(uinfo->value.enumerated.name, imux->items[index].label); return 0;}int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, snd_ctl_elem_value_t *ucontrol, hda_nid_t nid, unsigned int *cur_val){ unsigned int idx; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; if (*cur_val == idx && ! codec->in_resume) return 0; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, imux->items[idx].index); *cur_val = idx; return 1;}/* * Multi-channel / digital-out PCM helper functions *//* * open the digital out in the exclusive mode */int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout){ down(&codec->spdif_mutex); if (mout->dig_out_used) { up(&codec->spdif_mutex); return -EBUSY; /* already being used */ } mout->dig_out_used = HDA_DIG_EXCLUSIVE; up(&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){ down(&codec->spdif_mutex); mout->dig_out_used = 0; up(&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, snd_pcm_substream_t *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, snd_pcm_substream_t *substream){ hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; int i; down(&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); } } up(&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); /* 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); down(&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; } up(&codec->spdif_mutex); return 0;}/* * Helper for automatic ping configuration *//* parse all pin widgets and store the useful pin nids to cfg */int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg){ hda_nid_t nid, nid_start; int i, j, nodes; short seq, sequences[4], assoc_line_out; 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 = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); 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; 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: case AC_JACK_SPEAKER: 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_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; } return 0;}#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); } 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); 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 snd_kcontrol_new_t * * This function resumes the mixer controls in the snd_kcontrol_new_t 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, snd_kcontrol_new_t *knew){ snd_ctl_elem_value_t *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 + -