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
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)
2324 * @llp_offset: llp offset in memory window
25+ * @boundary: wrap boundary should be used for the LLP frame counter
26+ * @delay: Calculated and stored in pointer callback. The stored value is
27+ * returned in the delay callback.
2428 */
2529struct sof_ipc4_timestamp_info {
2630 struct sof_ipc4_copier * host_copier ;
2731 struct sof_ipc4_copier * dai_copier ;
2832 u64 stream_start_offset ;
33+ u64 stream_end_offset ;
2934 u32 llp_offset ;
35+
36+ u64 boundary ;
37+ snd_pcm_sframes_t delay ;
3038};
3139
3240static int sof_ipc4_set_multi_pipeline_state (struct snd_sof_dev * sdev , u32 state ,
@@ -726,6 +734,10 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
726734 if (abi_version < SOF_IPC4_FW_REGS_ABI_VER )
727735 support_info = false;
728736
737+ /* For delay reporting the get_host_byte_counter callback is needed */
738+ if (!sof_ops (sdev ) || !sof_ops (sdev )-> get_host_byte_counter )
739+ support_info = false;
740+
729741 for_each_pcm_streams (stream ) {
730742 pipeline_list = & spcm -> stream [stream ].pipeline_list ;
731743
@@ -858,7 +870,6 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
858870 struct sof_ipc4_copier * host_copier = time_info -> host_copier ;
859871 struct sof_ipc4_copier * dai_copier = time_info -> dai_copier ;
860872 struct sof_ipc4_pipeline_registers ppl_reg ;
861- u64 stream_start_position ;
862873 u32 dai_sample_size ;
863874 u32 ch , node_index ;
864875 u32 offset ;
@@ -875,38 +886,51 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
875886 if (ppl_reg .stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION )
876887 return - EINVAL ;
877888
878- stream_start_position = ppl_reg .stream_start_offset ;
879889 ch = dai_copier -> data .out_format .fmt_cfg ;
880890 ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT (ch );
881891 dai_sample_size = (dai_copier -> data .out_format .bit_depth >> 3 ) * ch ;
882- /* convert offset to sample count */
883- do_div (stream_start_position , dai_sample_size );
884- time_info -> stream_start_offset = stream_start_position ;
892+
893+ /* convert offsets to frame count */
894+ time_info -> stream_start_offset = ppl_reg .stream_start_offset ;
895+ do_div (time_info -> stream_start_offset , dai_sample_size );
896+ time_info -> stream_end_offset = ppl_reg .stream_end_offset ;
897+ do_div (time_info -> stream_end_offset , dai_sample_size );
898+
899+ /*
900+ * Calculate the wrap boundary need to be used for delay calculation
901+ * The host counter is in bytes, it will wrap earlier than the frames
902+ * based link counter.
903+ */
904+ time_info -> boundary = div64_u64 (~((u64 )0 ),
905+ frames_to_bytes (substream -> runtime , 1 ));
906+ /* Initialize the delay value to 0 (no delay) */
907+ time_info -> delay = 0 ;
885908
886909 return 0 ;
887910}
888911
889- static snd_pcm_sframes_t sof_ipc4_pcm_delay (struct snd_soc_component * component ,
890- struct snd_pcm_substream * substream )
912+ static int sof_ipc4_pcm_pointer (struct snd_soc_component * component ,
913+ struct snd_pcm_substream * substream ,
914+ snd_pcm_uframes_t * pointer )
891915{
892916 struct snd_sof_dev * sdev = snd_soc_component_get_drvdata (component );
893917 struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd (substream );
894918 struct sof_ipc4_timestamp_info * time_info ;
895919 struct sof_ipc4_llp_reading_slot llp ;
896- snd_pcm_uframes_t head_ptr , tail_ptr ;
920+ snd_pcm_uframes_t head_cnt , tail_cnt ;
897921 struct snd_sof_pcm_stream * stream ;
922+ u64 dai_cnt , host_cnt , host_ptr ;
898923 struct snd_sof_pcm * spcm ;
899- u64 tmp_ptr ;
900924 int ret ;
901925
902926 spcm = snd_sof_find_spcm_dai (component , rtd );
903927 if (!spcm )
904- return 0 ;
928+ return - EOPNOTSUPP ;
905929
906930 stream = & spcm -> stream [substream -> stream ];
907931 time_info = stream -> private ;
908932 if (!time_info )
909- return 0 ;
933+ return - EOPNOTSUPP ;
910934
911935 /*
912936 * stream_start_offset is updated to memory window by FW based on
@@ -916,46 +940,116 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component,
916940 if (time_info -> stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION ) {
917941 ret = sof_ipc4_get_stream_start_offset (sdev , substream , stream , time_info );
918942 if (ret < 0 )
919- return 0 ;
943+ return - EOPNOTSUPP ;
920944 }
921945
946+ /* For delay calculation we need the host counter */
947+ host_cnt = snd_sof_pcm_get_host_byte_counter (sdev , component , substream );
948+ host_ptr = host_cnt ;
949+
950+ /* convert the host_cnt to frames */
951+ host_cnt = div64_u64 (host_cnt , frames_to_bytes (substream -> runtime , 1 ));
952+
922953 /*
923954 * If the LLP counter is not reported by firmware in the SRAM window
924- * then read the dai (link) position via host accessible means if
955+ * then read the dai (link) counter via host accessible means if
925956 * available.
926957 */
927958 if (!time_info -> llp_offset ) {
928- tmp_ptr = snd_sof_pcm_get_dai_frame_counter (sdev , component , substream );
929- if (!tmp_ptr )
930- return 0 ;
959+ dai_cnt = snd_sof_pcm_get_dai_frame_counter (sdev , component , substream );
960+ if (!dai_cnt )
961+ return - EOPNOTSUPP ;
931962 } else {
932963 sof_mailbox_read (sdev , time_info -> llp_offset , & llp , sizeof (llp ));
933- tmp_ptr = ((u64 )llp .reading .llp_u << 32 ) | llp .reading .llp_l ;
964+ dai_cnt = ((u64 )llp .reading .llp_u << 32 ) | llp .reading .llp_l ;
934965 }
966+ dai_cnt += time_info -> stream_end_offset ;
935967
936- /* In two cases dai dma position is not accurate
968+ /* In two cases dai dma counter is not accurate
937969 * (1) dai pipeline is started before host pipeline
938- * (2) multiple streams mixed into one. Each stream has the same dai dma position
970+ * (2) multiple streams mixed into one. Each stream has the same dai dma
971+ * counter
972+ *
973+ * Firmware calculates correct stream_start_offset for all cases
974+ * including above two.
975+ * Driver subtracts stream_start_offset from dai dma counter to get
976+ * accurate one
977+ */
978+
979+ /*
980+ * On stream start the dai counter might not yet have reached the
981+ * stream_start_offset value which means that no frames have left the
982+ * DSP yet from the audio stream (on playback, capture streams have
983+ * offset of 0 as we start capturing right away).
984+ * In this case we need to adjust the distance between the counters by
985+ * increasing the host counter by (offset - dai_counter).
986+ * Otherwise the dai_counter needs to be adjusted to reflect the number
987+ * of valid frames passed on the DAI side.
939988 *
940- * Firmware calculates correct stream_start_offset for all cases including above two.
941- * Driver subtracts stream_start_offset from dai dma position to get accurate one
989+ * The delay is the difference between the counters on the two
990+ * sides of the DSP.
942991 */
943- tmp_ptr -= time_info -> stream_start_offset ;
992+ if (dai_cnt < time_info -> stream_start_offset ) {
993+ host_cnt += time_info -> stream_start_offset - dai_cnt ;
994+ dai_cnt = 0 ;
995+ } else {
996+ dai_cnt -= time_info -> stream_start_offset ;
997+ }
998+
999+ /* Wrap the dai counter at the boundary where the host counter wraps */
1000+ div64_u64_rem (dai_cnt , time_info -> boundary , & dai_cnt );
9441001
945- /* Calculate the delay taking into account that both pointer can wrap */
946- div64_u64_rem (tmp_ptr , substream -> runtime -> boundary , & tmp_ptr );
9471002 if (substream -> stream == SNDRV_PCM_STREAM_PLAYBACK ) {
948- head_ptr = substream -> runtime -> status -> hw_ptr ;
949- tail_ptr = tmp_ptr ;
1003+ head_cnt = host_cnt ;
1004+ tail_cnt = dai_cnt ;
9501005 } else {
951- head_ptr = tmp_ptr ;
952- tail_ptr = substream -> runtime -> status -> hw_ptr ;
1006+ head_cnt = dai_cnt ;
1007+ tail_cnt = host_cnt ;
1008+ }
1009+
1010+ if (head_cnt < tail_cnt ) {
1011+ time_info -> delay = time_info -> boundary - tail_cnt + head_cnt ;
1012+ goto out ;
9531013 }
9541014
955- if (head_ptr < tail_ptr )
956- return substream -> runtime -> boundary - tail_ptr + head_ptr ;
1015+ time_info -> delay = head_cnt - tail_cnt ;
1016+
1017+ out :
1018+ /*
1019+ * Convert the host byte counter to PCM pointer which wraps in buffer
1020+ * and it is in frames
1021+ */
1022+ div64_u64_rem (host_ptr , snd_pcm_lib_buffer_bytes (substream ), & host_ptr );
1023+ * pointer = bytes_to_frames (substream -> runtime , host_ptr );
1024+
1025+ return 0 ;
1026+ }
1027+
1028+ static snd_pcm_sframes_t sof_ipc4_pcm_delay (struct snd_soc_component * component ,
1029+ struct snd_pcm_substream * substream )
1030+ {
1031+ struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd (substream );
1032+ struct sof_ipc4_timestamp_info * time_info ;
1033+ struct snd_sof_pcm_stream * stream ;
1034+ struct snd_sof_pcm * spcm ;
1035+
1036+ spcm = snd_sof_find_spcm_dai (component , rtd );
1037+ if (!spcm )
1038+ return 0 ;
1039+
1040+ stream = & spcm -> stream [substream -> stream ];
1041+ time_info = stream -> private ;
1042+ /*
1043+ * Report the stored delay value calculated in the pointer callback.
1044+ * In the unlikely event that the calculation was skipped/aborted, the
1045+ * default 0 delay returned.
1046+ */
1047+ if (time_info )
1048+ return time_info -> delay ;
1049+
1050+ /* No delay information available, report 0 as delay */
1051+ return 0 ;
9571052
958- return head_ptr - tail_ptr ;
9591053}
9601054
9611055const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
@@ -965,6 +1059,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
9651059 .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup ,
9661060 .pcm_setup = sof_ipc4_pcm_setup ,
9671061 .pcm_free = sof_ipc4_pcm_free ,
1062+ .pointer = sof_ipc4_pcm_pointer ,
9681063 .delay = sof_ipc4_pcm_delay ,
9691064 .ipc_first_on_start = true,
9701065 .platform_stop_during_hw_free = true,
0 commit comments