dsp: preload voice and copp topologies

Set topology info to q6core service in ADSP so that the
modules in a topology can be loaded before the usecase starts,
thus reducing usecase setup latency. The same topology is
unloaded before vocproc or copp is destroyed or replaced.

Change-Id: I5f8872130fb09f049e080c61edb8bb0b05e5e2de
Signed-off-by: Vikram Panduranga <vpandura@codeaurora.org>
diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c
index 5e82550..bd02392 100644
--- a/asoc/msm-pcm-routing-v2.c
+++ b/asoc/msm-pcm-routing-v2.c
@@ -147,6 +147,9 @@
 static int msm_routing_send_device_pp_params(int port_id,  int copp_idx,
 					     int fe_id);
 
+static void msm_routing_load_topology(size_t data_size, void *data);
+static void msm_routing_unload_topology(uint32_t topology_id);
+
 static int msm_routing_get_bit_width(unsigned int format)
 {
 	int bit_width;
@@ -1547,6 +1550,7 @@
 			}
 			topology = adm_get_topology_for_port_copp_idx(
 					msm_bedais[i].port_id, idx);
+			msm_routing_unload_topology(topology);
 			adm_close(msm_bedais[i].port_id, fdai->perf_mode, idx);
 			pr_debug("%s:copp:%ld,idx bit fe:%d,type:%d,be:%d\n",
 				 __func__, copp, fedai_id, session_type, i);
@@ -1745,6 +1749,7 @@
 			port_id = msm_bedais[reg].port_id;
 			topology = adm_get_topology_for_port_copp_idx(port_id,
 								      idx);
+			msm_routing_unload_topology(topology);
 			adm_close(msm_bedais[reg].port_id, fdai->perf_mode,
 				  idx);
 			pr_debug("%s: copp: %ld, reset idx bit fe:%d, type: %d, be:%d topology=0x%x\n",
@@ -16308,6 +16313,7 @@
 			port_id = bedai->port_id;
 			topology = adm_get_topology_for_port_copp_idx(port_id,
 								     idx);
+			msm_routing_unload_topology(topology);
 			adm_close(bedai->port_id, fdai->perf_mode, idx);
 			pr_debug("%s: copp:%ld,idx bit fe:%d, type:%d,be:%d topology=0x%x\n",
 				 __func__, copp, i, session_type, be_id,
@@ -16600,6 +16606,60 @@
 	return 0;
 }
 
+static uint32_t msm_routing_get_topology(size_t data_size, void *data)
+{
+	uint32_t topology = NULL_COPP_TOPOLOGY;
+	void *cal_info = NULL;
+	uint32_t size = 0;
+
+	/* Retrieve cal_info size from cal data*/
+	size = data_size - sizeof(struct audio_cal_type_basic);
+	cal_info = kzalloc(size, GFP_KERNEL);
+
+	if (!cal_info)
+		goto done;
+
+	memcpy(cal_info,
+		((uint8_t *)data + sizeof(struct audio_cal_type_basic)), size);
+
+	topology = ((struct audio_cal_info_adm_top *)cal_info)->topology;
+	kfree(cal_info);
+	cal_info = NULL;
+
+done:
+	pr_debug("%s: Using topology %d\n", __func__, topology);
+
+	return topology;
+}
+
+static void msm_routing_load_topology(size_t data_size, void *data)
+{
+	uint32_t topology_id;
+	int ret;
+
+	topology_id = msm_routing_get_topology(data_size, data);
+	if (topology_id != NULL_COPP_TOPOLOGY)
+		ret = q6core_load_unload_topo_modules(topology_id,
+			CORE_LOAD_TOPOLOGY);
+	if (ret < 0)
+		pr_debug("%s %d load topology failed\n",
+				 __func__, topology_id);
+
+}
+
+static void msm_routing_unload_topology(uint32_t topology_id)
+{
+	int ret;
+
+	if (topology_id != NULL_COPP_TOPOLOGY)
+		ret = q6core_load_unload_topo_modules(topology_id,
+			CORE_UNLOAD_TOPOLOGY);
+	if (ret < 0)
+		pr_debug("%s %d unload topology failed\n",
+				 __func__, topology_id);
+
+}
+
 static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_value *ucontrol)
 {
@@ -17065,6 +17125,11 @@
 		ret = -EINVAL;
 		goto done;
 	}
+	/* Pre-load if it is ADM topology */
+	if ((cal_index == ADM_TOPOLOGY_CAL_TYPE_IDX) ||
+		(cal_index == ADM_LSM_TOPOLOGY_CAL_TYPE_IDX)) {
+		msm_routing_load_topology(data_size, data);
+	}
 done:
 	return ret;
 }