From 7e58376e99700a02467237d598b656f462484d0a Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 2 Aug 2019 11:01:33 +0530 Subject: [PATCH] Asoc: apr: 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. In analog codec driver defers until Q6 core ready. Change-Id: I1c82d9da55e771299df356a2771de2b2e62348cf Signed-off-by: Soumya Managoli --- drivers/soc/qcom/qdsp6v2/Makefile | 10 ++--- drivers/soc/qcom/qdsp6v2/apr.c | 84 +++++++++++++++++++++++++++++++++++- drivers/soc/qcom/qdsp6v2/apr_dummy.c | 58 +++++++++++++++++++++++++ include/linux/qdsp6v2/apr.h | 2 + 4 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 drivers/soc/qcom/qdsp6v2/apr_dummy.c diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile index 250cc88ba32d..7d1993202d67 100644 --- a/drivers/soc/qcom/qdsp6v2/Makefile +++ b/drivers/soc/qcom/qdsp6v2/Makefile @@ -1,7 +1,7 @@ -obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o voice_svc.o -obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o voice_svc.o -obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o voice_svc.o -obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o voice_svc.o +obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o voice_svc.o apr_dummy.o +obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o voice_svc.o apr_dummy.o +obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o voice_svc.o apr_dummy.o +obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o voice_svc.o apr_dummy.o obj-$(CONFIG_MSM_QDSP6_APRV2_VM) += apr_vm.o apr_v2.o voice_svc.o obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += msm_audio_ion.o obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += msm_audio_ion_vm.o @@ -11,4 +11,4 @@ obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o obj-$(CONFIG_EXT_ANC) += sdsp-anc.o audio_anc.o audio-anc-dev-mgr.o -obj-$(CONFIG_MSM_LPASS_RESOURCE_MANAGER) += lpass_resource_mgr.o \ No newline at end of file +obj-$(CONFIG_MSM_LPASS_RESOURCE_MANAGER) += lpass_resource_mgr.o diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c index 4bc1999485a7..8215674f111b 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -36,9 +37,11 @@ #include #include #include +#include #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; @@ -47,6 +50,7 @@ static wait_queue_head_t modem_wait; static bool is_modem_up; static bool is_initial_modem_boot; static bool is_initial_adsp_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); @@ -57,6 +61,7 @@ struct apr_reset_work { }; static bool apr_cf_debug; +static struct delayed_work add_chld_dev_work; #ifdef CONFIG_DEBUG_FS static struct dentry *debugfs_apr_debug; @@ -263,6 +268,11 @@ int apr_set_q6_state(enum apr_subsys_state state) if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED) return -EINVAL; atomic_set(&q6.q6_state, state); + if (state == APR_SUBSYS_LOADED && !is_child_devices_loaded) { + schedule_delayed_work(&add_chld_dev_work, + msecs_to_jiffies(100)); + is_child_devices_loaded = true; + } return 0; } EXPORT_SYMBOL_GPL(apr_set_q6_state); @@ -279,11 +289,27 @@ static void apr_adsp_down(unsigned long opcode) dispatch_event(opcode, APR_DEST_QDSP6); } +static void apr_add_child_devices(struct work_struct *work) +{ + int ret; + + ret = of_platform_populate(apr_dev_ptr->of_node, + NULL, NULL, apr_dev_ptr); + if (ret) + dev_err(apr_dev_ptr, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); +} + static void apr_adsp_up(void) { if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN) wake_up(&dsp_wait); + if (!is_child_devices_loaded) { + schedule_delayed_work(&add_chld_dev_work, + msecs_to_jiffies(100)); + is_child_devices_loaded = true; + } } int apr_wait_for_device_up(int dest_id) @@ -1057,7 +1083,23 @@ static struct notifier_block modem_service_nb = { .priority = 0, }; -static int __init apr_init(void) +static void apr_cleanup(void) +{ + int i, j, k; + + 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; @@ -1087,10 +1129,50 @@ static int __init apr_init(void) subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN, &modem_service_nb); + apr_dev_ptr = &pdev->dev; + INIT_DELAYED_WORK(&add_chld_dev_work, apr_add_child_devices); + return 0; +} + +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; } device_initcall(apr_init); +static void __exit apr_exit(void) +{ + apr_dummy_exit(); + platform_driver_unregister(&apr_driver); +} +__exitcall(apr_exit); + +MODULE_DESCRIPTION("APR DRIVER"); +MODULE_DEVICE_TABLE(of, apr_machine_of_match); + static int __init apr_late_init(void) { int ret = 0; diff --git a/drivers/soc/qcom/qdsp6v2/apr_dummy.c b/drivers/soc/qcom/qdsp6v2/apr_dummy.c new file mode 100644 index 000000000000..517a49d1afdd --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_dummy.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2019, 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 +#include +#include +#include +#include + +static int apr_dummy_probe(struct platform_device *pdev) +{ + 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"); +MODULE_DEVICE_TABLE(of, apr_dummy_dt_match); diff --git a/include/linux/qdsp6v2/apr.h b/include/linux/qdsp6v2/apr.h index e7e2a53a70d7..d80320e74661 100644 --- a/include/linux/qdsp6v2/apr.h +++ b/include/linux/qdsp6v2/apr.h @@ -210,4 +210,6 @@ static inline int apr_end_rx_rt(void *handle) int apr_start_rx_rt(void *handle); int apr_end_rx_rt(void *handle); #endif +int apr_dummy_init(void); +void apr_dummy_exit(void); #endif -- 2.11.0