📄 hda_codec.c
字号:
case 0x10: if (!(val & AC_SUPPCM_BITS_16)) return 0; break; case 0x20: if (!(val & AC_SUPPCM_BITS_20)) return 0; break; case 0x30: if (!(val & AC_SUPPCM_BITS_24)) return 0; break; case 0x40: if (!(val & AC_SUPPCM_BITS_32)) return 0; break; default: return 0; } } else { /* FIXME: check for float32 and AC3? */ } return 1;}/* * PCM stuff */static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ return 0;}static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream){ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0;}static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); return 0;}static int __devinit set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info){ /* query support PCM information from the given NID */ if (info->nid && (!info->rates || !info->formats)) { snd_hda_query_supported_pcm(codec, info->nid, info->rates ? NULL : &info->rates, info->formats ? NULL : &info->formats, info->maxbps ? NULL : &info->maxbps); } if (info->ops.open == NULL) info->ops.open = hda_pcm_default_open_close; if (info->ops.close == NULL) info->ops.close = hda_pcm_default_open_close; if (info->ops.prepare == NULL) { snd_assert(info->nid, return -EINVAL); info->ops.prepare = hda_pcm_default_prepare; } if (info->ops.cleanup == NULL) { snd_assert(info->nid, return -EINVAL); info->ops.cleanup = hda_pcm_default_cleanup; } return 0;}/** * snd_hda_build_pcms - build PCM information * @bus: the BUS * * Create PCM information for each codec included in the bus. * * The build_pcms codec patch is requested to set up codec->num_pcms and * codec->pcm_info properly. The array is referred by the top-level driver * to create its PCM instances. * The allocated codec->pcm_info should be released in codec->patch_ops.free * callback. * * At least, substreams, channels_min and channels_max must be filled for * each stream. substreams = 0 indicates that the stream doesn't exist. * When rates and/or formats are zero, the supported values are queried * from the given nid. The nid is used also by the default ops.prepare * and ops.cleanup callbacks. * * 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 __devinit snd_hda_build_pcms(struct hda_bus *bus){ struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, 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 * @num_configs: number of config enums * @models: array of model name strings * @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, int num_configs, const char **models, const struct snd_pci_quirk *tbl){ if (codec->bus->modelname && models) { int i; for (i = 0; i < num_configs; i++) { if (models[i] && !strcmp(codec->bus->modelname, models[i])) { snd_printd(KERN_INFO "hda_codec: model '%s' is " "selected\n", models[i]); return i; } } } if (!codec->bus->pci || !tbl) return -1; tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl); if (!tbl) return -1; if (tbl->value >= 0 && tbl->value < num_configs) {#ifdef CONFIG_SND_DEBUG_DETECT char tmp[10]; const char *model = NULL; if (models) model = models[tbl->value]; if (!model) { sprintf(tmp, "#%d", tbl->value); model = tmp; } snd_printdd(KERN_INFO "hda_codec: model '%s' is selected " "for config %x:%x (%s)\n", model, tbl->subvendor, tbl->subdevice, (tbl->name ? tbl->name : "Unknown device"));#endif return tbl->value; } return -1;}/** * snd_hda_add_new_ctls - create controls from the array * @codec: the HDA codec * @knew: the array of struct snd_kcontrol_new * * 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, struct snd_kcontrol_new *knew){ int err; for (; knew->name; knew++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) { if (!codec->addr) return err; kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; kctl->id.device = codec->addr; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; } } return 0;}#ifdef CONFIG_SND_HDA_POWER_SAVEstatic void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state);static void hda_power_work(struct work_struct *work){ struct hda_codec *codec = container_of(work, struct hda_codec, power_work.work); if (!codec->power_on || codec->power_count) { codec->power_transition = 0; return; } hda_call_codec_suspend(codec); if (codec->bus->ops.pm_notify) codec->bus->ops.pm_notify(codec);}static void hda_keep_power_on(struct hda_codec *codec){ codec->power_count++; codec->power_on = 1;}void snd_hda_power_up(struct hda_codec *codec){ codec->power_count++; if (codec->power_on || codec->power_transition) return; codec->power_on = 1; if (codec->bus->ops.pm_notify) codec->bus->ops.pm_notify(codec); hda_call_codec_resume(codec); cancel_delayed_work(&codec->power_work); codec->power_transition = 0;}void snd_hda_power_down(struct hda_codec *codec){ --codec->power_count; if (!codec->power_on || codec->power_count || codec->power_transition) return; if (power_save) { codec->power_transition = 1; /* avoid reentrance */ schedule_delayed_work(&codec->power_work, msecs_to_jiffies(power_save * 1000)); }}int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid){ struct hda_amp_list *p; int ch, v; if (!check->amplist) return 0; for (p = check->amplist; p->nid; p++) { if (p->nid == nid) break; } if (!p->nid) return 0; /* nothing changed */ for (p = check->amplist; p->nid; p++) { for (ch = 0; ch < 2; ch++) { v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, p->idx); if (!(v & HDA_AMP_MUTE) && v > 0) { if (!check->power_on) { check->power_on = 1; snd_hda_power_up(codec); } return 1; } } } if (check->power_on) { check->power_on = 0; snd_hda_power_down(codec); } return 0;}#endif/* * Channel mode helper */int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, const struct hda_channel_mode *chmode, int num_chmodes){ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = num_chmodes; if (uinfo->value.enumerated.item >= num_chmodes) uinfo->value.enumerated.item = num_chmodes - 1; sprintf(uinfo->value.enumerated.name, "%dch", chmode[uinfo->value.enumerated.item].channels); return 0;}int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, int num_chmodes, int max_channels){ int i; for (i = 0; i < num_chmodes; i++) { if (max_channels == chmode[i].channels) { ucontrol->value.enumerated.item[0] = i; break; } } return 0;}int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, int num_chmodes, int *max_channelsp){ unsigned int mode; mode = ucontrol->value.enumerated.item[0]; snd_assert(mode < num_chmodes, return -EINVAL); if (*max_channelsp == chmode[mode].channels) return 0; /* change the current channel setting */ *max_channelsp = chmode[mode].channels; if (chmode[mode].sequence) snd_hda_sequence_write_cache(codec, chmode[mode].sequence); return 1;}/* * input MUX helper */int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo){ unsigned int index; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = imux->num_items; if (!imux->num_items) return 0; 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, struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, unsigned int *cur_val){ unsigned int idx; if (!imux->num_items) return 0; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; if (*cur_val == idx) return 0; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, imux->items[idx].index); *cur_val = idx; return 1;}/* * Multi-channel / digital-out PCM helper functions *//* setup SPDIF output stream */static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format){ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ if (codec->spdif_ctls & AC_DIG1_ENABLE) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); /* turn on again (if needed) */ if (codec->spdif_ctls & AC_DIG1_ENABLE) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, codec->spdif_ctls & 0xff);}/* * 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 == HDA_DIG_ANALOG_DUP) /* already opened as analog dup; reset it once */ snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); mout->dig_out_used = HDA_DIG_EXCLUSIVE; mutex_unlock(&codec->spdif_mutex); return 0;}int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream){ mutex_lock(&codec->spdif_mutex); setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format); 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -