Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
72f770c6
Commit
72f770c6
authored
Feb 23, 2015
by
Takashi Iwai
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/timestamp' into for-next
parents
0eee62e0
c72638bd
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
444 additions
and
55 deletions
+444
-55
Documentation/sound/alsa/timestamping.txt
Documentation/sound/alsa/timestamping.txt
+200
-0
include/sound/pcm.h
include/sound/pcm.h
+64
-2
include/uapi/sound/asound.h
include/uapi/sound/asound.h
+31
-5
sound/core/pcm_compat.c
sound/core/pcm_compat.c
+23
-5
sound/core/pcm_lib.c
sound/core/pcm_lib.c
+59
-29
sound/core/pcm_native.c
sound/core/pcm_native.c
+38
-3
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_controller.c
+29
-11
No files found.
Documentation/sound/alsa/timestamping.txt
0 → 100644
View file @
72f770c6
The ALSA API can provide two different system timestamps:
- Trigger_tstamp is the system time snapshot taken when the .trigger
callback is invoked. This snapshot is taken by the ALSA core in the
general case, but specific hardware may have synchronization
capabilities or conversely may only be able to provide a correct
estimate with a delay. In the latter two cases, the low-level driver
is responsible for updating the trigger_tstamp at the most appropriate
and precise moment. Applications should not rely solely on the first
trigger_tstamp but update their internal calculations if the driver
provides a refined estimate with a delay.
- tstamp is the current system timestamp updated during the last
event or application query.
The difference (tstamp - trigger_tstamp) defines the elapsed time.
The ALSA API provides reports two basic pieces of information, avail
and delay, which combined with the trigger and current system
timestamps allow for applications to keep track of the 'fullness' of
the ring buffer and the amount of queued samples.
The use of these different pointers and time information depends on
the application needs:
- 'avail' reports how much can be written in the ring buffer
- 'delay' reports the time it will take to hear a new sample after all
queued samples have been played out.
When timestamps are enabled, the avail/delay information is reported
along with a snapshot of system time. Applications can select from
CLOCK_REALTIME (NTP corrections including going backwards),
CLOCK_MONOTONIC (NTP corrections but never going backwards),
CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode
dynamically with sw_params
The ALSA API also provide an audio_tstamp which reflects the passage
of time as measured by different components of audio hardware. In
ascii-art, this could be represented as follows (for the playback
case):
--------------------------------------------------------------> time
^ ^ ^ ^ ^
| | | | |
analog link dma app FullBuffer
time time time time time
| | | | |
|< codec delay >|<--hw delay-->|<queued samples>|<---avail->|
|<----------------- delay---------------------->| |
|<----ring buffer length---->|
The analog time is taken at the last stage of the playback, as close
as possible to the actual transducer
The link time is taken at the output of the SOC/chipset as the samples
are pushed on a link. The link time can be directly measured if
supported in hardware by sample counters or wallclocks (e.g. with
HDAudio 24MHz or PTP clock for networked solutions) or indirectly
estimated (e.g. with the frame counter in USB).
The DMA time is measured using counters - typically the least reliable
of all measurements due to the bursty natured of DMA transfers.
The app time corresponds to the time tracked by an application after
writing in the ring buffer.
The application can query what the hardware supports, define which
audio time it wants reported by selecting the relevant settings in
audio_tstamp_config fields, get an estimate of the timestamp
accuracy. It can also request the delay-to-analog be included in the
measurement. Direct access to the link time is very interesting on
platforms that provide an embedded DSP; measuring directly the link
time with dedicated hardware, possibly synchronized with system time,
removes the need to keep track of internal DSP processing times and
latency.
In case the application requests an audio tstamp that is not supported
in hardware/low-level driver, the type is overridden as DEFAULT and the
timestamp will report the DMA time based on the hw_pointer value.
For backwards compatibility with previous implementations that did not
provide timestamp selection, with a zero-valued COMPAT timestamp type
the results will default to the HDAudio wall clock for playback
streams and to the DMA time (hw_ptr) in all other cases.
The audio timestamp accuracy can be returned to user-space, so that
appropriate decisions are made:
- for dma time (default), the granularity of the transfers can be
inferred from the steps between updates and in turn provide
information on how much the application pointer can be rewound
safely.
- the link time can be used to track long-term drifts between audio
and system time using the (tstamp-trigger_tstamp)/audio_tstamp
ratio, the precision helps define how much smoothing/low-pass
filtering is required. The link time can be either reset on startup
or reported as is (the latter being useful to compare progress of
different streams - but may require the wallclock to be always
running and not wrap-around during idle periods). If supported in
hardware, the absolute link time could also be used to define a
precise start time (patches WIP)
- including the delay in the audio timestamp may
counter-intuitively not increase the precision of timestamps, e.g. if a
codec includes variable-latency DSP processing or a chain of
hardware components the delay is typically not known with precision.
The accuracy is reported in nanosecond units (using an unsigned 32-bit
word), which gives a max precision of 4.29s, more than enough for
audio applications...
Due to the varied nature of timestamping needs, even for a single
application, the audio_tstamp_config can be changed dynamically. In
the STATUS ioctl, the parameters are read-only and do not allow for
any application selection. To work around this limitation without
impacting legacy applications, a new STATUS_EXT ioctl is introduced
with read/write parameters. ALSA-lib will be modified to make use of
STATUS_EXT and effectively deprecate STATUS.
The ALSA API only allows for a single audio timestamp to be reported
at a time. This is a conscious design decision, reading the audio
timestamps from hardware registers or from IPC takes time, the more
timestamps are read the more imprecise the combined measurements
are. To avoid any interpretation issues, a single (system, audio)
timestamp is reported. Applications that need different timestamps
will be required to issue multiple queries and perform an
interpolation of the results
In some hardware-specific configuration, the system timestamp is
latched by a low-level audio subsytem, and the information provided
back to the driver. Due to potential delays in the communication with
the hardware, there is a risk of misalignment with the avail and delay
information. To make sure applications are not confused, a
driver_timestamp field is added in the snd_pcm_status structure; this
timestamp shows when the information is put together by the driver
before returning from the STATUS and STATUS_EXT ioctl. in most cases
this driver_timestamp will be identical to the regular system tstamp.
Examples of typestamping with HDaudio:
1. DMA timestamp, no compensation for DMA+analog delay
$ ./audio_time -p --ts_type=1
playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662
playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837
playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420
playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051
playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751
playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822
2. DMA timestamp, compensation for DMA+analog delay
$ ./audio_time -p --ts_type=1 -d
playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153
playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947
playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685
playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349
playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694
3. link timestamp, compensation for DMA+analog delay
$ ./audio_time -p --ts_type=2 -d
playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787
playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801
playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591
playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779
playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687
playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146
Example 1 shows that the timestamp at the DMA level is close to 1ms
ahead of the actual playback time (as a side time this sort of
measurement can help define rewind safeguards). Compensating for the
DMA-link delay in example 2 helps remove the hardware buffering abut
the information is still very jittery, with up to one sample of
error. In example 3 where the timestamps are measured with the link
wallclock, the timestamps show a monotonic behavior and a lower
dispersion.
Example 3 and 4 are with USB audio class. Example 3 shows a high
offset between audio time and system time due to buffering. Example 4
shows how compensating for the delay exposes a 1ms accuracy (due to
the use of the frame counter by the driver)
Example 3: DMA timestamp, no compensation for delay, delta of ~5ms
$ ./audio_time -p -Dhw:1 -t1
playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981
playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864
playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912
playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935
playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821
playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259
playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664
Example 4: DMA timestamp, compensation for delay, delay of ~1ms
$ ./audio_time -p -Dhw:1 -t1 -d
playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520
playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740
playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081
playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907
playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824
playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847
include/sound/pcm.h
View file @
72f770c6
...
...
@@ -60,6 +60,9 @@ struct snd_pcm_hardware {
struct
snd_pcm_substream
;
struct
snd_pcm_audio_tstamp_config
;
/* definitions further down */
struct
snd_pcm_audio_tstamp_report
;
struct
snd_pcm_ops
{
int
(
*
open
)(
struct
snd_pcm_substream
*
substream
);
int
(
*
close
)(
struct
snd_pcm_substream
*
substream
);
...
...
@@ -71,8 +74,10 @@ struct snd_pcm_ops {
int
(
*
prepare
)(
struct
snd_pcm_substream
*
substream
);
int
(
*
trigger
)(
struct
snd_pcm_substream
*
substream
,
int
cmd
);
snd_pcm_uframes_t
(
*
pointer
)(
struct
snd_pcm_substream
*
substream
);
int
(
*
wall_clock
)(
struct
snd_pcm_substream
*
substream
,
struct
timespec
*
audio_ts
);
int
(
*
get_time_info
)(
struct
snd_pcm_substream
*
substream
,
struct
timespec
*
system_ts
,
struct
timespec
*
audio_ts
,
struct
snd_pcm_audio_tstamp_config
*
audio_tstamp_config
,
struct
snd_pcm_audio_tstamp_report
*
audio_tstamp_report
);
int
(
*
copy
)(
struct
snd_pcm_substream
*
substream
,
int
channel
,
snd_pcm_uframes_t
pos
,
void
__user
*
buf
,
snd_pcm_uframes_t
count
);
...
...
@@ -281,6 +286,58 @@ struct snd_pcm_hw_constraint_ranges {
struct
snd_pcm_hwptr_log
;
/*
* userspace-provided audio timestamp config to kernel,
* structure is for internal use only and filled with dedicated unpack routine
*/
struct
snd_pcm_audio_tstamp_config
{
/* 5 of max 16 bits used */
u32
type_requested
:
4
;
u32
report_delay
:
1
;
/* add total delay to A/D or D/A */
};
static
inline
void
snd_pcm_unpack_audio_tstamp_config
(
__u32
data
,
struct
snd_pcm_audio_tstamp_config
*
config
)
{
config
->
type_requested
=
data
&
0xF
;
config
->
report_delay
=
(
data
>>
4
)
&
1
;
}
/*
* kernel-provided audio timestamp report to user-space
* structure is for internal use only and read by dedicated pack routine
*/
struct
snd_pcm_audio_tstamp_report
{
/* 6 of max 16 bits used for bit-fields */
/* for backwards compatibility */
u32
valid
:
1
;
/* actual type if hardware could not support requested timestamp */
u32
actual_type
:
4
;
/* accuracy represented in ns units */
u32
accuracy_report
:
1
;
/* 0 if accuracy unknown, 1 if accuracy field is valid */
u32
accuracy
;
/* up to 4.29s, will be packed in separate field */
};
static
inline
void
snd_pcm_pack_audio_tstamp_report
(
__u32
*
data
,
__u32
*
accuracy
,
const
struct
snd_pcm_audio_tstamp_report
*
report
)
{
u32
tmp
;
tmp
=
report
->
accuracy_report
;
tmp
<<=
4
;
tmp
|=
report
->
actual_type
;
tmp
<<=
1
;
tmp
|=
report
->
valid
;
*
data
&=
0xffff
;
/* zero-clear MSBs */
*
data
|=
(
tmp
<<
16
);
*
accuracy
=
report
->
accuracy
;
}
struct
snd_pcm_runtime
{
/* -- Status -- */
struct
snd_pcm_substream
*
trigger_master
;
...
...
@@ -361,6 +418,11 @@ struct snd_pcm_runtime {
struct
snd_dma_buffer
*
dma_buffer_p
;
/* allocated buffer */
/* -- audio timestamp config -- */
struct
snd_pcm_audio_tstamp_config
audio_tstamp_config
;
struct
snd_pcm_audio_tstamp_report
audio_tstamp_report
;
struct
timespec
driver_tstamp
;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct
snd_pcm_oss_runtime
oss
;
...
...
include/uapi/sound/asound.h
View file @
72f770c6
...
...
@@ -143,7 +143,7 @@ struct snd_hwdep_dsp_image {
* *
*****************************************************************************/
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1
2
)
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1
3
)
typedef
unsigned
long
snd_pcm_uframes_t
;
typedef
signed
long
snd_pcm_sframes_t
;
...
...
@@ -270,10 +270,17 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000
/* playback and capture stream are somewhat correlated */
#define SNDRV_PCM_INFO_SYNC_START 0x00400000
/* pcm support some kind of sync go */
#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000
/* period wakeup can be disabled */
#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000
/* has audio wall clock for audio/system time sync */
#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000
/* (Deprecated)has audio wall clock for audio/system time sync */
#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000
/* report hardware link audio time, reset on startup */
#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000
/* report absolute hardware link audio time, not reset on startup */
#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000
/* report estimated link audio time */
#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000
/* report synchronized audio/system time */
#define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000
/* internal kernel flag - trigger in drain */
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000
/* internal kernel flag - FIFO size is in frames */
typedef
int
__bitwise
snd_pcm_state_t
;
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0)
/* stream is open */
#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1)
/* stream has a setup */
...
...
@@ -411,6 +418,22 @@ struct snd_pcm_channel_info {
unsigned
int
step
;
/* samples distance in bits */
};
enum
{
/*
* first definition for backwards compatibility only,
* maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else
*/
SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT
=
0
,
/* timestamp definitions */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT
=
1
,
/* DMA time, reported as per hw_ptr */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK
=
2
,
/* link time reported by sample or wallclock counter, reset on startup */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE
=
3
,
/* link time reported by sample or wallclock counter, not reset on startup */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED
=
4
,
/* link time estimated indirectly */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
=
5
,
/* link time synchronized with system time */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST
=
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
};
struct
snd_pcm_status
{
snd_pcm_state_t
state
;
/* stream state */
struct
timespec
trigger_tstamp
;
/* time when stream was started/stopped/paused */
...
...
@@ -422,9 +445,11 @@ struct snd_pcm_status {
snd_pcm_uframes_t
avail_max
;
/* max frames available on hw since last status */
snd_pcm_uframes_t
overrange
;
/* count of ADC (capture) overrange detections from last status */
snd_pcm_state_t
suspended_state
;
/* suspended stream state */
__u32
reserved_alignment
;
/* must be filled with zero */
struct
timespec
audio_tstamp
;
/* from sample counter or wall clock */
unsigned
char
reserved
[
56
-
sizeof
(
struct
timespec
)];
/* must be filled with zero */
__u32
audio_tstamp_data
;
/* needed for 64-bit alignment, used for configs/report to/from userspace */
struct
timespec
audio_tstamp
;
/* sample counter, wall clock, PHC or on-demand sync'ed */
struct
timespec
driver_tstamp
;
/* useful in case reference system tstamp is reported with delay */
__u32
audio_tstamp_accuracy
;
/* in ns units, only valid if indicated in audio_tstamp_data */
unsigned
char
reserved
[
52
-
2
*
sizeof
(
struct
timespec
)];
/* must be filled with zero */
};
struct
snd_pcm_mmap_status
{
...
...
@@ -537,6 +562,7 @@ enum {
#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22)
#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status)
#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40)
#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)
...
...
sound/core/pcm_compat.c
View file @
72f770c6
...
...
@@ -194,18 +194,30 @@ struct snd_pcm_status32 {
u32
avail_max
;
u32
overrange
;
s32
suspended_state
;
u32
reserved_alignment
;
u32
audio_tstamp_data
;
struct
compat_timespec
audio_tstamp
;
unsigned
char
reserved
[
56
-
sizeof
(
struct
compat_timespec
)];
struct
compat_timespec
driver_tstamp
;
u32
audio_tstamp_accuracy
;
unsigned
char
reserved
[
52
-
2
*
sizeof
(
struct
compat_timespec
)];
}
__attribute__
((
packed
));
static
int
snd_pcm_status_user_compat
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_status32
__user
*
src
)
struct
snd_pcm_status32
__user
*
src
,
bool
ext
)
{
struct
snd_pcm_status
status
;
int
err
;
memset
(
&
status
,
0
,
sizeof
(
status
));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
* ignore rest of status structure
*/
if
(
ext
&&
get_user
(
status
.
audio_tstamp_data
,
(
u32
__user
*
)(
&
src
->
audio_tstamp_data
)))
return
-
EFAULT
;
err
=
snd_pcm_status
(
substream
,
&
status
);
if
(
err
<
0
)
return
err
;
...
...
@@ -222,7 +234,10 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
put_user
(
status
.
avail_max
,
&
src
->
avail_max
)
||
put_user
(
status
.
overrange
,
&
src
->
overrange
)
||
put_user
(
status
.
suspended_state
,
&
src
->
suspended_state
)
||
compat_put_timespec
(
&
status
.
audio_tstamp
,
&
src
->
audio_tstamp
))
put_user
(
status
.
audio_tstamp_data
,
&
src
->
audio_tstamp_data
)
||
compat_put_timespec
(
&
status
.
audio_tstamp
,
&
src
->
audio_tstamp
)
||
compat_put_timespec
(
&
status
.
driver_tstamp
,
&
src
->
driver_tstamp
)
||
put_user
(
status
.
audio_tstamp_accuracy
,
&
src
->
audio_tstamp_accuracy
))
return
-
EFAULT
;
return
err
;
...
...
@@ -457,6 +472,7 @@ enum {
SNDRV_PCM_IOCTL_HW_PARAMS32
=
_IOWR
(
'A'
,
0x11
,
struct
snd_pcm_hw_params32
),
SNDRV_PCM_IOCTL_SW_PARAMS32
=
_IOWR
(
'A'
,
0x13
,
struct
snd_pcm_sw_params32
),
SNDRV_PCM_IOCTL_STATUS32
=
_IOR
(
'A'
,
0x20
,
struct
snd_pcm_status32
),
SNDRV_PCM_IOCTL_STATUS_EXT32
=
_IOWR
(
'A'
,
0x24
,
struct
snd_pcm_status32
),
SNDRV_PCM_IOCTL_DELAY32
=
_IOR
(
'A'
,
0x21
,
s32
),
SNDRV_PCM_IOCTL_CHANNEL_INFO32
=
_IOR
(
'A'
,
0x32
,
struct
snd_pcm_channel_info32
),
SNDRV_PCM_IOCTL_REWIND32
=
_IOW
(
'A'
,
0x46
,
u32
),
...
...
@@ -517,7 +533,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case
SNDRV_PCM_IOCTL_SW_PARAMS32
:
return
snd_pcm_ioctl_sw_params_compat
(
substream
,
argp
);
case
SNDRV_PCM_IOCTL_STATUS32
:
return
snd_pcm_status_user_compat
(
substream
,
argp
);
return
snd_pcm_status_user_compat
(
substream
,
argp
,
false
);
case
SNDRV_PCM_IOCTL_STATUS_EXT32
:
return
snd_pcm_status_user_compat
(
substream
,
argp
,
true
);
case
SNDRV_PCM_IOCTL_SYNC_PTR32
:
return
snd_pcm_ioctl_sync_ptr_compat
(
substream
,
argp
);
case
SNDRV_PCM_IOCTL_CHANNEL_INFO32
:
...
...
sound/core/pcm_lib.c
View file @
72f770c6
...
...
@@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
return
0
;
}
static
void
update_audio_tstamp
(
struct
snd_pcm_substream
*
substream
,
struct
timespec
*
curr_tstamp
,
struct
timespec
*
audio_tstamp
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
u64
audio_frames
,
audio_nsecs
;
struct
timespec
driver_tstamp
;
if
(
runtime
->
tstamp_mode
!=
SNDRV_PCM_TSTAMP_ENABLE
)
return
;
if
(
!
(
substream
->
ops
->
get_time_info
)
||
(
runtime
->
audio_tstamp_report
.
actual_type
==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT
))
{
/*
* provide audio timestamp derived from pointer position
* add delay only if requested
*/
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
;
}
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
;
/*
* re-take a driver timestamp to let apps detect if the reference tstamp
* read by low-level hardware was provided with a delay
*/
snd_pcm_gettime
(
substream
->
runtime
,
(
struct
timespec
*
)
&
driver_tstamp
);
runtime
->
driver_tstamp
=
driver_tstamp
;
}
static
int
snd_pcm_update_hw_ptr0
(
struct
snd_pcm_substream
*
substream
,
unsigned
int
in_interrupt
)
{
...
...
@@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
pos
=
substream
->
ops
->
pointer
(
substream
);
curr_jiffies
=
jiffies
;
if
(
runtime
->
tstamp_mode
==
SNDRV_PCM_TSTAMP_ENABLE
)
{
if
((
substream
->
ops
->
get_time_info
)
&&
(
runtime
->
audio_tstamp_config
.
type_requested
!=
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT
))
{
substream
->
ops
->
get_time_info
(
substream
,
&
curr_tstamp
,
&
audio_tstamp
,
&
runtime
->
audio_tstamp_config
,
&
runtime
->
audio_tstamp_report
);
/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
if
(
runtime
->
audio_tstamp_report
.
actual_type
==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT
)
snd_pcm_gettime
(
runtime
,
(
struct
timespec
*
)
&
curr_tstamp
);
}
else
snd_pcm_gettime
(
runtime
,
(
struct
timespec
*
)
&
curr_tstamp
);
if
((
runtime
->
hw
.
info
&
SNDRV_PCM_INFO_HAS_WALL_CLOCK
)
&&
(
substream
->
ops
->
wall_clock
))
substream
->
ops
->
wall_clock
(
substream
,
&
audio_tstamp
);
}
if
(
pos
==
SNDRV_PCM_POS_XRUN
)
{
...
...
@@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_delta_check:
if
(
runtime
->
status
->
hw_ptr
==
new_hw_ptr
)
if
(
runtime
->
status
->
hw_ptr
==
new_hw_ptr
)
{
update_audio_tstamp
(
substream
,
&
curr_tstamp
,
&
audio_tstamp
);
return
0
;
}
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
&&
runtime
->
silence_size
>
0
)
...
...
@@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_BUG_ON
(
crossed_boundary
!=
1
);
runtime
->
hw_ptr_wrap
+=
runtime
->
boundary
;
}
if
(
runtime
->
tstamp_mode
==
SNDRV_PCM_TSTAMP_ENABLE
)
{
runtime
->
status
->
tstamp
=
curr_tstamp
;
if
(
!
(
runtime
->
hw
.
info
&
SNDRV_PCM_INFO_HAS_WALL_CLOCK
))
{
/*
* no wall clock available, provide audio timestamp
* derived from pointer position+delay
*/
u64
audio_frames
,
audio_nsecs
;
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
audio_frames
=
runtime
->
hw_ptr_wrap
+
runtime
->
status
->
hw_ptr
-
runtime
->
delay
;
else
audio_frames
=
runtime
->
hw_ptr_wrap
+
runtime
->
status
->
hw_ptr
+
runtime
->
delay
;
audio_nsecs
=
div_u64
(
audio_frames
*
1000000000LL
,
runtime
->
rate
);
audio_tstamp
=
ns_to_timespec
(
audio_nsecs
);
}
runtime
->
status
->
audio_tstamp
=
audio_tstamp
;
}
update_audio_tstamp
(
substream
,
&
curr_tstamp
,
&
audio_tstamp
);
return
snd_pcm_update_state
(
substream
,
runtime
);
}
...
...
sound/core/pcm_native.c
View file @
72f770c6
...
...
@@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
snd_pcm_stream_lock_irq
(
substream
);
snd_pcm_unpack_audio_tstamp_config
(
status
->
audio_tstamp_data
,
&
runtime
->
audio_tstamp_config
);
/* backwards compatible behavior */
if
(
runtime
->
audio_tstamp_config
.
type_requested
==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT
)
{
if
(
runtime
->
hw
.
info
&
SNDRV_PCM_INFO_HAS_WALL_CLOCK
)
runtime
->
audio_tstamp_config
.
type_requested
=
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK
;
else
runtime
->
audio_tstamp_config
.
type_requested
=
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT
;
runtime
->
audio_tstamp_report
.
valid
=
0
;
}
else
runtime
->
audio_tstamp_report
.
valid
=
1
;
status
->
state
=
runtime
->
status
->
state
;
status
->
suspended_state
=
runtime
->
status
->
suspended_state
;
if
(
status
->
state
==
SNDRV_PCM_STATE_OPEN
)
...
...
@@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
snd_pcm_update_hw_ptr
(
substream
);
if
(
runtime
->
tstamp_mode
==
SNDRV_PCM_TSTAMP_ENABLE
)
{
status
->
tstamp
=
runtime
->
status
->
tstamp
;
status
->
driver_tstamp
=
runtime
->
driver_tstamp
;
status
->
audio_tstamp
=
runtime
->
status
->
audio_tstamp
;
if
(
runtime
->
audio_tstamp_report
.
valid
==
1
)
/* backwards compatibility, no report provided in COMPAT mode */
snd_pcm_pack_audio_tstamp_report
(
&
status
->
audio_tstamp_data
,
&
status
->
audio_tstamp_accuracy
,
&
runtime
->
audio_tstamp_report
);
goto
_tstamp_end
;
}
}
else
{
...
...
@@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
}
static
int
snd_pcm_status_user
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_status
__user
*
_status
)
struct
snd_pcm_status
__user
*
_status
,
bool
ext
)
{
struct
snd_pcm_status
status
;
int
res
;
memset
(
&
status
,
0
,
sizeof
(
status
));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
* ignore rest of status structure
*/
if
(
ext
&&
get_user
(
status
.
audio_tstamp_data
,
(
u32
__user
*
)(
&
_status
->
audio_tstamp_data
)))
return
-
EFAULT
;
res
=
snd_pcm_status
(
substream
,
&
status
);
if
(
res
<
0
)
return
res
;
...
...
@@ -2723,7 +2756,9 @@ static int snd_pcm_common_ioctl1(struct file *file,
case
SNDRV_PCM_IOCTL_SW_PARAMS
:
return
snd_pcm_sw_params_user
(
substream
,
arg
);
case
SNDRV_PCM_IOCTL_STATUS
:
return
snd_pcm_status_user
(
substream
,
arg
);
return
snd_pcm_status_user
(
substream
,
arg
,
false
);
case
SNDRV_PCM_IOCTL_STATUS_EXT
:
return
snd_pcm_status_user
(
substream
,
arg
,
true
);
case
SNDRV_PCM_IOCTL_CHANNEL_INFO
:
return
snd_pcm_channel_info_user
(
substream
,
arg
);
case
SNDRV_PCM_IOCTL_PREPARE
:
...
...
sound/pci/hda/hda_controller.c
View file @
72f770c6
...
...
@@ -731,17 +731,32 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
azx_get_position
(
chip
,
azx_dev
));
}
static
int
azx_get_wallclock_tstamp
(
struct
snd_pcm_substream
*
substream
,
struct
timespec
*
ts
)
static
int
azx_get_time_info
(
struct
snd_pcm_substream
*
substream
,
struct
timespec
*
system_ts
,
struct
timespec
*
audio_ts
,
struct
snd_pcm_audio_tstamp_config
*
audio_tstamp_config
,
struct
snd_pcm_audio_tstamp_report
*
audio_tstamp_report
)
{
struct
azx_dev
*
azx_dev
=
get_azx_dev
(
substream
);
u64
nsec
;
if
((
substream
->
runtime
->
hw
.
info
&
SNDRV_PCM_INFO_HAS_LINK_ATIME
)
&&
(
audio_tstamp_config
->
type_requested
==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK
))
{
snd_pcm_gettime
(
substream
->
runtime
,
system_ts
);
nsec
=
timecounter_read
(
&
azx_dev
->
azx_tc
);
nsec
=
div_u64
(
nsec
,
3
);
/* can be optimized */
if
(
audio_tstamp_config
->
report_delay
)
nsec
=
azx_adjust_codec_delay
(
substream
,
nsec
);
*
ts
=
ns_to_timespec
(
nsec
);
*
audio_ts
=
ns_to_timespec
(
nsec
);
audio_tstamp_report
->
actual_type
=
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK
;
audio_tstamp_report
->
accuracy_report
=
1
;
/* rest of structure is valid */
audio_tstamp_report
->
accuracy
=
42
;
/* 24 MHz WallClock == 42ns resolution */
}
else
audio_tstamp_report
->
actual_type
=
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT
;
return
0
;
}
...
...
@@ -755,7 +770,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
/* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE
|
SNDRV_PCM_INFO_SYNC_START
|
SNDRV_PCM_INFO_HAS_WALL_CLOCK
|
SNDRV_PCM_INFO_HAS_WALL_CLOCK
|
/* legacy */
SNDRV_PCM_INFO_HAS_LINK_ATIME
|
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
),
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
.
rates
=
SNDRV_PCM_RATE_48000
,
...
...
@@ -841,10 +857,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
return
-
EINVAL
;
}
/* disable
WALLCLOCK
timestamps for capture streams
/* disable
LINK_ATIME
timestamps for capture streams
until we figure out how to handle digital inputs */
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_CAPTURE
)
runtime
->
hw
.
info
&=
~
SNDRV_PCM_INFO_HAS_WALL_CLOCK
;
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_CAPTURE
)
{
runtime
->
hw
.
info
&=
~
SNDRV_PCM_INFO_HAS_WALL_CLOCK
;
/* legacy */
runtime
->
hw
.
info
&=
~
SNDRV_PCM_INFO_HAS_LINK_ATIME
;
}
spin_lock_irqsave
(
&
chip
->
reg_lock
,
flags
);
azx_dev
->
substream
=
substream
;
...
...
@@ -876,7 +894,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
.
prepare
=
azx_pcm_prepare
,
.
trigger
=
azx_pcm_trigger
,
.
pointer
=
azx_pcm_pointer
,
.
wall_clock
=
azx_get_wallclock_tstamp
,
.
get_time_info
=
azx_get_time_info
,
.
mmap
=
azx_pcm_mmap
,
.
page
=
snd_pcm_sgbuf_ops_page
,
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment