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 */
2931struct 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+
5162static inline struct sof_ipc4_timestamp_info *
5263sof_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+
10521092static 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+
11021148out :
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