[alsa-devel] Improving status timestamp accuracy

Alan Young consult.awy at gmail.com
Sun Jun 5 12:33:20 CEST 2016


On 05/06/16 02:14, Raymond Yau wrote:
> the point only increment in DMA brust size , so it is not sample accurate
>
> https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/include/linux/dmaengine.h
>
> |* @DMA_RESIDUE_GRANULARITY_BURST: |Residue is updated after each 
> transferred
> |* burst. This is typically only supported if the hardware has a 
> progress * register of some sort (E.g. a register with the current 
> read/write address * or a register with the amount of 
> bursts/beats/bytes that have been * transferred or still need to be 
> transferred).|
> ||
> |if the driver point callback does not read from hardware register , it 
> should use |
> ||
> |
> |DMA_RESIDUE_GRANULARITY_DESCRIPTOR: Residue reporting is not support. 
> The * DMA channel is only able to tell whether a descriptor has been 
> completed or * not, which means residue reporting is not supported by 
> this channel. The * residue field of the dma_tx_state field will 
> always be 0.|
> |


Yes, I understand that. And that is exactly my point. Because of this a 
pcm_status() result is only accurate to a granularity of period in most 
cases.

In fact, some drivers, for example imx sdma, declare 
DMA_RESIDUE_GRANULARITY_BURST accuracy because sometimes they may have 
such an accuracy but in practice, at least for audio, they only actually 
achieve period accuracy.

Regardless of what value of DMA_RESIDUE_GRANULARITY_xxx that a driver 
claims to support, it is not really defined how fine a burst might be. 
So the end result is, from the point of view of audio, that the 
resulting position obtained by the pointer() call is pretty inaccurate. 
Hence my proposal to attempt to improve the accuracy of the pcm_status() 
result given the above constraints.

The following patch gives an idea of what I am considering:

diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 6b5a811..ea5b525 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -234,7 +234,8 @@ int snd_pcm_update_state(struct snd_pcm_substream 
*substream,

  static void update_audio_tstamp(struct snd_pcm_substream *substream,
                  struct timespec *curr_tstamp,
-                struct timespec *audio_tstamp)
+                struct timespec *audio_tstamp,
+                unsigned int adjust_existing_audio_tstamp)
  {
      struct snd_pcm_runtime *runtime = substream->runtime;
      u64 audio_frames, audio_nsecs;
@@ -252,17 +253,23 @@ static void update_audio_tstamp(struct 
snd_pcm_substream *substream,
           * add delay only if requested
           */

-        audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;
+        if (adjust_existing_audio_tstamp && 
runtime->status->tstamp->tv_sec) {
+            struct timespec delta =
+                timespec_sub(*curr_tstamp, runtime->status->tstamp);
+            *audio_tstamp = timespec_add(*audio_tstamp, delta);
+        } else {
+            audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;

-        if (runtime->audio_tstamp_config.report_delay) {
-            if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                audio_frames -=  runtime->delay;
-            else
-                audio_frames +=  runtime->delay;
+            if (runtime->audio_tstamp_config.report_delay) {
+                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                    audio_frames -=  runtime->delay;
+                else
+                    audio_frames +=  runtime->delay;
+            }
+            audio_nsecs = div_u64(audio_frames * 1000000000LL,
+                    runtime->rate);
+            *audio_tstamp = ns_to_timespec(audio_nsecs);
          }
-        audio_nsecs = div_u64(audio_frames * 1000000000LL,
-                runtime->rate);
-        *audio_tstamp = ns_to_timespec(audio_nsecs);
      }
      runtime->status->audio_tstamp = *audio_tstamp;
      runtime->status->tstamp = *curr_tstamp;
@@ -454,7 +461,7 @@ static int snd_pcm_update_hw_ptr0(struct 
snd_pcm_substream *substream,

   no_delta_check:
      if (runtime->status->hw_ptr == new_hw_ptr) {
-        update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
+        update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp, 
!in_interrupt);
          return 0;
      }

@@ -479,7 +486,7 @@ static int snd_pcm_update_hw_ptr0(struct 
snd_pcm_substream *substream,
          runtime->hw_ptr_wrap += runtime->boundary;
      }

-    update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
+    update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp, 
!in_interrupt);

      return snd_pcm_update_state(substream, runtime);
  }



More information about the Alsa-devel mailing list