diff options
Diffstat (limited to 'sound/hda')
-rw-r--r-- | sound/hda/hda_bus_type.c | 2 | ||||
-rw-r--r-- | sound/hda/hdac_device.c | 156 |
2 files changed, 113 insertions, 45 deletions
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index 4cd94178df..cce2c30511 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -76,7 +76,7 @@ static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env) return 0; } -struct bus_type snd_hda_bus_type = { +const struct bus_type snd_hda_bus_type = { .name = "hdaudio", .match = hda_bus_match, .uevent = hda_uevent, diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index bbf7bcdb44..7f7b67fe1b 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -13,6 +13,7 @@ #include <sound/hdaudio.h> #include <sound/hda_regmap.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); @@ -725,32 +726,77 @@ static const struct hda_rate_tbl rate_bits[] = { { 0 } /* terminator */ }; +static snd_pcm_format_t snd_hdac_format_normalize(snd_pcm_format_t format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S20_LE: + case SNDRV_PCM_FORMAT_S24_LE: + return SNDRV_PCM_FORMAT_S32_LE; + + case SNDRV_PCM_FORMAT_U20_LE: + case SNDRV_PCM_FORMAT_U24_LE: + return SNDRV_PCM_FORMAT_U32_LE; + + case SNDRV_PCM_FORMAT_S20_BE: + case SNDRV_PCM_FORMAT_S24_BE: + return SNDRV_PCM_FORMAT_S32_BE; + + case SNDRV_PCM_FORMAT_U20_BE: + case SNDRV_PCM_FORMAT_U24_BE: + return SNDRV_PCM_FORMAT_U32_BE; + + default: + return format; + } +} + /** - * 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) + * snd_hdac_stream_format_bits - obtain bits per sample value. + * @format: the PCM format. + * @subformat: the PCM subformat. + * @maxbits: the maximum bits per sample. * - * Calculate the format bitset from the given rate, channels and th PCM format. + * Return: The number of bits per sample. + */ +unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat, + unsigned int maxbits) +{ + struct snd_pcm_hw_params params; + unsigned int bits; + + memset(¶ms, 0, sizeof(params)); + + params_set_format(¶ms, snd_hdac_format_normalize(format)); + snd_mask_set(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT), + (__force unsigned int)subformat); + + bits = snd_pcm_hw_params_bits(¶ms); + if (maxbits) + return min(bits, maxbits); + return bits; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits); + +/** + * snd_hdac_stream_format - convert format parameters to SDxFMT value. + * @channels: the number of channels. + * @bits: bits per sample. + * @rate: the sample rate. * - * Return zero if invalid. + * Return: The format bitset or zero if invalid. */ -unsigned int snd_hdac_calc_stream_format(unsigned int rate, - unsigned int channels, - snd_pcm_format_t format, - unsigned int maxbps, - unsigned short spdif_ctls) +unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate) { - int i; unsigned int val = 0; + int i; - for (i = 0; rate_bits[i].hz; i++) + 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; @@ -758,7 +804,7 @@ unsigned int snd_hdac_calc_stream_format(unsigned int rate, return 0; val |= channels - 1; - switch (snd_pcm_format_width(format)) { + switch (bits) { case 8: val |= AC_FMT_BITS_8; break; @@ -766,25 +812,42 @@ unsigned int snd_hdac_calc_stream_format(unsigned int rate, val |= AC_FMT_BITS_16; break; case 20: + val |= AC_FMT_BITS_20; + break; case 24: + val |= AC_FMT_BITS_24; + break; 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; + val |= AC_FMT_BITS_32; break; default: return 0; } - if (spdif_ctls & AC_DIG1_NONAUDIO) + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_format); + +/** + * snd_hdac_spdif_stream_format - convert format parameters to SDxFMT value. + * @channels: the number of channels. + * @bits: bits per sample. + * @rate: the sample rate. + * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant). + * + * Return: The format bitset or zero if invalid. + */ +unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits, + unsigned int rate, unsigned short spdif_ctls) +{ + unsigned int val = snd_hdac_stream_format(channels, bits, rate); + + if (val && spdif_ctls & AC_DIG1_NONAUDIO) val |= AC_FMT_TYPE_NON_PCM; return val; } -EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format); +EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format); static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) { @@ -817,15 +880,17 @@ static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid) * @nid: NID to query * @ratesp: the pointer to store the detected rate bitflags * @formatsp: the pointer to store the detected formats + * @subformatsp: the pointer to store the detected subformats for S32_LE format * @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. + * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp, + * @subformatsp or @bpsp 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) + u32 *ratesp, u64 *formatsp, u32 *subformatsp, + unsigned int *bpsp) { unsigned int i, val, wcaps; @@ -848,9 +913,10 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, *ratesp = rates; } - if (formatsp || bpsp) { - u64 formats = 0; + if (formatsp || subformatsp || bpsp) { unsigned int streams, bps; + u32 subformats = 0; + u64 formats = 0; streams = query_stream_param(codec, nid); if (!streams) @@ -866,24 +932,24 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, formats |= SNDRV_PCM_FMTBIT_S16_LE; bps = 16; } - if (wcaps & AC_WCAP_DIGITAL) { - if (val & AC_SUPPCM_BITS_32) + if (val & AC_SUPPCM_BITS_20) { + formats |= SNDRV_PCM_FMTBIT_S32_LE; + subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_20; + bps = 20; + } + if (val & AC_SUPPCM_BITS_24) { + formats |= SNDRV_PCM_FMTBIT_S32_LE; + subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_24; + bps = 24; + } + if (val & AC_SUPPCM_BITS_32) { + if (wcaps & AC_WCAP_DIGITAL) { formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; - if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) + } else { 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) + subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_MAX; 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 */ @@ -911,6 +977,8 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, } if (formatsp) *formatsp = formats; + if (subformatsp) + *subformatsp = subformats; if (bpsp) *bpsp = bps; } |