19
19
* struct sof_ipc4_timestamp_info - IPC4 timestamp info
20
20
* @host_copier: the host copier of the pcm stream
21
21
* @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)
23
24
* @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.
24
28
*/
25
29
struct sof_ipc4_timestamp_info {
26
30
struct sof_ipc4_copier * host_copier ;
27
31
struct sof_ipc4_copier * dai_copier ;
28
32
u64 stream_start_offset ;
33
+ u64 stream_end_offset ;
29
34
u32 llp_offset ;
35
+
36
+ u64 boundary ;
37
+ snd_pcm_sframes_t delay ;
30
38
};
31
39
32
40
static 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
726
734
if (abi_version < SOF_IPC4_FW_REGS_ABI_VER )
727
735
support_info = false;
728
736
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
+
729
741
for_each_pcm_streams (stream ) {
730
742
pipeline_list = & spcm -> stream [stream ].pipeline_list ;
731
743
@@ -858,7 +870,6 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
858
870
struct sof_ipc4_copier * host_copier = time_info -> host_copier ;
859
871
struct sof_ipc4_copier * dai_copier = time_info -> dai_copier ;
860
872
struct sof_ipc4_pipeline_registers ppl_reg ;
861
- u64 stream_start_position ;
862
873
u32 dai_sample_size ;
863
874
u32 ch , node_index ;
864
875
u32 offset ;
@@ -875,38 +886,51 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
875
886
if (ppl_reg .stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION )
876
887
return - EINVAL ;
877
888
878
- stream_start_position = ppl_reg .stream_start_offset ;
879
889
ch = dai_copier -> data .out_format .fmt_cfg ;
880
890
ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT (ch );
881
891
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 ;
885
908
886
909
return 0 ;
887
910
}
888
911
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 )
891
915
{
892
916
struct snd_sof_dev * sdev = snd_soc_component_get_drvdata (component );
893
917
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd (substream );
894
918
struct sof_ipc4_timestamp_info * time_info ;
895
919
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 ;
897
921
struct snd_sof_pcm_stream * stream ;
922
+ u64 dai_cnt , host_cnt , host_ptr ;
898
923
struct snd_sof_pcm * spcm ;
899
- u64 tmp_ptr ;
900
924
int ret ;
901
925
902
926
spcm = snd_sof_find_spcm_dai (component , rtd );
903
927
if (!spcm )
904
- return 0 ;
928
+ return - EOPNOTSUPP ;
905
929
906
930
stream = & spcm -> stream [substream -> stream ];
907
931
time_info = stream -> private ;
908
932
if (!time_info )
909
- return 0 ;
933
+ return - EOPNOTSUPP ;
910
934
911
935
/*
912
936
* 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,
916
940
if (time_info -> stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION ) {
917
941
ret = sof_ipc4_get_stream_start_offset (sdev , substream , stream , time_info );
918
942
if (ret < 0 )
919
- return 0 ;
943
+ return - EOPNOTSUPP ;
920
944
}
921
945
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
+
922
953
/*
923
954
* 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
925
956
* available.
926
957
*/
927
958
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 ;
931
962
} else {
932
963
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 ;
934
965
}
966
+ dai_cnt += time_info -> stream_end_offset ;
935
967
936
- /* In two cases dai dma position is not accurate
968
+ /* In two cases dai dma counter is not accurate
937
969
* (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.
939
988
*
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.
942
991
*/
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 );
944
1001
945
- /* Calculate the delay taking into account that both pointer can wrap */
946
- div64_u64_rem (tmp_ptr , substream -> runtime -> boundary , & tmp_ptr );
947
1002
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 ;
950
1005
} 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 ;
953
1013
}
954
1014
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 ;
957
1052
958
- return head_ptr - tail_ptr ;
959
1053
}
960
1054
961
1055
const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
@@ -965,6 +1059,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
965
1059
.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup ,
966
1060
.pcm_setup = sof_ipc4_pcm_setup ,
967
1061
.pcm_free = sof_ipc4_pcm_free ,
1062
+ .pointer = sof_ipc4_pcm_pointer ,
968
1063
.delay = sof_ipc4_pcm_delay ,
969
1064
.ipc_first_on_start = true,
970
1065
.platform_stop_during_hw_free = true,
0 commit comments