From 33c4d5dd5a73674680a27e9faa0c0c7607b4ccc3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 13:17:39 +0200 Subject: [PATCH 01/17] ALSA: pcm: Introduce MSBITS subformat interface Improve granularity of format selection for S32/U32 formats by adding constants representing 20, 24 and 32 most significant bits. To make it easy for drivers to utilize those constants, introduce snd_pcm_subformat_width() and snd_pcm_hw_params_bps(). While the former is self-explanatory, the latter returns the bit-per-sample value based on format, subformat and msbits characteristics of the provided hw_params. Signed-off-by: Cezary Rojewski --- include/sound/pcm.h | 8 ++++++++ include/sound/pcm_params.h | 2 ++ include/uapi/sound/asound.h | 5 ++++- sound/core/pcm.c | 3 +++ sound/core/pcm_lib.c | 30 ++++++++++++++++++++++++++++++ sound/core/pcm_misc.c | 23 +++++++++++++++++++++++ tools/include/uapi/sound/asound.h | 5 ++++- 7 files changed, 74 insertions(+), 2 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 19f564606ac42e..f78441ff9d8548 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -31,6 +31,7 @@ struct snd_pcm_hardware { unsigned int info; /* SNDRV_PCM_INFO_* */ u64 formats; /* SNDRV_PCM_FMTBIT_* */ + u64 subformats; /* SNDRV_PCM_SUBFMTBIT_* */ unsigned int rates; /* SNDRV_PCM_RATE_* */ unsigned int rate_min; /* min rate */ unsigned int rate_max; /* max rate */ @@ -219,6 +220,12 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_BE #endif +#define _SNDRV_PCM_SUBFMTBIT(fmt) BIT((__force int)SNDRV_PCM_SUBFORMAT_##fmt) +#define SNDRV_PCM_SUBFMTBIT_STD _SNDRV_PCM_SUBFMTBIT(STD) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_20 _SNDRV_PCM_SUBFMTBIT(MSBITS_20) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_24 _SNDRV_PCM_SUBFMTBIT(MSBITS_24) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_32 _SNDRV_PCM_SUBFMTBIT(MSBITS_32) + struct snd_pcm_file { struct snd_pcm_substream *substream; int no_compat_mmap; @@ -1130,6 +1137,7 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format); int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); +int snd_pcm_subformat_width(snd_pcm_subformat_t subformat); void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, const struct snd_pcm_ops *ops); diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index ba184f49f7e18c..df17c7d3e853d7 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -362,6 +362,8 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p) return snd_pcm_format_physical_width(params_format(p)); } +int snd_pcm_hw_params_bps(const struct snd_pcm_hw_params *p); + static inline void params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt) { diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index f9939da4112272..30a43541ed003f 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -267,7 +267,10 @@ typedef int __bitwise snd_pcm_format_t; typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) -#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((__force snd_pcm_subformat_t) 1) +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((__force snd_pcm_subformat_t) 2) +#define SNDRV_PCM_SUBFORMAT_MSBITS_32 ((__force snd_pcm_subformat_t) 3) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_32 #define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 1c0bb3a07bff7f..643afe1fa28b8f 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -265,6 +265,9 @@ static const char * const snd_pcm_access_names[] = { static const char * const snd_pcm_subformat_names[] = { SUBFORMAT(STD), + SUBFORMAT(MSBITS_20), + SUBFORMAT(MSBITS_24), + SUBFORMAT(MSBITS_32), }; static const char * const snd_pcm_tstamp_mode_names[] = { diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9c121a921b04a6..ed7e3dc6b529a4 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1706,6 +1706,36 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, } EXPORT_SYMBOL(snd_pcm_hw_param_last); +/** + * snd_pcm_hw_params_bps - Get the number of bits per the sample. + * @p: hardware parameters + * + * Return: The number of bits per sample based on the format, + * subformat and msbits the specified hw params has. + */ +int snd_pcm_hw_params_bps(const struct snd_pcm_hw_params *p) +{ + snd_pcm_subformat_t subformat = params_subformat(p); + snd_pcm_format_t format = params_format(p); + int width; + + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_BE: + width = snd_pcm_subformat_width(subformat); + if (width) + return width; + if (p->msbits) + return (int)p->msbits; + fallthrough; + default: + return snd_pcm_format_width(format); + } +} +EXPORT_SYMBOL(snd_pcm_hw_params_bps); + static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg) { diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 5588b6a1ee8bd0..127bb36fd2e4c5 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -482,6 +482,29 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int } EXPORT_SYMBOL(snd_pcm_format_set_silence); +/** + * snd_pcm_subformat_width - return the bit-width of the subformat + * @subformat: the subformat to check + * + * Return: The bit-width of the subformat, or a negative error code + * otherwise. + */ +int snd_pcm_subformat_width(snd_pcm_subformat_t subformat) +{ + switch (subformat) { + case SNDRV_PCM_SUBFORMAT_MSBITS_20: + return 20; + case SNDRV_PCM_SUBFORMAT_MSBITS_24: + return 24; + case SNDRV_PCM_SUBFORMAT_MSBITS_32: + return 32; + case SNDRV_PCM_SUBFORMAT_STD: + default: + return 0; + } +} +EXPORT_SYMBOL(snd_pcm_subformat_width); + /** * snd_pcm_hw_limit_rates - determine rate_min/rate_max fields * @hw: the pcm hw instance diff --git a/tools/include/uapi/sound/asound.h b/tools/include/uapi/sound/asound.h index f9939da4112272..30a43541ed003f 100644 --- a/tools/include/uapi/sound/asound.h +++ b/tools/include/uapi/sound/asound.h @@ -267,7 +267,10 @@ typedef int __bitwise snd_pcm_format_t; typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) -#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((__force snd_pcm_subformat_t) 1) +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((__force snd_pcm_subformat_t) 2) +#define SNDRV_PCM_SUBFORMAT_MSBITS_32 ((__force snd_pcm_subformat_t) 3) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_32 #define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ From fcd069e52883977d343300e5d5110175353ad91e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 13:25:17 +0200 Subject: [PATCH 02/17] ALSA: pcm: Honor subformat when configuring substream Substream value is currently hardcoded to SNDRV_PCM_SUBFORMAT_STD. Update the constraint procedure so that subformat selection is not ignored. Signed-off-by: Cezary Rojewski --- sound/core/pcm_native.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 95fc56e403b157..01f2879ff8e2b9 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2614,6 +2614,8 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) int err; unsigned int mask = 0; + /* All PCMs support at least the default STD subformat. */ + hw->subformats |= PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD); if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) @@ -2634,8 +2636,7 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) if (err < 0) return err; - err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, - PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD)); + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, hw->subformats); if (err < 0) return err; From c3e1ccf7665272e70fcdaa0370b010c39acad9c5 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 13:00:14 +0200 Subject: [PATCH 03/17] ALSA: hda: Honor subformat when querying PCMs Update mechanism for querying supported PCMs to allow for granular format selection when container size is 32 bits. Currently always the highest bit depth is selected, regardless of how many actual formats codec in question supports. Signed-off-by: Cezary Rojewski --- include/sound/hda_codec.h | 5 +++-- include/sound/hdaudio.h | 2 +- sound/hda/hdac_device.c | 42 ++++++++++++++++++++---------------- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/patch_hdmi.c | 1 + sound/soc/codecs/hdac_hdmi.c | 3 ++- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5497dc9c396a5a..2ffefcfe06aa2d 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -141,6 +141,7 @@ struct hda_pcm_stream { hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */ u32 rates; /* supported rates */ u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */ + u64 subformats; /* supported subformats (SNDRV_PCM_SUBFMTBIT_) */ unsigned int maxbps; /* supported max. bit per sample */ const struct snd_pcm_chmap_elem *chmap; /* chmap to override */ struct hda_pcm_ops ops; @@ -448,8 +449,8 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, #define snd_hda_codec_cleanup_stream(codec, nid) \ __snd_hda_codec_cleanup_stream(codec, nid, 0) -#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \ - snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp) +#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, subfmtp, bpsp) \ + snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, subfmtp, bpsp) #define snd_hda_is_supported_format(codec, nid, fmt) \ snd_hdac_is_supported_format(&(codec)->core, nid, fmt) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 32c59053b48edc..000fc72aaece9d 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -146,7 +146,7 @@ unsigned int snd_hdac_calc_stream_format(unsigned int rate, 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); + u32 *ratesp, u64 *formatsp, u64 *subformatsp, unsigned int *bpsp); bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, unsigned int format); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index bbf7bcdb449a8b..eb4ece908bca77 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -817,15 +817,16 @@ 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 * @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, u64 *subformatsp, unsigned int *bpsp) { unsigned int i, val, wcaps; @@ -848,8 +849,9 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, *ratesp = rates; } - if (formatsp || bpsp) { + if (formatsp || subformatsp || bpsp) { u64 formats = 0; + u64 subformats = 0; unsigned int streams, bps; streams = query_stream_param(codec, nid); @@ -866,24 +868,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) { + subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_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) 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 +913,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; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 33af707a65ab11..ab160b7ddd2eb4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3163,6 +3163,7 @@ static int set_pcm_default_values(struct hda_codec *codec, err = snd_hda_query_supported_pcm(codec, info->nid, info->rates ? NULL : &info->rates, info->formats ? NULL : &info->formats, + info->subformats ? NULL : &info->subformats, info->maxbps ? NULL : &info->maxbps); if (err < 0) return err; @@ -3757,6 +3758,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, snd_hda_query_supported_pcm(codec, mout->dig_out_nid, &mout->spdif_rates, &mout->spdif_formats, + NULL, &mout->spdif_maxbps); } mutex_lock(&codec->spdif_mutex); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 1cde2a69bdb4ba..687b8b8fd7acb6 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1977,6 +1977,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) err = snd_hda_query_supported_pcm(codec, cvt_nid, &per_cvt->rates, &per_cvt->formats, + NULL, &per_cvt->maxbps); if (err < 0) return err; diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 8b6b7602969488..fb3f8a5654853c 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -670,6 +670,7 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) err = snd_hdac_query_supported_pcm(hdev, cvt->nid, &cvt->params.rates, &cvt->params.formats, + NULL, &cvt->params.maxbps); if (err < 0) dev_err(&hdev->dev, @@ -1577,7 +1578,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev, list_for_each_entry(cvt, &hdmi->cvt_list, head) { ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, - &rates, &formats, &bps); + &rates, &formats, NULL, &bps); if (ret) return ret; From 1f8b4a09a2d63d3227c5a0dd37e0b79946e25033 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 14:55:43 +0200 Subject: [PATCH 04/17] ASoC: pcm: Honor subformat when configuring runtime Subformat options are ignored when setting up hardware parameters and assigning PCM stream capabilities. Account for them to allow for granular format selection. Signed-off-by: Cezary Rojewski --- include/sound/soc.h | 1 + sound/soc/soc-pcm.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index fa2337a3cf4c77..05148d4ed0fd0d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -619,6 +619,7 @@ enum snd_soc_trigger_order { struct snd_soc_pcm_stream { const char *stream_name; u64 formats; /* SNDRV_PCM_FMTBIT_* */ + u64 subformats; /* SNDRV_PCM_SUBFMTBIT_* */ unsigned int rates; /* SNDRV_PCM_RATE_* */ unsigned int rate_min; /* min rate */ unsigned int rate_max; /* max rate */ diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 3aa6b988cb4b49..bceb90f0198d3d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -526,6 +526,7 @@ static void soc_pcm_hw_init(struct snd_pcm_hardware *hw) hw->channels_min = 0; hw->channels_max = UINT_MAX; hw->formats = ULLONG_MAX; + hw->subformats = ULLONG_MAX; } static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, @@ -554,6 +555,12 @@ static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw, hw->formats &= p->formats; } +static void soc_pcm_hw_update_subformat(struct snd_pcm_hardware *hw, + struct snd_soc_pcm_stream *p) +{ + hw->subformats &= p->subformats; +} + /** * snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream * @rtd: ASoC PCM runtime @@ -592,6 +599,7 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, soc_pcm_hw_update_chan(hw, cpu_stream); soc_pcm_hw_update_rate(hw, cpu_stream); soc_pcm_hw_update_format(hw, cpu_stream); + soc_pcm_hw_update_subformat(hw, cpu_stream); } cpu_chan_min = hw->channels_min; cpu_chan_max = hw->channels_max; @@ -613,6 +621,7 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, soc_pcm_hw_update_chan(hw, codec_stream); soc_pcm_hw_update_rate(hw, codec_stream); soc_pcm_hw_update_format(hw, codec_stream); + soc_pcm_hw_update_subformat(hw, codec_stream); } /* Verify both a valid CPU DAI and a valid CODEC DAI were found */ @@ -637,6 +646,7 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) { struct snd_pcm_hardware *hw = &substream->runtime->hw; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + u64 subformats = hw->subformats; u64 formats = hw->formats; /* @@ -648,6 +658,8 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) if (formats) hw->formats &= formats; + if (subformats) + hw->subformats &= subformats; } static int soc_pcm_components_open(struct snd_pcm_substream *substream) @@ -1676,6 +1688,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_dai *dai; int stream = substream->stream; + u64 subformats = hw->subformats; u64 formats = hw->formats; int i; @@ -1683,6 +1696,8 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) if (formats) hw->formats &= formats; + if (subformats) + hw->subformats &= subformats; for_each_rtd_cpu_dais(fe, i, dai) { struct snd_soc_pcm_stream *cpu_stream; @@ -1699,6 +1714,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) soc_pcm_hw_update_rate(hw, cpu_stream); soc_pcm_hw_update_chan(hw, cpu_stream); soc_pcm_hw_update_format(hw, cpu_stream); + soc_pcm_hw_update_subformat(hw, cpu_stream); } } @@ -1736,6 +1752,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); soc_pcm_hw_update_format(hw, codec_stream); + soc_pcm_hw_update_subformat(hw, codec_stream); } } } From f92e880e3a176efc429f4ea485ddfb9cee559034 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:24:54 +0200 Subject: [PATCH 05/17] ALSA: hda: Upgrade stream-format infrastructure Introduce a set of functions that facilite SDxFMT-related calculations in atomic manner: snd_hdac_format_normalize() - format converter. S20_LE, S24_LE and their unsigned and BE friends are invalid from HDAudio perspective but still can be specified as function argument due to compatibility reasons. snd_hdac_stream_format_bps() - obtain just the bits-per-sample value. Does not ignore subformat and msbits parameters. snd_hdac_stream_format() and snd_hdac_spdif_stream_format() - obtain the SDxFMT value given the audio format parameters. The former is stripped away of spdif-related information. Useful for users that do not care about them. Signed-off-by: Cezary Rojewski --- include/sound/hdaudio.h | 5 ++ sound/hda/hdac_device.c | 124 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 000fc72aaece9d..a7c2f02110fa42 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -140,6 +140,11 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *start_id); +unsigned int snd_hdac_stream_format_bps(snd_pcm_format_t format, snd_pcm_subformat_t subformat, + unsigned int maxbps); +unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bps, unsigned int rate); +unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bps, + unsigned int rate, unsigned short spdif_ctls); unsigned int snd_hdac_calc_stream_format(unsigned int rate, unsigned int channels, snd_pcm_format_t format, diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index eb4ece908bca77..13e27d38f2feff 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); @@ -725,6 +726,129 @@ 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_stream_format_bps - obtain bits per sample value. + * @format: the PCM format. + * @subformat: the PCM subformat. + * @maxbps: the maximum bits per sample. + * + * Return: The number of bits per sample. + */ +unsigned int snd_hdac_stream_format_bps(snd_pcm_format_t format, snd_pcm_subformat_t subformat, + unsigned int maxbps) +{ + struct snd_pcm_hw_params params; + unsigned int bps; + + 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); + + bps = snd_pcm_hw_params_bps(¶ms); + if (maxbps) + return min(bps, maxbps); + return bps; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bps); + +/** + * snd_hdac_stream_format - convert format parameters to SDxFMT value. + * @channels: the number of channels. + * @bps: bits per sample. + * @rate: the sample rate. + * + * Return: The format bitset or zero if invalid. + */ +unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bps, unsigned int rate) +{ + unsigned int val = 0; + int 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; + + if (channels == 0 || channels > 8) + return 0; + val |= channels - 1; + + switch (bps) { + case 8: + val |= AC_FMT_BITS_8; + break; + case 16: + val |= AC_FMT_BITS_16; + break; + case 20: + val |= AC_FMT_BITS_20; + break; + case 24: + val |= AC_FMT_BITS_24; + break; + case 32: + val |= AC_FMT_BITS_32; + break; + default: + return 0; + } + + 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. + * @bps: 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 bps, + unsigned int rate, unsigned short spdif_ctls) +{ + unsigned int val = snd_hdac_stream_format(channels, bps, rate); + + if (val && spdif_ctls & AC_DIG1_NONAUDIO) + val |= AC_FMT_TYPE_NON_PCM; + + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format); + /** * snd_hdac_calc_stream_format - calculate the format bitset * @rate: the sample rate From ce49474b8f7cd88a3ee3528e6ba5faae0aebcbba Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:52:42 +0200 Subject: [PATCH 06/17] ALSA: hda: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/pci/hda/hda_controller.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 406779625fb593..0646ef0afd4901 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -151,7 +151,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int format_val, stream_tag; + unsigned int format_val, stream_tag, bps; int err; struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); @@ -165,11 +165,9 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) } snd_hdac_stream_reset(azx_stream(azx_dev)); - format_val = snd_hdac_calc_stream_format(runtime->rate, - runtime->channels, - runtime->format, - hinfo->maxbps, - ctls); + bps = snd_hdac_stream_format_bps(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps); + + format_val = snd_hdac_spdif_stream_format(runtime->channels, bps, runtime->rate, ctls); if (!format_val) { dev_err(chip->card->dev, "invalid format_val, rate=%d, ch=%d, format=%d\n", From 9259b37a2abc272b07221fa89dceede99b5c60ea Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:52:07 +0200 Subject: [PATCH 07/17] ALSA: hda/hdmi: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/pci/hda/patch_hdmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 687b8b8fd7acb6..dff2d72219829e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1655,7 +1655,6 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, #define I915_SILENT_RATE 48000 #define I915_SILENT_CHANNELS 2 -#define I915_SILENT_FORMAT SNDRV_PCM_FORMAT_S16_LE #define I915_SILENT_FORMAT_BITS 16 #define I915_SILENT_FMT_MASK 0xf @@ -1668,8 +1667,8 @@ static void silent_stream_enable_i915(struct hda_codec *codec, per_pin->dev_id, I915_SILENT_RATE); /* trigger silent stream generation in hw */ - format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, - I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); + format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS, + I915_SILENT_RATE); snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); usleep_range(100, 200); From 20c2126bd5f8b66d5d7c50beed8ab834ec5941aa Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:52:17 +0200 Subject: [PATCH 08/17] ALSA: hda/ca0132: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/pci/hda/patch_ca0132.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 748a3c40966e97..aa312441604f8c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3022,8 +3022,7 @@ static int dma_convert_to_hda_format(struct hda_codec *codec, { unsigned int format_val; - format_val = snd_hdac_calc_stream_format(sample_rate, - channels, SNDRV_PCM_FORMAT_S32_LE, 32, 0); + format_val = snd_hdac_stream_format(channels, 32, sample_rate); if (hda_format) *hda_format = (unsigned short)format_val; From 9f1bc042f4aa5a06906229a8d940854cee999701 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 26 Jul 2023 16:52:24 +0200 Subject: [PATCH 09/17] ASoC: codecs: hda: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. While at it, complete PCM stream initialization with subformat options. Signed-off-by: Cezary Rojewski --- sound/soc/codecs/hda-dai.c | 6 ++++-- sound/soc/codecs/hda.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c index 5371ff08626180..20b070c66c6f6a 100644 --- a/sound/soc/codecs/hda-dai.c +++ b/sound/soc/codecs/hda-dai.c @@ -76,13 +76,15 @@ static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd struct hdac_stream *stream; struct hda_codec *codec; unsigned int format; + unsigned int bps; int ret; codec = dev_to_hda_codec(dai->dev); stream = substream->runtime->private_data; stream_info = snd_soc_dai_get_dma_data(dai, substream); - format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - runtime->sample_bits, 0); + + bps = snd_hdac_stream_format_bps(runtime->format, runtime->subformat, stream_info->maxbps); + format = snd_hdac_stream_format(runtime->channels, bps, runtime->rate); ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream); if (ret < 0) { diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index d57b043d6bfefe..d2117e36ddd107 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -52,6 +52,7 @@ static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count, stream->channels_max = pcm->stream[dir].channels_max; stream->rates = pcm->stream[dir].rates; stream->formats = pcm->stream[dir].formats; + stream->subformats = pcm->stream[dir].subformats; stream->sig_bits = pcm->stream[dir].maxbps; capture_dais: @@ -71,6 +72,7 @@ static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count, stream->channels_max = pcm->stream[dir].channels_max; stream->rates = pcm->stream[dir].rates; stream->formats = pcm->stream[dir].formats; + stream->subformats = pcm->stream[dir].subformats; stream->sig_bits = pcm->stream[dir].maxbps; } From 5736c7e11c568783498dd9cd7b7ec042dc7b7a1f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:47:03 +0200 Subject: [PATCH 10/17] ASoC: codecs: hdac_hda: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/soc/codecs/hdac_hda.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index b025b5edc7c2d5..e1d790010f4329 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -215,18 +215,16 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct hdac_hda_priv *hda_pvt; unsigned int format_val; unsigned int maxbps; + unsigned int bps; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) maxbps = dai->driver->playback.sig_bits; else maxbps = dai->driver->capture.sig_bits; + bps = snd_hdac_stream_format_bps(params_format(params), SNDRV_PCM_SUBFORMAT_STD, maxbps); hda_pvt = snd_soc_component_get_drvdata(component); - format_val = snd_hdac_calc_stream_format(params_rate(params), - params_channels(params), - params_format(params), - maxbps, - 0); + format_val = snd_hdac_stream_format(params_channels(params), bps, params_rate(params)); if (!format_val) { dev_err(dai->dev, "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n", From a9bb7c8b2ca35e3af3e6ea7c27e381cda3bf7c0b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:46:41 +0200 Subject: [PATCH 11/17] ASoC: codecs: hdac_hdmi: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/soc/codecs/hdac_hdmi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index fb3f8a5654853c..44837462dcd3a6 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -468,13 +468,14 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; + unsigned int bps; int format; dai_map = &hdmi->dai_map[dai->id]; - format = snd_hdac_calc_stream_format(params_rate(hparams), - params_channels(hparams), params_format(hparams), - dai->driver->playback.sig_bits, 0); + bps = snd_hdac_stream_format_bps(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD, + dai->driver->playback.sig_bits); + format = snd_hdac_stream_format(params_channels(hparams), bps, params_rate(hparams)); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (!pcm) From 015312bb49fdf1a36b594a255bff415b0abf07a8 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:39:25 +0200 Subject: [PATCH 12/17] ASoC: Intel Skylake: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/soc/intel/skylake/skl-pcm.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ac3dc8c63c260d..4613a133581925 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -128,6 +128,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; + unsigned int bps; int err; hstream = snd_hdac_get_stream(bus, params->stream, @@ -138,8 +139,8 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); - format_val = snd_hdac_calc_stream_format(params->s_freq, - params->ch, params->format, params->host_bps, 0); + bps = snd_hdac_stream_format_bps(params->format, SNDRV_PCM_SUBFORMAT_STD, params->host_bps); + format_val = snd_hdac_stream_format(params->ch, bps, params->s_freq); dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", format_val, params->s_freq, params->ch, params->format); @@ -177,6 +178,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) struct hdac_ext_stream *stream; struct hdac_ext_link *link; unsigned char stream_tag; + unsigned int bps; hstream = snd_hdac_get_stream(bus, params->stream, params->link_dma_id + 1); @@ -185,8 +187,9 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); - format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, - params->format, params->link_bps, 0); + + bps = snd_hdac_stream_format_bps(params->format, SNDRV_PCM_SUBFORMAT_STD, params->link_bps); + format_val = snd_hdac_stream_format(params->ch, bps, params->s_freq); dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", format_val, params->s_freq, params->ch, params->format); From 47ab5ecfbfaf5f309d2a20896320c09a2d17b94d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 10 Aug 2023 17:39:11 +0200 Subject: [PATCH 13/17] ASoC: SOF: Intel: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/soc/sof/intel/hda-dai-ops.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 012a75f366ab4c..51275d50a6a478 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -213,9 +213,10 @@ static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev, link_bps = codec_dai->driver->playback.sig_bits; else link_bps = codec_dai->driver->capture.sig_bits; + link_bps = snd_hdac_stream_format_bps(params_format(params), SNDRV_PCM_SUBFORMAT_STD, + link_bps); - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), - params_format(params), link_bps, 0); + format_val = snd_hdac_stream_format(params_channels(params), link_bps, params_rate(params)); dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, params_rate(params), params_channels(params), params_format(params)); @@ -238,11 +239,11 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) { unsigned int format_val; + unsigned int bps; - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), - params_format(params), - params_physical_width(params), - 0); + bps = snd_hdac_stream_format_bps(params_format(params), SNDRV_PCM_SUBFORMAT_STD, + params_physical_width(params)); + format_val = snd_hdac_stream_format(params_channels(params), bps, params_rate(params)); dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, params_rate(params), params_channels(params), params_format(params)); @@ -258,6 +259,7 @@ static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev, snd_pcm_format_t format; unsigned int channels; unsigned int width; + unsigned int bps; channels = params_channels(params); format = params_format(params); @@ -269,10 +271,8 @@ static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev, width = 32; } - format_val = snd_hdac_calc_stream_format(params_rate(params), channels, - format, - width, - 0); + bps = snd_hdac_stream_format_bps(format, SNDRV_PCM_SUBFORMAT_STD, width); + format_val = snd_hdac_stream_format(channels, bps, params_rate(params)); dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, params_rate(params), channels, format); From 177d6b6275c78463582943ab87946841b3a6ffb1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 26 Jul 2023 16:56:56 +0200 Subject: [PATCH 14/17] ASoC: Intel: avs: Switch to new stream-format interface To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism. Signed-off-by: Cezary Rojewski --- sound/soc/intel/avs/loader.c | 4 ++-- sound/soc/intel/avs/path.c | 2 +- sound/soc/intel/avs/pcm.c | 19 ++++++++++++++----- sound/soc/intel/avs/probes.c | 3 +-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 56bb0a59249d54..2e3dd3d86b16f7 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -371,7 +371,7 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) hstream = hdac_stream(estream); /* code loading performed with default format */ - sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0); + sdfmt = snd_hdac_stream_format(1, 32, 48000); ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab); if (ret < 0) goto release_stream; @@ -438,7 +438,7 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) stream = hdac_stream(estream); /* code loading performed with default format */ - sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0); + sdfmt = snd_hdac_stream_format(1, 32, 48000); ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab); if (ret < 0) goto release_stream; diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index adbe23a47847b7..be9e455ee0a20d 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -87,7 +87,7 @@ static bool avs_test_hw_params(struct snd_pcm_hw_params *params, return (params_rate(params) == fmt->sampling_freq && params_channels(params) == fmt->num_channels && params_physical_width(params) == fmt->bit_depth && - params_width(params) == fmt->valid_bit_depth); + snd_pcm_hw_params_bps(params) == fmt->valid_bit_depth); } static struct avs_tplg_path * diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 1fbb2c2fadb553..ea6b784f2067f4 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -335,20 +335,25 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct hdac_ext_stream *link_stream = runtime->private_data; + struct snd_soc_pcm_stream *stream_info; + struct hdac_ext_stream *link_stream; struct hdac_ext_link *link; struct hda_codec *codec; struct hdac_bus *bus; unsigned int format_val; + unsigned int bps; int ret; + link_stream = runtime->private_data; if (link_stream->link_prepared) return 0; codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); bus = &codec->bus->core; - format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - runtime->sample_bits, 0); + stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); + bps = snd_hdac_stream_format_bps(runtime->format, runtime->subformat, + stream_info->sig_bits); + format_val = snd_hdac_stream_format(runtime->channels, bps, runtime->rate); snd_hdac_ext_stream_decouple(bus, link_stream, true); snd_hdac_ext_stream_reset(link_stream); @@ -601,11 +606,13 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_stream *stream_info; struct avs_dma_data *data; struct avs_dev *adev = to_avs_dev(dai->dev); struct hdac_ext_stream *host_stream; struct hdac_bus *bus; unsigned int format_val; + unsigned int bps; int ret; data = snd_soc_dai_get_dma_data(dai, substream); @@ -618,8 +625,10 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so snd_hdac_ext_stream_decouple(bus, data->host_stream, true); snd_hdac_stream_reset(hdac_stream(host_stream)); - format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - runtime->sample_bits, 0); + stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); + bps = snd_hdac_stream_format_bps(runtime->format, runtime->subformat, + stream_info->sig_bits); + format_val = snd_hdac_stream_format(runtime->channels, bps, runtime->rate); ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); if (ret < 0) diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 275928281c6c63..1e67b6549be7db 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -140,8 +140,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, bps = snd_pcm_format_physical_width(format); if (bps < 0) return bps; - format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out, - format, bps, 0); + format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); if (ret < 0) return ret; From eb37244a9117c316c38cb301c49fc8c110346f59 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Aug 2023 17:41:37 +0200 Subject: [PATCH 15/17] ALSA: hda: Drop snd_hdac_calc_stream_format() There are no users of the function. Signed-off-by: Cezary Rojewski --- include/sound/hdaudio.h | 5 ---- sound/hda/hdac_device.c | 61 ----------------------------------------- 2 files changed, 66 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index a7c2f02110fa42..24d689b95cac7f 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -145,11 +145,6 @@ unsigned int snd_hdac_stream_format_bps(snd_pcm_format_t format, snd_pcm_subform unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bps, unsigned int rate); unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bps, unsigned int rate, unsigned short spdif_ctls); -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); int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, u64 *subformatsp, unsigned int *bpsp); bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 13e27d38f2feff..612d780d69f4e6 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -849,67 +849,6 @@ unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bp } EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_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) - * - * 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, - snd_pcm_format_t 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; From f6235757a8bf696fdd0b8380ac28649b2dd776b7 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 4 Aug 2023 13:45:00 +0200 Subject: [PATCH 16/17] ASoC: Intel: avs: Kill S24_LE in HDAudio streaming Eliminate all occurrences of S24_LE within the HDAudio related pcm code, both HOST and LINK side. Replace those with MSBITS subformats to allow for granular selection when S32_LE is the format of choice. Signed-off-by: Cezary Rojewski --- sound/soc/intel/avs/pcm.c | 12 +++++++++--- sound/soc/intel/avs/topology.c | 9 ++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index ea6b784f2067f4..22f897db6fce5a 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1055,8 +1055,10 @@ static const struct snd_pcm_hardware avs_pcm_hardware = { SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_32, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, @@ -1286,16 +1288,20 @@ static const struct snd_soc_dai_driver hda_cpu_dai = { .channels_max = 8, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_32, }, .capture = { .channels_min = 1, .channels_max = 8, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_32, }, }; diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 45d0eb2a8e7105..3dab3c3efe3b1e 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1456,8 +1456,15 @@ static int avs_dai_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - if (pcm) + u64 subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24; + + if (pcm) { dai_drv->ops = &avs_dai_fe_ops; + dai_drv->capture.subformats |= subformats; + dai_drv->playback.subformats |= subformats; + } + return 0; } From 03d751b20f472f3258aa8500df3a75e31e5ad2f8 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 26 Jul 2023 17:02:44 +0200 Subject: [PATCH 17/17] ASoC: Intel: avs: Unhardcode HDAudio BE DAI drivers description To not expose more than in fact is supported by the codec, update CPU DAI initialization procedure to rely on codec capabilities instead of hardcoding them. This includes subformat which is currently ignored. As capabilities for HDMI streams are initialized on PCM open, leave it as is for now. Signed-off-by: Cezary Rojewski --- sound/soc/intel/avs/pcm.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 22f897db6fce5a..895eba3db6f016 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -17,6 +17,7 @@ #include "avs.h" #include "path.h" #include "topology.h" +#include "../../codecs/hda.h" struct avs_dma_data { struct avs_tplg_path_template *template; @@ -1378,6 +1379,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component) ret = -ENOMEM; goto exit; } + + if (!hda_codec_is_display(codec)) { + dais[i].playback.formats = pcm->stream[0].formats; + dais[i].playback.subformats = pcm->stream[0].subformats; + dais[i].playback.rates = pcm->stream[0].rates; + dais[i].playback.channels_min = pcm->stream[0].channels_min; + dais[i].playback.channels_max = pcm->stream[0].channels_max; + dais[i].playback.sig_bits = pcm->stream[0].maxbps; + } } if (pcm->stream[1].substreams) { @@ -1388,6 +1398,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component) ret = -ENOMEM; goto exit; } + + if (!hda_codec_is_display(codec)) { + dais[i].capture.formats = pcm->stream[1].formats; + dais[i].capture.subformats = pcm->stream[1].subformats; + dais[i].capture.rates = pcm->stream[1].rates; + dais[i].capture.channels_min = pcm->stream[1].channels_min; + dais[i].capture.channels_max = pcm->stream[1].channels_max; + dais[i].capture.sig_bits = pcm->stream[1].maxbps; + } } dai = snd_soc_register_dai(component, &dais[i], false);