}
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
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;
}
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);
}
/**