diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index a3618caa5e..6be0b163d5 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -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; } diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index af15843263..678badc363 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -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, diff --git a/dsp/avtimer.c b/dsp/avtimer.c index 554f030409..8d9402c917 100644 --- a/dsp/avtimer.c +++ b/dsp/avtimer.c @@ -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) { diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 29d8064f65..41a1319478 100644 --- a/dsp/q6asm.c +++ b/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: diff --git a/include/dsp/q6asm-v2.h b/include/dsp/q6asm-v2.h index de7e31b9ee..5db8fde22a 100644 --- a/include/dsp/q6asm-v2.h +++ b/include/dsp/q6asm-v2.h @@ -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, diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h index 61bede7f99..8bac34a7ed 100644 --- a/include/dsp/q6core.h +++ b/include/dsp/q6core.h @@ -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); diff --git a/include/uapi/sound/devdep_params.h b/include/uapi/sound/devdep_params.h index 66a9cacfca..abdf10636f 100644 --- a/include/uapi/sound/devdep_params.h +++ b/include/uapi/sound/devdep_params.h @@ -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