aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2007-12-13 04:19:42 -0500
committerJaroslav Kysela <perex@perex.cz>2008-01-31 11:29:31 -0500
commitb751eef1fdffca5532344285f2fad0c60d2f0158 (patch)
treed504104c6315a8abc4b3f657f8f4828fb55a8795
parent25543fa785a32ce22e7374ba403eb6d38854d037 (diff)
[ALSA] Use posix clock monotonic for PCM and timer timestamps
We need an accurate and continuous (monotonic) time sources to do accurate synchronization among more timing sources. This patch allows to enable monotonic timestamps for ALSA PCM devices and enables monotonic timestamps for ALSA timer devices. Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--include/sound/asound.h10
-rw-r--r--include/sound/pcm.h10
-rw-r--r--sound/core/pcm_lib.c2
-rw-r--r--sound/core/pcm_native.c25
-rw-r--r--sound/core/timer.c16
5 files changed, 52 insertions, 11 deletions
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 3ad534149c06..475eb71d65ba 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -138,7 +138,7 @@ enum {
138 * * 138 * *
139 *****************************************************************************/ 139 *****************************************************************************/
140 140
141#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) 141#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9)
142 142
143typedef unsigned long snd_pcm_uframes_t; 143typedef unsigned long snd_pcm_uframes_t;
144typedef signed long snd_pcm_sframes_t; 144typedef signed long snd_pcm_sframes_t;
@@ -435,9 +435,15 @@ struct snd_xfern {
435}; 435};
436 436
437enum { 437enum {
438 SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /* gettimeofday equivalent */
439 SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, /* posix_clock_monotonic equivalent */
440 SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
441};
442
443enum {
438 SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), 444 SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
439 SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info), 445 SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info),
440 SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int), 446 SNDRV_PCM_IOCTL_TTSTAMP = _IOW('A', 0x03, int),
441 SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params), 447 SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params),
442 SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params), 448 SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params),
443 SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), 449 SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 5e9cc460075e..65f636223d39 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -323,6 +323,7 @@ struct snd_pcm_runtime {
323 323
324 /* -- timer -- */ 324 /* -- timer -- */
325 unsigned int timer_resolution; /* timer resolution */ 325 unsigned int timer_resolution; /* timer resolution */
326 int tstamp_type; /* timestamp type */
326 327
327 /* -- DMA -- */ 328 /* -- DMA -- */
328 unsigned char *dma_area; /* DMA area */ 329 unsigned char *dma_area; /* DMA area */
@@ -952,6 +953,15 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
952void snd_pcm_timer_init(struct snd_pcm_substream *substream); 953void snd_pcm_timer_init(struct snd_pcm_substream *substream);
953void snd_pcm_timer_done(struct snd_pcm_substream *substream); 954void snd_pcm_timer_done(struct snd_pcm_substream *substream);
954 955
956static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
957 struct timespec *tv)
958{
959 if (runtime->tstamp_type == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
960 do_posix_clock_monotonic_gettime(tv);
961 else
962 getnstimeofday(tv);
963}
964
955/* 965/*
956 * Memory 966 * Memory
957 */ 967 */
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 93d7ca502730..db3d7e934ec3 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -188,7 +188,7 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs
188 snd_pcm_sframes_t delta; 188 snd_pcm_sframes_t delta;
189 189
190 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP) 190 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP)
191 getnstimeofday((struct timespec *)&runtime->status->tstamp); 191 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
192 pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 192 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
193 if (pos == SNDRV_PCM_POS_XRUN) { 193 if (pos == SNDRV_PCM_POS_XRUN) {
194 xrun(substream); 194 xrun(substream);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 6245bdaffa68..cdeae7c46e3b 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -598,9 +598,9 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
598 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP) 598 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP)
599 status->tstamp = runtime->status->tstamp; 599 status->tstamp = runtime->status->tstamp;
600 else 600 else
601 getnstimeofday(&status->tstamp); 601 snd_pcm_gettime(runtime, &status->tstamp);
602 } else 602 } else
603 getnstimeofday(&status->tstamp); 603 snd_pcm_gettime(runtime, &status->tstamp);
604 status->appl_ptr = runtime->control->appl_ptr; 604 status->appl_ptr = runtime->control->appl_ptr;
605 status->hw_ptr = runtime->status->hw_ptr; 605 status->hw_ptr = runtime->status->hw_ptr;
606 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 606 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -688,7 +688,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
688 if (runtime->trigger_master == NULL) 688 if (runtime->trigger_master == NULL)
689 return; 689 return;
690 if (runtime->trigger_master == substream) { 690 if (runtime->trigger_master == substream) {
691 getnstimeofday(&runtime->trigger_tstamp); 691 snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
692 } else { 692 } else {
693 snd_pcm_trigger_tstamp(runtime->trigger_master); 693 snd_pcm_trigger_tstamp(runtime->trigger_master);
694 runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; 694 runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
@@ -2519,6 +2519,21 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
2519 return -EFAULT; 2519 return -EFAULT;
2520 return 0; 2520 return 0;
2521} 2521}
2522
2523static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
2524{
2525 struct snd_pcm_runtime *runtime = substream->runtime;
2526 int arg;
2527
2528 if (get_user(arg, _arg))
2529 return -EFAULT;
2530 if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
2531 return -EINVAL;
2532 runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
2533 if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
2534 runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
2535 return 0;
2536}
2522 2537
2523static int snd_pcm_common_ioctl1(struct file *file, 2538static int snd_pcm_common_ioctl1(struct file *file,
2524 struct snd_pcm_substream *substream, 2539 struct snd_pcm_substream *substream,
@@ -2531,8 +2546,8 @@ static int snd_pcm_common_ioctl1(struct file *file,
2531 return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; 2546 return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
2532 case SNDRV_PCM_IOCTL_INFO: 2547 case SNDRV_PCM_IOCTL_INFO:
2533 return snd_pcm_info_user(substream, arg); 2548 return snd_pcm_info_user(substream, arg);
2534 case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ 2549 case SNDRV_PCM_IOCTL_TTSTAMP:
2535 return 0; 2550 return snd_pcm_tstamp(substream, arg);
2536 case SNDRV_PCM_IOCTL_HW_REFINE: 2551 case SNDRV_PCM_IOCTL_HW_REFINE:
2537 return snd_pcm_hw_refine_user(substream, arg); 2552 return snd_pcm_hw_refine_user(substream, arg);
2538 case SNDRV_PCM_IOCTL_HW_PARAMS: 2553 case SNDRV_PCM_IOCTL_HW_PARAMS:
diff --git a/sound/core/timer.c b/sound/core/timer.c
index e7dc56ca4b97..7e5fe2d91662 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -44,11 +44,14 @@
44#endif 44#endif
45 45
46static int timer_limit = DEFAULT_TIMER_LIMIT; 46static int timer_limit = DEFAULT_TIMER_LIMIT;
47static int timer_tstamp_monotonic = 1;
47MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); 48MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
48MODULE_DESCRIPTION("ALSA timer interface"); 49MODULE_DESCRIPTION("ALSA timer interface");
49MODULE_LICENSE("GPL"); 50MODULE_LICENSE("GPL");
50module_param(timer_limit, int, 0444); 51module_param(timer_limit, int, 0444);
51MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); 52MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
53module_param(timer_tstamp_monotonic, int, 0444);
54MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
52 55
53struct snd_timer_user { 56struct snd_timer_user {
54 struct snd_timer_instance *timeri; 57 struct snd_timer_instance *timeri;
@@ -381,7 +384,10 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
381 struct snd_timer_instance *ts; 384 struct snd_timer_instance *ts;
382 struct timespec tstamp; 385 struct timespec tstamp;
383 386
384 getnstimeofday(&tstamp); 387 if (timer_tstamp_monotonic)
388 do_posix_clock_monotonic_gettime(&tstamp);
389 else
390 getnstimeofday(&tstamp);
385 snd_assert(event >= SNDRV_TIMER_EVENT_START && 391 snd_assert(event >= SNDRV_TIMER_EVENT_START &&
386 event <= SNDRV_TIMER_EVENT_PAUSE, return); 392 event <= SNDRV_TIMER_EVENT_PAUSE, return);
387 if (event == SNDRV_TIMER_EVENT_START || 393 if (event == SNDRV_TIMER_EVENT_START ||
@@ -1182,8 +1188,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
1182 spin_unlock(&tu->qlock); 1188 spin_unlock(&tu->qlock);
1183 return; 1189 return;
1184 } 1190 }
1185 if (tu->last_resolution != resolution || ticks > 0) 1191 if (tu->last_resolution != resolution || ticks > 0) {
1186 getnstimeofday(&tstamp); 1192 if (timer_tstamp_monotonic)
1193 do_posix_clock_monotonic_gettime(&tstamp);
1194 else
1195 getnstimeofday(&tstamp);
1196 }
1187 if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && 1197 if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
1188 tu->last_resolution != resolution) { 1198 tu->last_resolution != resolution) {
1189 r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 1199 r1.event = SNDRV_TIMER_EVENT_RESOLUTION;