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>
diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c
index af15843..678badc 100644
--- a/asoc/msm-pcm-q6-v2.c
+++ b/asoc/msm-pcm-q6-v2.c
@@ -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 @@
 	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,