Skip to content

Commit 07e6734

Browse files
committed
ASoC: SOF: ipc4-pcm: fix delay calculation when DSP resamples
When the sampling rates going in (host) and out (dai) from the DSP are different, the IPC4 delay reporting does not work correctly. Add support for this case by scaling the all raw position values to a common timebase before calculating real-time delay for the PCM. Fixes: 0ea0668 ("ASoC: SOF: ipc4-pcm: Correct the delay calculation") Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
1 parent ea07943 commit 07e6734

File tree

1 file changed

+68
-22
lines changed

1 file changed

+68
-22
lines changed

sound/soc/sof/ipc4-pcm.c

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
* struct sof_ipc4_timestamp_info - IPC4 timestamp info
2020
* @host_copier: the host copier of the pcm stream
2121
* @dai_copier: the dai copier of the pcm stream
22-
* @stream_start_offset: reported by fw in memory window (converted to frames)
23-
* @stream_end_offset: reported by fw in memory window (converted to frames)
22+
* @stream_start_offset: reported by fw in memory window (converted to
23+
* frames at host_copier sampling rate)
24+
* @stream_end_offset: reported by fw in memory window (converted to
25+
* frames at host_copier sampling rate)
2426
* @llp_offset: llp offset in memory window
25-
* @boundary: wrap boundary should be used for the LLP frame counter
2627
* @delay: Calculated and stored in pointer callback. The stored value is
27-
* returned in the delay callback.
28+
* returned in the delay callback. Expressed in frames at host copier
29+
* sampling rate.
2830
*/
2931
struct sof_ipc4_timestamp_info {
3032
struct sof_ipc4_copier *host_copier;
@@ -33,7 +35,6 @@ struct sof_ipc4_timestamp_info {
3335
u64 stream_end_offset;
3436
u32 llp_offset;
3537

36-
u64 boundary;
3738
snd_pcm_sframes_t delay;
3839
};
3940

@@ -1049,6 +1050,35 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
10491050
return 0;
10501051
}
10511052

1053+
static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value)
1054+
{
1055+
u64 dai_rate, host_rate;
1056+
1057+
if (!time_info->dai_copier || !time_info->host_copier)
1058+
return value;
1059+
1060+
/*
1061+
* copiers do not change sampling rate, so we can use the
1062+
* out_format independently of stream direction
1063+
*/
1064+
dai_rate = time_info->dai_copier->data.out_format.sampling_frequency;
1065+
host_rate = time_info->host_copier->data.out_format.sampling_frequency;
1066+
1067+
if (!dai_rate || !host_rate || dai_rate == host_rate)
1068+
return value;
1069+
1070+
/* take care not to overflow u64, rates can be up to 768000 */
1071+
if (value > U32_MAX) {
1072+
value = div64_u64(value, dai_rate);
1073+
value *= host_rate;
1074+
} else {
1075+
value *= host_rate;
1076+
value = div64_u64(value, dai_rate);
1077+
}
1078+
1079+
return value;
1080+
}
1081+
10521082
static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10531083
struct snd_pcm_substream *substream,
10541084
struct snd_sof_pcm_stream *sps,
@@ -1079,7 +1109,8 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10791109
* The ChainDMA uses 2x 1ms ping-pong buffer, dai side starts
10801110
* when 1ms data is available
10811111
*/
1082-
time_info->stream_start_offset = substream->runtime->rate / MSEC_PER_SEC;
1112+
u64 offset = substream->runtime->rate / MSEC_PER_SEC;
1113+
time_info->stream_start_offset = sof_ipc4_frames_dai_to_host(time_info, offset);
10831114
goto out;
10841115
}
10851116

@@ -1099,20 +1130,30 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10991130
time_info->stream_end_offset = ppl_reg.stream_end_offset;
11001131
do_div(time_info->stream_end_offset, dai_sample_size);
11011132

1133+
/* convert to host frame time */
1134+
time_info->stream_start_offset =
1135+
sof_ipc4_frames_dai_to_host(time_info, time_info->stream_start_offset);
1136+
time_info->stream_end_offset =
1137+
sof_ipc4_frames_dai_to_host(time_info, time_info->stream_end_offset);
1138+
11021139
out:
1103-
/*
1104-
* Calculate the wrap boundary need to be used for delay calculation
1105-
* The host counter is in bytes, it will wrap earlier than the frames
1106-
* based link counter.
1107-
*/
1108-
time_info->boundary = div64_u64(~((u64)0),
1109-
frames_to_bytes(substream->runtime, 1));
11101140
/* Initialize the delay value to 0 (no delay) */
11111141
time_info->delay = 0;
11121142

11131143
return 0;
11141144
}
11151145

1146+
/*
1147+
* Modulus to use to compare host and link counters. This is required
1148+
* as host/link counters use different units (bytes/frames) and the
1149+
* sampling rates may be different (when resampling), so the raw hardware
1150+
* counters will wrap around at different times. To calculate
1151+
* differences, use DELAY_BOUNDARY as a common modulus. This value must
1152+
* be smaller than the wrap-around point of any hardware counter, as
1153+
* expressed in units of the host frame counter.
1154+
*/
1155+
#define DELAY_BOUNDARY U32_MAX
1156+
11161157
static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11171158
struct snd_pcm_substream *substream,
11181159
snd_pcm_uframes_t *pointer)
@@ -1149,6 +1190,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11491190

11501191
/* For delay calculation we need the host counter */
11511192
host_cnt = snd_sof_pcm_get_host_byte_counter(sdev, component, substream);
1193+
1194+
/* Store the original value to host_ptr */
11521195
host_ptr = host_cnt;
11531196

11541197
/* convert the host_cnt to frames */
@@ -1167,6 +1210,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11671210
sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
11681211
dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
11691212
}
1213+
1214+
dai_cnt = sof_ipc4_frames_dai_to_host(time_info, dai_cnt);
11701215
dai_cnt += time_info->stream_end_offset;
11711216

11721217
/* In two cases dai dma counter is not accurate
@@ -1200,8 +1245,12 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
12001245
dai_cnt -= time_info->stream_start_offset;
12011246
}
12021247

1203-
/* Wrap the dai counter at the boundary where the host counter wraps */
1204-
div64_u64_rem(dai_cnt, time_info->boundary, &dai_cnt);
1248+
/* dai/host_cnt converted to same unit, but the values will
1249+
* wrap the counters at different times, so use a common
1250+
* modulo before any comparisons
1251+
*/
1252+
div64_u64_rem(dai_cnt, DELAY_BOUNDARY, &dai_cnt);
1253+
div64_u64_rem(host_cnt, DELAY_BOUNDARY, &host_cnt);
12051254

12061255
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
12071256
head_cnt = host_cnt;
@@ -1211,14 +1260,11 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
12111260
tail_cnt = host_cnt;
12121261
}
12131262

1214-
if (head_cnt < tail_cnt) {
1215-
time_info->delay = time_info->boundary - tail_cnt + head_cnt;
1216-
goto out;
1217-
}
1218-
1219-
time_info->delay = head_cnt - tail_cnt;
1263+
if (unlikely(head_cnt < tail_cnt))
1264+
time_info->delay = DELAY_BOUNDARY - tail_cnt + head_cnt;
1265+
else
1266+
time_info->delay = head_cnt - tail_cnt;
12201267

1221-
out:
12221268
/*
12231269
* Convert the host byte counter to PCM pointer which wraps in buffer
12241270
* and it is in frames

0 commit comments

Comments
 (0)