ASoC: don't use array if single pattern

Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> says:

Current ASoC supports snd_soc_daifmt_parse_format() which can specify DAI
format by "dai-format" property from DT.
But strictly speaking, it is SW settings, so doesn't match to DT's policy.

Current ASoC is supporting auto format select via
snd_soc_dai_ops :: .auto_selectable_formats.
But the user is very few today.

DT doesn't need to specify the DAI format via "dai-format", if both CPU
and Codec drivers were supporting .auto_selectable_formats. It will be
automatically selected from .auto_selectable_formats.

But, I noticed that current auto format select method can't handle all cases.
For example, current .auto_selectable_formats is like below

	static u64 xxx_auto_formats[] = {
(A)		/* First Priority */
		SND_SOC_POSSIBLE_DAIFMT_I2S	|
		SND_SOC_POSSIBLE_DAIFMT_LEFT_J	|
		SND_SOC_POSSIBLE_DAIFMT_NB_NF	|
		SND_SOC_POSSIBLE_DAIFMT_NB_IF	|	(x)
		SND_SOC_POSSIBLE_DAIFMT_IB_NF	|
		SND_SOC_POSSIBLE_DAIFMT_IB_IF,		(x)

		/* Second Priority */
(B)		SND_SOC_POSSIBLE_DAIFMT_DSP_A	|	(y)
		SND_SOC_POSSIBLE_DAIFMT_DSP_B,		(y)
	};

It try to find DAI format from (A) first, and next it will use (A | B).
But it can't handle the format if some format were independent.
For example, DSP_x (y) can't use with xB_IF (x), etc.

So, I would like to update the method. New method doesn't use OR.
It try to find DAI format from (a), next it will use (b).

	static u64 xxx_auto_formats[] = {
(a)		/* First Priority */
		SND_SOC_POSSIBLE_DAIFMT_I2S	|
		SND_SOC_POSSIBLE_DAIFMT_LEFT_J	|
		SND_SOC_POSSIBLE_DAIFMT_NB_NF	|
		SND_SOC_POSSIBLE_DAIFMT_NB_IF	|
		SND_SOC_POSSIBLE_DAIFMT_IB_NF	|
		SND_SOC_POSSIBLE_DAIFMT_IB_IF,

		/* Second Priority */
(b)		SND_SOC_POSSIBLE_DAIFMT_DSP_A	|
		SND_SOC_POSSIBLE_DAIFMT_DSP_B	|
		SND_SOC_POSSIBLE_DAIFMT_NB_NF	|
		SND_SOC_POSSIBLE_DAIFMT_IB_NF,
	};

Switch old method to new method, Current auto select user need to update
.auto_selectable_formats. Fortunately, current few users doesn't have
above limitation. update (A)(B) to (a)(b) style is possible.

	a = A
	b = A | B

I would like to update method, and add .auto_selectable_formats
support on all drivers.

One note is that auto select might not find best format on some CPU/Codec
combination. So "dai-format" is necessary anyway.

And, there haven't been any big problems on .auto_selectable_formats,
because there were few users.
But if all drivers try to use this, it cannot be denied that they may
encounter unknown problems... In such case, "dai-format" can help, though.

Link: https://patch.msgid.link/87v7bs36m0.wl-kuninori.morimoto.gx@renesas.com
This commit is contained in:
Mark Brown 2026-06-12 18:58:00 +01:00
commit fc408ab6e9
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
16 changed files with 202 additions and 282 deletions

View File

@ -81,10 +81,10 @@ struct clk;
/*
* define GATED -> CONT. GATED will be selected if both are selected.
* see
* snd_soc_runtime_get_dai_fmt()
* soc_dai_convert_possiblefmt_to_daifmt()
*/
#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT 16
#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_MASK (0xFFFF << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_MASK (0xFFFFULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_GATED (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_CONT (0x2ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
@ -140,14 +140,6 @@ struct clk;
#define SND_SOC_DAIFMT_BP_FC SND_SOC_DAIFMT_CBP_CFC
#define SND_SOC_DAIFMT_BC_FC SND_SOC_DAIFMT_CBC_CFC
/* Describes the possible PCM format */
#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT 48
#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_MASK (0xFFFFULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_CBP_CFP (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_CBC_CFP (0x2ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_CBP_CFC (0x4ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
#define SND_SOC_POSSIBLE_DAIFMT_CBC_CFC (0x8ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
#define SND_SOC_DAIFMT_INV_MASK 0x0f00
@ -192,8 +184,7 @@ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio);
void snd_soc_dai_set_bclk_clk(struct snd_soc_dai *dai, struct clk *bclk);
/* Digital Audio interface formatting */
int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd);
u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority);
unsigned int snd_soc_dai_auto_select_format(const struct snd_soc_pcm_runtime *rtd);
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,

View File

@ -748,11 +748,6 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
/*
* Select below from Sound Card, not Auto
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
static const u64 ak4613_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J;

View File

@ -778,17 +778,13 @@ static int ak4619_dai_startup(struct snd_pcm_substream *substream,
}
static u64 ak4619_dai_formats[] = {
/*
* Select below from Sound Card, not here
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
/* First Priority */
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J,
/* Second Priority */
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};

View File

@ -1720,11 +1720,6 @@ static int da7213_set_component_pll(struct snd_soc_component *component,
return _da7213_set_component_pll(component, pll_id, source, fref, fout);
}
/*
* Select below from Sound Card, not Auto
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
static const u64 da7213_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |

View File

@ -238,15 +238,13 @@ static int framer_dai_startup(struct snd_pcm_substream *substream,
return 0;
}
static const u64 framer_dai_formats[] = {
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
static const u64 framer_dai_formats = SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops framer_dai_ops = {
.startup = framer_dai_startup,
.set_tdm_slot = framer_dai_set_tdm_slot,
.auto_selectable_formats = framer_dai_formats,
.num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats),
.auto_selectable_formats = &framer_dai_formats,
.num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver framer_dai_driver = {

View File

@ -860,18 +860,17 @@ static int idt821034_dai_startup(struct snd_pcm_substream *substream,
return 0;
}
static const u64 idt821034_dai_formats[] = {
static const u64 idt821034_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops idt821034_dai_ops = {
.startup = idt821034_dai_startup,
.hw_params = idt821034_dai_hw_params,
.set_tdm_slot = idt821034_dai_set_tdm_slot,
.set_fmt = idt821034_dai_set_fmt,
.auto_selectable_formats = idt821034_dai_formats,
.num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats),
.auto_selectable_formats = &idt821034_dai_formats,
.num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver idt821034_dai_driver = {

View File

@ -563,12 +563,6 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
}
static const u64 pcm3168a_dai_formats[] = {
/*
* Select below from Sound Card, not here
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
/*
* First Priority
*/
@ -581,6 +575,8 @@ static const u64 pcm3168a_dai_formats[] = {
* see
* pcm3168a_hw_params()
*/
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,

View File

@ -817,18 +817,17 @@ static int peb2466_dai_startup(struct snd_pcm_substream *substream,
&peb2466_sample_bits_constr);
}
static const u64 peb2466_dai_formats[] = {
static const u64 peb2466_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops peb2466_dai_ops = {
.startup = peb2466_dai_startup,
.hw_params = peb2466_dai_hw_params,
.set_tdm_slot = peb2466_dai_set_tdm_slot,
.set_fmt = peb2466_dai_set_fmt,
.auto_selectable_formats = peb2466_dai_formats,
.num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats),
.auto_selectable_formats = &peb2466_dai_formats,
.num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver peb2466_dai_driver = {

View File

@ -778,6 +778,18 @@ static void graph_link_init(struct simple_util_priv *priv,
graph_parse_daifmt(ports_cpu, &daifmt);
graph_parse_daifmt(ports_codec, &daifmt);
graph_parse_daifmt(lnk, &daifmt);
if (daifmt) {
struct device *dev = simple_priv_to_dev(priv);
/*
* Recommend to use Auto Select by using .auto_selectable_formats.
* linux/sound/soc/renesas/rcar/core.c can be good sample for it.
*
* One note is that Audio Graph Card2 still keeps compatible to set
* DAI format via DT.
*/
dev_warn_once(dev, "use .auto_selectable_formats on each corresponding CPU/Codec driver");
}
graph_util_parse_link_direction(lnk, &playback_only, &capture_only);
graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only);

View File

@ -191,13 +191,6 @@ static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct
}
static const u64 test_dai_formats =
/*
* Select below from Sound Card, not auto
* SND_SOC_POSSIBLE_DAIFMT_BP_FP
* SND_SOC_POSSIBLE_DAIFMT_BC_FP
* SND_SOC_POSSIBLE_DAIFMT_BP_FC
* SND_SOC_POSSIBLE_DAIFMT_BC_FC
*/
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |

View File

@ -1773,11 +1773,6 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
/*
* Select below from Sound Card, not auto
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
static const u64 fsi_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |

View File

@ -1058,9 +1058,6 @@ static const u64 rsnd_soc_dai_formats[] = {
* 1st Priority
*
* Well tested formats.
* Select below from Sound Card, not auto
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
@ -1074,8 +1071,15 @@ static const u64 rsnd_soc_dai_formats[] = {
*
* Supported, but not well tested
*/
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
SND_SOC_POSSIBLE_DAIFMT_DSP_B |
SND_SOC_POSSIBLE_DAIFMT_NB_NF |
SND_SOC_POSSIBLE_DAIFMT_NB_IF |
SND_SOC_POSSIBLE_DAIFMT_IB_NF |
SND_SOC_POSSIBLE_DAIFMT_IB_IF,
};
static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,

View File

@ -363,11 +363,6 @@ static int msiof_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
/*
* Select below from Sound Card, not auto
* SND_SOC_DAIFMT_CBC_CFC
* SND_SOC_DAIFMT_CBP_CFP
*/
static const u64 msiof_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_NB_NF;

View File

@ -1284,163 +1284,6 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtimes);
static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *dai, *not_used;
u64 pos, possible_fmt;
unsigned int mask = 0, dai_fmt = 0;
int i, j, priority, pri, until;
/*
* Get selectable format from each DAIs.
*
****************************
* NOTE
* Using .auto_selectable_formats is not mandatory,
* we can select format manually from Sound Card.
* When use it, driver should list well tested format only.
****************************
*
* ex)
* auto_selectable_formats (= SND_SOC_POSSIBLE_xxx)
* (A) (B) (C)
* DAI0_: { 0x000F, 0x00F0, 0x0F00 };
* DAI1 : { 0xF000, 0x0F00 };
* (X) (Y)
*
* "until" will be 3 in this case (MAX array size from DAI0 and DAI1)
* Here is dev_dbg() message and comments
*
* priority = 1
* DAI0: (pri, fmt) = (1, 000000000000000F) // 1st check (A) DAI1 is not selected
* DAI1: (pri, fmt) = (0, 0000000000000000) // Necessary Waste
* DAI0: (pri, fmt) = (1, 000000000000000F) // 2nd check (A)
* DAI1: (pri, fmt) = (1, 000000000000F000) // (X)
* priority = 2
* DAI0: (pri, fmt) = (2, 00000000000000FF) // 3rd check (A) + (B)
* DAI1: (pri, fmt) = (1, 000000000000F000) // (X)
* DAI0: (pri, fmt) = (2, 00000000000000FF) // 4th check (A) + (B)
* DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y)
* priority = 3
* DAI0: (pri, fmt) = (3, 0000000000000FFF) // 5th check (A) + (B) + (C)
* DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y)
* found auto selected format: 0000000000000F00
*/
until = snd_soc_dai_get_fmt_max_priority(rtd);
for (priority = 1; priority <= until; priority++) {
for_each_rtd_dais(rtd, j, not_used) {
possible_fmt = ULLONG_MAX;
for_each_rtd_dais(rtd, i, dai) {
u64 fmt = 0;
pri = (j >= i) ? priority : priority - 1;
fmt = snd_soc_dai_get_fmt(dai, pri);
possible_fmt &= fmt;
}
if (possible_fmt)
goto found;
}
}
/* Not Found */
return;
found:
/*
* convert POSSIBLE_DAIFMT to DAIFMT
*
* Some basic/default settings on each is defined as 0.
* see
* SND_SOC_DAIFMT_NB_NF
* SND_SOC_DAIFMT_GATED
*
* SND_SOC_DAIFMT_xxx_MASK can't notice it if Sound Card specify
* these value, and will be overwrite to auto selected value.
*
* To avoid such issue, loop from 63 to 0 here.
* Small number of SND_SOC_POSSIBLE_xxx will be Hi priority.
* Basic/Default settings of each part and above are defined
* as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx.
*/
for (i = 63; i >= 0; i--) {
pos = 1ULL << i;
switch (possible_fmt & pos) {
/*
* for format
*/
case SND_SOC_POSSIBLE_DAIFMT_I2S:
case SND_SOC_POSSIBLE_DAIFMT_RIGHT_J:
case SND_SOC_POSSIBLE_DAIFMT_LEFT_J:
case SND_SOC_POSSIBLE_DAIFMT_DSP_A:
case SND_SOC_POSSIBLE_DAIFMT_DSP_B:
case SND_SOC_POSSIBLE_DAIFMT_AC97:
case SND_SOC_POSSIBLE_DAIFMT_PDM:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | i;
break;
/*
* for clock
*/
case SND_SOC_POSSIBLE_DAIFMT_CONT:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_CONT;
break;
case SND_SOC_POSSIBLE_DAIFMT_GATED:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_GATED;
break;
/*
* for clock invert
*/
case SND_SOC_POSSIBLE_DAIFMT_NB_NF:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_NF;
break;
case SND_SOC_POSSIBLE_DAIFMT_NB_IF:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_IF;
break;
case SND_SOC_POSSIBLE_DAIFMT_IB_NF:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_NF;
break;
case SND_SOC_POSSIBLE_DAIFMT_IB_IF:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_IF;
break;
/*
* for clock provider / consumer
*/
case SND_SOC_POSSIBLE_DAIFMT_CBP_CFP:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFP;
break;
case SND_SOC_POSSIBLE_DAIFMT_CBC_CFP:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFP;
break;
case SND_SOC_POSSIBLE_DAIFMT_CBP_CFC:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFC;
break;
case SND_SOC_POSSIBLE_DAIFMT_CBC_CFC:
dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFC;
break;
}
}
/*
* Some driver might have very complex limitation.
* In such case, user want to auto-select non-limitation part,
* and want to manually specify complex part.
*
* Or for example, if both CPU and Codec can be clock provider,
* but because of its quality, user want to specify it manually.
*
* Use manually specified settings if sound card did.
*/
if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK))
mask |= SND_SOC_DAIFMT_FORMAT_MASK;
if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_MASK))
mask |= SND_SOC_DAIFMT_CLOCK_MASK;
if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_INV_MASK))
mask |= SND_SOC_DAIFMT_INV_MASK;
if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK))
mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
dai_link->dai_fmt |= (dai_fmt & mask);
}
/**
* snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime
* @rtd: The runtime for which the DAI link format should be changed
@ -1519,8 +1362,7 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card,
if (ret < 0)
return ret;
snd_soc_runtime_get_dai_fmt(rtd);
ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
ret = snd_soc_runtime_set_dai_fmt(rtd, snd_soc_dai_auto_select_format(rtd));
if (ret)
goto err;

View File

@ -138,68 +138,185 @@ void snd_soc_dai_set_bclk_clk(struct snd_soc_dai *dai, struct clk *bclk)
}
EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_clk);
int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd)
static int soc_dai_fmt_match_cnt(u64 fmt)
{
struct snd_soc_dai *dai;
int i, max = 0;
int cnt = 0;
/*
* return max num if *ALL* DAIs have .auto_selectable_formats
*/
for_each_rtd_dais(rtd, i, dai) {
if (dai->driver->ops &&
dai->driver->ops->num_auto_selectable_formats)
max = max(max, dai->driver->ops->num_auto_selectable_formats);
else
return 0;
}
if (fmt & SND_SOC_POSSIBLE_DAIFMT_FORMAT_MASK)
cnt++;
if (fmt & SND_SOC_POSSIBLE_DAIFMT_CLOCK_MASK)
cnt++;
if (fmt & SND_SOC_POSSIBLE_DAIFMT_INV_MASK)
cnt++;
return max;
return cnt;
}
/**
* snd_soc_dai_get_fmt - get supported audio format.
* @dai: DAI
* @priority: priority level of supported audio format.
*
* This should return only formats implemented with high
* quality by the DAI so that the core can configure a
* format which will work well with other devices.
* For example devices which don't support both edges of the
* LRCLK signal in I2S style formats should only list DSP
* modes. This will mean that sometimes fewer formats
* are reported here than are supported by set_fmt().
*/
u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority)
static void soc_dai_auto_select_format(u64 fmt, const struct snd_soc_pcm_runtime *rtd,
int idx, u64 *best_fmt)
{
const struct snd_soc_dai_ops *ops = dai->driver->ops;
u64 fmt = 0;
int i, max = 0, until = priority;
struct snd_soc_dai *dai;
const struct snd_soc_dai_ops *ops;
int max_idx = rtd->dai_link->num_cpus + rtd->dai_link->num_codecs;
u64 available_fmt;
/*
* Collect auto_selectable_formats until priority
* NOTE
* It doesn't support Multi CPU/Codec for now
*/
if (rtd->dai_link->num_cpus != 1 ||
rtd->dai_link->num_codecs != 1)
return;
if (idx >= max_idx)
return;
dai = rtd->dais[idx];
ops = dai->driver->ops;
/* zero chance of auto select format */
if (!ops || !ops->num_auto_selectable_formats)
return;
/*
****************************
* NOTE
****************************
* Using .auto_selectable_formats is not mandatory,
* It try to find best formats as much as possible, but automatically selecting the
* perfect format is impossible. So you can select full or missing format manually
* from Sound Card.
*
* ex)
* auto_selectable_formats[] = { A, B, C };
* (A, B, C = SND_SOC_POSSIBLE_DAIFMT_xxx)
* CPU Codec
* (A)[0] I2S/LEFT_J : IB_NF/IB_IF (X)[0] I2S/DSP_A: NB_NF : GATED
* (B)[1] DSP_A/DSP_B: NB_NF/IB_NF (Y)[1] LEFT_J: NB_NF : GATED
* (C)[2] ...
*
* priority = 1 : A
* priority = 2 : A | B
* priority = 3 : A | B | C
* priority = 4 : A | B | C
* 1. (A) -> (X) : I2S :update best format
* 2. (A) -> (Y) : LEFT_J
* 3. (B) -> (X) : DSP_A/NB_NF :update best format
* 4. (B) -> (Y) : NB_NF
* 5. (C) -> (X) ...
* 6. (C) -> (Y) ...
* ...
*
* In above case GATED will not be selected
*/
if (ops)
max = ops->num_auto_selectable_formats;
if (max < until)
until = max;
/* find best formats */
for (int i = 0; i < ops->num_auto_selectable_formats; i++) {
available_fmt = fmt & ops->auto_selectable_formats[i];
if (ops && ops->auto_selectable_formats)
for (i = 0; i < until; i++)
fmt |= ops->auto_selectable_formats[i];
/* In case of last DAI */
if (idx + 1 >= max_idx) {
int cnt1 = soc_dai_fmt_match_cnt(*best_fmt);
int cnt2 = soc_dai_fmt_match_cnt(available_fmt);
return fmt;
if (cnt1 < cnt2)
*best_fmt = available_fmt;
}
/* parse with next DAI */
else {
soc_dai_auto_select_format(available_fmt, rtd, idx + 1, best_fmt);
}
}
}
static unsigned int soc_dai_convert_possiblefmt_to_daifmt(u64 possible_fmt, unsigned int configured_fmt)
{
unsigned int fmt = 0;
unsigned int mask = 0;
/*
* convert POSSIBLE_DAIFMT to DAIFMT
*
* Some basic/default settings on each is defined as 0.
* see
* SND_SOC_DAIFMT_NB_NF
* SND_SOC_DAIFMT_GATED
*
* SND_SOC_DAIFMT_xxx_MASK can't notice it if Sound Card specify
* these value, and will be overwrite to auto selected value.
*
* To avoid such issue, loop from 63 to 0 here.
* Small number of SND_SOC_POSSIBLE_xxx will be Hi priority.
* Basic/Default settings of each part and above are defined
* as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx.
*/
for (int i = 63; i >= 0; i--) {
u64 pos = 1ULL << i;
switch (possible_fmt & pos) {
/*
* for format
*/
case SND_SOC_POSSIBLE_DAIFMT_I2S:
case SND_SOC_POSSIBLE_DAIFMT_RIGHT_J:
case SND_SOC_POSSIBLE_DAIFMT_LEFT_J:
case SND_SOC_POSSIBLE_DAIFMT_DSP_A:
case SND_SOC_POSSIBLE_DAIFMT_DSP_B:
case SND_SOC_POSSIBLE_DAIFMT_AC97:
case SND_SOC_POSSIBLE_DAIFMT_PDM:
fmt = (fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | i;
break;
/*
* for clock
*/
case SND_SOC_POSSIBLE_DAIFMT_CONT:
fmt = (fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_CONT;
break;
case SND_SOC_POSSIBLE_DAIFMT_GATED:
fmt = (fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_GATED;
break;
/*
* for clock invert
*/
case SND_SOC_POSSIBLE_DAIFMT_NB_NF:
fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_NF;
break;
case SND_SOC_POSSIBLE_DAIFMT_NB_IF:
fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_IF;
break;
case SND_SOC_POSSIBLE_DAIFMT_IB_NF:
fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_NF;
break;
case SND_SOC_POSSIBLE_DAIFMT_IB_IF:
fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_IF;
break;
}
}
/*
* Some driver might have very complex limitation.
* In such case, user want to auto-select non-limitation part,
* and want to manually specify complex part.
*
* Or for example, if both CPU and Codec can be clock provider,
* but because of its quality, user want to specify it manually.
*
* Ignore already configured format if exist
*/
if (!(configured_fmt & SND_SOC_DAIFMT_FORMAT_MASK))
mask |= SND_SOC_DAIFMT_FORMAT_MASK;
if (!(configured_fmt & SND_SOC_DAIFMT_CLOCK_MASK))
mask |= SND_SOC_DAIFMT_CLOCK_MASK;
if (!(configured_fmt & SND_SOC_DAIFMT_INV_MASK))
mask |= SND_SOC_DAIFMT_INV_MASK;
if (!(configured_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK))
mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
return configured_fmt | (fmt & mask);
}
unsigned int snd_soc_dai_auto_select_format(const struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai_link *dai_link = rtd->dai_link;
u64 possible_fmt = 0;
soc_dai_auto_select_format(~0, rtd, 0, &possible_fmt);
return soc_dai_convert_possiblefmt_to_daifmt(possible_fmt, dai_link->dai_fmt);
}
/**

View File

@ -183,13 +183,6 @@ static const struct snd_soc_component_driver dummy_codec = {
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
/*
* Select these from Sound Card Manually
* SND_SOC_POSSIBLE_DAIFMT_CBP_CFP
* SND_SOC_POSSIBLE_DAIFMT_CBP_CFC
* SND_SOC_POSSIBLE_DAIFMT_CBC_CFP
* SND_SOC_POSSIBLE_DAIFMT_CBC_CFC
*/
static const u64 dummy_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |