Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a7352414 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "audio-kernel: add support to query presentation position from DSP"

parents 8ee45b38 38d66474
Loading
Loading
Loading
Loading
+47 −9
Original line number Diff line number Diff line
@@ -3170,13 +3170,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;
	}
@@ -3187,17 +3192,50 @@ static int msm_compr_get_metadata(struct snd_compr_stream *cstream,
		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);
			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);

		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;
	}
	return ret;
}

+96 −1
Original line number Diff line number Diff line
@@ -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,
+55 −0
Original line number Diff line number Diff line
@@ -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)
{
+40 −12
Original line number Diff line number Diff line
@@ -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,7 +2369,8 @@ 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) |
			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",
@@ -9762,15 +9771,17 @@ int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
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;
@@ -9783,8 +9794,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;
	}

@@ -9822,12 +9833,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);

/**
@@ -9879,7 +9907,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:
+9 −2
Original line number Diff line number Diff line
@@ -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,
Loading