1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // Copyright 2019 NXP 4 // 5 // Author: Daniel Baluta <daniel.baluta@nxp.com> 6 // 7 8 #include <linux/firmware.h> 9 #include <linux/module.h> 10 #include <linux/moduleparam.h> 11 #include <linux/pm_runtime.h> 12 #include <sound/sof.h> 13 14 #include "ops.h" 15 #include "imx/imx-ops.h" 16 17 static char *fw_path; 18 module_param(fw_path, charp, 0444); 19 MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware."); 20 21 static char *tplg_path; 22 module_param(tplg_path, charp, 0444); 23 MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); 24 25 /* platform specific devices */ 26 #if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) 27 static struct sof_dev_desc sof_of_imx8qxp_desc = { 28 .default_fw_path = "imx/sof", 29 .default_tplg_path = "imx/sof-tplg", 30 .default_fw_filename = "sof-imx8x.ri", 31 .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", 32 .ops = &sof_imx8x_ops, 33 }; 34 35 static struct sof_dev_desc sof_of_imx8qm_desc = { 36 .default_fw_path = "imx/sof", 37 .default_tplg_path = "imx/sof-tplg", 38 .default_fw_filename = "sof-imx8.ri", 39 .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", 40 .ops = &sof_imx8_ops, 41 }; 42 #endif 43 44 #if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) 45 static struct sof_dev_desc sof_of_imx8mp_desc = { 46 .default_fw_path = "imx/sof", 47 .default_tplg_path = "imx/sof-tplg", 48 .default_fw_filename = "sof-imx8m.ri", 49 .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", 50 .ops = &sof_imx8m_ops, 51 }; 52 #endif 53 54 static const struct dev_pm_ops sof_of_pm = { 55 .prepare = snd_sof_prepare, 56 .complete = snd_sof_complete, 57 SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) 58 SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, 59 NULL) 60 }; 61 62 static void sof_of_probe_complete(struct device *dev) 63 { 64 /* allow runtime_pm */ 65 pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); 66 pm_runtime_use_autosuspend(dev); 67 pm_runtime_set_active(dev); 68 pm_runtime_enable(dev); 69 70 pm_runtime_mark_last_busy(dev); 71 pm_runtime_put_autosuspend(dev); 72 } 73 74 static int sof_of_probe(struct platform_device *pdev) 75 { 76 struct device *dev = &pdev->dev; 77 const struct sof_dev_desc *desc; 78 struct snd_sof_pdata *sof_pdata; 79 80 dev_info(&pdev->dev, "DT DSP detected"); 81 82 sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); 83 if (!sof_pdata) 84 return -ENOMEM; 85 86 desc = device_get_match_data(dev); 87 if (!desc) 88 return -ENODEV; 89 90 if (!desc->ops) { 91 dev_err(dev, "error: no matching DT descriptor ops\n"); 92 return -ENODEV; 93 } 94 95 sof_pdata->desc = desc; 96 sof_pdata->dev = &pdev->dev; 97 sof_pdata->fw_filename = desc->default_fw_filename; 98 99 if (fw_path) 100 sof_pdata->fw_filename_prefix = fw_path; 101 else 102 sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path; 103 104 if (tplg_path) 105 sof_pdata->tplg_filename_prefix = tplg_path; 106 else 107 sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path; 108 109 /* set callback to be called on successful device probe to enable runtime_pm */ 110 sof_pdata->sof_probe_complete = sof_of_probe_complete; 111 112 /* call sof helper for DSP hardware probe */ 113 return snd_sof_device_probe(dev, sof_pdata); 114 } 115 116 static int sof_of_remove(struct platform_device *pdev) 117 { 118 pm_runtime_disable(&pdev->dev); 119 120 /* call sof helper for DSP hardware remove */ 121 snd_sof_device_remove(&pdev->dev); 122 123 return 0; 124 } 125 126 static const struct of_device_id sof_of_ids[] = { 127 #if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) 128 { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, 129 { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, 130 #endif 131 #if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) 132 { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, 133 #endif 134 { } 135 }; 136 MODULE_DEVICE_TABLE(of, sof_of_ids); 137 138 /* DT driver definition */ 139 static struct platform_driver snd_sof_of_driver = { 140 .probe = sof_of_probe, 141 .remove = sof_of_remove, 142 .driver = { 143 .name = "sof-audio-of", 144 .pm = &sof_of_pm, 145 .of_match_table = sof_of_ids, 146 }, 147 }; 148 module_platform_driver(snd_sof_of_driver); 149 150 MODULE_LICENSE("Dual BSD/GPL"); 151