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:
Surendar Karka 2019-03-29 18:08:07 +05:30
parent ae8957e9b4
commit 38d66474e1
7 changed files with 258 additions and 26 deletions

View File

@ -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;
}

View File

@ -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,

View File

@ -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)
{

View File

@ -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:

View File

@ -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,

View File

@ -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);

View File

@ -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