mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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:
commit
fc408ab6e9
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user