Commit 72f770c6 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/timestamp' into for-next

parents 0eee62e0 c72638bd
This diff is collapsed.
...@@ -60,6 +60,9 @@ struct snd_pcm_hardware { ...@@ -60,6 +60,9 @@ struct snd_pcm_hardware {
struct snd_pcm_substream; struct snd_pcm_substream;
struct snd_pcm_audio_tstamp_config; /* definitions further down */
struct snd_pcm_audio_tstamp_report;
struct snd_pcm_ops { struct snd_pcm_ops {
int (*open)(struct snd_pcm_substream *substream); int (*open)(struct snd_pcm_substream *substream);
int (*close)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream);
...@@ -71,8 +74,10 @@ struct snd_pcm_ops { ...@@ -71,8 +74,10 @@ struct snd_pcm_ops {
int (*prepare)(struct snd_pcm_substream *substream); int (*prepare)(struct snd_pcm_substream *substream);
int (*trigger)(struct snd_pcm_substream *substream, int cmd); int (*trigger)(struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
int (*wall_clock)(struct snd_pcm_substream *substream, int (*get_time_info)(struct snd_pcm_substream *substream,
struct timespec *audio_ts); 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, int (*copy)(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, snd_pcm_uframes_t pos,
void __user *buf, snd_pcm_uframes_t count); void __user *buf, snd_pcm_uframes_t count);
...@@ -281,6 +286,58 @@ struct snd_pcm_hw_constraint_ranges { ...@@ -281,6 +286,58 @@ struct snd_pcm_hw_constraint_ranges {
struct snd_pcm_hwptr_log; 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 { struct snd_pcm_runtime {
/* -- Status -- */ /* -- Status -- */
struct snd_pcm_substream *trigger_master; struct snd_pcm_substream *trigger_master;
...@@ -361,6 +418,11 @@ struct snd_pcm_runtime { ...@@ -361,6 +418,11 @@ struct snd_pcm_runtime {
struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ 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) #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */ /* -- OSS things -- */
struct snd_pcm_oss_runtime oss; struct snd_pcm_oss_runtime oss;
......
...@@ -143,7 +143,7 @@ struct snd_hwdep_dsp_image { ...@@ -143,7 +143,7 @@ struct snd_hwdep_dsp_image {
* * * *
*****************************************************************************/ *****************************************************************************/
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12) #define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13)
typedef unsigned long snd_pcm_uframes_t; typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t; typedef signed long snd_pcm_sframes_t;
...@@ -270,10 +270,17 @@ typedef int __bitwise snd_pcm_subformat_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_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_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_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_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 */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
typedef int __bitwise snd_pcm_state_t; 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_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 */ #define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */
...@@ -411,6 +418,22 @@ struct snd_pcm_channel_info { ...@@ -411,6 +418,22 @@ struct snd_pcm_channel_info {
unsigned int step; /* samples distance in bits */ 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 { struct snd_pcm_status {
snd_pcm_state_t state; /* stream state */ snd_pcm_state_t state; /* stream state */
struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */
...@@ -422,9 +445,11 @@ struct snd_pcm_status { ...@@ -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 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_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */
snd_pcm_state_t suspended_state; /* suspended stream state */ snd_pcm_state_t suspended_state; /* suspended stream state */
__u32 reserved_alignment; /* 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; /* from sample counter or wall clock */ struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */
unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */ 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 { struct snd_pcm_mmap_status {
...@@ -537,6 +562,7 @@ enum { ...@@ -537,6 +562,7 @@ enum {
#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) #define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) #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_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_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40) #define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40)
#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41) #define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)
......
...@@ -194,18 +194,30 @@ struct snd_pcm_status32 { ...@@ -194,18 +194,30 @@ struct snd_pcm_status32 {
u32 avail_max; u32 avail_max;
u32 overrange; u32 overrange;
s32 suspended_state; s32 suspended_state;
u32 reserved_alignment; u32 audio_tstamp_data;
struct compat_timespec audio_tstamp; 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)); } __attribute__((packed));
static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, 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; struct snd_pcm_status status;
int err; 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); err = snd_pcm_status(substream, &status);
if (err < 0) if (err < 0)
return err; return err;
...@@ -222,7 +234,10 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, ...@@ -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.avail_max, &src->avail_max) ||
put_user(status.overrange, &src->overrange) || put_user(status.overrange, &src->overrange) ||
put_user(status.suspended_state, &src->suspended_state) || 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 -EFAULT;
return err; return err;
...@@ -457,6 +472,7 @@ enum { ...@@ -457,6 +472,7 @@ enum {
SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32), 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_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32), 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_DELAY32 = _IOR('A', 0x21, s32),
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), 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 ...@@ -517,7 +533,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_SW_PARAMS32: case SNDRV_PCM_IOCTL_SW_PARAMS32:
return snd_pcm_ioctl_sw_params_compat(substream, argp); return snd_pcm_ioctl_sw_params_compat(substream, argp);
case SNDRV_PCM_IOCTL_STATUS32: 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: case SNDRV_PCM_IOCTL_SYNC_PTR32:
return snd_pcm_ioctl_sync_ptr_compat(substream, argp); return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
case SNDRV_PCM_IOCTL_CHANNEL_INFO32: case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
......
...@@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, ...@@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
return 0; 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, static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
unsigned int in_interrupt) unsigned int in_interrupt)
{ {
...@@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
pos = substream->ops->pointer(substream); pos = substream->ops->pointer(substream);
curr_jiffies = jiffies; curr_jiffies = jiffies;
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); if ((substream->ops->get_time_info) &&
(runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && substream->ops->get_time_info(substream, &curr_tstamp,
(substream->ops->wall_clock)) &audio_tstamp,
substream->ops->wall_clock(substream, &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 (pos == SNDRV_PCM_POS_XRUN) { if (pos == SNDRV_PCM_POS_XRUN) {
...@@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
} }
no_delta_check: 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; return 0;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0) runtime->silence_size > 0)
...@@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_BUG_ON(crossed_boundary != 1); snd_BUG_ON(crossed_boundary != 1);
runtime->hw_ptr_wrap += runtime->boundary; 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)) { update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
/*
* 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;
}
return snd_pcm_update_state(substream, runtime); return snd_pcm_update_state(substream, runtime);
} }
......
...@@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream, ...@@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream); 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->state = runtime->status->state;
status->suspended_state = runtime->status->suspended_state; status->suspended_state = runtime->status->suspended_state;
if (status->state == SNDRV_PCM_STATE_OPEN) if (status->state == SNDRV_PCM_STATE_OPEN)
...@@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream, ...@@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
snd_pcm_update_hw_ptr(substream); snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
status->tstamp = runtime->status->tstamp; status->tstamp = runtime->status->tstamp;
status->driver_tstamp = runtime->driver_tstamp;
status->audio_tstamp = status->audio_tstamp =
runtime->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; goto _tstamp_end;
} }
} else { } else {
...@@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream, ...@@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
} }
static int snd_pcm_status_user(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; struct snd_pcm_status status;
int res; int res;
memset(&status, 0, sizeof(status)); 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); res = snd_pcm_status(substream, &status);
if (res < 0) if (res < 0)
return res; return res;
...@@ -2723,7 +2756,9 @@ static int snd_pcm_common_ioctl1(struct file *file, ...@@ -2723,7 +2756,9 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_SW_PARAMS: case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg); return snd_pcm_sw_params_user(substream, arg);
case SNDRV_PCM_IOCTL_STATUS: 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: case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg); return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE: case SNDRV_PCM_IOCTL_PREPARE:
......
...@@ -731,17 +731,32 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) ...@@ -731,17 +731,32 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
azx_get_position(chip, azx_dev)); azx_get_position(chip, azx_dev));
} }
static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream, static int azx_get_time_info(struct snd_pcm_substream *substream,
struct timespec *ts) 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); struct azx_dev *azx_dev = get_azx_dev(substream);
u64 nsec; u64 nsec;
nsec = timecounter_read(&azx_dev->azx_tc); if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
nsec = div_u64(nsec, 3); /* can be optimized */ (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
nsec = azx_adjust_codec_delay(substream, nsec);
*ts = ns_to_timespec(nsec); 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);
*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; return 0;
} }
...@@ -755,7 +770,8 @@ static struct snd_pcm_hardware azx_pcm_hw = { ...@@ -755,7 +770,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
/* SNDRV_PCM_INFO_RESUME |*/ /* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START | 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), SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000, .rates = SNDRV_PCM_RATE_48000,
...@@ -841,10 +857,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) ...@@ -841,10 +857,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
return -EINVAL; return -EINVAL;
} }
/* disable WALLCLOCK timestamps for capture streams /* disable LINK_ATIME timestamps for capture streams
until we figure out how to handle digital inputs */ until we figure out how to handle digital inputs */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; 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); spin_lock_irqsave(&chip->reg_lock, flags);
azx_dev->substream = substream; azx_dev->substream = substream;
...@@ -876,7 +894,7 @@ static struct snd_pcm_ops azx_pcm_ops = { ...@@ -876,7 +894,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
.prepare = azx_pcm_prepare, .prepare = azx_pcm_prepare,
.trigger = azx_pcm_trigger, .trigger = azx_pcm_trigger,
.pointer = azx_pcm_pointer, .pointer = azx_pcm_pointer,
.wall_clock = azx_get_wallclock_tstamp, .get_time_info = azx_get_time_info,
.mmap = azx_pcm_mmap, .mmap = azx_pcm_mmap,
.page = snd_pcm_sgbuf_ops_page, .page = snd_pcm_sgbuf_ops_page,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment