ASoC: imx-audmix: Split capture device to be a new

Merge series from Shengjiu Wang <shengjiu.wang@nxp.com>:

The transmitter and receiver part of the SAI interface need to be
configured with different master/slave mode, especially to work
with the audiomix module.

The SAI1 TX is in master mode, but SAI1 RX is in slave mode.
So add another two DAIs for TX and RX separately in fsl_sai driver.

There will be three devices for audiomix sound card, hw:x,0 is
the playback device for one SAI, hw:x,1 is the playback device
for another SAI, hw:x,2 is the capture device for audmix
output.
This commit is contained in:
Mark Brown 2024-06-27 12:30:52 +01:00
commit d2b1955656
No known key found for this signature in database
GPG key ID: 24D68B725D5487D0
4 changed files with 155 additions and 85 deletions

View file

@ -326,15 +326,6 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.capture = {
.stream_name = "AUDMIX-Capture-0",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.ops = &fsl_audmix_dai_ops,
},
{
@ -349,8 +340,13 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.ops = &fsl_audmix_dai_ops,
},
{
.id = 2,
.name = "audmix-2",
.capture = {
.stream_name = "AUDMIX-Capture-1",
.stream_name = "AUDMIX-Capture-0",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,

View file

@ -357,18 +357,18 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_BP_FP:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
sai->is_consumer_mode = false;
sai->is_consumer_mode[tx] = false;
break;
case SND_SOC_DAIFMT_BC_FC:
sai->is_consumer_mode = true;
sai->is_consumer_mode[tx] = true;
break;
case SND_SOC_DAIFMT_BP_FC:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
sai->is_consumer_mode = false;
sai->is_consumer_mode[tx] = false;
break;
case SND_SOC_DAIFMT_BC_FP:
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
sai->is_consumer_mode = true;
sai->is_consumer_mode[tx] = true;
break;
default:
return -EINVAL;
@ -400,6 +400,16 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
return ret;
}
static int fsl_sai_set_dai_fmt_tx(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true);
}
static int fsl_sai_set_dai_fmt_rx(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false);
}
static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
@ -412,7 +422,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
bool support_1_1_ratio = sai->verid.version >= 0x0301;
/* Don't apply to consumer mode */
if (sai->is_consumer_mode)
if (sai->is_consumer_mode[tx])
return 0;
/*
@ -575,7 +585,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
}
}
if (!sai->is_consumer_mode) {
if (!sai->is_consumer_mode[tx]) {
ret = fsl_sai_set_bclk(cpu_dai, tx, bclk);
if (ret)
return ret;
@ -613,7 +623,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
* RCR5(TCR5) for playback(capture), or there will be sync error.
*/
if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) {
if (!sai->is_consumer_mode[tx] && fsl_sai_dir_is_synced(sai, adir)) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
FSL_SAI_CR4_CHMOD_MASK,
@ -683,7 +693,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
* FSD_MSTR bit for this specific case.
*/
if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
!sai->is_consumer_mode)
!sai->is_consumer_mode[tx])
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FSD_MSTR, 0);
@ -697,7 +707,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
/* Enable FSD_MSTR after configuring word width */
if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
!sai->is_consumer_mode)
!sai->is_consumer_mode[tx])
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR);
@ -720,7 +730,7 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
FSL_SAI_CR3_TRCE_MASK, 0);
if (!sai->is_consumer_mode &&
if (!sai->is_consumer_mode[tx] &&
sai->mclk_streams & BIT(substream->stream)) {
clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
sai->mclk_streams &= ~BIT(substream->stream);
@ -759,7 +769,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
* This is a hardware bug, and will be fix in the
* next sai version.
*/
if (!sai->is_consumer_mode) {
if (!sai->is_consumer_mode[tx]) {
/* Software Reset */
regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
/* Clear SR bit to finish the reset */
@ -914,6 +924,30 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.startup = fsl_sai_startup,
};
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_tx_ops = {
.probe = fsl_sai_dai_probe,
.set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt_tx,
.set_tdm_slot = fsl_sai_set_dai_tdm_slot,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
.startup = fsl_sai_startup,
};
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_rx_ops = {
.probe = fsl_sai_dai_probe,
.set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt_rx,
.set_tdm_slot = fsl_sai_set_dai_tdm_slot,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
.startup = fsl_sai_startup,
};
static int fsl_sai_dai_resume(struct snd_soc_component *component)
{
struct fsl_sai *sai = snd_soc_component_get_drvdata(component);
@ -931,7 +965,9 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component)
return 0;
}
static struct snd_soc_dai_driver fsl_sai_dai_template = {
static struct snd_soc_dai_driver fsl_sai_dai_template[] = {
{
.name = "sai-tx-rx",
.playback = {
.stream_name = "CPU-Playback",
.channels_min = 1,
@ -951,6 +987,33 @@ static struct snd_soc_dai_driver fsl_sai_dai_template = {
.formats = FSL_SAI_FORMATS,
},
.ops = &fsl_sai_pcm_dai_ops,
},
{
.name = "sai-tx",
.playback = {
.stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 32,
.rate_min = 8000,
.rate_max = 2822400,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_SAI_FORMATS,
},
.ops = &fsl_sai_pcm_dai_tx_ops,
},
{
.name = "sai-rx",
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 32,
.rate_min = 8000,
.rate_max = 2822400,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_SAI_FORMATS,
},
.ops = &fsl_sai_pcm_dai_rx_ops,
},
};
static const struct snd_soc_component_driver fsl_component = {
@ -1399,15 +1462,15 @@ static int fsl_sai_probe(struct platform_device *pdev)
return ret;
}
memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template,
sizeof(fsl_sai_dai_template));
memcpy(&sai->cpu_dai_drv, fsl_sai_dai_template,
sizeof(*fsl_sai_dai_template) * ARRAY_SIZE(fsl_sai_dai_template));
/* Sync Tx with Rx as default by following old DT binding */
sai->synchronous[RX] = true;
sai->synchronous[TX] = false;
sai->cpu_dai_drv.symmetric_rate = 1;
sai->cpu_dai_drv.symmetric_channels = 1;
sai->cpu_dai_drv.symmetric_sample_bits = 1;
sai->cpu_dai_drv[0].symmetric_rate = 1;
sai->cpu_dai_drv[0].symmetric_channels = 1;
sai->cpu_dai_drv[0].symmetric_sample_bits = 1;
if (of_property_read_bool(np, "fsl,sai-synchronous-rx") &&
of_property_read_bool(np, "fsl,sai-asynchronous")) {
@ -1424,9 +1487,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
/* Discard all settings for asynchronous mode */
sai->synchronous[RX] = false;
sai->synchronous[TX] = false;
sai->cpu_dai_drv.symmetric_rate = 0;
sai->cpu_dai_drv.symmetric_channels = 0;
sai->cpu_dai_drv.symmetric_sample_bits = 0;
sai->cpu_dai_drv[0].symmetric_rate = 0;
sai->cpu_dai_drv[0].symmetric_channels = 0;
sai->cpu_dai_drv[0].symmetric_sample_bits = 0;
}
sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output");
@ -1505,7 +1568,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_component(dev, &fsl_component,
&sai->cpu_dai_drv, 1);
sai->cpu_dai_drv, ARRAY_SIZE(fsl_sai_dai_template));
if (ret)
goto err_pm_get_sync;

View file

@ -282,7 +282,7 @@ struct fsl_sai {
struct clk *pll11k_clk;
struct resource *res;
bool is_consumer_mode;
bool is_consumer_mode[2];
bool is_lsb_first;
bool is_dsp_mode;
bool is_pdm_mode;
@ -299,7 +299,7 @@ struct fsl_sai {
unsigned int bclk_ratio;
const struct fsl_sai_soc_data *soc_data;
struct snd_soc_dai_driver cpu_dai_drv;
struct snd_soc_dai_driver cpu_dai_drv[3];
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct fsl_sai_verid verid;

View file

@ -140,6 +140,13 @@ static const struct snd_soc_ops imx_audmix_be_ops = {
.hw_params = imx_audmix_be_hw_params,
};
static const char *name[][3] = {
{"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"},
{"sai-tx", "sai-tx", "sai-rx"},
{"AUDMIX-Playback-0", "AUDMIX-Playback-1", "CPU-Capture"},
{"CPU-Playback", "CPU-Playback", "AUDMIX-Capture-0"},
};
static int imx_audmix_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@ -150,7 +157,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
struct imx_audmix *priv;
int i, num_dai, ret;
const char *fe_name_pref = "HiFi-AUDMIX-FE-";
char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name;
char *be_name, *dai_name;
if (pdev->dev.parent) {
audmix_np = pdev->dev.parent->of_node;
@ -183,6 +190,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
num_dai += 1;
priv->num_dai = 2 * num_dai;
priv->dai = devm_kcalloc(&pdev->dev, priv->num_dai,
sizeof(struct snd_soc_dai_link), GFP_KERNEL);
@ -196,7 +204,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
if (!priv->dai_conf)
return -ENOMEM;
priv->num_dapm_routes = 3 * num_dai;
priv->num_dapm_routes = num_dai;
priv->dapm_routes = devm_kcalloc(&pdev->dev, priv->num_dapm_routes,
sizeof(struct snd_soc_dapm_route),
GFP_KERNEL);
@ -211,6 +219,10 @@ static int imx_audmix_probe(struct platform_device *pdev)
if (!dlc)
return -ENOMEM;
if (i == num_dai - 1)
ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, 0,
&args);
else
ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
&args);
if (ret < 0) {
@ -226,20 +238,14 @@ static int imx_audmix_probe(struct platform_device *pdev)
put_device(&cpu_pdev->dev);
dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
fe_name_pref, args.np->full_name + 1);
fe_name_pref, args.np->full_name);
if (!dai_name)
return -ENOMEM;
dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
if (i == 0) {
if (i == num_dai - 1)
out_cpu_np = args.np;
capture_dai_name =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, "CPU-Capture");
if (!capture_dai_name)
return -ENOMEM;
}
/*
* CPU == Platform
@ -252,27 +258,23 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->dai[i].num_cpus = 1;
priv->dai[i].num_codecs = 1;
priv->dai[i].num_platforms = 1;
priv->dai[i].name = dai_name;
priv->dai[i].name = name[0][i];
priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
priv->dai[i].cpus->of_node = args.np;
priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev);
priv->dai[i].cpus->dai_name = name[1][i];
priv->dai[i].dynamic = 1;
priv->dai[i].dpcm_playback = 1;
priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0);
if (i == num_dai - 1) {
priv->dai[i].dpcm_capture = 1;
priv->dai[i].dpcm_playback = 0;
}
priv->dai[i].ignore_pmdown_time = 1;
priv->dai[i].ops = &imx_audmix_fe_ops;
/* Add AUDMIX Backend */
be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"audmix-%d", i);
be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"AUDMIX-Playback-%d", i);
be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"AUDMIX-Capture-%d", i);
if (!be_name || !be_pb || !be_cp)
return -ENOMEM;
priv->dai[num_dai + i].cpus = &dlc[1];
priv->dai[num_dai + i].codecs = &snd_soc_dummy_dlc;
@ -284,24 +286,33 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->dai[num_dai + i].cpus->dai_name = be_name;
priv->dai[num_dai + i].no_pcm = 1;
priv->dai[num_dai + i].dpcm_playback = 1;
if (i == num_dai - 1) {
priv->dai[num_dai + i].dpcm_capture = 1;
priv->dai[num_dai + i].dpcm_playback = 0;
}
priv->dai[num_dai + i].ignore_pmdown_time = 1;
priv->dai[num_dai + i].ops = &imx_audmix_be_ops;
priv->dai_conf[i].dlc.of_node = args.np;
priv->dai_conf[i].name_prefix = dai_name;
if (i == num_dai - 1) {
priv->dapm_routes[i].sink =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, name[2][i]);
if (!priv->dapm_routes[i].sink)
return -ENOMEM;
priv->dapm_routes[i].source = name[3][i];
} else {
priv->dapm_routes[i].source =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, "CPU-Playback");
dai_name, name[3][i]);
if (!priv->dapm_routes[i].source)
return -ENOMEM;
priv->dapm_routes[i].sink = be_pb;
priv->dapm_routes[num_dai + i].source = be_pb;
priv->dapm_routes[num_dai + i].sink = be_cp;
priv->dapm_routes[2 * num_dai + i].source = be_cp;
priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name;
priv->dapm_routes[i].sink = name[2][i];
}
}
cpu_pdev = of_find_device_by_node(out_cpu_np);