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 ,
@@ -1079,7 +1119,8 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10791119 * The ChainDMA uses 2x 1ms ping-pong buffer, dai side starts
10801120 * when 1ms data is available
10811121 */
1082- time_info -> stream_start_offset = substream -> runtime -> rate / MSEC_PER_SEC ;
1122+ u64 offset = substream -> runtime -> rate / MSEC_PER_SEC ;
1123+ time_info -> stream_start_offset = sof_ipc4_frames_dai_to_host (time_info , offset );
10831124 goto out ;
10841125 }
10851126
@@ -1099,14 +1140,13 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
10991140 time_info -> stream_end_offset = ppl_reg .stream_end_offset ;
11001141 do_div (time_info -> stream_end_offset , dai_sample_size );
11011142
1143+ /* convert to host frame time */
1144+ time_info -> stream_start_offset =
1145+ sof_ipc4_frames_dai_to_host (time_info , time_info -> stream_start_offset );
1146+ time_info -> stream_end_offset =
1147+ sof_ipc4_frames_dai_to_host (time_info , time_info -> stream_end_offset );
1148+
11021149out :
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 ));
11101150 /* Initialize the delay value to 0 (no delay) */
11111151 time_info -> delay = 0 ;
11121152
@@ -1149,6 +1189,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11491189
11501190 /* For delay calculation we need the host counter */
11511191 host_cnt = snd_sof_pcm_get_host_byte_counter (sdev , component , substream );
1192+
1193+ /* Store the original value to host_ptr */
11521194 host_ptr = host_cnt ;
11531195
11541196 /* convert the host_cnt to frames */
@@ -1167,6 +1209,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
11671209 sof_mailbox_read (sdev , time_info -> llp_offset , & llp , sizeof (llp ));
11681210 dai_cnt = ((u64 )llp .reading .llp_u << 32 ) | llp .reading .llp_l ;
11691211 }
1212+
1213+ dai_cnt = sof_ipc4_frames_dai_to_host (time_info , dai_cnt );
11701214 dai_cnt += time_info -> stream_end_offset ;
11711215
11721216 /* In two cases dai dma counter is not accurate
@@ -1200,8 +1244,9 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
12001244 dai_cnt -= time_info -> stream_start_offset ;
12011245 }
12021246
1203- /* Wrap the dai counter at the boundary where the host counter wraps */
1204- div64_u64_rem (dai_cnt , time_info -> boundary , & dai_cnt );
1247+ /* Convert to a common base before comparisons */
1248+ div64_u64_rem (dai_cnt , DELAY_BOUNDARY , & dai_cnt );
1249+ div64_u64_rem (host_cnt , DELAY_BOUNDARY , & host_cnt );
12051250
12061251 if (substream -> stream == SNDRV_PCM_STREAM_PLAYBACK ) {
12071252 head_cnt = host_cnt ;
@@ -1211,14 +1256,11 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
12111256 tail_cnt = host_cnt ;
12121257 }
12131258
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 ;
1259+ if (unlikely (head_cnt < tail_cnt ))
1260+ time_info -> delay = DELAY_BOUNDARY - tail_cnt + head_cnt ;
1261+ else
1262+ time_info -> delay = head_cnt - tail_cnt ;
12201263
1221- out :
12221264 /*
12231265 * Convert the host byte counter to PCM pointer which wraps in buffer
12241266 * and it is in frames
0 commit comments