Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Intel SST loader on ACPI systems
  4 *
  5 * Copyright (C) 2013, Intel Corporation. All rights reserved.
  6 */
  7
  8#include <linux/acpi.h>
  9#include <linux/device.h>
 10#include <linux/firmware.h>
 11#include <linux/module.h>
 12#include <linux/platform_device.h>
 13
 14#include "sst-dsp.h"
 15#include <sound/soc-acpi.h>
 16#include <sound/soc-acpi-intel-match.h>
 17
 18#define SST_LPT_DSP_DMA_ADDR_OFFSET	0x0F0000
 19#define SST_WPT_DSP_DMA_ADDR_OFFSET	0x0FE000
 20#define SST_LPT_DSP_DMA_SIZE		(1024 - 1)
 21
 22/* Descriptor for setting up SST platform data */
 23struct sst_acpi_desc {
 24	const char *drv_name;
 25	struct snd_soc_acpi_mach *machines;
 26	/* Platform resource indexes. Must set to -1 if not used */
 27	int resindex_lpe_base;
 28	int resindex_pcicfg_base;
 29	int resindex_fw_base;
 30	int irqindex_host_ipc;
 31	int resindex_dma_base;
 32	/* Unique number identifying the SST core on platform */
 33	int sst_id;
 34	/* DMA only valid when resindex_dma_base != -1*/
 35	int dma_engine;
 36	int dma_size;
 37};
 38
 39struct sst_acpi_priv {
 40	struct platform_device *pdev_mach;
 41	struct platform_device *pdev_pcm;
 42	struct sst_pdata sst_pdata;
 43	struct sst_acpi_desc *desc;
 44	struct snd_soc_acpi_mach *mach;
 45};
 46
 47static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
 48{
 49	struct platform_device *pdev = context;
 50	struct device *dev = &pdev->dev;
 51	struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
 52	struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
 53	struct sst_acpi_desc *desc = sst_acpi->desc;
 54	struct snd_soc_acpi_mach *mach = sst_acpi->mach;
 55
 56	sst_pdata->fw = fw;
 57	if (!fw) {
 58		dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename);
 59		return;
 60	}
 61
 62	/* register PCM and DAI driver */
 63	sst_acpi->pdev_pcm =
 64		platform_device_register_data(dev, desc->drv_name, -1,
 65					      sst_pdata, sizeof(*sst_pdata));
 66	if (IS_ERR(sst_acpi->pdev_pcm)) {
 67		dev_err(dev, "Cannot register device %s. Error %d\n",
 68			desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm));
 69	}
 70
 71	return;
 72}
 73
 74static int sst_acpi_probe(struct platform_device *pdev)
 75{
 76	const struct acpi_device_id *id;
 77	struct device *dev = &pdev->dev;
 78	struct sst_acpi_priv *sst_acpi;
 79	struct sst_pdata *sst_pdata;
 80	struct snd_soc_acpi_mach *mach;
 81	struct sst_acpi_desc *desc;
 82	struct resource *mmio;
 83	int ret = 0;
 84
 85	sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL);
 86	if (sst_acpi == NULL)
 87		return -ENOMEM;
 88
 89	id = acpi_match_device(dev->driver->acpi_match_table, dev);
 90	if (!id)
 91		return -ENODEV;
 92
 93	desc = (struct sst_acpi_desc *)id->driver_data;
 94	mach = snd_soc_acpi_find_machine(desc->machines);
 95	if (mach == NULL) {
 96		dev_err(dev, "No matching ASoC machine driver found\n");
 97		return -ENODEV;
 98	}
 99
100	sst_pdata = &sst_acpi->sst_pdata;
101	sst_pdata->id = desc->sst_id;
102	sst_pdata->dma_dev = dev;
103	sst_acpi->desc = desc;
104	sst_acpi->mach = mach;
105
106	sst_pdata->resindex_dma_base = desc->resindex_dma_base;
107	if (desc->resindex_dma_base >= 0) {
108		sst_pdata->dma_engine = desc->dma_engine;
109		sst_pdata->dma_base = desc->resindex_dma_base;
110		sst_pdata->dma_size = desc->dma_size;
111	}
112
113	if (desc->irqindex_host_ipc >= 0)
114		sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
115
116	if (desc->resindex_lpe_base >= 0) {
117		mmio = platform_get_resource(pdev, IORESOURCE_MEM,
118					     desc->resindex_lpe_base);
119		if (mmio) {
120			sst_pdata->lpe_base = mmio->start;
121			sst_pdata->lpe_size = resource_size(mmio);
122		}
123	}
124
125	if (desc->resindex_pcicfg_base >= 0) {
126		mmio = platform_get_resource(pdev, IORESOURCE_MEM,
127					     desc->resindex_pcicfg_base);
128		if (mmio) {
129			sst_pdata->pcicfg_base = mmio->start;
130			sst_pdata->pcicfg_size = resource_size(mmio);
131		}
132	}
133
134	if (desc->resindex_fw_base >= 0) {
135		mmio = platform_get_resource(pdev, IORESOURCE_MEM,
136					     desc->resindex_fw_base);
137		if (mmio) {
138			sst_pdata->fw_base = mmio->start;
139			sst_pdata->fw_size = resource_size(mmio);
140		}
141	}
142
143	platform_set_drvdata(pdev, sst_acpi);
144	mach->pdata = sst_pdata;
145
146	/* register machine driver */
147	sst_acpi->pdev_mach =
148		platform_device_register_data(dev, mach->drv_name, -1,
149					      mach, sizeof(*mach));
150	if (IS_ERR(sst_acpi->pdev_mach))
151		return PTR_ERR(sst_acpi->pdev_mach);
152
153	/* continue SST probing after firmware is loaded */
154	ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename,
155				      dev, GFP_KERNEL, pdev, sst_acpi_fw_cb);
156	if (ret)
157		platform_device_unregister(sst_acpi->pdev_mach);
158
159	return ret;
160}
161
162static int sst_acpi_remove(struct platform_device *pdev)
163{
164	struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
165	struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
166
167	platform_device_unregister(sst_acpi->pdev_mach);
168	if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm))
169		platform_device_unregister(sst_acpi->pdev_pcm);
170	release_firmware(sst_pdata->fw);
171
172	return 0;
173}
174
175static struct sst_acpi_desc sst_acpi_haswell_desc = {
176	.drv_name = "haswell-pcm-audio",
177	.machines = snd_soc_acpi_intel_haswell_machines,
178	.resindex_lpe_base = 0,
179	.resindex_pcicfg_base = 1,
180	.resindex_fw_base = -1,
181	.irqindex_host_ipc = 0,
182	.sst_id = SST_DEV_ID_LYNX_POINT,
183	.dma_engine = SST_DMA_TYPE_DW,
184	.resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET,
185	.dma_size = SST_LPT_DSP_DMA_SIZE,
186};
187
188static struct sst_acpi_desc sst_acpi_broadwell_desc = {
189	.drv_name = "haswell-pcm-audio",
190	.machines = snd_soc_acpi_intel_broadwell_machines,
191	.resindex_lpe_base = 0,
192	.resindex_pcicfg_base = 1,
193	.resindex_fw_base = -1,
194	.irqindex_host_ipc = 0,
195	.sst_id = SST_DEV_ID_WILDCAT_POINT,
196	.dma_engine = SST_DMA_TYPE_DW,
197	.resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET,
198	.dma_size = SST_LPT_DSP_DMA_SIZE,
199};
200
201#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI)
202static struct sst_acpi_desc sst_acpi_baytrail_desc = {
203	.drv_name = "baytrail-pcm-audio",
204	.machines = snd_soc_acpi_intel_baytrail_legacy_machines,
205	.resindex_lpe_base = 0,
206	.resindex_pcicfg_base = 1,
207	.resindex_fw_base = 2,
208	.irqindex_host_ipc = 5,
209	.sst_id = SST_DEV_ID_BYT,
210	.resindex_dma_base = -1,
211};
212#endif
213
214static const struct acpi_device_id sst_acpi_match[] = {
215	{ "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
216	{ "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
217#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI)
218	{ "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
219#endif
220	{ }
221};
222MODULE_DEVICE_TABLE(acpi, sst_acpi_match);
223
224static struct platform_driver sst_acpi_driver = {
225	.probe = sst_acpi_probe,
226	.remove = sst_acpi_remove,
227	.driver = {
228		.name = "sst-acpi",
229		.acpi_match_table = ACPI_PTR(sst_acpi_match),
230	},
231};
232module_platform_driver(sst_acpi_driver);
233
234MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
235MODULE_DESCRIPTION("Intel SST loader on ACPI systems");
236MODULE_LICENSE("GPL v2");