ALSA: pcm: Introduce MSBITS subformat interface

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.

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. The approach of subformat being part of struct snd_pcm_hardware
is a compromise between ALSA and ASoC allowing for
hw_params-intersection code to be alloc/free-less while not adding any
new responsibilities to ASoC runtime structures.

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>
Link: https://lore.kernel.org/r/20231117120610.1755254-2-cezary.rojewski@intel.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Jaroslav Kysela 2023-11-17 13:05:55 +01:00 committed by Takashi Iwai
parent 2625786967
commit 2112aa0349
5 changed files with 73 additions and 6 deletions

View File

@ -32,6 +32,7 @@
struct snd_pcm_hardware { struct snd_pcm_hardware {
unsigned int info; /* SNDRV_PCM_INFO_* */ unsigned int info; /* SNDRV_PCM_INFO_* */
u64 formats; /* SNDRV_PCM_FMTBIT_* */ u64 formats; /* SNDRV_PCM_FMTBIT_* */
u32 subformats; /* for S32_LE, SNDRV_PCM_SUBFMTBIT_* */
unsigned int rates; /* SNDRV_PCM_RATE_* */ unsigned int rates; /* SNDRV_PCM_RATE_* */
unsigned int rate_min; /* min rate */ unsigned int rate_min; /* min rate */
unsigned int rate_max; /* max 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 #define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_BE
#endif #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_file {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
int no_compat_mmap; int no_compat_mmap;

View File

@ -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 unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_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; typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) #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 0x00000001 /* hardware supports mmap */
#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */

View File

@ -265,6 +265,9 @@ static const char * const snd_pcm_access_names[] = {
static const char * const snd_pcm_subformat_names[] = { static const char * const snd_pcm_subformat_names[] = {
SUBFORMAT(STD), SUBFORMAT(STD),
SUBFORMAT(MSBITS_MAX),
SUBFORMAT(MSBITS_20),
SUBFORMAT(MSBITS_24),
}; };
static const char * const snd_pcm_tstamp_mode_names[] = { static const char * const snd_pcm_tstamp_mode_names[] = {

View File

@ -479,6 +479,7 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
{ {
const struct snd_interval *i; const struct snd_interval *i;
const struct snd_mask *m; const struct snd_mask *m;
struct snd_mask *m_rw;
int err; int err;
if (!params->msbits) { if (!params->msbits) {
@ -487,6 +488,22 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
params->msbits = snd_interval_value(i); 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) { if (!params->rate_den) {
i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
if (snd_interval_single(i)) { 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); 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 && *subformats)
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) static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; 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) if (err < 0)
return err; return err;
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, err = snd_pcm_hw_constraint_subformats(runtime, 0, &hw->subformats);
PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
if (err < 0) if (err < 0)
return err; return err;

View File

@ -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 unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_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; typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) #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 0x00000001 /* hardware supports mmap */
#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */