Loading include/sound/hdaudio.h +9 −0 Original line number Original line Diff line number Diff line Loading @@ -123,6 +123,15 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *start_id); hda_nid_t *start_id); unsigned int snd_hdac_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps, unsigned short spdif_ctls); int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, unsigned int format); /** /** * snd_hdac_read_parm - read a codec parameter * snd_hdac_read_parm - read a codec parameter Loading sound/hda/hdac_device.c +300 −0 Original line number Original line Diff line number Diff line Loading @@ -10,6 +10,7 @@ #include <linux/pm_runtime.h> #include <linux/pm_runtime.h> #include <sound/hdaudio.h> #include <sound/hdaudio.h> #include <sound/hda_regmap.h> #include <sound/hda_regmap.h> #include <sound/pcm.h> #include "local.h" #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); static void setup_fg_nodes(struct hdac_device *codec); Loading Loading @@ -597,3 +598,302 @@ static int get_codec_vendor_name(struct hdac_device *codec) codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); return codec->vendor_name ? 0 : -ENOMEM; return codec->vendor_name ? 0 : -ENOMEM; } } /* * stream formats */ struct hda_rate_tbl { unsigned int hz; unsigned int alsa_bits; unsigned int hda_fmt; }; /* rate = base * mult / div */ #define HDA_RATE(base, mult, div) \ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ (((div) - 1) << AC_FMT_DIV_SHIFT)) 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, HDA_RATE(48, 1, 6) }, { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, #define AC_PAR_PCM_RATE_BITS 11 /* up to bits 10, 384kHZ isn't supported properly */ /* not autodetected value */ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, { 0 } /* terminator */ }; /** * snd_hdac_calc_stream_format - calculate the format bitset * @rate: the sample rate * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) * * Calculate the format bitset from the given rate, channels and th PCM format. * * Return zero if invalid. */ unsigned int snd_hdac_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps, unsigned short spdif_ctls) { 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) return 0; if (channels == 0 || channels > 8) return 0; val |= channels - 1; switch (snd_pcm_format_width(format)) { case 8: val |= AC_FMT_BITS_8; break; case 16: val |= AC_FMT_BITS_16; break; case 20: case 24: case 32: if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) val |= AC_FMT_BITS_32; else if (maxbps >= 24) val |= AC_FMT_BITS_24; else val |= AC_FMT_BITS_20; break; default: return 0; } if (spdif_ctls & AC_DIG1_NONAUDIO) val |= AC_FMT_TYPE_NON_PCM; return val; } EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format); static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) { unsigned int val = 0; if (nid != codec->afg && (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); if (!val || val == -1) val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; } static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid) { unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; } /** * snd_hdac_query_supported_pcm - query the supported PCM rates and formats * @codec: the codec object * @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_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp) { unsigned int i, val, wcaps; wcaps = get_wcaps(codec, nid); val = query_pcm_param(codec, nid); if (ratesp) { u32 rates = 0; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { if (val & (1 << i)) rates |= rate_bits[i].alsa_bits; } if (rates == 0) { dev_err(&codec->dev, "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); return -EIO; } *ratesp = rates; } if (formatsp || bpsp) { u64 formats = 0; unsigned int streams, bps; streams = query_stream_param(codec, nid); if (!streams) 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_24) bps = 24; else if (val & AC_SUPPCM_BITS_20) bps = 20; } } #if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; if (!bps) bps = 32; } #endif 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 (formats == 0) { dev_err(&codec->dev, "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, streams); return -EIO; } if (formatsp) *formatsp = formats; if (bpsp) *bpsp = bps; } return 0; } EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm); /** * snd_hdac_is_supported_format - Check the validity of the format * @codec: the codec object * @nid: NID to check * @format: the HD-audio format value to check * * Check whether the given node supports the format value. * * Returns true if supported, false if not. */ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, unsigned int format) { int i; unsigned int val = 0, rate, stream; val = query_pcm_param(codec, nid); if (!val) return false; rate = format & 0xff00; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) if (rate_bits[i].hda_fmt == rate) { if (val & (1 << i)) break; return false; } if (i >= AC_PAR_PCM_RATE_BITS) return false; stream = query_stream_param(codec, nid); if (!stream) return false; if (stream & AC_SUPFMT_PCM) { switch (format & 0xf0) { case 0x00: if (!(val & AC_SUPPCM_BITS_8)) return false; break; case 0x10: if (!(val & AC_SUPPCM_BITS_16)) return false; break; case 0x20: if (!(val & AC_SUPPCM_BITS_20)) return false; break; case 0x30: if (!(val & AC_SUPPCM_BITS_24)) return false; break; case 0x40: if (!(val & AC_SUPPCM_BITS_32)) return false; break; default: return false; } } else { /* FIXME: check for float32 and AC3? */ } return true; } EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format); sound/pci/hda/hda_codec.c +0 −305 Original line number Original line Diff line number Diff line Loading @@ -3191,311 +3191,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) return 0; return 0; } } /* * stream formats */ struct hda_rate_tbl { unsigned int hz; unsigned int alsa_bits; unsigned int hda_fmt; }; /* rate = base * mult / div */ #define HDA_RATE(base, mult, div) \ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ (((div) - 1) << AC_FMT_DIV_SHIFT)) 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, HDA_RATE(48, 1, 6) }, { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, #define AC_PAR_PCM_RATE_BITS 11 /* up to bits 10, 384kHZ isn't supported properly */ /* not autodetected value */ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, { 0 } /* terminator */ }; /** * snd_hda_calc_stream_format - calculate format bitset * @codec: HD-audio codec * @rate: the sample rate * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) * * Calculate the format bitset from the given rate, channels and th PCM format. * * Return zero if invalid. */ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps, unsigned short spdif_ctls) { 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) { codec_dbg(codec, "invalid rate %d\n", rate); return 0; } if (channels == 0 || channels > 8) { codec_dbg(codec, "invalid channels %d\n", channels); return 0; } val |= channels - 1; switch (snd_pcm_format_width(format)) { case 8: val |= AC_FMT_BITS_8; break; case 16: val |= AC_FMT_BITS_16; break; case 20: case 24: case 32: if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) val |= AC_FMT_BITS_32; else if (maxbps >= 24) val |= AC_FMT_BITS_24; else val |= AC_FMT_BITS_20; break; default: codec_dbg(codec, "invalid format width %d\n", snd_pcm_format_width(format)); return 0; } if (spdif_ctls & AC_DIG1_NONAUDIO) val |= AC_FMT_TYPE_NON_PCM; return val; } EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int val = 0; if (nid != codec->core.afg && (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (!val || val == -1) val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; } static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; } /** * 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) { unsigned int i, val, wcaps; wcaps = get_wcaps(codec, nid); val = query_pcm_param(codec, nid); if (ratesp) { u32 rates = 0; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { if (val & (1 << i)) rates |= rate_bits[i].alsa_bits; } if (rates == 0) { codec_err(codec, "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); return -EIO; } *ratesp = rates; } if (formatsp || bpsp) { u64 formats = 0; unsigned int streams, bps; streams = query_stream_param(codec, nid); if (!streams) 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_24) bps = 24; else if (val & AC_SUPPCM_BITS_20) bps = 20; } } #if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; if (!bps) bps = 32; } #endif 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 (formats == 0) { codec_err(codec, "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, streams); return -EIO; } if (formatsp) *formatsp = formats; if (bpsp) *bpsp = bps; } return 0; } EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm); /** * snd_hda_is_supported_format - Check the validity of the format * @codec: HD-audio codec * @nid: NID to check * @format: the HD-audio format value to check * * Check whether the given node supports the format value. * * 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; val = query_pcm_param(codec, nid); if (!val) return 0; rate = format & 0xff00; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) if (rate_bits[i].hda_fmt == rate) { if (val & (1 << i)) break; return 0; } if (i >= AC_PAR_PCM_RATE_BITS) return 0; stream = query_stream_param(codec, nid); if (!stream) 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; } EXPORT_SYMBOL_GPL(snd_hda_is_supported_format); /* /* * PCM stuff * PCM stuff */ */ Loading sound/pci/hda/hda_codec.h +5 −10 Original line number Original line Diff line number Diff line Loading @@ -365,8 +365,6 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); hda_nid_t nid, int recursive); int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, u8 *dev_list, int max_devices); u8 *dev_list, int max_devices); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); struct hda_verb { struct hda_verb { hda_nid_t nid; hda_nid_t nid; Loading Loading @@ -458,14 +456,11 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, int do_now); int do_now); #define snd_hda_codec_cleanup_stream(codec, nid) \ #define snd_hda_codec_cleanup_stream(codec, nid) \ __snd_hda_codec_cleanup_stream(codec, nid, 0) __snd_hda_codec_cleanup_stream(codec, nid, 0) unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, unsigned int rate, #define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \ unsigned int channels, snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp) unsigned int format, #define snd_hda_is_supported_format(codec, nid, fmt) \ unsigned int maxbps, snd_hdac_is_supported_format(&(codec)->core, nid, fmt) unsigned short spdif_ctls); int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, unsigned int format); extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; Loading sound/pci/hda/hda_controller.c +1 −2 Original line number Original line Diff line number Diff line Loading @@ -167,8 +167,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) } } snd_hdac_stream_reset(azx_stream(azx_dev)); snd_hdac_stream_reset(azx_stream(azx_dev)); format_val = snd_hda_calc_stream_format(apcm->codec, format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->rate, runtime->channels, runtime->channels, runtime->format, runtime->format, hinfo->maxbps, hinfo->maxbps, Loading Loading
include/sound/hdaudio.h +9 −0 Original line number Original line Diff line number Diff line Loading @@ -123,6 +123,15 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *start_id); hda_nid_t *start_id); unsigned int snd_hdac_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps, unsigned short spdif_ctls); int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, unsigned int format); /** /** * snd_hdac_read_parm - read a codec parameter * snd_hdac_read_parm - read a codec parameter Loading
sound/hda/hdac_device.c +300 −0 Original line number Original line Diff line number Diff line Loading @@ -10,6 +10,7 @@ #include <linux/pm_runtime.h> #include <linux/pm_runtime.h> #include <sound/hdaudio.h> #include <sound/hdaudio.h> #include <sound/hda_regmap.h> #include <sound/hda_regmap.h> #include <sound/pcm.h> #include "local.h" #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); static void setup_fg_nodes(struct hdac_device *codec); Loading Loading @@ -597,3 +598,302 @@ static int get_codec_vendor_name(struct hdac_device *codec) codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); return codec->vendor_name ? 0 : -ENOMEM; return codec->vendor_name ? 0 : -ENOMEM; } } /* * stream formats */ struct hda_rate_tbl { unsigned int hz; unsigned int alsa_bits; unsigned int hda_fmt; }; /* rate = base * mult / div */ #define HDA_RATE(base, mult, div) \ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ (((div) - 1) << AC_FMT_DIV_SHIFT)) 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, HDA_RATE(48, 1, 6) }, { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, #define AC_PAR_PCM_RATE_BITS 11 /* up to bits 10, 384kHZ isn't supported properly */ /* not autodetected value */ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, { 0 } /* terminator */ }; /** * snd_hdac_calc_stream_format - calculate the format bitset * @rate: the sample rate * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) * * Calculate the format bitset from the given rate, channels and th PCM format. * * Return zero if invalid. */ unsigned int snd_hdac_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps, unsigned short spdif_ctls) { 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) return 0; if (channels == 0 || channels > 8) return 0; val |= channels - 1; switch (snd_pcm_format_width(format)) { case 8: val |= AC_FMT_BITS_8; break; case 16: val |= AC_FMT_BITS_16; break; case 20: case 24: case 32: if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) val |= AC_FMT_BITS_32; else if (maxbps >= 24) val |= AC_FMT_BITS_24; else val |= AC_FMT_BITS_20; break; default: return 0; } if (spdif_ctls & AC_DIG1_NONAUDIO) val |= AC_FMT_TYPE_NON_PCM; return val; } EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format); static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) { unsigned int val = 0; if (nid != codec->afg && (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); if (!val || val == -1) val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; } static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid) { unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; } /** * snd_hdac_query_supported_pcm - query the supported PCM rates and formats * @codec: the codec object * @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_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp) { unsigned int i, val, wcaps; wcaps = get_wcaps(codec, nid); val = query_pcm_param(codec, nid); if (ratesp) { u32 rates = 0; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { if (val & (1 << i)) rates |= rate_bits[i].alsa_bits; } if (rates == 0) { dev_err(&codec->dev, "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); return -EIO; } *ratesp = rates; } if (formatsp || bpsp) { u64 formats = 0; unsigned int streams, bps; streams = query_stream_param(codec, nid); if (!streams) 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_24) bps = 24; else if (val & AC_SUPPCM_BITS_20) bps = 20; } } #if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; if (!bps) bps = 32; } #endif 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 (formats == 0) { dev_err(&codec->dev, "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, streams); return -EIO; } if (formatsp) *formatsp = formats; if (bpsp) *bpsp = bps; } return 0; } EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm); /** * snd_hdac_is_supported_format - Check the validity of the format * @codec: the codec object * @nid: NID to check * @format: the HD-audio format value to check * * Check whether the given node supports the format value. * * Returns true if supported, false if not. */ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, unsigned int format) { int i; unsigned int val = 0, rate, stream; val = query_pcm_param(codec, nid); if (!val) return false; rate = format & 0xff00; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) if (rate_bits[i].hda_fmt == rate) { if (val & (1 << i)) break; return false; } if (i >= AC_PAR_PCM_RATE_BITS) return false; stream = query_stream_param(codec, nid); if (!stream) return false; if (stream & AC_SUPFMT_PCM) { switch (format & 0xf0) { case 0x00: if (!(val & AC_SUPPCM_BITS_8)) return false; break; case 0x10: if (!(val & AC_SUPPCM_BITS_16)) return false; break; case 0x20: if (!(val & AC_SUPPCM_BITS_20)) return false; break; case 0x30: if (!(val & AC_SUPPCM_BITS_24)) return false; break; case 0x40: if (!(val & AC_SUPPCM_BITS_32)) return false; break; default: return false; } } else { /* FIXME: check for float32 and AC3? */ } return true; } EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
sound/pci/hda/hda_codec.c +0 −305 Original line number Original line Diff line number Diff line Loading @@ -3191,311 +3191,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) return 0; return 0; } } /* * stream formats */ struct hda_rate_tbl { unsigned int hz; unsigned int alsa_bits; unsigned int hda_fmt; }; /* rate = base * mult / div */ #define HDA_RATE(base, mult, div) \ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ (((div) - 1) << AC_FMT_DIV_SHIFT)) 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, HDA_RATE(48, 1, 6) }, { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, #define AC_PAR_PCM_RATE_BITS 11 /* up to bits 10, 384kHZ isn't supported properly */ /* not autodetected value */ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, { 0 } /* terminator */ }; /** * snd_hda_calc_stream_format - calculate format bitset * @codec: HD-audio codec * @rate: the sample rate * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) * * Calculate the format bitset from the given rate, channels and th PCM format. * * Return zero if invalid. */ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps, unsigned short spdif_ctls) { 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) { codec_dbg(codec, "invalid rate %d\n", rate); return 0; } if (channels == 0 || channels > 8) { codec_dbg(codec, "invalid channels %d\n", channels); return 0; } val |= channels - 1; switch (snd_pcm_format_width(format)) { case 8: val |= AC_FMT_BITS_8; break; case 16: val |= AC_FMT_BITS_16; break; case 20: case 24: case 32: if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) val |= AC_FMT_BITS_32; else if (maxbps >= 24) val |= AC_FMT_BITS_24; else val |= AC_FMT_BITS_20; break; default: codec_dbg(codec, "invalid format width %d\n", snd_pcm_format_width(format)); return 0; } if (spdif_ctls & AC_DIG1_NONAUDIO) val |= AC_FMT_TYPE_NON_PCM; return val; } EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int val = 0; if (nid != codec->core.afg && (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (!val || val == -1) val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; } static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; } /** * 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) { unsigned int i, val, wcaps; wcaps = get_wcaps(codec, nid); val = query_pcm_param(codec, nid); if (ratesp) { u32 rates = 0; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { if (val & (1 << i)) rates |= rate_bits[i].alsa_bits; } if (rates == 0) { codec_err(codec, "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); return -EIO; } *ratesp = rates; } if (formatsp || bpsp) { u64 formats = 0; unsigned int streams, bps; streams = query_stream_param(codec, nid); if (!streams) 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_24) bps = 24; else if (val & AC_SUPPCM_BITS_20) bps = 20; } } #if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; if (!bps) bps = 32; } #endif 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 (formats == 0) { codec_err(codec, "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", nid, val, (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, streams); return -EIO; } if (formatsp) *formatsp = formats; if (bpsp) *bpsp = bps; } return 0; } EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm); /** * snd_hda_is_supported_format - Check the validity of the format * @codec: HD-audio codec * @nid: NID to check * @format: the HD-audio format value to check * * Check whether the given node supports the format value. * * 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; val = query_pcm_param(codec, nid); if (!val) return 0; rate = format & 0xff00; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) if (rate_bits[i].hda_fmt == rate) { if (val & (1 << i)) break; return 0; } if (i >= AC_PAR_PCM_RATE_BITS) return 0; stream = query_stream_param(codec, nid); if (!stream) 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; } EXPORT_SYMBOL_GPL(snd_hda_is_supported_format); /* /* * PCM stuff * PCM stuff */ */ Loading
sound/pci/hda/hda_codec.h +5 −10 Original line number Original line Diff line number Diff line Loading @@ -365,8 +365,6 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); hda_nid_t nid, int recursive); int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, u8 *dev_list, int max_devices); u8 *dev_list, int max_devices); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); struct hda_verb { struct hda_verb { hda_nid_t nid; hda_nid_t nid; Loading Loading @@ -458,14 +456,11 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, int do_now); int do_now); #define snd_hda_codec_cleanup_stream(codec, nid) \ #define snd_hda_codec_cleanup_stream(codec, nid) \ __snd_hda_codec_cleanup_stream(codec, nid, 0) __snd_hda_codec_cleanup_stream(codec, nid, 0) unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, unsigned int rate, #define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \ unsigned int channels, snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp) unsigned int format, #define snd_hda_is_supported_format(codec, nid, fmt) \ unsigned int maxbps, snd_hdac_is_supported_format(&(codec)->core, nid, fmt) unsigned short spdif_ctls); int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, unsigned int format); extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; Loading
sound/pci/hda/hda_controller.c +1 −2 Original line number Original line Diff line number Diff line Loading @@ -167,8 +167,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) } } snd_hdac_stream_reset(azx_stream(azx_dev)); snd_hdac_stream_reset(azx_stream(azx_dev)); format_val = snd_hda_calc_stream_format(apcm->codec, format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->rate, runtime->channels, runtime->channels, runtime->format, runtime->format, hinfo->maxbps, hinfo->maxbps, Loading