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
@@ -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+
10521082static 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+
11021139out :
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+
11161157static 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