audio-kernel: add support to query presentation position from DSP
Add support to query presentation position from DSP in system time domain. Change-Id: I42b4d234ddc256f93c01defbe2c74872a2a2cf3e Signed-off-by: Surendar Karka <skarka@codeaurora.org>
This commit is contained in:
parent
ae8957e9b4
commit
38d66474e1
@ -3153,13 +3153,18 @@ static int msm_compr_get_metadata(struct snd_compr_stream *cstream,
|
||||
struct msm_compr_audio *prtd;
|
||||
struct audio_client *ac;
|
||||
int ret = -EINVAL;
|
||||
uint64_t ses_time = 0, frames = 0, abs_time = 0;
|
||||
uint64_t *val = NULL;
|
||||
int64_t av_offset = 0;
|
||||
int32_t clock_id = -EINVAL;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!metadata || !cstream || !cstream->runtime)
|
||||
return ret;
|
||||
|
||||
if (metadata->key != SNDRV_COMPRESS_PATH_DELAY) {
|
||||
if (metadata->key != SNDRV_COMPRESS_PATH_DELAY &&
|
||||
metadata->key != SNDRV_COMPRESS_DSP_POSITION) {
|
||||
pr_err("%s, unsupported key %d\n", __func__, metadata->key);
|
||||
return ret;
|
||||
}
|
||||
@ -3170,17 +3175,50 @@ static int msm_compr_get_metadata(struct snd_compr_stream *cstream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ac = prtd->audio_client;
|
||||
ret = q6asm_get_path_delay(prtd->audio_client);
|
||||
if (ret) {
|
||||
pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret);
|
||||
return ret;
|
||||
switch (metadata->key) {
|
||||
case SNDRV_COMPRESS_PATH_DELAY:
|
||||
ac = prtd->audio_client;
|
||||
ret = q6asm_get_path_delay(prtd->audio_client);
|
||||
if (ret) {
|
||||
pr_err("%s: get_path_delay failed, ret=%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_debug("%s, path delay(in us) %u\n", __func__,
|
||||
ac->path_delay);
|
||||
metadata->value[0] = ac->path_delay;
|
||||
break;
|
||||
case SNDRV_COMPRESS_DSP_POSITION:
|
||||
clock_id = metadata->value[0];
|
||||
pr_debug("%s, clock_id %d\n", __func__, clock_id);
|
||||
ret = q6asm_get_session_time_v2(prtd->audio_client,
|
||||
&ses_time, &abs_time);
|
||||
if (ret) {
|
||||
pr_err("%s: q6asm_get_session_time_v2 failed, ret=%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
frames = div64_u64((ses_time * prtd->sample_rate), 1000000);
|
||||
|
||||
ret = avcs_core_query_timer_offset(&av_offset, clock_id);
|
||||
if (ret) {
|
||||
pr_err("%s: avcs query failed, ret=%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = (uint64_t *) &metadata->value[1];
|
||||
val[0] = frames;
|
||||
val[1] = abs_time + av_offset;
|
||||
pr_debug("%s, vals frames %lld, time %lld, avoff %lld, abst %lld, sess_time %llu sr %d\n",
|
||||
__func__, val[0], val[1], av_offset, abs_time,
|
||||
ses_time, prtd->sample_rate);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s, unsupported key %d\n", __func__, metadata->key);
|
||||
break;
|
||||
}
|
||||
|
||||
pr_debug("%s, path delay(in us) %u\n", __func__, ac->path_delay);
|
||||
|
||||
metadata->value[0] = ac->path_delay;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/devdep_params.h>
|
||||
#include <dsp/msm_audio_ion.h>
|
||||
#include <dsp/q6audio-v2.h>
|
||||
#include <dsp/q6core.h>
|
||||
@ -1156,12 +1157,106 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void __user *arg)
|
||||
{
|
||||
struct msm_audio *prtd = NULL;
|
||||
struct snd_soc_pcm_runtime *rtd = NULL;
|
||||
uint64_t ses_time = 0, abs_time = 0;
|
||||
int64_t av_offset = 0;
|
||||
int32_t clock_id = -EINVAL;
|
||||
int rc = 0;
|
||||
struct snd_pcm_prsnt_position userarg;
|
||||
|
||||
if (!substream || !substream->private_data) {
|
||||
pr_err("%s: Invalid %s\n", __func__,
|
||||
(!substream) ? "substream" : "private_data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!substream->runtime) {
|
||||
pr_err("%s substream runtime not found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prtd = substream->runtime->private_data;
|
||||
if (!prtd) {
|
||||
pr_err("%s prtd is null.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rtd = substream->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_IOCTL_DSP_POSITION:
|
||||
dev_dbg(rtd->dev, "%s: SNDRV_PCM_DSP_POSITION", __func__);
|
||||
if (!arg) {
|
||||
dev_err(rtd->dev, "%s: Invalid params DSP_POSITION\n",
|
||||
__func__);
|
||||
rc = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
memset(&userarg, 0, sizeof(userarg));
|
||||
if (copy_from_user(&userarg, arg, sizeof(userarg))) {
|
||||
dev_err(rtd->dev, "%s: err copyuser DSP_POSITION\n",
|
||||
__func__);
|
||||
rc = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
clock_id = userarg.clock_id;
|
||||
rc = q6asm_get_session_time_v2(prtd->audio_client, &ses_time,
|
||||
&abs_time);
|
||||
if (rc) {
|
||||
pr_err("%s: q6asm_get_session_time_v2 failed, rc=%d\n",
|
||||
__func__, rc);
|
||||
goto done;
|
||||
}
|
||||
userarg.frames = div64_u64((ses_time * prtd->samp_rate),
|
||||
1000000);
|
||||
|
||||
rc = avcs_core_query_timer_offset(&av_offset, clock_id);
|
||||
if (rc) {
|
||||
pr_err("%s: avcs offset query failed, rc=%d\n",
|
||||
__func__, rc);
|
||||
goto done;
|
||||
}
|
||||
|
||||
userarg.timestamp = abs_time + av_offset;
|
||||
if (copy_to_user(arg, &userarg, sizeof(userarg))) {
|
||||
dev_err(rtd->dev, "%s: err copy to user DSP_POSITION\n",
|
||||
__func__);
|
||||
rc = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
pr_debug("%s, vals f %lld, t %lld, avoff %lld, abst %lld, sess_time %llu sr %d\n",
|
||||
__func__, userarg.frames, userarg.timestamp,
|
||||
av_offset, abs_time, ses_time, prtd->samp_rate);
|
||||
break;
|
||||
default:
|
||||
rc = snd_pcm_lib_ioctl(substream, cmd, arg);
|
||||
break;
|
||||
}
|
||||
done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static int msm_pcm_compat_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void __user *arg)
|
||||
{
|
||||
return msm_pcm_ioctl(substream, cmd, arg);
|
||||
}
|
||||
#else
|
||||
#define msm_pcm_compat_ioctl NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy_user = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.ioctl = msm_pcm_ioctl,
|
||||
.compat_ioctl = msm_pcm_compat_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
|
@ -309,6 +309,61 @@ int avcs_core_query_timer(uint64_t *avtimer_tick)
|
||||
}
|
||||
EXPORT_SYMBOL(avcs_core_query_timer);
|
||||
|
||||
/*
|
||||
* avcs_core_query_timer_offset:
|
||||
* derive offset between system clock & avtimer clock
|
||||
*
|
||||
* @ avoffset: offset between system clock & avtimer clock
|
||||
* @ clock_id: clock id to get the system time
|
||||
*
|
||||
*/
|
||||
int avcs_core_query_timer_offset(int64_t *av_offset, int32_t clock_id)
|
||||
{
|
||||
uint32_t avtimer_msw = 0, avtimer_lsw = 0;
|
||||
uint64_t avtimer_tick_temp, avtimer_tick, sys_time = 0;
|
||||
struct timespec ts;
|
||||
|
||||
if ((avtimer.p_avtimer_lsw == NULL) ||
|
||||
(avtimer.p_avtimer_msw == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&ts, 0, sizeof(struct timespec));
|
||||
avtimer_lsw = ioread32(avtimer.p_avtimer_lsw);
|
||||
avtimer_msw = ioread32(avtimer.p_avtimer_msw);
|
||||
|
||||
switch (clock_id) {
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
getrawmonotonic(&ts);
|
||||
break;
|
||||
case CLOCK_BOOTTIME:
|
||||
get_monotonic_boottime(&ts);
|
||||
break;
|
||||
case CLOCK_MONOTONIC:
|
||||
ktime_get_ts(&ts);
|
||||
break;
|
||||
case CLOCK_REALTIME:
|
||||
ktime_get_real_ts(&ts);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unsupported clock id %d\n", __func__, clock_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sys_time = ts.tv_sec * 1000000LL + div64_u64(ts.tv_nsec, 1000);
|
||||
avtimer_tick_temp = (uint64_t)((uint64_t)avtimer_msw << 32) |
|
||||
avtimer_lsw;
|
||||
|
||||
avtimer_tick = mul_u64_u32_div(avtimer_tick_temp, avtimer.clk_mult,
|
||||
avtimer.clk_div);
|
||||
*av_offset = sys_time - avtimer_tick;
|
||||
pr_debug("%s: sys_time: %llu, offset %lld, avtimer tick %lld\n",
|
||||
__func__, sys_time, *av_offset, avtimer_tick);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(avcs_core_query_timer_offset);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AVTIMER_LEGACY)
|
||||
static void avcs_set_isp_fptr(bool enable)
|
||||
{
|
||||
|
52
dsp/q6asm.c
52
dsp/q6asm.c
@ -1868,12 +1868,20 @@ static void q6asm_process_mtmx_get_param_rsp(struct audio_client *ac,
|
||||
switch (cmdrsp->param_info.param_id) {
|
||||
case ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3:
|
||||
time = &cmdrsp->param_data.session_time;
|
||||
dev_vdbg(ac->dev, "%s: GET_TIME_V3, time_lsw=%x, time_msw=%x\n",
|
||||
dev_vdbg(ac->dev, "%s: GET_TIME_V3, time_lsw=%x, time_msw=%x, abs l %x, m %x\n",
|
||||
__func__, time->session_time_lsw,
|
||||
time->session_time_msw);
|
||||
ac->time_stamp = (uint64_t)(((uint64_t)
|
||||
time->session_time_msw,
|
||||
time->absolute_time_lsw,
|
||||
time->absolute_time_msw);
|
||||
ac->dsp_ts.abs_time_stamp = (uint64_t)(((uint64_t)
|
||||
time->absolute_time_msw << 32) |
|
||||
time->absolute_time_lsw);
|
||||
ac->dsp_ts.time_stamp = (uint64_t)(((uint64_t)
|
||||
time->session_time_msw << 32) |
|
||||
time->session_time_lsw);
|
||||
ac->dsp_ts.last_time_stamp = (uint64_t)(((uint64_t)
|
||||
time->time_stamp_msw << 32) |
|
||||
time->time_stamp_lsw);
|
||||
if (time->flags &
|
||||
ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK)
|
||||
dev_warn_ratelimited(ac->dev,
|
||||
@ -2361,8 +2369,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
|
||||
dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
|
||||
__func__,
|
||||
payload[0], payload[1], payload[2]);
|
||||
ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) |
|
||||
payload[1]);
|
||||
ac->dsp_ts.time_stamp =
|
||||
(uint64_t)(((uint64_t)payload[2] << 32) |
|
||||
payload[1]);
|
||||
} else {
|
||||
dev_err(ac->dev, "%s: payload size of %x is less than expected.n",
|
||||
__func__, data->payload_size);
|
||||
@ -9761,15 +9770,17 @@ fail_cmd:
|
||||
EXPORT_SYMBOL(q6asm_write_nolock);
|
||||
|
||||
/**
|
||||
* q6asm_get_session_time -
|
||||
* q6asm_get_session_time_v2 -
|
||||
* command to retrieve timestamp info
|
||||
*
|
||||
* @ac: Audio client handle
|
||||
* @tstamp: pointer to fill with timestamp info
|
||||
* @ses_time: pointer to fill with session timestamp info
|
||||
* @abs_time: pointer to fill with AVS timestamp info
|
||||
*
|
||||
* Returns 0 on success or error on failure
|
||||
*/
|
||||
int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
|
||||
int q6asm_get_session_time_v2(struct audio_client *ac, uint64_t *ses_time,
|
||||
uint64_t *abs_time)
|
||||
{
|
||||
struct asm_mtmx_strtr_get_params mtmx_params;
|
||||
int rc;
|
||||
@ -9782,8 +9793,8 @@ int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
|
||||
pr_err("%s: AC APR handle NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (tstamp == NULL) {
|
||||
pr_err("%s: tstamp NULL\n", __func__);
|
||||
if (ses_time == NULL) {
|
||||
pr_err("%s: tstamp args are NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -9821,12 +9832,29 @@ int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
*tstamp = ac->time_stamp;
|
||||
*ses_time = ac->dsp_ts.time_stamp;
|
||||
if (abs_time != NULL)
|
||||
*abs_time = ac->dsp_ts.abs_time_stamp;
|
||||
return 0;
|
||||
|
||||
fail_cmd:
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(q6asm_get_session_time_v2);
|
||||
|
||||
/**
|
||||
* q6asm_get_session_time -
|
||||
* command to retrieve timestamp info
|
||||
*
|
||||
* @ac: Audio client handle
|
||||
* @tstamp: pointer to fill with timestamp info
|
||||
*
|
||||
* Returns 0 on success or error on failure
|
||||
*/
|
||||
int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
|
||||
{
|
||||
return q6asm_get_session_time_v2(ac, tstamp, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(q6asm_get_session_time);
|
||||
|
||||
/**
|
||||
@ -9878,7 +9906,7 @@ int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp)
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
*tstamp = ac->time_stamp;
|
||||
*tstamp = ac->dsp_ts.time_stamp;
|
||||
return 0;
|
||||
|
||||
fail_cmd:
|
||||
|
@ -207,6 +207,12 @@ struct shared_io_config {
|
||||
uint32_t bufcnt;
|
||||
};
|
||||
|
||||
struct dsp_timestamp {
|
||||
uint64_t time_stamp;
|
||||
uint64_t abs_time_stamp;
|
||||
uint64_t last_time_stamp;
|
||||
};
|
||||
|
||||
struct audio_client {
|
||||
int session;
|
||||
app_cb cb;
|
||||
@ -218,7 +224,7 @@ struct audio_client {
|
||||
atomic_t mem_state;
|
||||
void *priv;
|
||||
uint32_t io_mode;
|
||||
uint64_t time_stamp;
|
||||
struct dsp_timestamp dsp_ts;
|
||||
struct apr_svc *apr;
|
||||
struct apr_svc *mmap_apr;
|
||||
struct apr_svc *apr2;
|
||||
@ -683,7 +689,8 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels,
|
||||
int q6asm_set_mute(struct audio_client *ac, int muteflag);
|
||||
|
||||
int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp);
|
||||
|
||||
int q6asm_get_session_time_v2(struct audio_client *ac, uint64_t *ses_time,
|
||||
uint64_t *abs_time);
|
||||
int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp);
|
||||
|
||||
int q6asm_send_stream_cmd(struct audio_client *ac,
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
bool q6core_is_adsp_ready(void);
|
||||
|
||||
int avcs_core_query_timer_offset(int64_t *av_offset, int32_t clock_id);
|
||||
int q6core_get_service_version(uint32_t service_id,
|
||||
struct avcs_fwk_ver_info *ver_info,
|
||||
size_t size);
|
||||
|
@ -61,6 +61,14 @@ struct snd_pcm_mmap_fd {
|
||||
int32_t actual_size;
|
||||
};
|
||||
|
||||
struct snd_pcm_prsnt_position {
|
||||
uint64_t timestamp;
|
||||
uint64_t frames;
|
||||
int32_t clock_id;
|
||||
};
|
||||
|
||||
#define SNDRV_PCM_IOCTL_MMAP_DATA_FD _IOWR('U', 0xd2, struct snd_pcm_mmap_fd)
|
||||
#define SNDRV_PCM_IOCTL_DSP_POSITION\
|
||||
_IOWR('U', 0xd3, struct snd_pcm_prsnt_position)
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user