* [PATCH 00/10] Add support for non-interleaved mode in qmc_audio
@ 2024-06-20 8:42 Herve Codina
2024-06-20 8:42 ` [PATCH 01/10] ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value Herve Codina
` (9 more replies)
0 siblings, 10 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
The qmc_audio driver supports only audio in interleaved mode.
Non-interleaved mode can be easily supported using several QMC channel
per DAI. In that case, data related to ch0 are sent to (received from)
the first QMC channel, data related to ch1 use the next QMC channel and
so on up to the last channel.
In terms of constraints and settings, the interleaved and
non-interleaved modes are slightly different.
In interleaved mode:
- The sample size should fit in the number of time-slots available for
the QMC channel.
- The number of audio channels should fit in the number of time-slots
(taking into account the sample size) available for the QMC channel.
In non-interleaved mode:
- The number of audio channels is the number of available QMC
channels.
- Each QMC channel should have the same number of time-slots.
- The sample size equals the number of time-slots of one QMC channel.
This series add support for the non-interleaved mode in the qmc_audio
driver and is composed of the following parts:
- Patches 1 and 2: Fix some issues in the qmc_audio
- Patches 3 to 6: Prepare qmc_audio for the non-interleaved mode
- Patches 7 and 8: Extend the QMC driver API
- Patches 9 and 10: The support for non-interleaved mode itself
Best regards,
Hervé
Herve Codina (10):
ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value
ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch
ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer
handling
ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in
completion routines
ASoC: fsl: fsl_qmc_audio: Introduce
qmc_audio_pcm_{read,write}_submit()
ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved()
soc: fsl: cpm1: qmc: Introduce functions to get a channel from a
phandle list
soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles()
dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC
channels per DAI
ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode.
.../bindings/sound/fsl,qmc-audio.yaml | 41 +-
drivers/soc/fsl/qe/qmc.c | 32 +-
include/soc/fsl/qe/qmc.h | 27 +-
sound/soc/fsl/fsl_qmc_audio.c | 590 +++++++++++++-----
4 files changed, 505 insertions(+), 185 deletions(-)
--
2.45.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 01/10] ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 02/10] ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch Herve Codina
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni, stable
devm_kasprintf() can return a NULL pointer on failure but this returned
value is not checked.
Fix this lack and check the returned value.
Fixes: 075c7125b11c ("ASoC: fsl: Add support for QMC audio")
Cc: stable@vger.kernel.org
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index bfaaa451735b..dd90ef16fa97 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -604,6 +604,8 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d",
np->parent->name, qmc_dai->id);
+ if (!qmc_dai->name)
+ return -ENOMEM;
qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
"fsl,qmc-chan");
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 02/10] ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
2024-06-20 8:42 ` [PATCH 01/10] ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 03/10] ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer handling Herve Codina
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
./scripts/checkpatch.pl --strict --codespell detected several issues
when running on the fsl_qmc_audio.c file:
- CHECK: spaces preferred around that '*' (ctx:VxV)
- CHECK: Alignment should match open parenthesis
- CHECK: Comparison to NULL could be written "!prtd"
- CHECK: spaces preferred around that '/' (ctx:VxV)
- CHECK: Lines should not end with a '('
- CHECK: Please don't use multiple blank lines
Some of them are present several times.
Fix all of these issues without any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 65 +++++++++++++++++------------------
1 file changed, 31 insertions(+), 34 deletions(-)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index dd90ef16fa97..917a32389f3d 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -54,7 +54,7 @@ static int qmc_audio_pcm_construct(struct snd_soc_component *component,
return ret;
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev,
- 64*1024, 64*1024);
+ 64 * 1024, 64 * 1024);
return 0;
}
@@ -89,8 +89,8 @@ static void qmc_audio_pcm_write_complete(void *context)
prtd->period_ptr_submitted = prtd->dma_buffer_start;
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_write_complete, prtd);
+ prtd->period_ptr_submitted, prtd->period_size,
+ qmc_audio_pcm_write_complete, prtd);
if (ret) {
dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
ret);
@@ -118,8 +118,8 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i
prtd->period_ptr_submitted = prtd->dma_buffer_start;
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_read_complete, prtd);
+ prtd->period_ptr_submitted, prtd->period_size,
+ qmc_audio_pcm_read_complete, prtd);
if (ret) {
dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
ret);
@@ -144,8 +144,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* Submit first chunk ... */
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_write_complete, prtd);
+ prtd->period_ptr_submitted, prtd->period_size,
+ qmc_audio_pcm_write_complete, prtd);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
ret);
@@ -159,8 +159,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
/* ... and send it */
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_write_complete, prtd);
+ prtd->period_ptr_submitted, prtd->period_size,
+ qmc_audio_pcm_write_complete, prtd);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
ret);
@@ -169,8 +169,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
} else {
/* Submit first chunk ... */
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_read_complete, prtd);
+ prtd->period_ptr_submitted, prtd->period_size,
+ qmc_audio_pcm_read_complete, prtd);
if (ret) {
dev_err(component->dev, "read_submit failed %d\n",
ret);
@@ -184,8 +184,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
/* ... and send it */
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_read_complete, prtd);
+ prtd->period_ptr_submitted, prtd->period_size,
+ qmc_audio_pcm_read_complete, prtd);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
ret);
@@ -220,8 +220,8 @@ static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *compone
}
static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
- const struct of_phandle_args *args,
- const char **dai_name)
+ const struct of_phandle_args *args,
+ const char **dai_name)
{
struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev);
struct snd_soc_dai_driver *dai_driver;
@@ -245,10 +245,10 @@ static const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE,
.period_bytes_min = 32,
- .period_bytes_max = 64*1024,
+ .period_bytes_max = 64 * 1024,
.periods_min = 2,
- .periods_max = 2*1024,
- .buffer_bytes_max = 64*1024,
+ .periods_max = 2 * 1024,
+ .buffer_bytes_max = 64 * 1024,
};
static int qmc_audio_pcm_open(struct snd_soc_component *component,
@@ -266,7 +266,7 @@ static int qmc_audio_pcm_open(struct snd_soc_component *component,
return ret;
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
- if (prtd == NULL)
+ if (!prtd)
return -ENOMEM;
runtime->private_data = prtd;
@@ -329,13 +329,13 @@ static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai,
ch.max = nb_ts;
break;
case 16:
- ch.max = nb_ts/2;
+ ch.max = nb_ts / 2;
break;
case 32:
- ch.max = nb_ts/4;
+ ch.max = nb_ts / 4;
break;
case 64:
- ch.max = nb_ts/8;
+ ch.max = nb_ts / 8;
break;
default:
dev_err(qmc_dai->dev, "format physical width %u not supported\n",
@@ -356,9 +356,8 @@ static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params
return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts);
}
-static int qmc_dai_hw_rule_capture_channels_by_format(
- struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
+static int qmc_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
{
struct qmc_dai *qmc_dai = rule->private;
@@ -394,18 +393,16 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai,
return snd_mask_refine(f_old, &f_new);
}
-static int qmc_dai_hw_rule_playback_format_by_channels(
- struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
+static int qmc_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
{
struct qmc_dai *qmc_dai = rule->private;
return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts);
}
-static int qmc_dai_hw_rule_capture_format_by_channels(
- struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
+static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
{
struct qmc_dai *qmc_dai = rule->private;
@@ -413,7 +410,7 @@ static int qmc_dai_hw_rule_capture_format_by_channels(
}
static int qmc_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+ struct snd_soc_dai *dai)
{
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
@@ -587,7 +584,8 @@ static u64 qmc_audio_formats(u8 nb_ts)
}
static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np,
- struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver)
+ struct qmc_dai *qmc_dai,
+ struct snd_soc_dai_driver *qmc_soc_dai_driver)
{
struct qmc_chan_info info;
u32 val;
@@ -704,7 +702,6 @@ static int qmc_audio_probe(struct platform_device *pdev)
i++;
}
-
platform_set_drvdata(pdev, qmc_audio);
ret = devm_snd_soc_register_component(qmc_audio->dev,
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 03/10] ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer handling
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
2024-06-20 8:42 ` [PATCH 01/10] ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value Herve Codina
2024-06-20 8:42 ` [PATCH 02/10] ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 04/10] ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in completion routines Herve Codina
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
The driver mixes some internal values for channel DMA buffer handling
and PCM pointer handling. In the currently supported interleaved mode,
this mix does not lead to any issues but in order to prepare the
support for the non-interleaved mode, having them clearly separated will
ease the support and avoid additional computation to convert values used
in channel DMA buffer management in values usable for PCM pointer.
Use a specific set of variable for PCM pointer handling and an other set
for channel DMA buffer.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 84 +++++++++++++++++++----------------
1 file changed, 46 insertions(+), 38 deletions(-)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index 917a32389f3d..e8281e548746 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -35,11 +35,16 @@ struct qmc_audio {
struct qmc_dai_prtd {
struct qmc_dai *qmc_dai;
- dma_addr_t dma_buffer_start;
- dma_addr_t period_ptr_submitted;
- dma_addr_t period_ptr_ended;
- dma_addr_t dma_buffer_end;
- size_t period_size;
+
+ snd_pcm_uframes_t buffer_ended;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
+
+ dma_addr_t ch_dma_addr_start;
+ dma_addr_t ch_dma_addr_current;
+ dma_addr_t ch_dma_addr_end;
+ size_t ch_dma_size;
+
struct snd_pcm_substream *substream;
};
@@ -65,13 +70,17 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime;
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
- prtd->dma_buffer_start = runtime->dma_addr;
- prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(params);
- prtd->period_size = params_period_bytes(params);
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
- prtd->period_ptr_ended = prtd->dma_buffer_start;
prtd->substream = substream;
+ prtd->buffer_ended = 0;
+ prtd->buffer_size = params_buffer_size(params);
+ prtd->period_size = params_period_size(params);
+
+ prtd->ch_dma_addr_start = runtime->dma_addr;
+ prtd->ch_dma_addr_end = runtime->dma_addr + params_buffer_bytes(params);
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+ prtd->ch_dma_size = params_period_bytes(params);
+
return 0;
}
@@ -80,16 +89,16 @@ static void qmc_audio_pcm_write_complete(void *context)
struct qmc_dai_prtd *prtd = context;
int ret;
- prtd->period_ptr_ended += prtd->period_size;
- if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
- prtd->period_ptr_ended = prtd->dma_buffer_start;
+ prtd->buffer_ended += prtd->period_size;
+ if (prtd->buffer_ended >= prtd->buffer_size)
+ prtd->buffer_ended = 0;
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
qmc_audio_pcm_write_complete, prtd);
if (ret) {
dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
@@ -104,21 +113,21 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i
struct qmc_dai_prtd *prtd = context;
int ret;
- if (length != prtd->period_size) {
+ if (length != prtd->ch_dma_size) {
dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
- length, prtd->period_size);
+ length, prtd->ch_dma_size);
}
- prtd->period_ptr_ended += prtd->period_size;
- if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
- prtd->period_ptr_ended = prtd->dma_buffer_start;
+ prtd->buffer_ended += prtd->period_size;
+ if (prtd->buffer_ended >= prtd->buffer_size)
+ prtd->buffer_ended = 0;
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
qmc_audio_pcm_read_complete, prtd);
if (ret) {
dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
@@ -144,7 +153,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* Submit first chunk ... */
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
qmc_audio_pcm_write_complete, prtd);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
@@ -153,13 +162,13 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
}
/* ... prepare next one ... */
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
qmc_audio_pcm_write_complete, prtd);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
@@ -169,7 +178,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
} else {
/* Submit first chunk ... */
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
qmc_audio_pcm_read_complete, prtd);
if (ret) {
dev_err(component->dev, "read_submit failed %d\n",
@@ -178,13 +187,13 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
}
/* ... prepare next one ... */
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
qmc_audio_pcm_read_complete, prtd);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
@@ -215,8 +224,7 @@ static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *compone
{
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
- return bytes_to_frames(substream->runtime,
- prtd->period_ptr_ended - prtd->dma_buffer_start);
+ return prtd->buffer_ended;
}
static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 04/10] ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in completion routines
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (2 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 03/10] ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer handling Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 05/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_audio_pcm_{read,write}_submit() Herve Codina
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
The current QMC audio driver uses only one QMC channel per DAI. The
context used by QMC channel transfer (read and write) completion
routines does not contains any QMC channel and the only one available
per DAI is used to schedule the next transfer.
This works pretty well with only one QMC channel per DAI.
The future support for non-inlerleave mode will use several QMC channel
per DAI. In that case, QMC channel transfer completion routines need to
identify the QMC channel related to the completion.
In order to fill this lack, even if identifying the current QMC channel
among several QMC channels is not needed for the current code, add one
indirection level and introduce the qmc_dai_chan data structrure.
This structure contains the QMC channel involved in the completion and
refererences to the runtime context (capture and playback) used by the
DAI.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 72 +++++++++++++++++++++++------------
1 file changed, 47 insertions(+), 25 deletions(-)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index e8281e548746..b07770257bad 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -17,13 +17,19 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
+struct qmc_dai_chan {
+ struct qmc_dai_prtd *prtd_tx;
+ struct qmc_dai_prtd *prtd_rx;
+ struct qmc_chan *qmc_chan;
+};
+
struct qmc_dai {
char *name;
int id;
struct device *dev;
- struct qmc_chan *qmc_chan;
unsigned int nb_tx_ts;
unsigned int nb_rx_ts;
+ struct qmc_dai_chan chan;
};
struct qmc_audio {
@@ -86,9 +92,12 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
static void qmc_audio_pcm_write_complete(void *context)
{
- struct qmc_dai_prtd *prtd = context;
+ struct qmc_dai_chan *chan = context;
+ struct qmc_dai_prtd *prtd;
int ret;
+ prtd = chan->prtd_tx;
+
prtd->buffer_ended += prtd->period_size;
if (prtd->buffer_ended >= prtd->buffer_size)
prtd->buffer_ended = 0;
@@ -97,9 +106,10 @@ static void qmc_audio_pcm_write_complete(void *context)
if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
- ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
+ ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete, prtd);
+ qmc_audio_pcm_write_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
ret);
@@ -110,9 +120,12 @@ static void qmc_audio_pcm_write_complete(void *context)
static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
{
- struct qmc_dai_prtd *prtd = context;
+ struct qmc_dai_chan *chan = context;
+ struct qmc_dai_prtd *prtd;
int ret;
+ prtd = chan->prtd_rx;
+
if (length != prtd->ch_dma_size) {
dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
length, prtd->ch_dma_size);
@@ -126,9 +139,10 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i
if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
- ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
+ ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete, prtd);
+ qmc_audio_pcm_read_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
ret);
@@ -151,10 +165,13 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->qmc_dai->chan.prtd_tx = prtd;
+
/* Submit first chunk ... */
- ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
+ ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete, prtd);
+ qmc_audio_pcm_write_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
ret);
@@ -167,19 +184,23 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
- ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
+ ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete, prtd);
+ qmc_audio_pcm_write_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
ret);
return ret;
}
} else {
+ prtd->qmc_dai->chan.prtd_rx = prtd;
+
/* Submit first chunk ... */
- ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
+ ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete, prtd);
+ qmc_audio_pcm_read_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
dev_err(component->dev, "read_submit failed %d\n",
ret);
@@ -192,9 +213,10 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
- ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
+ ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete, prtd);
+ qmc_audio_pcm_read_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
dev_err(component->dev, "write_submit failed %d\n",
ret);
@@ -489,7 +511,7 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
chan_param.mode = QMC_TRANSPARENT;
chan_param.transp.max_rx_buf_size = params_period_bytes(params);
- ret = qmc_chan_set_param(qmc_dai->qmc_chan, &chan_param);
+ ret = qmc_chan_set_param(qmc_dai->chan.qmc_chan, &chan_param);
if (ret) {
dev_err(dai->dev, "set param failed %d\n",
ret);
@@ -520,23 +542,23 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = qmc_chan_start(qmc_dai->qmc_chan, direction);
+ ret = qmc_chan_start(qmc_dai->chan.qmc_chan, direction);
if (ret)
return ret;
break;
case SNDRV_PCM_TRIGGER_STOP:
- ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
+ ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction);
if (ret)
return ret;
- ret = qmc_chan_reset(qmc_dai->qmc_chan, direction);
+ ret = qmc_chan_reset(qmc_dai->chan.qmc_chan, direction);
if (ret)
return ret;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
+ ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction);
if (ret)
return ret;
break;
@@ -613,10 +635,10 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
if (!qmc_dai->name)
return -ENOMEM;
- qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
- "fsl,qmc-chan");
- if (IS_ERR(qmc_dai->qmc_chan)) {
- ret = PTR_ERR(qmc_dai->qmc_chan);
+ qmc_dai->chan.qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
+ "fsl,qmc-chan");
+ if (IS_ERR(qmc_dai->chan.qmc_chan)) {
+ ret = PTR_ERR(qmc_dai->chan.qmc_chan);
return dev_err_probe(qmc_audio->dev, ret,
"dai %d get QMC channel failed\n", qmc_dai->id);
}
@@ -624,7 +646,7 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
qmc_soc_dai_driver->id = qmc_dai->id;
qmc_soc_dai_driver->name = qmc_dai->name;
- ret = qmc_chan_get_info(qmc_dai->qmc_chan, &info);
+ ret = qmc_chan_get_info(qmc_dai->chan.qmc_chan, &info);
if (ret) {
dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
qmc_dai->id, ret);
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 05/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_audio_pcm_{read,write}_submit()
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (3 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 04/10] ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in completion routines Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 06/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved() Herve Codina
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
Submitting data to QMC channels is done in several places: transfer
completions and DAI start. The operation done is simple and consist in
one function call.
With the future introduction of support for non-interleaved mode,
submitting data will be more complex.
To avoid copy/paste of code in several places, introduce
qmc_audio_pcm_{read,write}_submit() whose goal is to handle this
data submission.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 93 +++++++++++++++++------------------
1 file changed, 45 insertions(+), 48 deletions(-)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index b07770257bad..36145f1ddbf1 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -90,11 +90,29 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
return 0;
}
+static void qmc_audio_pcm_write_complete(void *context);
+
+static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
+{
+ int ret;
+
+ ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
+ qmc_audio_pcm_write_complete,
+ &prtd->qmc_dai->chan);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static void qmc_audio_pcm_write_complete(void *context)
{
struct qmc_dai_chan *chan = context;
struct qmc_dai_prtd *prtd;
- int ret;
prtd = chan->prtd_tx;
@@ -106,23 +124,33 @@ static void qmc_audio_pcm_write_complete(void *context)
if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
- ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete,
- &prtd->qmc_dai->chan);
+ qmc_audio_pcm_write_submit(prtd);
+
+ snd_pcm_period_elapsed(prtd->substream);
+}
+
+static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags);
+
+static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
+{
+ int ret;
+
+ ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
+ prtd->ch_dma_addr_current, prtd->ch_dma_size,
+ qmc_audio_pcm_read_complete,
+ &prtd->qmc_dai->chan);
if (ret) {
- dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
+ dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
ret);
}
- snd_pcm_period_elapsed(prtd->substream);
+ return 0;
}
static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
{
struct qmc_dai_chan *chan = context;
struct qmc_dai_prtd *prtd;
- int ret;
prtd = chan->prtd_rx;
@@ -139,14 +167,7 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i
if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
- ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
- ret);
- }
+ qmc_audio_pcm_read_submit(prtd);
snd_pcm_period_elapsed(prtd->substream);
}
@@ -168,15 +189,9 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
prtd->qmc_dai->chan.prtd_tx = prtd;
/* Submit first chunk ... */
- ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(component->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_write_submit(prtd);
+ if (ret)
return ret;
- }
/* ... prepare next one ... */
prtd->ch_dma_addr_current += prtd->ch_dma_size;
@@ -184,28 +199,16 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
- ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(component->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_write_submit(prtd);
+ if (ret)
return ret;
- }
} else {
prtd->qmc_dai->chan.prtd_rx = prtd;
/* Submit first chunk ... */
- ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(component->dev, "read_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_read_submit(prtd);
+ if (ret)
return ret;
- }
/* ... prepare next one ... */
prtd->ch_dma_addr_current += prtd->ch_dma_size;
@@ -213,15 +216,9 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
- ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(component->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_read_submit(prtd);
+ if (ret)
return ret;
- }
}
break;
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 06/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved()
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (4 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 05/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_audio_pcm_{read,write}_submit() Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 07/10] soc: fsl: cpm1: qmc: Introduce functions to get a channel from a phandle list Herve Codina
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
Constraints are set by qmc_dai_startup(). These constraints are specific
to the interleaved mode.
With the future introduction of support for non-interleaved mode, a new
set of constraints will be set. To make the code clear and keep
qmc_dai_startup() simple, extract the current interleaved mode
constraints settings to a specific function.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 37 +++++++++++++++++++++--------------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index 36145f1ddbf1..f70c6c8eec4a 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -436,24 +436,14 @@ static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *
return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts);
}
-static int qmc_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream,
+ struct qmc_dai *qmc_dai)
{
- struct qmc_dai_prtd *prtd = substream->runtime->private_data;
snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
- struct qmc_dai *qmc_dai;
unsigned int frame_bits;
int ret;
- qmc_dai = qmc_dai_get_data(dai);
- if (!qmc_dai) {
- dev_err(dai->dev, "Invalid dai\n");
- return -EINVAL;
- }
-
- prtd->qmc_dai = qmc_dai;
-
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format;
hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels;
@@ -468,7 +458,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
hw_rule_channels_by_format, qmc_dai,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (ret) {
- dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
+ dev_err(qmc_dai->dev, "Failed to add channels rule (%d)\n", ret);
return ret;
}
@@ -476,7 +466,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
hw_rule_format_by_channels, qmc_dai,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (ret) {
- dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
+ dev_err(qmc_dai->dev, "Failed to add format rule (%d)\n", ret);
return ret;
}
@@ -484,13 +474,30 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_FRAME_BITS,
frame_bits);
if (ret < 0) {
- dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
return ret;
}
return 0;
}
+static int qmc_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ struct qmc_dai *qmc_dai;
+
+ qmc_dai = qmc_dai_get_data(dai);
+ if (!qmc_dai) {
+ dev_err(dai->dev, "Invalid dai\n");
+ return -EINVAL;
+ }
+
+ prtd->qmc_dai = qmc_dai;
+
+ return qmc_dai_constraints_interleaved(substream, qmc_dai);
+}
+
static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 07/10] soc: fsl: cpm1: qmc: Introduce functions to get a channel from a phandle list
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (5 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 06/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved() Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 08/10] soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles() Herve Codina
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
qmc_chan_get_byphandle() and the resource managed version retrieve a
channel from a simple phandle.
Extend the API and introduce qmc_chan_get_byphandles_index() and the
resource managed version in order to retrieve a channel from a phandle
list using the provided index to identify the phandle in the list.
Also update qmc_chan_get_byphandle() and the resource managed version to
use qmc_chan_get_byphandles_index() and so avoid code duplication.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
drivers/soc/fsl/qe/qmc.c | 19 +++++++++++--------
include/soc/fsl/qe/qmc.h | 25 ++++++++++++++++++++++---
2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c
index f498db9abe35..e23d60018400 100644
--- a/drivers/soc/fsl/qe/qmc.c
+++ b/drivers/soc/fsl/qe/qmc.c
@@ -1777,13 +1777,15 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign
return qmc_chan;
}
-struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name)
+struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
+ const char *phandles_name,
+ int index)
{
struct of_phandle_args out_args;
struct qmc_chan *qmc_chan;
int ret;
- ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0,
+ ret = of_parse_phandle_with_fixed_args(np, phandles_name, 1, index,
&out_args);
if (ret < 0)
return ERR_PTR(ret);
@@ -1797,7 +1799,7 @@ struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phan
of_node_put(out_args.np);
return qmc_chan;
}
-EXPORT_SYMBOL(qmc_chan_get_byphandle);
+EXPORT_SYMBOL(qmc_chan_get_byphandles_index);
struct qmc_chan *qmc_chan_get_bychild(struct device_node *np)
{
@@ -1827,9 +1829,10 @@ static void devm_qmc_chan_release(struct device *dev, void *res)
qmc_chan_put(*qmc_chan);
}
-struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
- struct device_node *np,
- const char *phandle_name)
+struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev,
+ struct device_node *np,
+ const char *phandles_name,
+ int index)
{
struct qmc_chan *qmc_chan;
struct qmc_chan **dr;
@@ -1838,7 +1841,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
if (!dr)
return ERR_PTR(-ENOMEM);
- qmc_chan = qmc_chan_get_byphandle(np, phandle_name);
+ qmc_chan = qmc_chan_get_byphandles_index(np, phandles_name, index);
if (!IS_ERR(qmc_chan)) {
*dr = qmc_chan;
devres_add(dev, dr);
@@ -1848,7 +1851,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
return qmc_chan;
}
-EXPORT_SYMBOL(devm_qmc_chan_get_byphandle);
+EXPORT_SYMBOL(devm_qmc_chan_get_byphandles_index);
struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev,
struct device_node *np)
diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h
index 2a333fc1ea81..0fa7205145ce 100644
--- a/include/soc/fsl/qe/qmc.h
+++ b/include/soc/fsl/qe/qmc.h
@@ -16,11 +16,30 @@ struct device_node;
struct device;
struct qmc_chan;
-struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name);
+struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
+ const char *phandles_name,
+ int index);
+struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev,
+ struct device_node *np,
+ const char *phandles_name,
+ int index);
+
+static inline struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np,
+ const char *phandle_name)
+{
+ return qmc_chan_get_byphandles_index(np, phandle_name, 0);
+}
+
+static inline struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
+ struct device_node *np,
+ const char *phandle_name)
+{
+ return devm_qmc_chan_get_byphandles_index(dev, np, phandle_name, 0);
+}
+
struct qmc_chan *qmc_chan_get_bychild(struct device_node *np);
void qmc_chan_put(struct qmc_chan *chan);
-struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np,
- const char *phandle_name);
+
struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np);
enum qmc_mode {
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 08/10] soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles()
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (6 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 07/10] soc: fsl: cpm1: qmc: Introduce functions to get a channel from a phandle list Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-20 8:42 ` [PATCH 09/10] dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI Herve Codina
2024-06-20 8:42 ` [PATCH 10/10] ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode Herve Codina
9 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
No function in the QMC API is available to get the number of phandles
present in a phandle list.
Fill this lack introducing qmc_chan_count_phandles().
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
drivers/soc/fsl/qe/qmc.c | 13 +++++++++++++
include/soc/fsl/qe/qmc.h | 2 ++
2 files changed, 15 insertions(+)
diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c
index e23d60018400..76bb496305a0 100644
--- a/drivers/soc/fsl/qe/qmc.c
+++ b/drivers/soc/fsl/qe/qmc.c
@@ -1777,6 +1777,19 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign
return qmc_chan;
}
+int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name)
+{
+ int count;
+
+ /* phandles are fixed args phandles with one arg */
+ count = of_count_phandle_with_args(np, phandles_name, NULL);
+ if (count < 0)
+ return count;
+
+ return count / 2;
+}
+EXPORT_SYMBOL(qmc_chan_count_phandles);
+
struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
const char *phandles_name,
int index)
diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h
index 0fa7205145ce..294e42ea8d4c 100644
--- a/include/soc/fsl/qe/qmc.h
+++ b/include/soc/fsl/qe/qmc.h
@@ -16,6 +16,8 @@ struct device_node;
struct device;
struct qmc_chan;
+int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name);
+
struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
const char *phandles_name,
int index);
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 09/10] dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (7 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 08/10] soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles() Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-06-27 21:25 ` Rob Herring (Arm)
2024-06-20 8:42 ` [PATCH 10/10] ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode Herve Codina
9 siblings, 1 reply; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
The QMC audio uses one QMC channel per DAI and uses this QMC channel to
transmit interleaved audio channel samples.
In order to work in non-interleave mode, a QMC audio DAI needs to use
multiple QMC channels. In that case, the DAI maps each QMC channel to
exactly one audio channel.
Allow QMC audio DAIs with multiple QMC channels attached.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
.../bindings/sound/fsl,qmc-audio.yaml | 41 ++++++++++++++++---
1 file changed, 35 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml
index b522ed7dcc51..a23e49198c37 100644
--- a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml
@@ -12,7 +12,9 @@ maintainers:
description: |
The QMC audio is an ASoC component which uses QMC (QUICC Multichannel
Controller) channels to transfer the audio data.
- It provides as many DAI as the number of QMC channel used.
+ It provides several DAIs. For each DAI, the DAI is working in interleaved mode
+ if only one QMC channel is used by the DAI or it is working in non-interleaved
+ mode if several QMC channels are used by the DAI.
allOf:
- $ref: dai-common.yaml#
@@ -45,12 +47,19 @@ patternProperties:
fsl,qmc-chan:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- - items:
- - description: phandle to QMC node
- - description: Channel number
+ items:
+ - description: phandle to QMC node
+ - description: Channel number
+ minItems: 1
description:
- Should be a phandle/number pair. The phandle to QMC node and the QMC
- channel to use for this DAI.
+ Should be a phandle/number pair list. The list of phandle to QMC node
+ and the QMC channel pair to use for this DAI.
+ If only one phandle/number pair is provided, this DAI works in
+ interleaved mode, i.e. audio channels for this DAI are interleaved in
+ the QMC channel. If more than one pair is provided, this DAI works
+ in non-interleave mode. In that case the first audio channel uses the
+ the first QMC channel, the second audio channel uses the second QMC
+ channel, etc...
required:
- reg
@@ -79,6 +88,11 @@ examples:
reg = <17>;
fsl,qmc-chan = <&qmc 17>;
};
+ dai@18 {
+ reg = <18>;
+ /* Non-interleaved mode */
+ fsl,qmc-chan = <&qmc 18>, <&qmc 19>;
+ };
};
sound {
@@ -115,4 +129,19 @@ examples:
dai-tdm-slot-rx-mask = <0 0 1 0 1 0 1 0 1>;
};
};
+ simple-audio-card,dai-link@2 {
+ reg = <2>;
+ format = "dsp_b";
+ cpu {
+ sound-dai = <&audio_controller 18>;
+ };
+ codec {
+ sound-dai = <&codec3>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <8>;
+ /* TS 9, 10 */
+ dai-tdm-slot-tx-mask = <0 0 0 0 0 0 0 0 0 1 1>;
+ dai-tdm-slot-rx-mask = <0 0 0 0 0 0 0 0 0 1 1>;
+ };
+ };
};
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 10/10] ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode.
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
` (8 preceding siblings ...)
2024-06-20 8:42 ` [PATCH 09/10] dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI Herve Codina
@ 2024-06-20 8:42 ` Herve Codina
2024-07-01 7:47 ` Herve Codina
9 siblings, 1 reply; 13+ messages in thread
From: Herve Codina @ 2024-06-20 8:42 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
The current fsl_qmc_audio works in interleaved mode. The audio samples
are interleaved and all data are sent to (received from) one QMC
channel.
Using several QMC channels, non interleaved mode can be easily
supported. In that case, data related to ch0 are sent to (received from)
the first QMC channel, data related to ch1 use the next QMC channel and
so on up to the last channel.
In terms of constraints and settings, the two modes are slightly
different:
- Interleaved mode:
- The sample size should fit in the number of time-slots available
for the QMC channel.
- The number of audio channels should fit in the number of
time-slots (taking into account the sample size) available for the
QMC channel.
- Non-interleaved mode:
- The number of audio channels is the number of available QMC
channels.
- Each QMC channel should have the same number of time-slots.
- The sample size equals the number of time-slots of one QMC
channel.
Add support for the non-interleaved mode allowing multiple QMC channel
per DAI. The DAI switches in non-interleaved mode when more that one QMC
channel is available.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
sound/soc/fsl/fsl_qmc_audio.c | 371 +++++++++++++++++++++++++++-------
1 file changed, 296 insertions(+), 75 deletions(-)
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index f70c6c8eec4a..1560731c8372 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -29,7 +29,11 @@ struct qmc_dai {
struct device *dev;
unsigned int nb_tx_ts;
unsigned int nb_rx_ts;
- struct qmc_dai_chan chan;
+
+ unsigned int nb_chans_avail;
+ unsigned int nb_chans_used_tx;
+ unsigned int nb_chans_used_rx;
+ struct qmc_dai_chan *chans;
};
struct qmc_audio {
@@ -50,7 +54,10 @@ struct qmc_dai_prtd {
dma_addr_t ch_dma_addr_current;
dma_addr_t ch_dma_addr_end;
size_t ch_dma_size;
+ size_t ch_dma_offset;
+ unsigned int channels;
+ DECLARE_BITMAP(chans_pending, 64);
struct snd_pcm_substream *substream;
};
@@ -69,6 +76,17 @@ static int qmc_audio_pcm_construct(struct snd_soc_component *component,
return 0;
}
+static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access)
+{
+ switch (access) {
+ case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+ case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ return true;
+ default:
+ return false;
+ }
+};
+
static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -76,6 +94,14 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime;
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ /*
+ * In interleaved mode, the driver uses one QMC channel for all audio
+ * channels whereas in non-interleaved mode, it uses one QMC channel per
+ * audio channel.
+ */
+ prtd->channels = qmc_audio_access_is_interleaved(params_access(params)) ?
+ 1 : params_channels(params);
+
prtd->substream = substream;
prtd->buffer_ended = 0;
@@ -83,9 +109,10 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
prtd->period_size = params_period_size(params);
prtd->ch_dma_addr_start = runtime->dma_addr;
- prtd->ch_dma_addr_end = runtime->dma_addr + params_buffer_bytes(params);
+ prtd->ch_dma_offset = params_buffer_bytes(params) / prtd->channels;
+ prtd->ch_dma_addr_end = runtime->dma_addr + prtd->ch_dma_offset;
prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
- prtd->ch_dma_size = params_period_bytes(params);
+ prtd->ch_dma_size = params_period_bytes(params) / prtd->channels;
return 0;
}
@@ -94,16 +121,23 @@ static void qmc_audio_pcm_write_complete(void *context);
static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
{
+ unsigned int i;
int ret;
- ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_write_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
- ret);
- return ret;
+ for (i = 0; i < prtd->channels; i++) {
+ bitmap_set(prtd->chans_pending, i, 1);
+
+ ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan,
+ prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+ prtd->ch_dma_size,
+ qmc_audio_pcm_write_complete,
+ &prtd->qmc_dai->chans[i]);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n",
+ i, ret);
+ bitmap_clear(prtd->chans_pending, i, 1);
+ return ret;
+ }
}
return 0;
@@ -116,6 +150,16 @@ static void qmc_audio_pcm_write_complete(void *context)
prtd = chan->prtd_tx;
+ /* Mark the current channel as completed */
+ bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
+
+ /*
+ * All QMC channels involved must have completed their transfer before
+ * submitting a new one.
+ */
+ if (!bitmap_empty(prtd->chans_pending, 64))
+ return;
+
prtd->buffer_ended += prtd->period_size;
if (prtd->buffer_ended >= prtd->buffer_size)
prtd->buffer_ended = 0;
@@ -133,15 +177,23 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i
static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
{
+ unsigned int i;
int ret;
- ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
- prtd->ch_dma_addr_current, prtd->ch_dma_size,
- qmc_audio_pcm_read_complete,
- &prtd->qmc_dai->chan);
- if (ret) {
- dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
- ret);
+ for (i = 0; i < prtd->channels; i++) {
+ bitmap_set(prtd->chans_pending, i, 1);
+
+ ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan,
+ prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+ prtd->ch_dma_size,
+ qmc_audio_pcm_read_complete,
+ &prtd->qmc_dai->chans[i]);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n",
+ i, ret);
+ bitmap_clear(prtd->chans_pending, i, 1);
+ return ret;
+ }
}
return 0;
@@ -154,11 +206,21 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i
prtd = chan->prtd_rx;
+ /* Mark the current channel as completed */
+ bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
+
if (length != prtd->ch_dma_size) {
dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
length, prtd->ch_dma_size);
}
+ /*
+ * All QMC channels involved must have completed their transfer before
+ * submitting a new one.
+ */
+ if (!bitmap_empty(prtd->chans_pending, 64))
+ return;
+
prtd->buffer_ended += prtd->period_size;
if (prtd->buffer_ended >= prtd->buffer_size)
prtd->buffer_ended = 0;
@@ -176,6 +238,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ unsigned int i;
int ret;
if (!prtd->qmc_dai) {
@@ -185,8 +248,10 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ bitmap_zero(prtd->chans_pending, 64);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- prtd->qmc_dai->chan.prtd_tx = prtd;
+ for (i = 0; i < prtd->channels; i++)
+ prtd->qmc_dai->chans[i].prtd_tx = prtd;
/* Submit first chunk ... */
ret = qmc_audio_pcm_write_submit(prtd);
@@ -203,7 +268,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
if (ret)
return ret;
} else {
- prtd->qmc_dai->chan.prtd_rx = prtd;
+ for (i = 0; i < prtd->channels; i++)
+ prtd->qmc_dai->chans[i].prtd_rx = prtd;
/* Submit first chunk ... */
ret = qmc_audio_pcm_read_submit(prtd);
@@ -270,6 +336,7 @@ static const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_PAUSE,
.period_bytes_min = 32,
.period_bytes_max = 64 * 1024,
@@ -442,6 +509,7 @@ static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream,
snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
unsigned int frame_bits;
+ u64 access;
int ret;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -478,6 +546,44 @@ static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream,
return ret;
}
+ access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED |
+ 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+ access);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substream,
+ struct qmc_dai *qmc_dai)
+{
+ unsigned int frame_bits;
+ u64 access;
+ int ret;
+
+ frame_bits = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+ qmc_dai->nb_rx_ts * 8 : qmc_dai->nb_tx_ts * 8;
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED |
+ 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+ access);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+ return ret;
+ }
+
return 0;
}
@@ -495,7 +601,9 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
prtd->qmc_dai = qmc_dai;
- return qmc_dai_constraints_interleaved(substream, qmc_dai);
+ return qmc_dai->nb_chans_avail > 1 ?
+ qmc_dai_constraints_noninterleaved(substream, qmc_dai) :
+ qmc_dai_constraints_interleaved(substream, qmc_dai);
}
static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
@@ -503,7 +611,9 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct qmc_chan_param chan_param = {0};
+ unsigned int nb_chans_used;
struct qmc_dai *qmc_dai;
+ unsigned int i;
int ret;
qmc_dai = qmc_dai_get_data(dai);
@@ -512,15 +622,34 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /*
+ * In interleaved mode, the driver uses one QMC channel for all audio
+ * channels whereas in non-interleaved mode, it uses one QMC channel per
+ * audio channel.
+ */
+ nb_chans_used = qmc_audio_access_is_interleaved(params_access(params)) ?
+ 1 : params_channels(params);
+
+ if (nb_chans_used > qmc_dai->nb_chans_avail) {
+ dev_err(dai->dev, "Not enough qmc_chans. Need %u, avail %u\n",
+ nb_chans_used, qmc_dai->nb_chans_avail);
+ return -EINVAL;
+ }
+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
chan_param.mode = QMC_TRANSPARENT;
- chan_param.transp.max_rx_buf_size = params_period_bytes(params);
- ret = qmc_chan_set_param(qmc_dai->chan.qmc_chan, &chan_param);
- if (ret) {
- dev_err(dai->dev, "set param failed %d\n",
- ret);
- return ret;
+ chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used;
+ for (i = 0; i < nb_chans_used; i++) {
+ ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param);
+ if (ret) {
+ dev_err(dai->dev, "chans[%u], set param failed %d\n",
+ i, ret);
+ return ret;
+ }
}
+ qmc_dai->nb_chans_used_rx = nb_chans_used;
+ } else {
+ qmc_dai->nb_chans_used_tx = nb_chans_used;
}
return 0;
@@ -529,9 +658,12 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
+ unsigned int nb_chans_used;
struct qmc_dai *qmc_dai;
+ unsigned int i;
int direction;
- int ret;
+ int ret = 0;
+ int ret_tmp;
qmc_dai = qmc_dai_get_data(dai);
if (!qmc_dai) {
@@ -539,30 +671,50 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL;
}
- direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- QMC_CHAN_WRITE : QMC_CHAN_READ;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = QMC_CHAN_WRITE;
+ nb_chans_used = qmc_dai->nb_chans_used_tx;
+ } else {
+ direction = QMC_CHAN_READ;
+ nb_chans_used = qmc_dai->nb_chans_used_rx;
+ }
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = qmc_chan_start(qmc_dai->chan.qmc_chan, direction);
- if (ret)
- return ret;
+ for (i = 0; i < nb_chans_used; i++) {
+ ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction);
+ if (ret)
+ goto err_stop;
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
- ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction);
- if (ret)
- return ret;
- ret = qmc_chan_reset(qmc_dai->chan.qmc_chan, direction);
+ /* Stop and reset all QMC channels and return the first error encountered */
+ for (i = 0; i < nb_chans_used; i++) {
+ ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+ if (!ret)
+ ret = ret_tmp;
+ if (ret_tmp)
+ continue;
+
+ ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
+ if (!ret)
+ ret = ret_tmp;
+ }
if (ret)
return ret;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction);
+ /* Stop all QMC channels and return the first error encountered */
+ for (i = 0; i < nb_chans_used; i++) {
+ ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+ if (!ret)
+ ret = ret_tmp;
+ }
if (ret)
return ret;
break;
@@ -572,6 +724,13 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
}
return 0;
+
+err_stop:
+ while (i--) {
+ qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+ qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
+ }
+ return ret;
}
static const struct snd_soc_dai_ops qmc_dai_ops = {
@@ -580,7 +739,7 @@ static const struct snd_soc_dai_ops qmc_dai_ops = {
.hw_params = qmc_dai_hw_params,
};
-static u64 qmc_audio_formats(u8 nb_ts)
+static u64 qmc_audio_formats(u8 nb_ts, bool is_noninterleaved)
{
unsigned int format_width;
unsigned int chan_width;
@@ -612,6 +771,13 @@ static u64 qmc_audio_formats(u8 nb_ts)
if (format_width > chan_width || chan_width % format_width)
continue;
+ /*
+ * In non interleaved mode, we can only support formats that
+ * can fit only 1 time in the channel
+ */
+ if (is_noninterleaved && format_width != chan_width)
+ continue;
+
formats_mask |= pcm_format_to_bits(format);
}
return formats_mask;
@@ -622,6 +788,12 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
struct snd_soc_dai_driver *qmc_soc_dai_driver)
{
struct qmc_chan_info info;
+ unsigned long rx_fs_rate;
+ unsigned long tx_fs_rate;
+ unsigned int nb_tx_ts;
+ unsigned int nb_rx_ts;
+ unsigned int i;
+ int count;
u32 val;
int ret;
@@ -639,56 +811,105 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
if (!qmc_dai->name)
return -ENOMEM;
- qmc_dai->chan.qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
- "fsl,qmc-chan");
- if (IS_ERR(qmc_dai->chan.qmc_chan)) {
- ret = PTR_ERR(qmc_dai->chan.qmc_chan);
- return dev_err_probe(qmc_audio->dev, ret,
- "dai %d get QMC channel failed\n", qmc_dai->id);
- }
+ count = qmc_chan_count_phandles(np, "fsl,qmc-chan");
+ if (count < 0)
+ return dev_err_probe(qmc_audio->dev, count,
+ "dai %d get number of QMC channel failed\n", qmc_dai->id);
+ if (!count)
+ return dev_err_probe(qmc_audio->dev, -EINVAL,
+ "dai %d no QMC channel defined\n", qmc_dai->id);
- qmc_soc_dai_driver->id = qmc_dai->id;
- qmc_soc_dai_driver->name = qmc_dai->name;
+ qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL);
+ if (!qmc_dai->chans)
+ return -ENOMEM;
- ret = qmc_chan_get_info(qmc_dai->chan.qmc_chan, &info);
- if (ret) {
- dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
- qmc_dai->id, ret);
- return ret;
- }
- dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
- qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts);
+ for (i = 0; i < count; i++) {
+ qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np,
+ "fsl,qmc-chan", i);
+ if (IS_ERR(qmc_dai->chans[i].qmc_chan)) {
+ return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan),
+ "dai %d get QMC channel %d failed\n", qmc_dai->id, i);
+ }
- if (info.mode != QMC_TRANSPARENT) {
- dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n",
- qmc_dai->id, info.mode);
- return -EINVAL;
+ ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info);
+ if (ret) {
+ dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n",
+ qmc_dai->id, i, ret);
+ return ret;
+ }
+ dev_info(qmc_audio->dev, "dai %d QMC channel %d mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
+ qmc_dai->id, i, info.mode, info.nb_tx_ts, info.nb_rx_ts);
+
+ if (info.mode != QMC_TRANSPARENT) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d mode %d is not QMC_TRANSPARENT\n",
+ qmc_dai->id, i, info.mode);
+ return -EINVAL;
+ }
+
+ /*
+ * All channels must have the same number of Tx slots and the
+ * same numbers of Rx slots.
+ */
+ if (i == 0) {
+ nb_tx_ts = info.nb_tx_ts;
+ nb_rx_ts = info.nb_rx_ts;
+ tx_fs_rate = info.tx_fs_rate;
+ rx_fs_rate = info.rx_fs_rate;
+ } else {
+ if (nb_tx_ts != info.nb_tx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Tx timeslots (%u instead of %u)\n",
+ qmc_dai->id, i, info.nb_tx_ts, nb_tx_ts);
+ return -EINVAL;
+ }
+ if (nb_rx_ts != info.nb_rx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Rx timeslots (%u instead of %u)\n",
+ qmc_dai->id, i, info.nb_rx_ts, nb_rx_ts);
+ return -EINVAL;
+ }
+ if (tx_fs_rate != info.tx_fs_rate) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Tx frame sample rate (%lu instead of %lu)\n",
+ qmc_dai->id, i, info.tx_fs_rate, tx_fs_rate);
+ return -EINVAL;
+ }
+ if (rx_fs_rate != info.rx_fs_rate) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Rx frame sample rate (%lu instead of %lu)\n",
+ qmc_dai->id, i, info.rx_fs_rate, rx_fs_rate);
+ return -EINVAL;
+ }
+ }
}
- qmc_dai->nb_tx_ts = info.nb_tx_ts;
- qmc_dai->nb_rx_ts = info.nb_rx_ts;
+
+ qmc_dai->nb_chans_avail = count;
+ qmc_dai->nb_tx_ts = nb_tx_ts * count;
+ qmc_dai->nb_rx_ts = nb_rx_ts * count;
+
+ qmc_soc_dai_driver->id = qmc_dai->id;
+ qmc_soc_dai_driver->name = qmc_dai->name;
qmc_soc_dai_driver->playback.channels_min = 0;
qmc_soc_dai_driver->playback.channels_max = 0;
- if (qmc_dai->nb_tx_ts) {
+ if (nb_tx_ts) {
qmc_soc_dai_driver->playback.channels_min = 1;
- qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts;
+ qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts;
}
- qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts);
+ qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts,
+ count > 1 ? true : false);
qmc_soc_dai_driver->capture.channels_min = 0;
qmc_soc_dai_driver->capture.channels_max = 0;
- if (qmc_dai->nb_rx_ts) {
+ if (nb_rx_ts) {
qmc_soc_dai_driver->capture.channels_min = 1;
- qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts;
+ qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts;
}
- qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts);
-
- qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate);
- qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate;
- qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate;
- qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate);
- qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate;
- qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate;
+ qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts,
+ count > 1 ? true : false);
+
+ qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate);
+ qmc_soc_dai_driver->playback.rate_min = tx_fs_rate;
+ qmc_soc_dai_driver->playback.rate_max = tx_fs_rate;
+ qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rx_fs_rate);
+ qmc_soc_dai_driver->capture.rate_min = rx_fs_rate;
+ qmc_soc_dai_driver->capture.rate_max = rx_fs_rate;
qmc_soc_dai_driver->ops = &qmc_dai_ops;
--
2.45.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 09/10] dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI
2024-06-20 8:42 ` [PATCH 09/10] dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI Herve Codina
@ 2024-06-27 21:25 ` Rob Herring (Arm)
0 siblings, 0 replies; 13+ messages in thread
From: Rob Herring (Arm) @ 2024-06-27 21:25 UTC (permalink / raw)
To: Herve Codina
Cc: linux-arm-kernel, Xiubo Li, Shengjiu Wang, devicetree,
Takashi Iwai, Mark Brown, linuxppc-dev, Jaroslav Kysela,
Liam Girdwood, Nicolin Chen, linux-kernel, linux-sound,
Qiang Zhao, Conor Dooley, Fabio Estevam, Krzysztof Kozlowski,
Christophe Leroy, Thomas Petazzoni, alsa-devel
On Thu, 20 Jun 2024 10:42:56 +0200, Herve Codina wrote:
> The QMC audio uses one QMC channel per DAI and uses this QMC channel to
> transmit interleaved audio channel samples.
>
> In order to work in non-interleave mode, a QMC audio DAI needs to use
> multiple QMC channels. In that case, the DAI maps each QMC channel to
> exactly one audio channel.
>
> Allow QMC audio DAIs with multiple QMC channels attached.
>
> Signed-off-by: Herve Codina <herve.codina@bootlin.com>
> ---
> .../bindings/sound/fsl,qmc-audio.yaml | 41 ++++++++++++++++---
> 1 file changed, 35 insertions(+), 6 deletions(-)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 10/10] ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode.
2024-06-20 8:42 ` [PATCH 10/10] ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode Herve Codina
@ 2024-07-01 7:47 ` Herve Codina
0 siblings, 0 replies; 13+ messages in thread
From: Herve Codina @ 2024-07-01 7:47 UTC (permalink / raw)
To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Qiang Zhao, Shengjiu Wang,
Xiubo Li, Fabio Estevam, Nicolin Chen, Jaroslav Kysela,
Takashi Iwai, Christophe Leroy
Cc: alsa-devel, linuxppc-dev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, Thomas Petazzoni
Hi,
On Thu, 20 Jun 2024 10:42:57 +0200
Herve Codina <herve.codina@bootlin.com> wrote:
...
> +static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access)
> +{
> + switch (access) {
> + case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
> + case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
> + return true;
> + default:
> + return false;
> + }
> +};
> +
The ';' at the end of the function should not be here and will be removed
in the next iteration.
Also, this function will be changed to
--- 8< ---
static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access)
{
switch (access) {
case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
return true;
default:
break;
}
return false;
}
--- 8< ---
Hervé
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2024-07-01 7:47 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-20 8:42 [PATCH 00/10] Add support for non-interleaved mode in qmc_audio Herve Codina
2024-06-20 8:42 ` [PATCH 01/10] ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value Herve Codina
2024-06-20 8:42 ` [PATCH 02/10] ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch Herve Codina
2024-06-20 8:42 ` [PATCH 03/10] ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer handling Herve Codina
2024-06-20 8:42 ` [PATCH 04/10] ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in completion routines Herve Codina
2024-06-20 8:42 ` [PATCH 05/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_audio_pcm_{read,write}_submit() Herve Codina
2024-06-20 8:42 ` [PATCH 06/10] ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved() Herve Codina
2024-06-20 8:42 ` [PATCH 07/10] soc: fsl: cpm1: qmc: Introduce functions to get a channel from a phandle list Herve Codina
2024-06-20 8:42 ` [PATCH 08/10] soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles() Herve Codina
2024-06-20 8:42 ` [PATCH 09/10] dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI Herve Codina
2024-06-27 21:25 ` Rob Herring (Arm)
2024-06-20 8:42 ` [PATCH 10/10] ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode Herve Codina
2024-07-01 7:47 ` Herve Codina
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).