From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
ASoC is using dai_link which specify DAI format (= dai_link->dai_fmt), and it is selected by Sound Card driver in corrent implementation. In other words, Sound Card needs to setup it.
But, it should be possible to automatically selected from CPU and Codec driver settings.
This patch adds new snd_soc_runtime_get_dai_fmt() and .get_dai_fmt callback for it.
We can still select it via Sound Card driver same as before. If both CPU / Codec driver had .get_dai_fmt callback, dai_fmt will be automatically selected from each driver.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc-dai.h | 35 ++++++++++++++++++++ sound/soc/soc-core.c | 71 +++++++++++++++++++++++++++++++++++++++++ sound/soc/soc-dai.c | 19 +++++++++++ sound/soc/soc-utils.c | 26 +++++++++++++++ 4 files changed, 151 insertions(+)
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 1358a0ceb4d0..0a4afb84f12e 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -36,6 +36,17 @@ struct snd_compr_stream; #define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J #define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
+/* Describes the possible PCM format */ +#define SND_SOC_POSSIBLE_DAIFMT_FORMAT_SHIFT 0 +#define SND_SOC_POSSIBLE_DAIFMT_FORMAT_MASK (0xFFFF << SND_SOC_POSSIBLE_DAIFMT_FORMAT_SHIFT) +#define SND_SOC_POSSIBLE_DAIFMT_I2S (1 << SND_SOC_DAI_FORMAT_I2S) +#define SND_SOC_POSSIBLE_DAIFMT_RIGHT_J (1 << SND_SOC_DAI_FORMAT_RIGHT_J) +#define SND_SOC_POSSIBLE_DAIFMT_LEFT_J (1 << SND_SOC_DAI_FORMAT_LEFT_J) +#define SND_SOC_POSSIBLE_DAIFMT_DSP_A (1 << SND_SOC_DAI_FORMAT_DSP_A) +#define SND_SOC_POSSIBLE_DAIFMT_DSP_B (1 << SND_SOC_DAI_FORMAT_DSP_B) +#define SND_SOC_POSSIBLE_DAIFMT_AC97 (1 << SND_SOC_DAI_FORMAT_AC97) +#define SND_SOC_POSSIBLE_DAIFMT_PDM (1 << SND_SOC_DAI_FORMAT_PDM) + /* * DAI Clock gating. * @@ -45,6 +56,12 @@ struct snd_compr_stream; #define SND_SOC_DAIFMT_CONT (1 << 4) /* continuous clock */ #define SND_SOC_DAIFMT_GATED (0 << 4) /* clock is gated */
+/* Describes the possible PCM format */ +#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_CONT (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT) +#define SND_SOC_POSSIBLE_DAIFMT_GATED (0x0ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT) + /* * DAI hardware signal polarity. * @@ -71,6 +88,14 @@ struct snd_compr_stream; #define SND_SOC_DAIFMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */ #define SND_SOC_DAIFMT_IB_IF (4 << 8) /* invert BCLK + FRM */
+/* Describes the possible PCM format */ +#define SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT 32 +#define SND_SOC_POSSIBLE_DAIFMT_INV_MASK (0xFFFFULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT) +#define SND_SOC_POSSIBLE_DAIFMT_NB_NF (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT) +#define SND_SOC_POSSIBLE_DAIFMT_NB_IF (0x2ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT) +#define SND_SOC_POSSIBLE_DAIFMT_IB_NF (0x4ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT) +#define SND_SOC_POSSIBLE_DAIFMT_IB_IF (0x8ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT) + /* * DAI hardware clock providers/consumers * @@ -89,6 +114,14 @@ struct snd_compr_stream; #define SND_SOC_DAIFMT_CBM_CFS SND_SOC_DAIFMT_CBP_CFC #define SND_SOC_DAIFMT_CBS_CFS 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 @@ -131,6 +164,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio);
/* Digital Audio interface formatting */ +u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai); 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, @@ -236,6 +270,7 @@ struct snd_soc_dai_ops { * DAI format configuration * Called by soc_card drivers, normally in their hw_params. */ + u64 (*get_fmt)(struct snd_soc_dai *dai); int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt); int (*xlate_tdm_slot_mask)(unsigned int slots, unsigned int *tx_mask, unsigned int *rx_mask); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e241c35fb63a..621e82adda90 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1054,6 +1054,76 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime);
+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; + u64 pos, possible_fmt = ULLONG_MAX; + unsigned int mask = 0, dai_fmt = 0; + int i; + + for_each_rtd_dais(rtd, i, dai) + possible_fmt &= snd_soc_dai_get_fmt(dai); + + if (!possible_fmt) + return; + + /* convert POSSIBLE_DAIFMT to DAIFMT */ + for (i = 63; i >= 0; i--) { + pos = 1ULL << i; + switch (possible_fmt & pos) { + 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; + case SND_SOC_POSSIBLE_DAIFMT_CONT: + dai_fmt |= SND_SOC_DAIFMT_CONT; + break; + 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; + 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; + } + } + + /* use original dai_fmt if sound card specify */ + 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_MASTER_MASK)) + mask |= SND_SOC_DAIFMT_MASTER_MASK; + + dai_link->dai_fmt = 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 @@ -1132,6 +1202,7 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, if (ret < 0) return ret;
+ snd_soc_runtime_get_dai_fmt(rtd); if (dai_link->dai_fmt) { ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); if (ret) diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 080fbe053fc5..109a562ebeb9 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -134,6 +134,25 @@ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio);
+/** + * snd_soc_dai_get_fmt - get enable DAI hardware audio format. + * @dai: DAI + * @fmt: SND_SOC_POSSIBLE_DAIFMT_* format value. + * + * Configures the DAI hardware format and clocking. + */ +u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai) +{ + u64 fmt = 0; + + if (dai->driver->ops && + dai->driver->ops->get_fmt) + fmt = dai->driver->ops->get_fmt(dai); + + return fmt; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_get_fmt); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 98383fd76224..b92b1d07ec83 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -97,6 +97,31 @@ static const struct snd_soc_component_driver dummy_codec = { SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static u64 dummy_dai_get_fmt(struct snd_soc_dai *dai) +{ + return 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_AC97 | + SND_SOC_POSSIBLE_DAIFMT_PDM | + SND_SOC_POSSIBLE_DAIFMT_CONT | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF | + SND_SOC_POSSIBLE_DAIFMT_CBP_CFP | + SND_SOC_POSSIBLE_DAIFMT_CBC_CFP | + SND_SOC_POSSIBLE_DAIFMT_CBP_CFC | + SND_SOC_POSSIBLE_DAIFMT_CBC_CFC; +} + +static const struct snd_soc_dai_ops dummy_dai_ops = { + .get_fmt = dummy_dai_get_fmt, +}; + /* * The dummy CODEC is only meant to be used in situations where there is no * actual hardware. @@ -122,6 +147,7 @@ static struct snd_soc_dai_driver dummy_dai = { .rates = STUB_RATES, .formats = STUB_FORMATS, }, + .ops = &dummy_dai_ops, };
int snd_soc_dai_is_dummy(struct snd_soc_dai *dai)