diff options
| author | Mark Brown <broonie@kernel.org> | 2026-06-12 18:58:00 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-06-12 18:58:00 +0100 |
| commit | fc408ab6e9cd76ad0c9638642d56ba05ab447d79 (patch) | |
| tree | b04c1ae466531d1e1f5e836805d828367b83eb38 | |
| parent | 69b4141b428bcf2cf7a863950c0d6e5c5ae89ac1 (diff) | |
| parent | 442cfd58e71260dcd983e393f750e36fe7ab34d0 (diff) | |
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
| -rw-r--r-- | include/sound/soc-dai.h | 15 | ||||
| -rw-r--r-- | sound/soc/codecs/ak4613.c | 5 | ||||
| -rw-r--r-- | sound/soc/codecs/ak4619.c | 8 | ||||
| -rw-r--r-- | sound/soc/codecs/da7213.c | 5 | ||||
| -rw-r--r-- | sound/soc/codecs/framer-codec.c | 8 | ||||
| -rw-r--r-- | sound/soc/codecs/idt821034.c | 9 | ||||
| -rw-r--r-- | sound/soc/codecs/pcm3168a.c | 8 | ||||
| -rw-r--r-- | sound/soc/codecs/peb2466.c | 9 | ||||
| -rw-r--r-- | sound/soc/generic/audio-graph-card2.c | 12 | ||||
| -rw-r--r-- | sound/soc/generic/test-component.c | 7 | ||||
| -rw-r--r-- | sound/soc/renesas/fsi.c | 5 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/core.c | 12 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/msiof.c | 5 | ||||
| -rw-r--r-- | sound/soc/soc-core.c | 160 | ||||
| -rw-r--r-- | sound/soc/soc-dai.c | 207 | ||||
| -rw-r--r-- | sound/soc/soc-utils.c | 7 |
16 files changed, 201 insertions, 281 deletions
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index df010a91b350..ba3ae56c6b06 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -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, diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 3b198b9b4605..3e0696b5abf5 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -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; diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c index 755c002f0f15..d9c9f6b20028 100644 --- a/sound/soc/codecs/ak4619.c +++ b/sound/soc/codecs/ak4619.c @@ -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, }; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 98b8858ded02..4bf91ab2553a 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -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 | diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c index 6f57a3aeecc8..a87a78390172 100644 --- a/sound/soc/codecs/framer-codec.c +++ b/sound/soc/codecs/framer-codec.c @@ -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 = { diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 39bafefa6a18..084090ccef77 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -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 = { diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 4503f2f0724e..cb6a6f08f2f8 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -564,12 +564,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 */ SND_SOC_POSSIBLE_DAIFMT_I2S | @@ -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, diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 2d5163c15d0d..2d71d204d8fa 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -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 = { diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 0202ed0ee78e..6894bb936cfd 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -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); diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index fc40d024152e..6f9f498c4c5c 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -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 | diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c index fb376569b470..ae86014c3819 100644 --- a/sound/soc/renesas/fsi.c +++ b/sound/soc/renesas/fsi.c @@ -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 | diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index 9ce56cd84f46..b7954746e953 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -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, diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c index 2671abc028cc..128543fc4fc9 100644 --- a/sound/soc/renesas/rcar/msiof.c +++ b/sound/soc/renesas/rcar/msiof.c @@ -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; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ac9b2269c26e..86b6c752a56b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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; diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 1719ddcefa4b..b098d1100b4b 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -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) +{ + int cnt = 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 cnt; +} + +static void soc_dai_auto_select_format(u64 fmt, const struct snd_soc_pcm_runtime *rtd, + int idx, u64 *best_fmt) { struct snd_soc_dai *dai; - int i, max = 0; + const struct snd_soc_dai_ops *ops; + int max_idx = rtd->dai_link->num_cpus + rtd->dai_link->num_codecs; + u64 available_fmt; /* - * return max num if *ALL* DAIs have .auto_selectable_formats + * NOTE + * It doesn't support Multi CPU/Codec for now */ - 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 (rtd->dai_link->num_cpus != 1 || + rtd->dai_link->num_codecs != 1) + return; - return max; -} + if (idx >= max_idx) + return; -/** - * 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) -{ - const struct snd_soc_dai_ops *ops = dai->driver->ops; - u64 fmt = 0; - int i, max = 0, until = priority; + dai = rtd->dais[idx]; + ops = dai->driver->ops; + + /* zero chance of auto select format */ + if (!ops || !ops->num_auto_selectable_formats) + return; /* - * Collect auto_selectable_formats until priority + **************************** + * 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]; + + /* 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); + + 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; - if (ops && ops->auto_selectable_formats) - for (i = 0; i < until; i++) - fmt |= ops->auto_selectable_formats[i]; + soc_dai_auto_select_format(~0, rtd, 0, &possible_fmt); - return fmt; + return soc_dai_convert_possiblefmt_to_daifmt(possible_fmt, dai_link->dai_fmt); } /** diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 9cb7567e263e..87e9c86ca4f8 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -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 | |
