[PATCH v5 04/16] ALSA: hda: Upgrade stream-format infrastructure

Cezary Rojewski
2023-11-17T12:04:16Z

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_bits()
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_bits() - 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_params.h | 2 +
sound/core/pcm_lib.c | 34 ++++++++++
sound/hda/hdac_device.c | 124 +++++++++++++++++++++++++++++++++++++
4 files changed, 165 insertions(+)

diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index 7cfea9e7f6e4..b00c631c4053 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_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+ unsigned int maxbits);
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate);
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+ 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_params.h b/include/sound/pcm_params.h
index ba184f49f7e1..fbf35df6e5cf 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_bits(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..41103e5c43ce 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1706,6 +1706,40 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
}
EXPORT_SYMBOL(snd_pcm_hw_param_last);

+/**
+ * snd_pcm_hw_params_bits - 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_bits(const struct snd_pcm_hw_params *p)
+{
+ snd_pcm_subformat_t subformat = params_subformat(p);
+ snd_pcm_format_t format = params_format(p);
+
+ 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:
+ 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:
+ break;
+ }
+ fallthrough;
+ default:
+ return snd_pcm_format_width(format);
+ }
+}
+EXPORT_SYMBOL(snd_pcm_hw_params_bits);
+
static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg)
{
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index cde4d5c33771..556bd24f4014 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_bits - obtain bits per sample value.
+ * @format: the PCM format.
+ * @subformat: the PCM subformat.
+ * @maxbits: the maximum bits per sample.
+ *
+ * Return: The number of bits per sample.
+ */
+unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+ unsigned int maxbits)
+{
+ struct snd_pcm_hw_params params;
+ unsigned int bits;
+
+ memset(&params, 0, sizeof(params));
+
+ params_set_format(&params, snd_hdac_format_normalize(format));
+ snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT),
+ (__force unsigned int)subformat);
+
+ bits = snd_pcm_hw_params_bits(&params);
+ if (maxbits)
+ return min(bits, maxbits);
+ return bits;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits);
+
+/**
+ * snd_hdac_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, 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 (bits) {
+ 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.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
+ * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant).
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+ unsigned int rate, unsigned short spdif_ctls)
+{
+ unsigned int val = snd_hdac_stream_format(channels, bits, rate);
+
+ if (val && spdif_ctls & AC_DIG1_NONAUDIO)
+ val |= AC_FMT_TYPE_NON_PCM;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format);
+
/**
* snd_hdac_calc_stream_format - calculate the format bitset
* @rate: the sample rate
--
2.25.1