audio-lnx: Fix sound card failure at stability runs

In stability reboot tests, deferred audio drivers
are not invoked after lpass loading sometimes and
results in sound card failure. Change APR to platform
device and after APR status changes to loaded state,
add dummy module child device under APR which invokes
deferred audio drivers and sound card registers successfully.

Change-Id: Ib0c0f7ec1d7dd93a1b54a9a66260861223d55c67
Signed-off-by: Laxminath Kasam <lkasam@codeaurora.org>
diff --git a/include/ipc/apr.h b/include/ipc/apr.h
index 6b4817e..c24e639 100644
--- a/include/ipc/apr.h
+++ b/include/ipc/apr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -190,4 +190,6 @@
 uint16_t apr_get_reset_domain(uint16_t proc);
 int apr_start_rx_rt(void *handle);
 int apr_end_rx_rt(void *handle);
+int apr_dummy_init(void);
+void apr_dummy_exit(void);
 #endif
diff --git a/ipc/Kbuild b/ipc/Kbuild
index 72c9ef1..b9f1869 100644
--- a/ipc/Kbuild
+++ b/ipc/Kbuild
@@ -71,12 +71,14 @@
 APRV_GLINK += apr.o
 APRV_GLINK += apr_v2.o
 APRV_GLINK += apr_tal_glink.o
+APRV_GLINK += apr_dummy.o
 endif
 
 ifdef CONFIG_MSM_QDSP6_APRV3_GLINK
 APRV_GLINK += apr.o
 APRV_GLINK += apr_v3.o
 APRV_GLINK += apr_tal_glink.o
+APRV_GLINK += apr_dummy.o
 endif
 
 ifdef CONFIG_WCD_DSP_GLINK
diff --git a/ipc/apr.c b/ipc/apr.c
index 9f9cf91..cfc1df0 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>
@@ -36,6 +37,7 @@
 
 #define APR_PKT_IPC_LOG_PAGE_CNT 2
 
+static struct device *apr_dev_ptr;
 static struct apr_q6 q6;
 static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
 static void *apr_pkt_ctx;
@@ -43,6 +45,7 @@
 static wait_queue_head_t modem_wait;
 static bool is_modem_up;
 static bool is_initial_boot;
+static bool is_child_devices_loaded;
 /* Subsystem restart: QDSP6 data, functions */
 static struct workqueue_struct *apr_reset_workqueue;
 static void apr_reset_deregister(struct work_struct *work);
@@ -284,6 +287,33 @@
 	if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
 							APR_SUBSYS_DOWN)
 		wake_up(&dsp_wait);
+
+	if (!is_child_devices_loaded) {
+		struct platform_device *pdev;
+		struct device_node *node;
+		int ret;
+
+		for_each_child_of_node(apr_dev_ptr->of_node, node) {
+			pdev = platform_device_alloc(node->name, -1);
+			if (!pdev) {
+				dev_err(apr_dev_ptr, "%s: pdev memory alloc failed\n",
+					__func__);
+				return;
+			}
+			pdev->dev.parent = apr_dev_ptr;
+			pdev->dev.of_node = node;
+
+			ret = platform_device_add(pdev);
+			if (ret) {
+				dev_err(apr_dev_ptr,
+					"%s: Cannot add platform device\n",
+					__func__);
+				platform_device_put(pdev);
+				return;
+			}
+		}
+		is_child_devices_loaded = true;
+	}
 }
 
 int apr_wait_for_device_up(int dest_id)
@@ -1092,7 +1122,25 @@
 )
 #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)
+		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;
 
@@ -1125,15 +1173,46 @@
 			      &modem_service_nb);
 
 	apr_tal_init();
+	apr_dev_ptr = &pdev->dev;
 	return apr_debug_init();
 }
+
+static int apr_remove(struct platform_device *pdev)
+{
+	apr_cleanup();
+	return 0;
+}
+
+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,
+	}
+};
+
+static int __init apr_init(void)
+{
+	platform_driver_register(&apr_driver);
+	apr_dummy_init();
+	return 0;
+}
 module_init(apr_init);
 
-void __exit apr_exit(void)
+static void __exit apr_exit(void)
 {
-	subsys_notif_deregister("apr_modem");
-	subsys_notif_deregister("apr_adsp");
+	apr_dummy_exit();
+	platform_driver_unregister(&apr_driver);
 }
 module_exit(apr_exit);
-MODULE_DESCRIPTION("APR module");
+
+MODULE_DESCRIPTION("APR DRIVER");
 MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, apr_machine_of_match);
diff --git a/ipc/apr_dummy.c b/ipc/apr_dummy.c
new file mode 100644
index 0000000..288ba82
--- /dev/null
+++ b/ipc/apr_dummy.c
@@ -0,0 +1,57 @@
+/* Copyright (c) 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+
+static int apr_dummy_probe(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int apr_dummy_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id apr_dummy_dt_match[] = {
+	{.compatible = "qcom,msm-audio-apr-dummy"},
+	{}
+};
+
+static struct platform_driver apr_dummy_driver = {
+	.driver = {
+		.name = "apr_dummy",
+		.owner = THIS_MODULE,
+		.of_match_table = apr_dummy_dt_match,
+	},
+	.probe = apr_dummy_probe,
+	.remove = apr_dummy_remove,
+};
+
+int __init apr_dummy_init(void)
+{
+	platform_driver_register(&apr_dummy_driver);
+	return 0;
+}
+
+void apr_dummy_exit(void)
+{
+	platform_driver_unregister(&apr_dummy_driver);
+}
+
+MODULE_DESCRIPTION("APR dummy module driver");
+MODULE_LICENSE("GPL v2");