Skip to content

Commit aba6ee4

Browse files
kv2019iujfalusi
authored andcommitted
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 <[email protected]>
1 parent dbed0a3 commit aba6ee4

File tree

1 file changed

+62
-21
lines changed

1 file changed

+62
-21
lines changed

sound/soc/sof/ipc4-pcm.c

Lines changed: 62 additions & 21 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

@@ -48,6 +49,16 @@ struct sof_ipc4_pcm_stream_priv {
4849
bool chain_dma_allocated;
4950
};
5051

52+
/*
53+
* Modulus to use to compare host and link position counters. The sampling
54+
* rates may be different, so the raw hardware counters will wrap
55+
* around at different times. To calculate differences, use
56+
* DELAY_BOUNDARY as a common modulus. This value must be smaller than
57+
* the wrap-around point of any hardware counter, and larger than any
58+
* valid delay measurement.
59+
*/
60+
#define DELAY_BOUNDARY U32_MAX
61+
5162
static inline struct sof_ipc4_timestamp_info *
5263
sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps)
5364
{
@@ -1049,6 +1060,35 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
10491060
return 0;
10501061
}
10511062

1063+
static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value)
1064+
{
1065+
u64 dai_rate, host_rate;
1066+
1067+
if (!time_info->dai_copier || !time_info->host_copier)
1068+
return value;
1069+
1070+
/*
1071+
* copiers do not change sampling rate, so we can use the
1072+
* out_format independently of stream direction
1073+
*/
1074+
dai_rate = time_info->dai_copier->data.out_format.sampling_frequency;
1075+
host_rate = time_info->host_copier->data.out_format.sampling_frequency;
1076+
1077+
if (!dai_rate || !host_rate || dai_rate == host_rate)
1078+
return value;
1079+
1080+
/* take care not to overflow u64, rates can be up to 768000 */
1081+
if (value > U32_MAX) {
1082+
value = div64_u64(value, dai_rate);
1083+
value *= host_rate;
1084+
} else {
1085+
value *= host_rate;
1086+
value = div64_u64(value, dai_rate);
1087+
}
1088+
1089+
return value;
1090+
}
1091+
10521092
static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10531093
struct snd_pcm_substream *substream,
10541094
struct snd_sof_pcm_stream *sps,
@@ -1099,14 +1139,13 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10991139
time_info->stream_end_offset = ppl_reg.stream_end_offset;
11001140
do_div(time_info->stream_end_offset, dai_sample_size);
11011141

1142+
/* convert to host frame time */
1143+
time_info->stream_start_offset =
1144+
sof_ipc4_frames_dai_to_host(time_info, time_info->stream_start_offset);
1145+
time_info->stream_end_offset =
1146+
sof_ipc4_frames_dai_to_host(time_info, time_info->stream_end_offset);
1147+
11021148
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));
11101149
/* Initialize the delay value to 0 (no delay) */
11111150
time_info->delay = 0;
11121151

@@ -1149,6 +1188,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11491188

11501189
/* For delay calculation we need the host counter */
11511190
host_cnt = snd_sof_pcm_get_host_byte_counter(sdev, component, substream);
1191+
1192+
/* Store the original value to host_ptr */
11521193
host_ptr = host_cnt;
11531194

11541195
/* convert the host_cnt to frames */
@@ -1167,6 +1208,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11671208
sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
11681209
dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
11691210
}
1211+
1212+
dai_cnt = sof_ipc4_frames_dai_to_host(time_info, dai_cnt);
11701213
dai_cnt += time_info->stream_end_offset;
11711214

11721215
/* In two cases dai dma counter is not accurate
@@ -1200,8 +1243,9 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
12001243
dai_cnt -= time_info->stream_start_offset;
12011244
}
12021245

1203-
/* Wrap the dai counter at the boundary where the host counter wraps */
1204-
div64_u64_rem(dai_cnt, time_info->boundary, &dai_cnt);
1246+
/* Convert to a common base before comparisons */
1247+
dai_cnt &= DELAY_BOUNDARY;
1248+
host_cnt &= DELAY_BOUNDARY;
12051249

12061250
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
12071251
head_cnt = host_cnt;
@@ -1211,14 +1255,11 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
12111255
tail_cnt = host_cnt;
12121256
}
12131257

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;
1258+
if (unlikely(head_cnt < tail_cnt))
1259+
time_info->delay = DELAY_BOUNDARY - tail_cnt + head_cnt;
1260+
else
1261+
time_info->delay = head_cnt - tail_cnt;
12201262

1221-
out:
12221263
/*
12231264
* Convert the host byte counter to PCM pointer which wraps in buffer
12241265
* and it is in frames

0 commit comments

Comments
 (0)