ASoC: msm: qdsp6v2: Propagate device HW delay to AFE.
- HW delay for each supported device is maintained in
ACDB. This needs to be propagated to AFE for timestamp
calculation in voice path. AFE provides device HW and
SW delays to CVD; based on which timestamps are calculated
for voice packets.
Cherrypick from: I959676c1ee4e0568e661e7666a61c98152344047
Change-Id: I8f9d3438aebb4a9e66100d062453f2ac066748c9
Signed-off-by: Pavan Chikkala <pavanc@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index be190f9..7a2be38 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -37,6 +37,10 @@
atomic_t cal_paddr;
};
+struct hw_delay_entry {
+ uint32_t sample_rate;
+ uint32_t delay_usec;
+};
struct acdb_cal_data {
uint32_t num_cal_blocks;
struct acdb_atomic_cal_block *cal_blocks;
@@ -61,5 +65,6 @@
void get_vocstrm_cal(struct acdb_cal_data *cal_data);
void get_vocvol_cal(struct acdb_cal_data *cal_data);
void get_sidetone_cal(struct sidetone_cal *cal_data);
+int get_hw_delay(int32_t path, struct hw_delay_entry *delay_info);
#endif
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index f05376a..6da12a0 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -18,9 +18,10 @@
#include <linux/msm_ion.h>
#include <linux/mm.h>
#include <mach/qdsp6v2/audio_acdb.h>
-
+#include <linux/slab.h>
#define MAX_NETWORKS 15
+#define MAX_HW_DELAY_ENTRIES 25
struct sidetone_atomic_cal {
atomic_t enable;
@@ -69,6 +70,9 @@
atomic64_t paddr;
atomic64_t kvaddr;
atomic64_t mem_len;
+ /* Av sync delay info */
+ struct hw_delay hw_delay_rx;
+ struct hw_delay hw_delay_tx;
};
static struct acdb_data acdb_data;
@@ -177,6 +181,124 @@
atomic_read(&acdb_data.vocvol_total_cal_size);
}
+int get_hw_delay(int32_t path, struct hw_delay_entry *entry)
+{
+ int i, result = 0;
+ struct hw_delay *delay = NULL;
+ struct hw_delay_entry *info = NULL;
+ pr_debug("%s,\n", __func__);
+
+ if (entry == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ result = -EINVAL;
+ goto ret;
+ }
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ result = -EINVAL;
+ goto ret;
+ }
+ mutex_lock(&acdb_data.acdb_mutex);
+ if (path == RX_CAL)
+ delay = &acdb_data.hw_delay_rx;
+ else if (path == TX_CAL)
+ delay = &acdb_data.hw_delay_tx;
+ else
+ pr_err("ACDB=> %s Invalid path: %d\n",__func__,path);
+
+ if ((delay == NULL) || ((delay != NULL) && delay->num_entries == 0)) {
+ pr_err("ACDB=> %s Invalid delay/ delay entries\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ info = (struct hw_delay_entry *)(delay->delay_info);
+ if (info == NULL) {
+ pr_err("ACDB=> %s Delay entries info is NULL\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
+ for (i = 0; i < delay->num_entries; i++) {
+ if (info[i].sample_rate == entry->sample_rate) {
+ entry->delay_usec = info[i].delay_usec;
+ break;
+ }
+ }
+ if (i == delay->num_entries) {
+ pr_err("ACDB=> %s: Unable to find delay for sample rate %d\n",
+ __func__, entry->sample_rate);
+ result = -EINVAL;
+ }
+
+done:
+ mutex_unlock(&acdb_data.acdb_mutex);
+ret:
+ pr_err("ACDB=> %s: Path = %d samplerate = %u usec = %u status %d\n",
+ __func__, path, entry->sample_rate, entry->delay_usec, result);
+ return result;
+}
+
+int store_hw_delay(int32_t path, void *arg)
+{
+ int result = 0;
+ struct hw_delay delay;
+ struct hw_delay *delay_dest = NULL;
+ pr_debug("%s,\n", __func__);
+
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0) || (arg == NULL)) {
+ pr_err("ACDB=> Bad path/ pointer sent to %s, path: %d\n",
+ __func__, path);
+ result = -EINVAL;
+ goto done;
+ }
+ result = copy_from_user((void *)&delay, (void *)arg,
+ sizeof(struct hw_delay));
+ if (result) {
+ pr_err("ACDB=> %s failed to copy hw delay: result=%d path=%d\n",
+ __func__, result, path);
+ result = -EFAULT;
+ goto done;
+ }
+ if ((delay.num_entries <= 0) ||
+ (delay.num_entries > MAX_HW_DELAY_ENTRIES)) {
+ pr_debug("ACDB=> %s incorrect no of hw delay entries: %d\n",
+ __func__, delay.num_entries);
+ result = -EINVAL;
+ goto done;
+ }
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ result = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("ACDB=> %s : Path = %d num_entries = %d\n",
+ __func__, path, delay.num_entries);
+
+ mutex_lock(&acdb_data.acdb_mutex);
+ if (path == RX_CAL)
+ delay_dest = &acdb_data.hw_delay_rx;
+ else if (path == TX_CAL)
+ delay_dest = &acdb_data.hw_delay_tx;
+
+ delay_dest->num_entries = delay.num_entries;
+
+ result = copy_from_user(delay_dest->delay_info,
+ delay.delay_info,
+ (sizeof(struct hw_delay_entry)*
+ delay.num_entries));
+ if (result) {
+ pr_err("ACDB=> %s failed to copy hw delay info res=%d path=%d",
+ __func__, result, path);
+ result = -EFAULT;
+ }
+ mutex_unlock(&acdb_data.acdb_mutex);
+done:
+ return result;
+}
+
void get_anc_cal(struct acdb_cal_block *cal_block)
{
pr_debug("%s\n", __func__);
@@ -610,11 +732,45 @@
}
atomic_inc(&usage_count);
+
return result;
+
+}
+
+static void allocate_hw_delay_entries(void)
+{
+
+ /* Allocate memory for hw delay entries */
+
+ acdb_data.hw_delay_rx.num_entries = 0;
+ acdb_data.hw_delay_tx.num_entries = 0;
+ acdb_data.hw_delay_rx.delay_info =
+ kmalloc(sizeof(struct hw_delay_entry)*
+ MAX_HW_DELAY_ENTRIES,
+ GFP_KERNEL);
+ if (acdb_data.hw_delay_rx.delay_info == NULL) {
+ pr_err("%s : Failed to allocate av sync delay entries rx\n",
+ __func__);
+ }
+ acdb_data.hw_delay_tx.delay_info =
+ kmalloc(sizeof(struct hw_delay_entry)*
+ MAX_HW_DELAY_ENTRIES,
+ GFP_KERNEL);
+ if (acdb_data.hw_delay_tx.delay_info == NULL) {
+ pr_err("%s : Failed to allocate av sync delay entries tx\n",
+ __func__);
+ }
+
+ return;
}
static int deregister_memory(void)
{
+ mutex_lock(&acdb_data.acdb_mutex);
+ kfree(acdb_data.hw_delay_tx.delay_info);
+ kfree(acdb_data.hw_delay_rx.delay_info);
+ mutex_unlock(&acdb_data.acdb_mutex);
+
if (atomic64_read(&acdb_data.mem_len)) {
mutex_lock(&acdb_data.acdb_mutex);
atomic_set(&acdb_data.vocstrm_total_cal_size, 0);
@@ -638,6 +794,7 @@
unsigned long mem_len;
mutex_lock(&acdb_data.acdb_mutex);
+ allocate_hw_delay_entries();
acdb_data.ion_client =
msm_ion_client_create(UINT_MAX, "audio_acdb_client");
if (IS_ERR_OR_NULL(acdb_data.ion_client)) {
@@ -674,7 +831,7 @@
atomic64_set(&acdb_data.mem_len, mem_len);
mutex_unlock(&acdb_data.acdb_mutex);
- pr_debug("%s done! paddr = 0x%lx, "
+ pr_debug("%s: done! paddr = 0x%lx, "
"kvaddr = 0x%lx, len = x%lx\n",
__func__,
(long)atomic64_read(&acdb_data.paddr),
@@ -762,6 +919,12 @@
}
store_asm_topology(topology);
goto done;
+ case AUDIO_SET_HW_DELAY_RX:
+ result = store_hw_delay(RX_CAL, (void *)arg);
+ goto done;
+ case AUDIO_SET_HW_DELAY_TX:
+ result = store_hw_delay(TX_CAL, (void *)arg);
+ goto done;
}
if (copy_from_user(&size, (void *) arg, sizeof(size))) {
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index e7f06b5..d88d3e4 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -39,8 +39,10 @@
(AUDIO_MAX_COMMON_IOCTL_NUM+16), unsigned)
#define AUDIO_SET_AFE_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+17), unsigned)
-
-
+#define AUDIO_SET_HW_DELAY_RX _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+18), struct hw_delay)
+#define AUDIO_SET_HW_DELAY_TX _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+19), struct hw_delay)
#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30)
/* ACDB structures */
@@ -54,6 +56,10 @@
uint16_t gain;
};
+struct hw_delay {
+ uint32_t num_entries;
+ void *delay_info;
+};
/* For Real-Time Audio Calibration */
#define AUDIO_GET_RTAC_ADM_INFO _IOR(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_ACDB_IOCTL+1), unsigned)
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 0711b2b..f303c62 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -304,6 +304,14 @@
#define AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG 0x000100D9
#define AFE_PORT_CMD_I2S_CONFIG 0x000100E7
+#define AFE_PARAM_ID_DEVICE_HW_DELAY 0x00010243
+#define AFE_API_VERSION_DEVICE_HW_DELAY 0x1
+
+struct afe_param_id_device_hw_delay_cfg {
+ uint32_t device_hw_delay_minor_version;
+ uint32_t delay_in_us;
+} __packed;
+
union afe_port_config {
struct afe_port_pcm_cfg pcm;
struct afe_port_mi2s_cfg mi2s;
@@ -417,6 +425,7 @@
struct afe_param_channels channels;
struct afe_param_loopback_gain loopback_gain;
struct afe_param_loopback_cfg loopback_cfg;
+ struct afe_param_id_device_hw_delay_cfg hw_delay;
} __attribute__((packed)) param;
} __attribute__ ((packed));
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 1a8c84c..e9e3a0d 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -422,6 +422,83 @@
return;
}
+static int afe_send_hw_delay(u16 port_id, u32 rate)
+{
+ struct hw_delay_entry delay_entry;
+ struct afe_port_cmd_set_param config;
+ int index = 0;
+ int ret = -EINVAL;
+
+ pr_debug("%s\n", __func__);
+
+ delay_entry.sample_rate = rate;
+ if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX)
+ ret = get_hw_delay(TX_CAL, &delay_entry);
+ else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX)
+ ret = get_hw_delay(RX_CAL, &delay_entry);
+
+ if (ret != 0) {
+ pr_debug("%s: Failed to get hw delay info\n", __func__);
+ goto done;
+ }
+ index = port_id;
+ if (index < 0) {
+ pr_debug("%s: AFE port index invalid!\n", __func__);
+ goto done;
+ }
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM;
+
+ config.port_id = port_id;
+ config.payload_size = sizeof(struct afe_param_payload_base)+
+ sizeof(struct afe_param_id_device_hw_delay_cfg);
+ config.payload_address = 0;
+
+ config.payload.base.module_id = AFE_MODULE_ID_PORT_INFO ;
+ config.payload.base.param_id = AFE_PARAM_ID_DEVICE_HW_DELAY;
+ config.payload.base.param_size = sizeof(struct afe_param_id_device_hw_delay_cfg);
+ config.payload.base.reserved = 0;
+
+ config.payload.param.hw_delay.delay_in_us = delay_entry.delay_usec;
+ config.payload.param.hw_delay.device_hw_delay_minor_version =
+ AFE_API_VERSION_DEVICE_HW_DELAY;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout IF CONFIG\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+done:
+ pr_debug("%s port_id %u rate %u delay_usec %d status %d\n",
+ __func__, port_id, rate, delay_entry.delay_usec, ret);
+ return ret;
+}
+
void afe_send_cal(u16 port_id)
{
pr_debug("%s\n", __func__);
@@ -570,6 +647,7 @@
/* send AFE cal */
afe_send_cal(port_id);
+ afe_send_hw_delay(port_id, rate);
start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);