ASoC: soc-pcm: tidyup soc_pcm_pointer()'s delay update method

Merge series from Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>:

	Current soc_pcm_pointer() is checking runtime->delay,
	but it might be updated silently by component's .point callback.
	It is strange and difficult to find/know the issue.  This patch
	adds .delay callback for component, and solve the issue.
This commit is contained in:
Mark Brown 2021-11-29 16:43:16 +00:00
commit 7be10cef0f
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
8 changed files with 95 additions and 40 deletions

View File

@ -148,6 +148,8 @@ struct snd_soc_component_driver {
struct vm_area_struct *vma); struct vm_area_struct *vma);
int (*ack)(struct snd_soc_component *component, int (*ack)(struct snd_soc_component *component,
struct snd_pcm_substream *substream); struct snd_pcm_substream *substream);
snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
const struct snd_compress_ops *compress_ops; const struct snd_compress_ops *compress_ops;
@ -505,5 +507,7 @@ int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd,
void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd,
void *stream, int rollback); void *stream, int rollback);
int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream); int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream);
void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay);
#endif /* __SOC_COMPONENT_H */ #endif /* __SOC_COMPONENT_H */

View File

@ -208,8 +208,6 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream); struct snd_pcm_substream *substream);
void snd_soc_dai_shutdown(struct snd_soc_dai *dai, void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream, int rollback); struct snd_pcm_substream *substream, int rollback);
snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
void snd_soc_dai_suspend(struct snd_soc_dai *dai); void snd_soc_dai_suspend(struct snd_soc_dai *dai);
void snd_soc_dai_resume(struct snd_soc_dai *dai); void snd_soc_dai_resume(struct snd_soc_dai *dai);
int snd_soc_dai_compress_new(struct snd_soc_dai *dai, int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
@ -238,6 +236,8 @@ int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd,
int rollback); int rollback);
int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream,
int cmd); int cmd);
void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream,
snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay);
int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, int snd_soc_dai_compr_startup(struct snd_soc_dai *dai,
struct snd_compr_stream *cstream); struct snd_compr_stream *cstream);

View File

@ -1003,6 +1003,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data; struct audio_substream_data *rtd = runtime->private_data;
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
if (!rtd) if (!rtd)
return -EINVAL; return -EINVAL;
@ -1023,7 +1024,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
} }
if (bytescount > 0) { if (bytescount > 0) {
delay = do_div(bytescount, period_bytes); delay = do_div(bytescount, period_bytes);
runtime->delay = bytes_to_frames(runtime, delay); adata->delay += bytes_to_frames(runtime, delay);
} }
} else { } else {
buffersize = frames_to_bytes(runtime, runtime->buffer_size); buffersize = frames_to_bytes(runtime, runtime->buffer_size);
@ -1035,6 +1036,17 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
return bytes_to_frames(runtime, pos); return bytes_to_frames(runtime, pos);
} }
static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
snd_pcm_sframes_t delay = adata->delay;
adata->delay = 0;
return delay;
}
static int acp_dma_prepare(struct snd_soc_component *component, static int acp_dma_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
@ -1198,6 +1210,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = {
.hw_params = acp_dma_hw_params, .hw_params = acp_dma_hw_params,
.trigger = acp_dma_trigger, .trigger = acp_dma_trigger,
.pointer = acp_dma_pointer, .pointer = acp_dma_pointer,
.delay = acp_dma_delay,
.prepare = acp_dma_prepare, .prepare = acp_dma_prepare,
.pcm_construct = acp_dma_new, .pcm_construct = acp_dma_new,
}; };

View File

@ -151,6 +151,7 @@ struct audio_drv_data {
struct snd_pcm_substream *capture_i2sbt_stream; struct snd_pcm_substream *capture_i2sbt_stream;
void __iomem *acp_mmio; void __iomem *acp_mmio;
u32 asic_type; u32 asic_type;
snd_pcm_sframes_t delay;
}; };
/* /*

View File

@ -653,10 +653,21 @@ static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component,
dev_err(rtd->dev, "sst: error code = %d\n", ret_val); dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
return ret_val; return ret_val;
} }
substream->runtime->delay = str_info->pcm_delay;
return str_info->buffer_ptr; return str_info->buffer_ptr;
} }
static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream = substream->runtime->private_data;
struct pcm_stream_info *str_info = &stream->stream_info;
if (sst_get_stream_status(stream) == SST_PLATFORM_INIT)
return 0;
return str_info->pcm_delay;
}
static int sst_soc_pcm_new(struct snd_soc_component *component, static int sst_soc_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd) struct snd_soc_pcm_runtime *rtd)
{ {
@ -695,6 +706,7 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = {
.open = sst_soc_open, .open = sst_soc_open,
.trigger = sst_soc_trigger, .trigger = sst_soc_trigger,
.pointer = sst_soc_pointer, .pointer = sst_soc_pointer,
.delay = sst_soc_delay,
.compress_ops = &sst_platform_compress_ops, .compress_ops = &sst_platform_compress_ops,
.pcm_construct = sst_soc_pcm_new, .pcm_construct = sst_soc_pcm_new,
}; };

View File

@ -932,6 +932,34 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
return 0; return 0;
} }
void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
snd_pcm_sframes_t *cpu_delay,
snd_pcm_sframes_t *codec_delay)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component;
snd_pcm_sframes_t delay;
int i;
/*
* We're looking for the delay through the full audio path so it needs to
* be the maximum of the Components doing transmit and the maximum of the
* Components doing receive (ie, all CPUs and all CODECs) rather than
* just the maximum of all Components.
*/
for_each_rtd_components(rtd, i, component) {
if (!component->driver->delay)
continue;
delay = component->driver->delay(component, substream);
if (snd_soc_component_is_codec(component))
*codec_delay = max(*codec_delay, delay);
else
*cpu_delay = max(*cpu_delay, delay);
}
}
int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream, int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg) unsigned int cmd, void *arg)
{ {

View File

@ -453,18 +453,6 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
soc_dai_mark_pop(dai, substream, startup); soc_dai_mark_pop(dai, substream, startup);
} }
snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream)
{
int delay = 0;
if (dai->driver->ops &&
dai->driver->ops->delay)
delay = dai->driver->ops->delay(substream, dai);
return delay;
}
int snd_soc_dai_compress_new(struct snd_soc_dai *dai, int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_pcm_runtime *rtd, int num)
{ {
@ -693,6 +681,34 @@ int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream,
return 0; return 0;
} }
void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream,
snd_pcm_sframes_t *cpu_delay,
snd_pcm_sframes_t *codec_delay)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai;
int i;
/*
* We're looking for the delay through the full audio path so it needs to
* be the maximum of the DAIs doing transmit and the maximum of the DAIs
* doing receive (ie, all CPUs and all CODECs) rather than just the maximum
* of all DAIs.
*/
/* for CPU */
for_each_rtd_cpu_dais(rtd, i, dai)
if (dai->driver->ops &&
dai->driver->ops->delay)
*cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai));
/* for Codec */
for_each_rtd_codec_dais(rtd, i, dai)
if (dai->driver->ops &&
dai->driver->ops->delay)
*codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai));
}
int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, int snd_soc_dai_compr_startup(struct snd_soc_dai *dai,
struct snd_compr_stream *cstream) struct snd_compr_stream *cstream)
{ {

View File

@ -1080,41 +1080,22 @@ start_err:
/* /*
* soc level wrapper for pointer callback * soc level wrapper for pointer callback
* If cpu_dai, codec_dai, component driver has the delay callback, then * If cpu_dai, codec_dai, component driver has the delay callback, then
* the runtime->delay will be updated accordingly. * the runtime->delay will be updated via snd_soc_pcm_component/dai_delay().
*/ */
static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
{ {
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai;
struct snd_soc_dai *codec_dai;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
snd_pcm_sframes_t codec_delay = 0; snd_pcm_sframes_t codec_delay = 0;
snd_pcm_sframes_t cpu_delay = 0; snd_pcm_sframes_t cpu_delay = 0;
int i;
/* clearing the previous total delay */
runtime->delay = 0;
offset = snd_soc_pcm_component_pointer(substream); offset = snd_soc_pcm_component_pointer(substream);
/* base delay if assigned in pointer callback */ /* should be called *after* snd_soc_pcm_component_pointer() */
delay = runtime->delay; snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay);
snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay);
for_each_rtd_cpu_dais(rtd, i, cpu_dai) { runtime->delay = cpu_delay + codec_delay;
cpu_delay = max(cpu_delay,
snd_soc_dai_delay(cpu_dai, substream));
}
delay += cpu_delay;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
codec_delay = max(codec_delay,
snd_soc_dai_delay(codec_dai, substream));
}
delay += codec_delay;
runtime->delay = delay;
return offset; return offset;
} }