ipc: make APR driver a platform driver

Change APR driver to platform driver and add any child
device only after ADSP up notification is received.
The idea is to have machine driver as a child device
under APR, and add the platform device for machine
driver only after ADSP is up. This will help invoke
audio drivers waiting with deferred probe and eventually
should help sound card registers successfully.

Change-Id: Ib0c0f7ec1d7dd93a1b54a9a66260861223d55c67
Signed-off-by: Laxminath Kasam <lkasam@codeaurora.org>
Signed-off-by: Meng Wang <mwang@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
This commit is contained in:
Banajit Goswami 2018-03-03 02:09:05 -08:00
parent 7b0ae02b27
commit 3d99f7a130

156
ipc/apr.c
View File

@ -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);