Merge "asoc: remove tdm optional properties boot logs"
diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c
index 9bcb635..9e65deb 100644
--- a/asoc/codecs/wcd-dsp-mgr.c
+++ b/asoc/codecs/wcd-dsp-mgr.c
@@ -1059,8 +1059,10 @@
 		dev_info(dev, "%s: create_ramdump_device failed\n", __func__);
 
 	ret = component_bind_all(dev, wdsp->ops);
-	if (ret < 0)
+	if (ret < 0) {
 		WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
+		return ret;
+	}
 
 	/* Make sure all components registered ops */
 	for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
diff --git a/asoc/codecs/wcd9360/wcd9360.c b/asoc/codecs/wcd9360/wcd9360.c
index c5fe3a0..cb13e47 100644
--- a/asoc/codecs/wcd9360/wcd9360.c
+++ b/asoc/codecs/wcd9360/wcd9360.c
@@ -8011,6 +8011,16 @@
 	pahu->wcd_ext_clk = wcd_ext_clk;
 	dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__,
 		pahu->wcd9xxx->mclk_rate);
+	/* Probe defer if mlck is failed */
+	ret = clk_prepare_enable(pahu->wcd_ext_clk);
+	if (ret) {
+		dev_dbg(pahu->dev, "%s: ext clk enable failed\n",
+			__func__);
+		ret = -EPROBE_DEFER;
+		goto err_cdc_reg;
+	}
+	clk_disable_unprepare(pahu->wcd_ext_clk);
+
 	/* Update codec register default values */
 	pahu_update_reg_defaults(pahu);
 	__pahu_enable_efuse_sensing(pahu);
diff --git a/asoc/codecs/wcd_cpe_services.c b/asoc/codecs/wcd_cpe_services.c
index fc8242a..e584cf0 100644
--- a/asoc/codecs/wcd_cpe_services.c
+++ b/asoc/codecs/wcd_cpe_services.c
@@ -654,7 +654,7 @@
 	hdr = CMI_GET_HEADER(payload);
 	service = CMI_HDR_GET_SERVICE(hdr);
 
-	notif.event = CPE_SVC_CMI_MSG;
+	notif.event = CMI_API_MSG;
 	notif.result = result;
 	notif.message = payload;
 
@@ -1170,7 +1170,7 @@
 	}
 
 	pr_debug("%s: boot complete\n", __func__);
-	return CPE_SVC_SUCCESS;
+	return CPE_PROC_SUCCESS;
 }
 
 static enum cpe_process_result cpe_process_send_msg(
diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c
index b644df7..7ba582a 100644
--- a/asoc/msm-compress-q6-v2.c
+++ b/asoc/msm-compress-q6-v2.c
@@ -3681,8 +3681,8 @@
 		goto done;
 	}
 
-	if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >=
-					sizeof(ucontrol->value.bytes.data)) {
+	if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+			- sizeof(struct msm_adsp_event_data)) {
 		pr_err("%s param length=%d  exceeds limit",
 			__func__, event_data->payload_len);
 		ret = -EINVAL;
diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c
index bbcbab3..b82128e 100644
--- a/asoc/msm-pcm-q6-v2.c
+++ b/asoc/msm-pcm-q6-v2.c
@@ -1138,8 +1138,8 @@
 		goto done;
 	}
 
-	if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >=
-					sizeof(ucontrol->value.bytes.data)) {
+	if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+			- sizeof(struct msm_adsp_event_data)) {
 		pr_err("%s param length=%d  exceeds limit",
 			__func__, event_data->payload_len);
 		ret = -EINVAL;
diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c
index f210745..ccbc0b7 100644
--- a/asoc/msm-qti-pp-config.c
+++ b/asoc/msm-qti-pp-config.c
@@ -1025,8 +1025,9 @@
 
 	event_data = (struct msm_adsp_event_data *)payload;
 	kctl->info(kctl, &kctl_info);
-	if (sizeof(struct msm_adsp_event_data)
-		+ event_data->payload_len > kctl_info.count) {
+
+	if (event_data->payload_len >
+		kctl_info.count - sizeof(struct msm_adsp_event_data)) {
 		pr_err("%s: payload length exceeds limit of %u bytes.\n",
 			__func__, kctl_info.count);
 		ret = -EINVAL;
diff --git a/asoc/msm-transcode-loopback-q6-v2.c b/asoc/msm-transcode-loopback-q6-v2.c
index 3c8e917..3d4f8bf 100644
--- a/asoc/msm-transcode-loopback-q6-v2.c
+++ b/asoc/msm-transcode-loopback-q6-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -539,8 +539,8 @@
 		goto done;
 	}
 
-	if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >=
-					sizeof(ucontrol->value.bytes.data)) {
+	if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+			- sizeof(struct msm_adsp_event_data)) {
 		pr_err("%s param length=%d  exceeds limit",
 			 __func__, event_data->payload_len);
 		ret = -EINVAL;
diff --git a/dsp/msm_audio_ion.c b/dsp/msm_audio_ion.c
index 63a386b..e073d94 100644
--- a/dsp/msm_audio_ion.c
+++ b/dsp/msm_audio_ion.c
@@ -41,7 +41,7 @@
 #define MSM_AUDIO_SMMU_SID_OFFSET 32
 
 struct msm_audio_ion_private {
-	bool audioheap_enabled;
+	bool smmu_enabled;
 	struct device *cb_dev;
 	struct dma_iommu_mapping *mapping;
 	u8 device_status;
@@ -83,6 +83,7 @@
 
 	struct msm_audio_alloc_data *alloc_data;
 	struct device *cb_dev;
+	unsigned long ionflag = 0;
 	int rc = 0;
 
 	cb_dev = msm_audio_ion_data.cb_dev;
@@ -104,9 +105,20 @@
 		dev_err(cb_dev,
 			"%s: Fail to attach dma_buf to CB, rc = %d\n",
 			__func__, rc);
-		goto err_attach;
+		goto free_alloc_data;
 	}
 
+	/* For uncached buffers, avoid cache maintanance */
+	rc = dma_buf_get_flags(alloc_data->dma_buf, &ionflag);
+	if (rc) {
+		dev_err(cb_dev, "%s: dma_buf_get_flags failed: %d\n",
+			__func__, rc);
+		goto detach_dma_buf;
+	}
+
+	if (!(ionflag & ION_FLAG_CACHED))
+		alloc_data->attach->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC;
+
 	/*
 	 * Get the scatter-gather list.
 	 * There is no info as this is a write buffer or
@@ -120,7 +132,7 @@
 		dev_err(cb_dev,
 			"%s: Fail to map attachment, rc = %d\n",
 			__func__, rc);
-		goto err_map_attach;
+		goto detach_dma_buf;
 	}
 
 	/* physical address from mapping */
@@ -130,10 +142,10 @@
 				     alloc_data);
 	return rc;
 
-err_map_attach:
+detach_dma_buf:
 	dma_buf_detach(alloc_data->dma_buf,
 		       alloc_data->attach);
-err_attach:
+free_alloc_data:
 	kfree(alloc_data);
 
 	return rc;
@@ -198,8 +210,10 @@
 			__func__, rc);
 		goto err;
 	}
-	/* Append the SMMU SID information to the IOVA address */
-	*addr |= msm_audio_ion_data.smmu_sid_bits;
+	if (msm_audio_ion_data.smmu_enabled) {
+		/* Append the SMMU SID information to the IOVA address */
+		*addr |= msm_audio_ion_data.smmu_sid_bits;
+	}
 
 	pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc);
 err:
@@ -307,6 +321,14 @@
 	return rc;
 }
 
+static u32 msm_audio_ion_get_smmu_sid_mode32(void)
+{
+	if (msm_audio_ion_data.smmu_enabled)
+		return upper_32_bits(msm_audio_ion_data.smmu_sid_bits);
+	else
+		return 0;
+}
+
 /**
  * msm_audio_ion_alloc -
  *        Allocs ION memory for given client name
@@ -325,7 +347,8 @@
 	int rc = -EINVAL;
 	unsigned long err_ion_ptr = 0;
 
-	if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+	if ((msm_audio_ion_data.smmu_enabled == true) &&
+	    !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
 		pr_debug("%s:probe is not done, deferred\n", __func__);
 		return -EPROBE_DEFER;
 	}
@@ -334,12 +357,18 @@
 		return -EINVAL;
 	}
 
-	*dma_buf = ion_alloc(bufsz, ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+	if (msm_audio_ion_data.smmu_enabled == true) {
+		pr_debug("%s: system heap is used\n", __func__);
+		*dma_buf = ion_alloc(bufsz, ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+	} else {
+		pr_debug("%s: audio heap is used\n", __func__);
+		*dma_buf = ion_alloc(bufsz, ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	}
 	if (IS_ERR_OR_NULL((void *)(*dma_buf))) {
 		if (IS_ERR((void *)(*dma_buf)))
 			err_ion_ptr = PTR_ERR((int *)(*dma_buf));
-		pr_err("%s:ION alloc fail err ptr=%ld\n",
-			__func__, err_ion_ptr);
+		pr_err("%s: ION alloc fail err ptr=%ld, smmu_enabled=%d\n",
+		       __func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled);
 		rc = -ENOMEM;
 		goto err;
 	}
@@ -383,8 +412,9 @@
 {
 	int rc = 0;
 
-	if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
-		pr_debug("%s:probe is not done, deferred\n", __func__);
+	if ((msm_audio_ion_data.smmu_enabled == true) &&
+	    !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+		pr_debug("%s: probe is not done, deferred\n", __func__);
 		return -EPROBE_DEFER;
 	}
 
@@ -598,7 +628,7 @@
 u32 msm_audio_populate_upper_32_bits(dma_addr_t pa)
 {
 	if (sizeof(dma_addr_t) == sizeof(u32))
-		return upper_32_bits(msm_audio_ion_data.smmu_sid_bits);
+		return msm_audio_ion_get_smmu_sid_mode32();
 	else
 		return upper_32_bits(pa);
 }
@@ -643,21 +673,43 @@
 static int msm_audio_ion_probe(struct platform_device *pdev)
 {
 	int rc = 0;
-	const char *msm_audio_ion_smmu = "qcom,smmu-version";
-	const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask";
-	enum apr_subsys_state q6_state;
-	struct device *dev = &pdev->dev;
 	u64 smmu_sid = 0;
 	u64 smmu_sid_mask = 0;
+	const char *msm_audio_ion_dt = "qcom,smmu-enabled";
+	const char *msm_audio_ion_smmu = "qcom,smmu-version";
+	const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask";
+	bool smmu_enabled;
+	enum apr_subsys_state q6_state;
+	struct device *dev = &pdev->dev;
 	struct of_phandle_args iommuspec;
 
+
 	if (dev->of_node == NULL) {
 		dev_err(dev,
 			"%s: device tree is not found\n",
 			__func__);
+		msm_audio_ion_data.smmu_enabled = 0;
 		return 0;
 	}
 
+	smmu_enabled = of_property_read_bool(dev->of_node,
+					     msm_audio_ion_dt);
+	msm_audio_ion_data.smmu_enabled = smmu_enabled;
+
+	if (!smmu_enabled) {
+		dev_dbg(dev, "%s: SMMU is Disabled\n", __func__);
+		goto exit;
+	}
+
+	q6_state = apr_get_q6_state();
+	if (q6_state == APR_SUBSYS_DOWN) {
+		dev_dbg(dev,
+			"defering %s, adsp_state %d\n",
+			__func__, q6_state);
+		return -EPROBE_DEFER;
+	}
+	dev_dbg(dev, "%s: adsp is ready\n", __func__);
+
 	rc = of_property_read_u32(dev->of_node,
 				msm_audio_ion_smmu,
 				&msm_audio_ion_data.smmu_version);
@@ -667,16 +719,8 @@
 			__func__);
 		return rc;
 	}
-	dev_dbg(dev, "%s: SMMU version is (%d)", __func__,
-			msm_audio_ion_data.smmu_version);
-	q6_state = apr_get_q6_state();
-	if (q6_state == APR_SUBSYS_DOWN) {
-		dev_dbg(dev,
-			"defering %s, adsp_state %d\n",
-			__func__, q6_state);
-		return -EPROBE_DEFER;
-	}
-	dev_dbg(dev, "%s: adsp is ready\n", __func__);
+	dev_dbg(dev, "%s: SMMU is Enabled. SMMU version is (%d)",
+		__func__, msm_audio_ion_data.smmu_version);
 
 	/* Get SMMU SID information from Devicetree */
 	rc = of_property_read_u64(dev->of_node,
@@ -688,6 +732,7 @@
 			__func__);
 		smmu_sid_mask = 0xFFFFFFFFFFFFFFFF;
 	}
+
 	rc = of_parse_phandle_with_args(dev->of_node, "iommus",
 					"#iommu-cells", 0, &iommuspec);
 	if (rc)
@@ -710,6 +755,7 @@
 		dev_err(dev, "%s: smmu init failed, err = %d\n",
 			__func__, rc);
 
+exit:
 	if (!rc)
 		msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED;
 
@@ -729,6 +775,7 @@
 		arm_iommu_release_mapping(mapping);
 	}
 
+	msm_audio_ion_data.smmu_enabled = 0;
 	msm_audio_ion_data.device_status = 0;
 	return 0;
 }
diff --git a/dsp/q6adm.c b/dsp/q6adm.c
index 282d7f7..7d59fbb 100644
--- a/dsp/q6adm.c
+++ b/dsp/q6adm.c
@@ -480,7 +480,7 @@
 				  int channel_index)
 {
 	struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL;
-	struct param_hdr_v3 data_v5;
+	struct param_hdr_v1 data_v5;
 	int ret = 0, port_idx, sz = 0, param_size = 0;
 	u16 *adm_pspd_params;
 	u16 *ptr;
@@ -513,7 +513,7 @@
 
 	sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) +
 	     sizeof(struct default_chmixer_param_id_coeff) +
-	     sizeof(struct param_hdr_v3) + param_size;
+	     sizeof(struct param_hdr_v1) + param_size;
 	pr_debug("%s: sz = %d\n", __func__, sz);
 	adm_params = kzalloc(sz, GFP_KERNEL);
 	if (!adm_params)
@@ -541,7 +541,7 @@
 	data_v5.param_size = param_size;
 	adm_params->payload_size =
 		sizeof(struct default_chmixer_param_id_coeff) +
-		sizeof(struct param_hdr_v3) + data_v5.param_size;
+		sizeof(struct param_hdr_v1) + data_v5.param_size;
 	adm_pspd_params = (u16 *)((u8 *)adm_params +
 			sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5));
 	memcpy(adm_pspd_params, &data_v5, sizeof(data_v5));
diff --git a/dsp/q6asm.c b/dsp/q6asm.c
index 5bfed89..55d82b7 100644
--- a/dsp/q6asm.c
+++ b/dsp/q6asm.c
@@ -2230,6 +2230,13 @@
 		 * package is composed of event type + size + actual payload
 		 */
 		payload_size = data->payload_size;
+		if (payload_size > UINT_MAX - sizeof(struct msm_adsp_event_data)) {
+			pr_err("%s: payload size = %d exceeds limit.\n",
+				__func__, payload_size);
+			spin_unlock(&(session[session_id].session_lock));
+			return -EINVAL;
+		}
+
 		pp_event_package = kzalloc(payload_size
 				+ sizeof(struct msm_adsp_event_data),
 				GFP_ATOMIC);
diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c
index 108e4be..3d87a8d 100644
--- a/dsp/q6lsm.c
+++ b/dsp/q6lsm.c
@@ -225,7 +225,7 @@
 
 	pr_debug("%s: Freeing session ID %d\n", __func__, client->session);
 	spin_lock_irqsave(&lsm_session_lock, flags);
-	lsm_session[client->session] = LSM_INVALID_SESSION_ID;
+	lsm_session[client->session] = NULL;
 	spin_unlock_irqrestore(&lsm_session_lock, flags);
 	client->session = LSM_INVALID_SESSION_ID;
 }
diff --git a/ipc/apr.c b/ipc/apr.c
index 331f057..3f07f6e 100644
--- a/ipc/apr.c
+++ b/ipc/apr.c
@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 #include <linux/sysfs.h>
 #include <linux/device.h>
+#include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/ipc_logging.h>
 #include <soc/qcom/subsystem_restart.h>
@@ -42,7 +43,6 @@
 static wait_queue_head_t dsp_wait;
 static wait_queue_head_t modem_wait;
 static bool is_modem_up;
-static bool is_initial_boot;
 /* Subsystem restart: QDSP6 data, functions */
 static struct workqueue_struct *apr_reset_workqueue;
 static void apr_reset_deregister(struct work_struct *work);
@@ -52,6 +52,21 @@
 	struct work_struct work;
 };
 
+struct apr_chld_device {
+	struct platform_device *pdev;
+	struct list_head node;
+};
+
+struct apr_private {
+	struct device *dev;
+	spinlock_t apr_lock;
+	bool is_initial_boot;
+	struct work_struct add_chld_dev_work;
+	spinlock_t apr_chld_lock;
+	struct list_head apr_chlds;
+};
+
+static struct apr_private *apr_priv;
 static bool apr_cf_debug;
 
 #ifdef CONFIG_DEBUG_FS
@@ -275,15 +290,65 @@
 
 static void apr_adsp_down(unsigned long opcode)
 {
+	pr_debug("%s: Q6 is Down\n", __func__);
 	apr_set_q6_state(APR_SUBSYS_DOWN);
 	dispatch_event(opcode, APR_DEST_QDSP6);
 }
 
+static void apr_add_child_devices(struct work_struct *work)
+{
+	int ret;
+	struct device_node *node;
+	struct platform_device *pdev;
+	struct apr_chld_device *apr_chld_dev;
+
+	for_each_child_of_node(apr_priv->dev->of_node, node) {
+		apr_chld_dev = kzalloc(sizeof(*apr_chld_dev), GFP_KERNEL);
+		if (!apr_chld_dev)
+			continue;
+		pdev = platform_device_alloc(node->name, -1);
+		if (!pdev) {
+			dev_err(apr_priv->dev,
+				"%s: pdev memory alloc failed for %s\n",
+				__func__, node->name);
+			kfree(apr_chld_dev);
+			continue;
+		}
+		pdev->dev.parent = apr_priv->dev;
+		pdev->dev.of_node = node;
+
+		ret = platform_device_add(pdev);
+		if (ret) {
+			dev_err(apr_priv->dev,
+				"%s: Cannot add platform device %s\n",
+				__func__, node->name);
+			platform_device_put(pdev);
+			kfree(apr_chld_dev);
+			continue;
+		}
+
+		apr_chld_dev->pdev = pdev;
+
+		spin_lock(&apr_priv->apr_chld_lock);
+		list_add_tail(&apr_chld_dev->node, &apr_priv->apr_chlds);
+		spin_unlock(&apr_priv->apr_chld_lock);
+
+		dev_dbg(apr_priv->dev, "%s: Added APR child dev: %s\n",
+			 __func__, dev_name(&pdev->dev));
+	}
+}
+
 static void apr_adsp_up(void)
 {
+	pr_debug("%s: Q6 is Up\n", __func__);
 	if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
 							APR_SUBSYS_DOWN)
 		wake_up(&dsp_wait);
+
+	spin_lock(&apr_priv->apr_lock);
+	if (apr_priv->is_initial_boot)
+		schedule_work(&apr_priv->add_chld_dev_work);
+	spin_unlock(&apr_priv->apr_lock);
 }
 
 int apr_wait_for_device_up(int dest_id)
@@ -1044,21 +1109,25 @@
 		 * recovery notifications during initial boot
 		 * up since everything is expected to be down.
 		 */
-		if (is_initial_boot) {
-			is_initial_boot = false;
+		spin_lock(&apr_priv->apr_lock);
+		if (apr_priv->is_initial_boot) {
+			spin_unlock(&apr_priv->apr_lock);
 			break;
 		}
+		spin_unlock(&apr_priv->apr_lock);
 		if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
 			apr_modem_down(opcode);
 		else
 			apr_adsp_down(opcode);
 		break;
 	case AUDIO_NOTIFIER_SERVICE_UP:
-		is_initial_boot = false;
 		if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
 			apr_modem_up();
 		else
 			apr_adsp_up();
+		spin_lock(&apr_priv->apr_lock);
+		apr_priv->is_initial_boot = false;
+		spin_unlock(&apr_priv->apr_lock);
 		break;
 	default:
 		break;
@@ -1092,13 +1161,43 @@
 )
 #endif
 
-static int __init apr_init(void)
+static void apr_cleanup(void)
+{
+	int i, j, k;
+
+	subsys_notif_deregister("apr_modem");
+	subsys_notif_deregister("apr_adsp");
+	if (apr_reset_workqueue) {
+		flush_workqueue(apr_reset_workqueue);
+		destroy_workqueue(apr_reset_workqueue);
+	}
+	mutex_destroy(&q6.lock);
+	for (i = 0; i < APR_DEST_MAX; i++) {
+		for (j = 0; j < APR_CLIENT_MAX; j++) {
+			mutex_destroy(&client[i][j].m_lock);
+			for (k = 0; k < APR_SVC_MAX; k++)
+				mutex_destroy(&client[i][j].svc[k].m_lock);
+		}
+	}
+}
+
+static int apr_probe(struct platform_device *pdev)
 {
 	int i, j, k;
 
 	init_waitqueue_head(&dsp_wait);
 	init_waitqueue_head(&modem_wait);
 
+	apr_priv = devm_kzalloc(&pdev->dev, sizeof(*apr_priv), GFP_KERNEL);
+	if (!apr_priv)
+		return -ENOMEM;
+
+	apr_priv->dev = &pdev->dev;
+	spin_lock_init(&apr_priv->apr_lock);
+	spin_lock_init(&apr_priv->apr_chld_lock);
+	INIT_LIST_HEAD(&apr_priv->apr_chlds);
+	INIT_WORK(&apr_priv->add_chld_dev_work, apr_add_child_devices);
+
 	for (i = 0; i < APR_DEST_MAX; i++)
 		for (j = 0; j < APR_CLIENT_MAX; j++) {
 			mutex_init(&client[i][j].m_lock);
@@ -1110,15 +1209,19 @@
 	apr_set_subsys_state();
 	mutex_init(&q6.lock);
 	apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
-	if (!apr_reset_workqueue)
+	if (!apr_reset_workqueue) {
+		apr_priv = NULL;
 		return -ENOMEM;
+	}
 
 	apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT,
 						"apr", 0);
 	if (!apr_pkt_ctx)
 		pr_err("%s: Unable to create ipc log context\n", __func__);
 
-	is_initial_boot = true;
+	spin_lock(&apr_priv->apr_lock);
+	apr_priv->is_initial_boot = true;
+	spin_unlock(&apr_priv->apr_lock);
 	subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
 			      &adsp_service_nb);
 	subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
@@ -1127,14 +1230,41 @@
 	apr_tal_init();
 	return apr_debug_init();
 }
-module_init(apr_init);
 
-void __exit apr_exit(void)
+static int apr_remove(struct platform_device *pdev)
 {
-	subsys_notif_deregister("apr_modem");
-	subsys_notif_deregister("apr_adsp");
+	struct apr_chld_device *chld, *tmp;
+
+	apr_cleanup();
 	apr_tal_exit();
+	spin_lock(&apr_priv->apr_chld_lock);
+	list_for_each_entry_safe(chld, tmp, &apr_priv->apr_chlds, node) {
+		platform_device_unregister(chld->pdev);
+		list_del(&chld->node);
+		kfree(chld);
+	}
+	spin_unlock(&apr_priv->apr_chld_lock);
+	apr_priv = NULL;
+	return 0;
 }
-module_exit(apr_exit);
-MODULE_DESCRIPTION("APR module");
+
+static const struct of_device_id apr_machine_of_match[]  = {
+	{ .compatible = "qcom,msm-audio-apr", },
+	{},
+};
+
+static struct platform_driver apr_driver = {
+	.probe = apr_probe,
+	.remove = apr_remove,
+	.driver = {
+		.name = "audio_apr",
+		.owner = THIS_MODULE,
+		.of_match_table = apr_machine_of_match,
+	}
+};
+
+module_platform_driver(apr_driver);
+
+MODULE_DESCRIPTION("APR DRIVER");
 MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, apr_machine_of_match);
diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c
index 8e1d8a2..21da65a 100644
--- a/ipc/wcd-dsp-glink.c
+++ b/ipc/wcd-dsp-glink.c
@@ -266,7 +266,7 @@
 
 	spin_lock(&ch->ch_lock);
 	rpdev = ch->handle;
-	if (rpdev || ch->ch_state == WDSP_CH_CONNECTED) {
+	if (rpdev && ch->ch_state == WDSP_CH_CONNECTED) {
 		spin_unlock(&ch->ch_lock);
 		ret = rpmsg_send(rpdev->ept, cpkt->payload,
 				 cpkt->payload_size);
@@ -275,8 +275,11 @@
 				__func__, ret);
 	} else {
 		spin_unlock(&ch->ch_lock);
-		dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
-			__func__, ch->ch_name);
+		if (rpdev)
+			dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
+				__func__, ch->ch_name);
+		else
+			dev_err(wpriv->dev, "%s: rpdev is NULL\n", __func__);
 	}
 	vfree(tx_buf);
 }