Forráskód Böngészése

Merge "ipc: make APR driver a platform driver"

Linux Build Service Account 7 éve
szülő
commit
b9b740446e
1 módosított fájl, 143 hozzáadás és 13 törlés
  1. 143 13
      ipc/apr.c

+ 143 - 13
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 void *apr_pkt_ctx;
 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 apr_reset_work {
 	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 @@ enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
 
 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 @@ static int apr_notifier_service_cb(struct notifier_block *this,
 		 * 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 @@ static int __init apr_debug_init(void)
 )
 #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 @@ static int __init apr_init(void)
 	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 @@ static int __init apr_init(void)
 	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);