📄 hda_codec.c
字号:
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), .info = snd_hda_spdif_out_switch_info, .get = snd_hda_spdif_out_switch_get, .put = snd_hda_spdif_out_switch_put, }, { } /* end */};/** * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls * @codec: the HDA codec * @nid: audio out widget NID * * Creates controls related with the SPDIF output. * Called from each patch supporting the SPDIF out. * * Returns 0 if successful, or a negative error code. */int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid){ int err; snd_kcontrol_t *kctl; snd_kcontrol_new_t *dig_mix; for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); kctl->private_value = nid; if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0) return err; } codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); return 0;}/* * SPDIF input */#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_infostatic int snd_hda_spdif_in_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = codec->spdif_in_enable; return 0;}static int snd_hda_spdif_in_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned int val = !!ucontrol->value.integer.value[0]; int change; down(&codec->spdif_mutex); change = codec->spdif_in_enable != val; if (change || codec->in_resume) { codec->spdif_in_enable = val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val); } up(&codec->spdif_mutex); return change;}static int snd_hda_spdif_in_status_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned short val; unsigned int sbits; val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); sbits = convert_to_spdif_status(val); ucontrol->value.iec958.status[0] = sbits; ucontrol->value.iec958.status[1] = sbits >> 8; ucontrol->value.iec958.status[2] = sbits >> 16; ucontrol->value.iec958.status[3] = sbits >> 24; return 0;}static snd_kcontrol_new_t dig_in_ctls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), .info = snd_hda_spdif_in_switch_info, .get = snd_hda_spdif_in_switch_get, .put = snd_hda_spdif_in_switch_put, }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_in_status_get, }, { } /* end */};/** * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls * @codec: the HDA codec * @nid: audio in widget NID * * Creates controls related with the SPDIF input. * Called from each patch supporting the SPDIF in. * * Returns 0 if successful, or a negative error code. */int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid){ int err; snd_kcontrol_t *kctl; snd_kcontrol_new_t *dig_mix; for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); kctl->private_value = nid; if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0) return err; } codec->spdif_in_enable = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) & 1; return 0;}/** * snd_hda_build_controls - build mixer controls * @bus: the BUS * * Creates mixer controls for each codec included in the bus. * * Returns 0 if successful, otherwise a negative error code. */int snd_hda_build_controls(struct hda_bus *bus){ struct list_head *p; /* build controls */ list_for_each(p, &bus->codec_list) { struct hda_codec *codec = list_entry(p, struct hda_codec, list); int err; if (! codec->patch_ops.build_controls) continue; err = codec->patch_ops.build_controls(codec); if (err < 0) return err; } /* initialize */ list_for_each(p, &bus->codec_list) { struct hda_codec *codec = list_entry(p, struct hda_codec, list); int err; if (! codec->patch_ops.init) continue; err = codec->patch_ops.init(codec); if (err < 0) return err; } return 0;}/* * stream formats */struct hda_rate_tbl { unsigned int hz; unsigned int alsa_bits; unsigned int hda_fmt;};static struct hda_rate_tbl rate_bits[] = { /* rate in Hz, ALSA rate bitmask, HDA format value */ /* autodetected value used in snd_hda_query_supported_pcm */ { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */ { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */ { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */ { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */ { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */ { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */ { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */ { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */ { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */ { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */ { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */ /* not autodetected value */ { 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */ { 0 } /* terminator */};/** * snd_hda_calc_stream_format - calculate format bitset * @rate: the sample rate * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps * * Calculate the format bitset from the given rate, channels and th PCM format. * * Return zero if invalid. */unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps){ int i; unsigned int val = 0; for (i = 0; rate_bits[i].hz; i++) if (rate_bits[i].hz == rate) { val = rate_bits[i].hda_fmt; break; } if (! rate_bits[i].hz) { snd_printdd("invalid rate %d\n", rate); return 0; } if (channels == 0 || channels > 8) { snd_printdd("invalid channels %d\n", channels); return 0; } val |= channels - 1; switch (snd_pcm_format_width(format)) { case 8: val |= 0x00; break; case 16: val |= 0x10; break; case 20: case 24: case 32: if (maxbps >= 32) val |= 0x40; else if (maxbps >= 24) val |= 0x30; else val |= 0x20; break; default: snd_printdd("invalid format width %d\n", snd_pcm_format_width(format)); return 0; } return val;}/** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec * @nid: NID to query * @ratesp: the pointer to store the detected rate bitflags * @formatsp: the pointer to store the detected formats * @bpsp: the pointer to store the detected format widths * * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp * or @bsps argument is ignored. * * Returns 0 if successful, otherwise a negative error code. */int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp){ int i; unsigned int val, streams; val = 0; if (nid != codec->afg && snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) { val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (val == -1) return -EIO; } if (! val) val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); if (ratesp) { u32 rates = 0; for (i = 0; rate_bits[i].hz; i++) { if (val & (1 << i)) rates |= rate_bits[i].alsa_bits; } *ratesp = rates; } if (formatsp || bpsp) { u64 formats = 0; unsigned int bps; unsigned int wcaps; wcaps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (streams == -1) return -EIO; if (! streams) { streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); if (streams == -1) return -EIO; } bps = 0; if (streams & AC_SUPFMT_PCM) { if (val & AC_SUPPCM_BITS_8) { formats |= SNDRV_PCM_FMTBIT_U8; bps = 8; } if (val & AC_SUPPCM_BITS_16) { formats |= SNDRV_PCM_FMTBIT_S16_LE; bps = 16; } if (wcaps & AC_WCAP_DIGITAL) { if (val & AC_SUPPCM_BITS_32) formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) formats |= SNDRV_PCM_FMTBIT_S32_LE; if (val & AC_SUPPCM_BITS_24) bps = 24; else if (val & AC_SUPPCM_BITS_20) bps = 20; } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|AC_SUPPCM_BITS_32)) { formats |= SNDRV_PCM_FMTBIT_S32_LE; if (val & AC_SUPPCM_BITS_32) bps = 32; else if (val & AC_SUPPCM_BITS_20) bps = 20; else if (val & AC_SUPPCM_BITS_24) bps = 24; } } else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */ formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; bps = 32; } else if (streams == AC_SUPFMT_AC3) { /* should be exclusive */ /* temporary hack: we have still no proper support * for the direct AC3 stream... */ formats |= SNDRV_PCM_FMTBIT_U8; bps = 8; } if (formatsp) *formatsp = formats; if (bpsp) *bpsp = bps; } return 0;}/** * snd_hda_is_supported_format - check whether the given node supports the format val * * Returns 1 if supported, 0 if not. */int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, unsigned int format){ int i; unsigned int val = 0, rate, stream; if (nid != codec->afg && snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) { val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (val == -1) return 0; } if (! val) { val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); if (val == -1) return 0; } rate = format & 0xff00; for (i = 0; rate_bits[i].hz; i++) if (rate_bits[i].hda_fmt == rate) { if (val & (1 << i)) break; return 0; } if (! rate_bits[i].hz) return 0; stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (stream == -1) return 0; if (! stream && nid != codec->afg) stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); if (! stream || stream == -1) return 0; if (stream & AC_SUPFMT_PCM) { switch (format & 0xf0) { case 0x00: if (! (val & AC_SUPPCM_BITS_8)) return 0; break; 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, snd_pcm_substream_t *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, snd_pcm_substream_t *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, snd_pcm_substream_t *substream){ snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); return 0;}static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info){ if (info->nid) { /* query support PCM information from the given NID */ if (! 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. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -