Merge branch 'topic/timestamp' into for-next
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
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)) {
|
||||
substream->ops->get_time_info(substream, &curr_tstamp,
|
||||
&audio_tstamp,
|
||||
&runtime->audio_tstamp_config,
|
||||
&runtime->audio_tstamp_report);
|
||||
|
||||
if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
|
||||
(substream->ops->wall_clock))
|
||||
substream->ops->wall_clock(substream, &audio_tstamp);
|
||||
/* 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) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user