ASoC: msm: qdsp6v2: Add support for OpenDSP
Add support for OpenDSP which allows custom topology
designed using the QACT tool to be sent over to the
Q6.
Change-Id: I8203f8b409870ca406832f279d01cc15a9c8ca4f
Signed-off-by: Ben Romberger <bromberg@codeaurora.org>
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index 471f10a..f0c4915 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -49,6 +49,10 @@
(AUDIO_MAX_COMMON_IOCTL_NUM+21), unsigned)
#define AUDIO_SET_LSM_CAL _IOW(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+22), unsigned)
+#define AUDIO_SET_ADM_CUSTOM_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+23), unsigned)
+#define AUDIO_SET_ASM_CUSTOM_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+24), unsigned)
#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30)
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 4555b08..cad737a 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -6215,6 +6215,22 @@
#define VOICE_CMD_GET_PARAM 0x0001133E
#define VOICE_EVT_GET_PARAM_ACK 0x00011008
+/* Set Q6 topologies */
+#define ASM_CMD_ADD_TOPOLOGIES 0x00010DBE
+#define ADM_CMD_ADD_TOPOLOGIES 0x00010335
+
+/* structure used for both ioctls */
+struct cmd_set_topologies {
+ struct apr_hdr hdr;
+ u32 payload_addr_lsw;
+ /* LSW of parameter data payload address.*/
+ u32 payload_addr_msw;
+ /* MSW of parameter data payload address.*/
+ u32 mem_map_handle;
+ /* Memory map handle returned by mem map command */
+ u32 payload_size;
+ /* Size in bytes of the variable payload in shared memory */
+} __packed;
/* SRS TRUMEDIA start */
/* topology */
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.c b/sound/soc/msm/qdsp6v2/audio_acdb.c
index 58fdc1b..b71132e 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.c
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.c
@@ -66,6 +66,12 @@
/* VocProc dev cfg cal*/
struct acdb_atomic_cal_block vocproc_dev_cal;
+ /* Custom topology */
+ struct acdb_atomic_cal_block adm_custom_topology;
+ struct acdb_atomic_cal_block asm_custom_topology;
+ atomic_t valid_adm_custom_top;
+ atomic_t valid_asm_custom_top;
+
/* AFE cal */
struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES];
@@ -134,6 +140,106 @@
atomic_set(&acdb_data.asm_topology, topology);
}
+void reset_custom_topology_flags(void)
+{
+ atomic_set(&acdb_data.valid_adm_custom_top, 1);
+ atomic_set(&acdb_data.valid_asm_custom_top, 1);
+}
+
+void get_adm_custom_topology(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ if (atomic_read(&acdb_data.valid_adm_custom_top) == 0) {
+ cal_block->cal_size = 0;
+ goto done;
+ }
+ atomic_set(&acdb_data.valid_adm_custom_top, 0);
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.adm_custom_topology.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.adm_custom_topology.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.adm_custom_topology.cal_kvaddr);
+done:
+ return;
+}
+
+void store_adm_custom_topology(struct cal_block *cal_block)
+{
+ pr_debug("%s,\n", __func__);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+
+ atomic_set(&acdb_data.adm_custom_topology.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.adm_custom_topology.cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.adm_custom_topology.cal_kvaddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
+void get_asm_custom_topology(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ if (atomic_read(&acdb_data.valid_asm_custom_top) == 0) {
+ cal_block->cal_size = 0;
+ goto done;
+ }
+ atomic_set(&acdb_data.valid_asm_custom_top, 0);
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.asm_custom_topology.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.asm_custom_topology.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.asm_custom_topology.cal_kvaddr);
+done:
+ return;
+}
+
+void store_asm_custom_topology(struct cal_block *cal_block)
+{
+ pr_debug("%s,\n", __func__);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+
+ atomic_set(&acdb_data.asm_custom_topology.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.asm_custom_topology.cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.asm_custom_topology.cal_kvaddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
void get_voice_cal_allocation(struct acdb_cal_block *cal_block)
{
cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION;
@@ -672,6 +778,8 @@
__func__);
}
+ atomic_set(&acdb_data.valid_adm_custom_top, 1);
+ atomic_set(&acdb_data.valid_asm_custom_top, 1);
atomic_inc(&usage_count);
return result;
}
@@ -925,6 +1033,12 @@
case AUDIO_SET_LSM_CAL:
store_lsm_cal((struct cal_block *)data);
goto done;
+ case AUDIO_SET_ADM_CUSTOM_TOPOLOGY:
+ store_adm_custom_topology((struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_ASM_CUSTOM_TOPOLOGY:
+ store_asm_custom_topology((struct cal_block *)data);
+ goto done;
default:
pr_err("ACDB=> ACDB ioctl not found!\n");
}
@@ -998,6 +1112,8 @@
memset(&acdb_data, 0, sizeof(acdb_data));
mutex_init(&acdb_data.acdb_mutex);
atomic_set(&usage_count, 0);
+ atomic_set(&acdb_data.valid_adm_custom_top, 1);
+ atomic_set(&acdb_data.valid_asm_custom_top, 1);
return misc_register(&acdb_misc);
}
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.h b/sound/soc/msm/qdsp6v2/audio_acdb.h
index 8528e3c..0b6110d 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.h
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.h
@@ -46,6 +46,9 @@
uint32_t get_adm_rx_topology(void);
uint32_t get_adm_tx_topology(void);
uint32_t get_asm_topology(void);
+void reset_custom_topology_flags(void);
+void get_adm_custom_topology(struct acdb_cal_block *cal_block);
+void get_asm_custom_topology(struct acdb_cal_block *cal_block);
void get_voice_cal_allocation(struct acdb_cal_block *cal_block);
void get_lsm_cal(struct acdb_cal_block *cal_block);
void get_anc_cal(struct acdb_cal_block *cal_block);
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index bc11304..685deef 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -46,8 +46,11 @@
/* 0 - (MAX_AUDPROC_TYPES -1): audproc handles */
/* (MAX_AUDPROC_TYPES -1) - (2 * MAX_AUDPROC_TYPES -1): audvol handles */
- atomic_t mem_map_cal_handles[(2 * MAX_AUDPROC_TYPES)];
+/* + 1 for custom ADM topology */
+ atomic_t mem_map_cal_handles[(2 * MAX_AUDPROC_TYPES) + 1];
atomic_t mem_map_cal_index;
+
+ int set_custom_topology;
};
static struct adm_ctl this_adm;
@@ -309,6 +312,8 @@
atomic_set(&this_adm.copp_stat[i], 0);
}
this_adm.apr = NULL;
+ reset_custom_topology_flags();
+ this_adm.set_custom_topology = 1;
}
pr_debug("Resetting calibration blocks");
for (i = 0; i < MAX_AUDPROC_TYPES; i++) {
@@ -350,6 +355,7 @@
case ADM_CMD_DEVICE_CLOSE_V5:
case ADM_CMD_SHARED_MEM_UNMAP_REGIONS:
case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
+ case ADM_CMD_ADD_TOPOLOGIES:
pr_debug("%s: Basic callback received, wake up.\n",
__func__);
atomic_set(&this_adm.copp_stat[index], 1);
@@ -435,6 +441,87 @@
return 0;
}
+void send_adm_custom_topology(int port_id)
+{
+ struct acdb_cal_block cal_block;
+ struct cmd_set_topologies adm_top;
+ int index;
+ int result;
+ int size = 4096;
+
+ get_adm_custom_topology(&cal_block);
+ if (cal_block.cal_size == 0) {
+ pr_debug("%s: no cal to send addr= 0x%x\n",
+ __func__, cal_block.cal_paddr);
+ goto done;
+ }
+
+ index = afe_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: invalid port idx %d portid %#x\n",
+ __func__, index, port_id);
+ goto done;
+ }
+
+ if (this_adm.set_custom_topology) {
+ /* specific index 4 for adm topology memory */
+ atomic_set(&this_adm.mem_map_cal_index, 4);
+
+ /* Only call this once */
+ this_adm.set_custom_topology = 0;
+
+ result = adm_memory_map_regions(port_id, &cal_block.cal_paddr,
+ 0, &size, 1);
+ if (result < 0) {
+ pr_err("%s: mmap did not work! addr = 0x%x, size = %d\n",
+ __func__, cal_block.cal_paddr,
+ cal_block.cal_size);
+ goto done;
+ }
+ }
+
+
+ adm_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_top.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(adm_top));
+ adm_top.hdr.src_svc = APR_SVC_ADM;
+ adm_top.hdr.src_domain = APR_DOMAIN_APPS;
+ adm_top.hdr.src_port = port_id;
+ adm_top.hdr.dest_svc = APR_SVC_ADM;
+ adm_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_top.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ adm_top.hdr.token = port_id;
+ adm_top.hdr.opcode = ADM_CMD_ADD_TOPOLOGIES;
+ adm_top.payload_addr_lsw = cal_block.cal_paddr;
+ adm_top.payload_addr_msw = 0;
+ adm_top.mem_map_handle = atomic_read(&this_adm.mem_map_cal_handles[4]);
+ adm_top.payload_size = cal_block.cal_size;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+ pr_debug("%s: Sending ADM_CMD_ADD_TOPOLOGIES payload = 0x%x, size = %d\n",
+ __func__, adm_top.payload_addr_lsw,
+ adm_top.payload_size);
+ result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_top);
+ if (result < 0) {
+ pr_err("%s: Set topologies failed port = 0x%x payload = 0x%x\n",
+ __func__, port_id, cal_block.cal_paddr);
+ goto done;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(this_adm.wait[index],
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: Set topologies timed out port = 0x%x, payload = 0x%x\n",
+ __func__, port_id, cal_block.cal_paddr);
+ goto done;
+ }
+
+done:
+ return;
+}
+
static int send_adm_cal_block(int port_id, struct acdb_cal_block *aud_cal)
{
s32 result = 0;
@@ -684,6 +771,7 @@
rtac_set_adm_handle(this_adm.apr);
}
+ send_adm_custom_topology(port_id);
/* Create a COPP if port id are not enabled */
if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
@@ -1154,6 +1242,7 @@
{
int i = 0;
this_adm.apr = NULL;
+ this_adm.set_custom_topology = 1;
for (i = 0; i < AFE_MAX_PORTS; i++) {
atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 3d8d5eb..b832c41 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -106,6 +106,9 @@
static int out_cold_index;
static char *out_buffer;
static char *in_buffer;
+static int set_custom_topology;
+static int topology_map_handle;
+
static int audio_output_latency_dbgfs_open(struct inode *inode,
struct file *file)
{
@@ -341,6 +344,90 @@
return;
}
+void send_asm_custom_topology(struct audio_client *ac)
+{
+ struct acdb_cal_block cal_block;
+ struct cmd_set_topologies asm_top;
+ struct audio_buffer *buf;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ int result;
+ int size = 4096;
+
+ get_asm_custom_topology(&cal_block);
+ if (cal_block.cal_size == 0) {
+ pr_debug("%s: no cal to send addr= 0x%x\n",
+ __func__, cal_block.cal_paddr);
+ goto done;
+ }
+
+ if (set_custom_topology) {
+ /* Only call this once */
+ set_custom_topology = 0;
+
+ /* Use first asm buf to map memory */
+ buf = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL);
+ if (!buf) {
+ pr_debug("%s: could not allocate temp memory\n",
+ __func__);
+ goto done;
+ }
+ buf[0].phys = cal_block.cal_paddr;
+ ac->port[0].buf = buf;
+
+ result = q6asm_memory_map_regions(ac, 0, size, 1, 1);
+ if (result < 0) {
+ pr_err("%s: mmap did not work! addr = 0x%x, size = %d\n",
+ __func__, cal_block.cal_paddr,
+ cal_block.cal_size);
+ goto done;
+ }
+
+ list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_addr_lsw == cal_block.cal_paddr) {
+ topology_map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+
+ kfree(buf);
+ }
+
+ q6asm_add_hdr(ac, &asm_top.hdr, APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(asm_top)), TRUE);
+
+ asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES;
+ asm_top.payload_addr_lsw = cal_block.cal_paddr;
+ asm_top.payload_addr_msw = 0;
+ asm_top.mem_map_handle = topology_map_handle;
+ asm_top.payload_size = cal_block.cal_size;
+
+ pr_debug("%s: Sending ASM_CMD_ADD_TOPOLOGIES payload = 0x%x, size = %d, map handle = 0x%x\n",
+ __func__, asm_top.payload_addr_lsw,
+ asm_top.payload_size, asm_top.mem_map_handle);
+
+ result = apr_send_pkt(ac->apr, (uint32_t *) &asm_top);
+ if (result < 0) {
+ pr_err("%s: Set topologies failed payload = 0x%x\n",
+ __func__, cal_block.cal_paddr);
+ goto done;
+ }
+
+ result = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (result < 0) {
+ pr_err("%s: Set topologies failed payload = 0x%x\n",
+ __func__, cal_block.cal_paddr);
+ goto done;
+ }
+
+
+done:
+ return;
+}
+
int q6asm_audio_client_buf_free(unsigned int dir,
struct audio_client *ac)
{
@@ -563,6 +650,8 @@
atomic_set(&ac->cmd_state, 0);
atomic_set(&ac->nowait_cmd_cnt, 0);
+ send_asm_custom_topology(ac);
+
pr_debug("%s: session[%d]\n", __func__, ac->session);
return ac;
@@ -822,6 +911,8 @@
apr_reset(this_mmap.apr);
atomic_set(&this_mmap.ref_cnt, 0);
this_mmap.apr = NULL;
+ reset_custom_topology_flags();
+ set_custom_topology = 1;
return 0;
}
sid = (data->token >> 8) & 0x0F;
@@ -956,6 +1047,8 @@
(uint32_t *)data->payload, ac->priv);
apr_reset(ac->apr);
ac->apr = NULL;
+ reset_custom_topology_flags();
+ set_custom_topology = 1;
return 0;
}
@@ -997,6 +1090,7 @@
case ASM_STREAM_CMD_OPEN_READWRITE_V2:
case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_CMD_ADD_TOPOLOGIES:
pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
__func__, payload[0], payload[1]);
if (atomic_read(&ac->cmd_state) && wakeup_flag) {
@@ -3564,6 +3658,7 @@
{
pr_debug("%s\n", __func__);
memset(session, 0, sizeof(session));
+ set_custom_topology = 1;
config_debug_fs_init();