From: Kuninori Morimoto Date: Tue, 9 Jun 2026 00:23:35 +0000 (+0000) Subject: ASoC: update auto format selection method X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c6b09cda390496bcc7bbbc4ec7eec26cf43d3f97;p=thirdparty%2Flinux.git ASoC: update auto format selection method Current DAI supports auto format selection. It allow to have array like below. (X) static u64 xxx_auto_formats[] = { (A) /* First Priority */ SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J, /* Second Priority */ (B) SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; It try to find available format from I2S/LEFT_J first (A). Then, try to find from I2S/LEFT_J/DSP_A/DSP_B if couldn't find (A)+(B). (OR:ed) In this method, it can't handle if there is format combination. For example, some driver has pattern. Pattern1 I2S/RIFHT_J/LEFT_J (FORMAT) and NB_NF/IB_IF/IB_NF/NB_IF (INV)_ Pattern2 DSP_A/DSP_B (FORMAT) and NB_NF/ IB_NF Because it will try to OR Pattern1 and Pattern2, un-supported pattern might be selected. This patch update method not to use OR, and assumes full format array. Above sample (X) need to be static u64 xxx_auto_formats[] = { /* 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, }; Note: It doesn't support Multi CPU/Codec for now Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87jys836k8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index c40823ba5456..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) @@ -184,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/soc-core.c b/sound/soc/soc-core.c index b87219be5860..86b6c752a56b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1284,148 +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; - } - } - - /* - * 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 @@ -1504,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); } /**