ALSA: hda: Upgrade stream-format infrastructure

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>
Acked-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20231117120610.1755254-5-cezary.rojewski@intel.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Cezary Rojewski 2023-11-17 13:05:58 +01:00 committed by Takashi Iwai
parent 4a6ba09e89
commit d24f1a090d
4 changed files with 165 additions and 0 deletions

View File

@ -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); hda_nid_t *conn_list, int max_conns);
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *start_id); hda_nid_t *start_id);
unsigned int snd_hdac_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 snd_hdac_calc_stream_format(unsigned int rate,
unsigned int channels, unsigned int channels,
snd_pcm_format_t format, snd_pcm_format_t format,

View File

@ -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)); 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 static inline void
params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt) params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt)
{ {

View File

@ -1706,6 +1706,40 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
} }
EXPORT_SYMBOL(snd_pcm_hw_param_last); 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, static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg) void *arg)
{ {

View File

@ -13,6 +13,7 @@
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include <sound/hda_regmap.h> #include <sound/hda_regmap.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "local.h" #include "local.h"
static void setup_fg_nodes(struct hdac_device *codec); static void setup_fg_nodes(struct hdac_device *codec);
@ -725,6 +726,129 @@ static const struct hda_rate_tbl rate_bits[] = {
{ 0 } /* terminator */ { 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 * snd_hdac_calc_stream_format - calculate the format bitset
* @rate: the sample rate * @rate: the sample rate