[PATCH v4 00/16] ALSA/ASoC: hda: Address format selection limitations and ambiguity
Patchset aims to address format selection restrictions present currently in the HDAudio library. Formats which we are concerned about are 20 and 24 valid bits per sample within 32 bit depth container. One may identify them as S20_LE and S24_LE except that those, according to comments found in include/uapi/sound/asound.h, are for LSB-aligned scenarios. HDAudio streams expect MSB-aligned data, no matter if we are speaking of HOST (SDxFMT) or LINK (PPLCxFMT) side - chapter 4.5.1 of the public HDAudio specification. In short, S20_LE and S24_LE are invalid options.
Right now, given the implementation of snd_hdac_query_supported_pcm() within sound/hda/hdac_device.c, even if a codec responds with: "I support all possible formats specified within HDAudio specification", there will be no option to open a 20/32 or 24/32 stream. The kernel will force the stream to be opened using the highest available bit depth.
After discussing subject initially with Jaroslav and Takashi, suggestion was made to utilize 'subformat' option to address the problem. The eye-opening discussion begun much earlier though, in 2019 [1].
Paired with PRs for alsa-utils [2] and alsa-lib [3].
Flow of changes:
The very first patch adds MSBITS subformat options to allow for granular 20/32, 24/32 and 32/32 format selection. The next two make sure subformat is actually honored during runtime. Most of that code is based on format-related API.
Follow up is upgrade to the hda stream-format interface - several functions are added to make the granular format selection simple in the HDAudio world. Core of the implementation is based on the existing snd_hdac_calc_stream_format(). The next ten patches are straightforward switch from one interface to another with cleanup of now-unsed function as a finishing touch.
Last but not least - the avs-driver, on which the problem analyzed and debugged, is updated to no longer acknowledge S24_LE as a valid format option.
Results with skylake-driver and snd_hda_intel show status quo on our RVPs. PR filed on SOF github shows promising results too [4].
Changes in v4: - fixed compilation issues in sof-driver, patch 12/16, reported by ikp - fixed sparse warnings in patch 01/16, reported by ikp - updated commit message for patch 03: "ASoC pcm: Honor subformat when configuring runtime", as snd_pcm_hw_copy() is gone since revision v3.
Changes in v3: - merged the first two patches as suggested by Jaroslav - re-authored patch 01 to Jaroslav, added my Co-developed-by. - added Jaroslav' Co-developed-by to patch 02. - 'subformats' field now S32_LE-specific. Given the fact that it is the only format currently requiring subformat-intervention, functionality is narrowed to reduce amount of memory allocations and cleanup. Suggested by Jaroslav. - note to the above: the hdaudio part converted 1:1 as requested, patch 02/16 - note #2: alsa part converted to S32_LE-specific yet without addition of the chicken bit. Instead, struct snd_pcm_hardware is updated with u32 subformat mask to do the job. - ALSA-core additions in form of snd_pcm_subformat_width() and snd_pcm_hw_params_bps() relocated from 01/16 to the user, patch 05.
Changes in v2: - patch 01/17, introduced struct snd_pcm_subformat which task is to represent subformat-mask on per format basis. Expectation is that manipulated arrays of subformats always end with a sentinel entry - patch 01/17, added snd_pcm_hw_copy() as the copying snd_pcm_hardware becomes non-trivial - patch 02/17, added hw_rule that produces final subformat mask based on provided formats as suggested by Jaroslav - patch 04/17, soc_pcm_hw_update_subformat() refactored as the subformat intersection becomes non-trivial - relevant functions releasing resources occupied by hda_pcm and snd_pcm_runtime updated to also kfree() subformats - except for 16/17, no changes to patches past 04/17, retaining acks for these
Changes in v1: - fixed UBSAN due to missing snd_pcm_subformat_names[] entries for new subformats - as HDMI stream capabilities are assigned on PCM open, patch 16/17 has been updated to ignore such codecs for now. A separate patchset will take care of this case - params_bps() reworded to snd_pcm_hw_params_bps() - fixed compilation issues in sof-driver, patch 13/17
[1]: https://lore.kernel.org/alsa-devel/20190905053302.9262-1-pawel.harlozinski@l... [2]: https://github.com/alsa-project/alsa-utils/pull/228 [3]: https://github.com/alsa-project/alsa-lib/pull/342 [4]: https://github.com/thesofproject/linux/pull/4539
Cezary Rojewski (15): ALSA: hda: Honor subformat when querying PCMs ASoC: pcm: Honor subformat when configuring runtime ALSA: hda: Upgrade stream-format infrastructure ALSA: hda: Switch to new stream-format interface ALSA: hda/hdmi: Switch to new stream-format interface ALSA: hda/ca0132: Switch to new stream-format interface ASoC: codecs: hda: Switch to new stream-format interface ASoC: codecs: hdac_hda: Switch to new stream-format interface ASoC: codecs: hdac_hdmi: Switch to new stream-format interface ASoC: Intel Skylake: Switch to new stream-format interface ASoC: SOF: Intel: Switch to new stream-format interface ASoC: Intel: avs: Switch to new stream-format interface ALSA: hda: Drop snd_hdac_calc_stream_format() ASoC: Intel: avs: Kill S24_LE format ASoC: Intel: avs: Unhardcode HDAudio BE DAI drivers description
Jaroslav Kysela (1): ALSA: pcm: Introduce MSBITS subformat interface
include/sound/hda_codec.h | 5 +- include/sound/hdaudio.h | 13 +-- include/sound/pcm.h | 8 ++ include/sound/pcm_params.h | 2 + include/sound/soc.h | 1 + include/uapi/sound/asound.h | 7 +- sound/core/pcm.c | 3 + sound/core/pcm_lib.c | 28 ++++++ sound/core/pcm_misc.c | 22 +++++ sound/core/pcm_native.c | 55 ++++++++++- sound/hda/hdac_device.c | 156 +++++++++++++++++++++--------- sound/pci/hda/hda_codec.c | 2 + sound/pci/hda/hda_controller.c | 10 +- sound/pci/hda/patch_ca0132.c | 3 +- sound/pci/hda/patch_hdmi.c | 6 +- sound/soc/codecs/hda-dai.c | 6 +- sound/soc/codecs/hda.c | 2 + sound/soc/codecs/hdac_hda.c | 8 +- sound/soc/codecs/hdac_hdmi.c | 10 +- sound/soc/intel/avs/loader.c | 4 +- sound/soc/intel/avs/path.c | 2 +- sound/soc/intel/avs/pcm.c | 58 +++++++++-- sound/soc/intel/avs/probes.c | 3 +- sound/soc/intel/avs/topology.c | 13 ++- sound/soc/intel/skylake/skl-pcm.c | 11 ++- sound/soc/soc-pcm.c | 10 ++ sound/soc/sof/intel/hda-dai-ops.c | 20 ++-- tools/include/uapi/sound/asound.h | 7 +- 28 files changed, 365 insertions(+), 110 deletions(-)
From: Jaroslav Kysela perex@perex.cz
Improve granularity of format selection for S32/U32 formats by adding constants representing 20, 24 and MAX most significant bits.
The MAX means the maximum number of significant bits which can the physical format hold. For 32-bit formats, MAX is related to 32 bits. For 8-bit formats, MAX is related to 8 bits etc.
The drivers may use snd_pcm_hw_constraint_subformats with a simple format -> subformats table.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Jaroslav Kysela perex@perex.cz Co-developed-by: Cezary Rojewski cezary.rojewski@intel.com Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- include/sound/pcm.h | 7 ++++ include/uapi/sound/asound.h | 7 ++-- sound/core/pcm.c | 3 ++ sound/core/pcm_native.c | 55 +++++++++++++++++++++++++++++-- tools/include/uapi/sound/asound.h | 7 ++-- 5 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 2a815373dac1..cc175c623dac 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -32,6 +32,7 @@ struct snd_pcm_hardware { unsigned int info; /* SNDRV_PCM_INFO_* */ u64 formats; /* SNDRV_PCM_FMTBIT_* */ + u32 subformats; /* for S32_LE, SNDRV_PCM_SUBFMTBIT_* */ unsigned int rates; /* SNDRV_PCM_RATE_* */ unsigned int rate_min; /* min rate */ unsigned int rate_max; /* max rate */ @@ -217,6 +218,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_MAX _SNDRV_PCM_SUBFMTBIT(MSBITS_MAX) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_20 _SNDRV_PCM_SUBFMTBIT(MSBITS_20) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_24 _SNDRV_PCM_SUBFMTBIT(MSBITS_24) + struct snd_pcm_file { struct snd_pcm_substream *substream; int no_compat_mmap; diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index f9939da41122..d5b9cfbd9cea 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -142,7 +142,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/
-#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 15) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 16)
typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -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_MAX ((__force snd_pcm_subformat_t) 1) +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((__force snd_pcm_subformat_t) 2) +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((__force snd_pcm_subformat_t) 3) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_24
#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 20bb2d7c8d4b..c4bc15f048b6 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_MAX), + SUBFORMAT(MSBITS_20), + SUBFORMAT(MSBITS_24), };
static const char * const snd_pcm_tstamp_mode_names[] = { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index f610b08f5a2b..5a9faa701059 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -479,6 +479,7 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, { const struct snd_interval *i; const struct snd_mask *m; + struct snd_mask *m_rw; int err;
if (!params->msbits) { @@ -487,6 +488,22 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, params->msbits = snd_interval_value(i); }
+ if (params->msbits) { + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m); + + if (snd_pcm_format_linear(format) && + snd_pcm_format_width(format) != params->msbits) { + m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT); + snd_mask_reset(m_rw, + (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX); + if (snd_mask_empty(m_rw)) + return -EINVAL; + } + } + } + if (!params->rate_den) { i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); if (snd_interval_single(i)) { @@ -2483,6 +2500,41 @@ static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, return snd_interval_refine(hw_param_interval(params, rule->var), &t); }
+static int snd_pcm_hw_rule_subformats(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_mask *sfmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT); + struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + u32 *subformats = rule->private; + snd_pcm_format_t f; + struct snd_mask m; + + snd_mask_none(&m); + /* All PCMs support at least the default STD subformat. */ + snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD); + + pcm_for_each_format(f) { + if (!snd_mask_test(fmask, (__force unsigned)f)) + continue; + + if (f == SNDRV_PCM_FORMAT_S32_LE) + m.bits[0] |= *subformats; + else if (snd_pcm_format_linear(f)) + snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX); + } + + return snd_mask_refine(sfmask, &m); +} + +static int snd_pcm_hw_constraint_subformats(struct snd_pcm_runtime *runtime, + unsigned int cond, u32 *subformats) +{ + return snd_pcm_hw_rule_add(runtime, cond, -1, + snd_pcm_hw_rule_subformats, (void *)subformats, + SNDRV_PCM_HW_PARAM_SUBFORMAT, + SNDRV_PCM_HW_PARAM_FORMAT, -1); +} + static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2634,8 +2686,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_subformats(runtime, 0, &hw->subformats); if (err < 0) return err;
diff --git a/tools/include/uapi/sound/asound.h b/tools/include/uapi/sound/asound.h index f9939da41122..d5b9cfbd9cea 100644 --- a/tools/include/uapi/sound/asound.h +++ b/tools/include/uapi/sound/asound.h @@ -142,7 +142,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/
-#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 15) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 16)
typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -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_MAX ((__force snd_pcm_subformat_t) 1) +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((__force snd_pcm_subformat_t) 2) +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((__force snd_pcm_subformat_t) 3) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_24
#define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */
On 16. 11. 23 12:22, Cezary Rojewski wrote:
From: Jaroslav Kysela perex@perex.cz
Improve granularity of format selection for S32/U32 formats by adding constants representing 20, 24 and MAX most significant bit > The MAX means the maximum number of significant bits which can the physical format hold. For 32-bit formats, MAX is related to 32 bits. For 8-bit formats, MAX is related to 8 bits etc.
The drivers may use snd_pcm_hw_constraint_subformats with a simple format -> subformats table.
I am afraid, the above sentence is no more correct and the current code does not follow my original idea. It's a bit step back to the initial code. But I admit that from the API POV, it's workable now (with the added refine mechanism for one format).
As noted several times, this is not my preferred implementation (I would keep only the constraint function which will be called by drivers on demand in the ALSA PCM core code). The latest proposed simplification may be applied in the ASoC core (store S32_LE subformat mask in snd_soc_pcm_runtime and install this intersected constraint for FE PCM - user space). Something like ASoC core does for the msbits constraint.
If nobody else thinks that it's a good direction, please, add a note to the comment that this implementation (extend snd_pcm_hardware structure) is a compromise for the ASoC code with details.
if (f == SNDRV_PCM_FORMAT_S32_LE)
Missing mask check: (f == SNDRV_PCM_FORMAT_S32_LE && *subformats)
Otherwise the MSBITS_MAX won't be set for S32_LE by default.
m.bits[0] |= *subformats;
Jaroslav
On 2023-11-16 4:10 PM, Jaroslav Kysela wrote:
On 16. 11. 23 12:22, Cezary Rojewski wrote:
From: Jaroslav Kysela perex@perex.cz
Improve granularity of format selection for S32/U32 formats by adding constants representing 20, 24 and MAX most significant bit > The MAX means the maximum number of significant bits which can the physical format hold. For 32-bit formats, MAX is related to 32 bits. For 8-bit formats, MAX is related to 8 bits etc.
The drivers may use snd_pcm_hw_constraint_subformats with a simple format -> subformats table.
I am afraid, the above sentence is no more correct and the current code does not follow my original idea. It's a bit step back to the initial code. But I admit that from the API POV, it's workable now (with the added refine mechanism for one format).
I'll update the commit message, indeed no longer up-to date. Will also try to explain the thought process which is currently missing, as you noted below.
As noted several times, this is not my preferred implementation (I would keep only the constraint function which will be called by drivers on demand in the ALSA PCM core code). The latest proposed simplification may be applied in the ASoC core (store S32_LE subformat mask in snd_soc_pcm_runtime and install this intersected constraint for FE PCM - user space). Something like ASoC core does for the msbits constraint.
If nobody else thinks that it's a good direction, please, add a note to the comment that this implementation (extend snd_pcm_hardware structure) is a compromise for the ASoC code with details.
I'd like to avoid complicating ASoC runtime structures as storing hw_params is not among their current task list, at least not currently.
But I'm open for any improvements in the future.
+ if (f == SNDRV_PCM_FORMAT_S32_LE)
Missing mask check: (f == SNDRV_PCM_FORMAT_S32_LE && *subformats)
Otherwise the MSBITS_MAX won't be set for S32_LE by default.
Ack.
+ m.bits[0] |= *subformats;
Jaroslav
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.
Acked-by: Mark Brown broonie@kernel.org Co-developed-by: Jaroslav Kysela perex@perex.cz Signed-off-by: Jaroslav Kysela perex@perex.cz Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- include/sound/hda_codec.h | 5 ++-- include/sound/hdaudio.h | 3 ++- sound/hda/hdac_device.c | 45 ++++++++++++++++++++---------------- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/patch_hdmi.c | 1 + sound/soc/codecs/hdac_hdmi.c | 3 ++- 6 files changed, 35 insertions(+), 24 deletions(-)
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5497dc9c396a..9c94ba7c183d 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_) */ + u32 subformats; /* for S32_LE format, 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 dd7c87bbc613..7cfea9e7f6e4 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -146,7 +146,8 @@ 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, u32 *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 bbf7bcdb449a..cde4d5c33771 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -817,15 +817,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 +850,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 +869,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 +914,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 01718b1fc9a7..12f02cdc9659 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 1cde2a69bdb4..687b8b8fd7ac 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 b9c5ffbfb5ba..3e4f632d8665 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;
Subformat options are ignored when setting up hardware parameters and assigning PCM stream capabilities. Account for them to allow for granular format selection.
As there is only one user currently (format S32_LE), subformat is represented by a simple u32 and stores flags only for that one user alone. Such approach allows for alloc/free-less code until there are more users on the horizon.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- include/sound/soc.h | 1 + sound/soc/soc-pcm.c | 10 ++++++++++ 2 files changed, 11 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 7792c393e238..ccc31bc41e92 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -620,6 +620,7 @@ enum snd_soc_trigger_order { struct snd_soc_pcm_stream { const char *stream_name; u64 formats; /* SNDRV_PCM_FMTBIT_* */ + u32 subformats; /* for S32_LE format, 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 323e4d7b6adf..9d688917cce2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -554,6 +554,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 +598,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 +620,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 */ @@ -1713,6 +1721,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); }
} @@ -1750,6 +1759,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); } } }
Introduce a set of functions that ultimately facilite SDxFMT-related calculations in atomic manner:
First, introduce snd_pcm_subformat_width() and snd_pcm_hw_params_bps() helpers that separate the base functionality from the HDAudio-specific one.
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.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- include/sound/hdaudio.h | 5 ++ include/sound/pcm.h | 1 + include/sound/pcm_params.h | 2 + sound/core/pcm_lib.c | 28 +++++++++ sound/core/pcm_misc.c | 22 +++++++ sound/hda/hdac_device.c | 124 +++++++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 7cfea9e7f6e4..964ccf1d8b5b 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/include/sound/pcm.h b/include/sound/pcm.h index cc175c623dac..40d6257d4353 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1135,6 +1135,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 ba184f49f7e1..df17c7d3e853 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/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a11cd7d6295f..05f649b0bf00 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1706,6 +1706,34 @@ 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; + 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 5588b6a1ee8b..62dd18091f54 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -482,6 +482,28 @@ 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 0 if result is dependent + * on other parameters in the configuration space. + */ +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_MAX: + 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/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index cde4d5c33771..f885724d10b0 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,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
On 16. 11. 23 12:22, Cezary Rojewski wrote:
Introduce a set of functions that ultimately facilite SDxFMT-related calculations in atomic manner:
First, introduce snd_pcm_subformat_width() and snd_pcm_hw_params_bps() helpers that separate the base functionality from the HDAudio-specific one.
I think, snd_pcm_subformat_width() should be implemented only as static helper for now. There are no users and only snd_pcm_hw_params_bps() makes sense for callers (format & subformat must be passed together).
Also note that _bps is mostly used for bits-per-second in the computer world, so maybe _bpsam may be used here to pay more attention by readers, but it's just my optional proposal. Maybe my head has some "fixed wiring" for this :)
Jaroslav
On 2023-11-16 4:10 PM, Jaroslav Kysela wrote:
On 16. 11. 23 12:22, Cezary Rojewski wrote:
Introduce a set of functions that ultimately facilite SDxFMT-related calculations in atomic manner:
First, introduce snd_pcm_subformat_width() and snd_pcm_hw_params_bps() helpers that separate the base functionality from the HDAudio-specific one.
I think, snd_pcm_subformat_width() should be implemented only as static helper for now. There are no users and only snd_pcm_hw_params_bps() makes sense for callers (format & subformat must be passed together).
Ack. Inlining into its only user.
Also note that _bps is mostly used for bits-per-second in the computer world, so maybe _bpsam may be used here to pay more attention by readers, but it's just my optional proposal. Maybe my head has some "fixed wiring" for this :)
The current shortcut is taken from existing intel/ code which utilizes 'bps' but I agree that it can be misleading.
What I did to solve the problem: opened both, HDAudio (bare) and HDAudio+ADSP specs and in both the relevant fields (the fields that relate to bits-per-sample) are called "BITS". Also searched google for the 'bits per sample' phrase and one of the first thing that came up was audacity doc which used the exact same phrase (bits).
All occurrences of 'bps' will be replaced by 'bits'.
Kind regards, Czarek
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 c42e9ffff9db..0ec3559cc834 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",
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 687b8b8fd7ac..dff2d7221982 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);
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 748a3c40966e..aa312441604f 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;
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.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 5371ff086261..20b070c66c6f 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 d57b043d6bfe..d2117e36ddd1 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; }
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 355f30779a34..c08375672661 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",
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 3e4f632d8665..824fb4862f22 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)
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 d0c02e8a6785..7331cbbf75ea 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -127,6 +127,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, @@ -137,8 +138,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); @@ -165,6 +166,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); @@ -173,8 +175,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);
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 87935554b1e4..94524f0aa1f6 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -208,14 +208,15 @@ static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev, struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); unsigned int link_bps; unsigned int format_val; + unsigned int bps;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) link_bps = codec_dai->driver->playback.sig_bits; else link_bps = codec_dai->driver->capture.sig_bits; + 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), 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);
To provide option for selecting different bit-per-sample than just the maximum one, use the new format calculation mechanism.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 65dd8f140fc1..e83ce6a35755 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 aa8b50b931c3..42338ed6835b 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 463dbba18426..595914f93846 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 = snd_soc_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(snd_soc_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_reset(link_stream); snd_hdac_ext_stream_setup(link_stream, format_val); @@ -600,10 +605,12 @@ 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; unsigned int format_val; + unsigned int bps; int ret;
data = snd_soc_dai_get_dma_data(dai, substream); @@ -614,8 +621,10 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
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 bdc6b30dc009..817e543036f2 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;
There are no users of the function.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 964ccf1d8b5b..2a8a763a3c14 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, u32 *subformatsp, unsigned int *bpsp); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index f885724d10b0..bcc4d90201b1 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;
Eliminate all occurrences of S24_LE within 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.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- sound/soc/intel/avs/pcm.c | 20 +++++++++++++++----- sound/soc/intel/avs/topology.c | 13 ++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 595914f93846..73f0fadb97b9 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1073,8 +1073,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_MAX, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, @@ -1225,8 +1227,10 @@ static const struct snd_soc_dai_driver i2s_dai_template = { .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .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_MAX, }, .capture = { .channels_min = 1, @@ -1234,8 +1238,10 @@ static const struct snd_soc_dai_driver i2s_dai_template = { .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .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_MAX, }, };
@@ -1310,16 +1316,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_MAX, }, .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_MAX, }, };
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index c74e9d622e4c..778236d3fd28 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1514,8 +1514,16 @@ 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) + u32 fe_subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX; + + if (pcm) { dai_drv->ops = &avs_dai_fe_ops; + dai_drv->capture.subformats = fe_subformats; + dai_drv->playback.subformats = fe_subformats; + } + return 0; }
@@ -1534,6 +1542,9 @@ static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_s /* Open LINK (BE) pipes last and close them first to prevent xruns. */ link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE; link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE; + } else { + /* Do not ignore codec capabilities. */ + link->dpcm_merged_format = 1; }
return 0;
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.
Acked-by: Mark Brown broonie@kernel.org Signed-off-by: Cezary Rojewski cezary.rojewski@intel.com --- 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 73f0fadb97b9..3e2c3f2f1d38 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; @@ -1406,6 +1407,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) { @@ -1416,6 +1426,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);
participants (2)
-
Cezary Rojewski
-
Jaroslav Kysela