cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

imx8.c (15696B)


      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// Hardware interface for audio DSP on i.MX8
      8
      9#include <linux/firmware.h>
     10#include <linux/of_platform.h>
     11#include <linux/of_address.h>
     12#include <linux/of_irq.h>
     13#include <linux/pm_domain.h>
     14
     15#include <linux/module.h>
     16#include <sound/sof.h>
     17#include <sound/sof/xtensa.h>
     18#include <linux/firmware/imx/ipc.h>
     19#include <linux/firmware/imx/dsp.h>
     20
     21#include <linux/firmware/imx/svc/misc.h>
     22#include <dt-bindings/firmware/imx/rsrc.h>
     23#include "../ops.h"
     24#include "../sof-of-dev.h"
     25#include "imx-common.h"
     26
     27/* DSP memories */
     28#define IRAM_OFFSET		0x10000
     29#define IRAM_SIZE		(2 * 1024)
     30#define DRAM0_OFFSET		0x0
     31#define DRAM0_SIZE		(32 * 1024)
     32#define DRAM1_OFFSET		0x8000
     33#define DRAM1_SIZE		(32 * 1024)
     34#define SYSRAM_OFFSET		0x18000
     35#define SYSRAM_SIZE		(256 * 1024)
     36#define SYSROM_OFFSET		0x58000
     37#define SYSROM_SIZE		(192 * 1024)
     38
     39#define RESET_VECTOR_VADDR	0x596f8000
     40
     41#define MBOX_OFFSET	0x800000
     42#define MBOX_SIZE	0x1000
     43
     44/* DSP clocks */
     45static struct clk_bulk_data imx8_dsp_clks[] = {
     46	{ .id = "ipg" },
     47	{ .id = "ocram" },
     48	{ .id = "core" },
     49};
     50
     51struct imx8_priv {
     52	struct device *dev;
     53	struct snd_sof_dev *sdev;
     54
     55	/* DSP IPC handler */
     56	struct imx_dsp_ipc *dsp_ipc;
     57	struct platform_device *ipc_dev;
     58
     59	/* System Controller IPC handler */
     60	struct imx_sc_ipc *sc_ipc;
     61
     62	/* Power domain handling */
     63	int num_domains;
     64	struct device **pd_dev;
     65	struct device_link **link;
     66
     67	struct imx_clocks *clks;
     68};
     69
     70static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
     71{
     72	return MBOX_OFFSET;
     73}
     74
     75static int imx8_get_window_offset(struct snd_sof_dev *sdev, u32 id)
     76{
     77	return MBOX_OFFSET;
     78}
     79
     80static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
     81{
     82	struct imx8_priv *priv = imx_dsp_get_data(ipc);
     83	unsigned long flags;
     84
     85	spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
     86	snd_sof_ipc_process_reply(priv->sdev, 0);
     87	spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
     88}
     89
     90static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
     91{
     92	struct imx8_priv *priv = imx_dsp_get_data(ipc);
     93	u32 p; /* panic code */
     94
     95	/* Read the message from the debug box. */
     96	sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
     97
     98	/* Check to see if the message is a panic code (0x0dead***) */
     99	if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
    100		snd_sof_dsp_panic(priv->sdev, p, true);
    101	else
    102		snd_sof_ipc_msgs_rx(priv->sdev);
    103}
    104
    105static struct imx_dsp_ops dsp_ops = {
    106	.handle_reply		= imx8_dsp_handle_reply,
    107	.handle_request		= imx8_dsp_handle_request,
    108};
    109
    110static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
    111{
    112	struct imx8_priv *priv = sdev->pdata->hw_pdata;
    113
    114	sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
    115			  msg->msg_size);
    116	imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
    117
    118	return 0;
    119}
    120
    121/*
    122 * DSP control.
    123 */
    124static int imx8x_run(struct snd_sof_dev *sdev)
    125{
    126	struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
    127	int ret;
    128
    129	ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
    130				      IMX_SC_C_OFS_SEL, 1);
    131	if (ret < 0) {
    132		dev_err(sdev->dev, "Error system address offset source select\n");
    133		return ret;
    134	}
    135
    136	ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
    137				      IMX_SC_C_OFS_AUDIO, 0x80);
    138	if (ret < 0) {
    139		dev_err(sdev->dev, "Error system address offset of AUDIO\n");
    140		return ret;
    141	}
    142
    143	ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
    144				      IMX_SC_C_OFS_PERIPH, 0x5A);
    145	if (ret < 0) {
    146		dev_err(sdev->dev, "Error system address offset of PERIPH %d\n",
    147			ret);
    148		return ret;
    149	}
    150
    151	ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
    152				      IMX_SC_C_OFS_IRQ, 0x51);
    153	if (ret < 0) {
    154		dev_err(sdev->dev, "Error system address offset of IRQ\n");
    155		return ret;
    156	}
    157
    158	imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
    159			    RESET_VECTOR_VADDR);
    160
    161	return 0;
    162}
    163
    164static int imx8_run(struct snd_sof_dev *sdev)
    165{
    166	struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
    167	int ret;
    168
    169	ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
    170				      IMX_SC_C_OFS_SEL, 0);
    171	if (ret < 0) {
    172		dev_err(sdev->dev, "Error system address offset source select\n");
    173		return ret;
    174	}
    175
    176	imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
    177			    RESET_VECTOR_VADDR);
    178
    179	return 0;
    180}
    181
    182static int imx8_probe(struct snd_sof_dev *sdev)
    183{
    184	struct platform_device *pdev =
    185		container_of(sdev->dev, struct platform_device, dev);
    186	struct device_node *np = pdev->dev.of_node;
    187	struct device_node *res_node;
    188	struct resource *mmio;
    189	struct imx8_priv *priv;
    190	struct resource res;
    191	u32 base, size;
    192	int ret = 0;
    193	int i;
    194
    195	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    196	if (!priv)
    197		return -ENOMEM;
    198
    199	priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL);
    200	if (!priv->clks)
    201		return -ENOMEM;
    202
    203	sdev->num_cores = 1;
    204	sdev->pdata->hw_pdata = priv;
    205	priv->dev = sdev->dev;
    206	priv->sdev = sdev;
    207
    208	/* power up device associated power domains */
    209	priv->num_domains = of_count_phandle_with_args(np, "power-domains",
    210						       "#power-domain-cells");
    211	if (priv->num_domains < 0) {
    212		dev_err(sdev->dev, "no power-domains property in %pOF\n", np);
    213		return priv->num_domains;
    214	}
    215
    216	priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
    217					  sizeof(*priv->pd_dev), GFP_KERNEL);
    218	if (!priv->pd_dev)
    219		return -ENOMEM;
    220
    221	priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
    222					sizeof(*priv->link), GFP_KERNEL);
    223	if (!priv->link)
    224		return -ENOMEM;
    225
    226	for (i = 0; i < priv->num_domains; i++) {
    227		priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
    228		if (IS_ERR(priv->pd_dev[i])) {
    229			ret = PTR_ERR(priv->pd_dev[i]);
    230			goto exit_unroll_pm;
    231		}
    232		priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
    233						DL_FLAG_STATELESS |
    234						DL_FLAG_PM_RUNTIME |
    235						DL_FLAG_RPM_ACTIVE);
    236		if (!priv->link[i]) {
    237			ret = -ENOMEM;
    238			dev_pm_domain_detach(priv->pd_dev[i], false);
    239			goto exit_unroll_pm;
    240		}
    241	}
    242
    243	ret = imx_scu_get_handle(&priv->sc_ipc);
    244	if (ret) {
    245		dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n",
    246			ret);
    247		goto exit_unroll_pm;
    248	}
    249
    250	priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
    251						      PLATFORM_DEVID_NONE,
    252						      pdev, sizeof(*pdev));
    253	if (IS_ERR(priv->ipc_dev)) {
    254		ret = PTR_ERR(priv->ipc_dev);
    255		goto exit_unroll_pm;
    256	}
    257
    258	priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
    259	if (!priv->dsp_ipc) {
    260		/* DSP IPC driver not probed yet, try later */
    261		ret = -EPROBE_DEFER;
    262		dev_err(sdev->dev, "Failed to get drvdata\n");
    263		goto exit_pdev_unregister;
    264	}
    265
    266	imx_dsp_set_data(priv->dsp_ipc, priv);
    267	priv->dsp_ipc->ops = &dsp_ops;
    268
    269	/* DSP base */
    270	mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    271	if (mmio) {
    272		base = mmio->start;
    273		size = resource_size(mmio);
    274	} else {
    275		dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
    276		ret = -EINVAL;
    277		goto exit_pdev_unregister;
    278	}
    279
    280	sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
    281	if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
    282		dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
    283			base, size);
    284		ret = -ENODEV;
    285		goto exit_pdev_unregister;
    286	}
    287	sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
    288
    289	res_node = of_parse_phandle(np, "memory-region", 0);
    290	if (!res_node) {
    291		dev_err(&pdev->dev, "failed to get memory region node\n");
    292		ret = -ENODEV;
    293		goto exit_pdev_unregister;
    294	}
    295
    296	ret = of_address_to_resource(res_node, 0, &res);
    297	of_node_put(res_node);
    298	if (ret) {
    299		dev_err(&pdev->dev, "failed to get reserved region address\n");
    300		goto exit_pdev_unregister;
    301	}
    302
    303	sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
    304							  resource_size(&res));
    305	if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
    306		dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
    307			base, size);
    308		ret = -ENOMEM;
    309		goto exit_pdev_unregister;
    310	}
    311	sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
    312
    313	/* set default mailbox offset for FW ready message */
    314	sdev->dsp_box.offset = MBOX_OFFSET;
    315
    316	/* init clocks info */
    317	priv->clks->dsp_clks = imx8_dsp_clks;
    318	priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks);
    319
    320	ret = imx8_parse_clocks(sdev, priv->clks);
    321	if (ret < 0)
    322		goto exit_pdev_unregister;
    323
    324	ret = imx8_enable_clocks(sdev, priv->clks);
    325	if (ret < 0)
    326		goto exit_pdev_unregister;
    327
    328	return 0;
    329
    330exit_pdev_unregister:
    331	platform_device_unregister(priv->ipc_dev);
    332exit_unroll_pm:
    333	while (--i >= 0) {
    334		device_link_del(priv->link[i]);
    335		dev_pm_domain_detach(priv->pd_dev[i], false);
    336	}
    337
    338	return ret;
    339}
    340
    341static int imx8_remove(struct snd_sof_dev *sdev)
    342{
    343	struct imx8_priv *priv = sdev->pdata->hw_pdata;
    344	int i;
    345
    346	imx8_disable_clocks(sdev, priv->clks);
    347	platform_device_unregister(priv->ipc_dev);
    348
    349	for (i = 0; i < priv->num_domains; i++) {
    350		device_link_del(priv->link[i]);
    351		dev_pm_domain_detach(priv->pd_dev[i], false);
    352	}
    353
    354	return 0;
    355}
    356
    357/* on i.MX8 there is 1 to 1 match between type and BAR idx */
    358static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
    359{
    360	/* Only IRAM and SRAM bars are valid */
    361	switch (type) {
    362	case SOF_FW_BLK_TYPE_IRAM:
    363	case SOF_FW_BLK_TYPE_SRAM:
    364		return type;
    365	default:
    366		return -EINVAL;
    367	}
    368}
    369
    370static void imx8_suspend(struct snd_sof_dev *sdev)
    371{
    372	int i;
    373	struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
    374
    375	for (i = 0; i < DSP_MU_CHAN_NUM; i++)
    376		imx_dsp_free_channel(priv->dsp_ipc, i);
    377
    378	imx8_disable_clocks(sdev, priv->clks);
    379}
    380
    381static int imx8_resume(struct snd_sof_dev *sdev)
    382{
    383	struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
    384	int ret;
    385	int i;
    386
    387	ret = imx8_enable_clocks(sdev, priv->clks);
    388	if (ret < 0)
    389		return ret;
    390
    391	for (i = 0; i < DSP_MU_CHAN_NUM; i++)
    392		imx_dsp_request_channel(priv->dsp_ipc, i);
    393
    394	return 0;
    395}
    396
    397static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev)
    398{
    399	int ret;
    400	const struct sof_dsp_power_state target_dsp_state = {
    401		.state = SOF_DSP_PM_D0,
    402	};
    403
    404	ret = imx8_resume(sdev);
    405	if (ret < 0)
    406		return ret;
    407
    408	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
    409}
    410
    411static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev)
    412{
    413	const struct sof_dsp_power_state target_dsp_state = {
    414		.state = SOF_DSP_PM_D3,
    415	};
    416
    417	imx8_suspend(sdev);
    418
    419	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
    420}
    421
    422static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
    423{
    424	const struct sof_dsp_power_state target_dsp_state = {
    425		.state = target_state,
    426	};
    427
    428	if (!pm_runtime_suspended(sdev->dev))
    429		imx8_suspend(sdev);
    430
    431	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
    432}
    433
    434static int imx8_dsp_resume(struct snd_sof_dev *sdev)
    435{
    436	int ret;
    437	const struct sof_dsp_power_state target_dsp_state = {
    438		.state = SOF_DSP_PM_D0,
    439	};
    440
    441	ret = imx8_resume(sdev);
    442	if (ret < 0)
    443		return ret;
    444
    445	if (pm_runtime_suspended(sdev->dev)) {
    446		pm_runtime_disable(sdev->dev);
    447		pm_runtime_set_active(sdev->dev);
    448		pm_runtime_mark_last_busy(sdev->dev);
    449		pm_runtime_enable(sdev->dev);
    450		pm_runtime_idle(sdev->dev);
    451	}
    452
    453	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
    454}
    455
    456static struct snd_soc_dai_driver imx8_dai[] = {
    457{
    458	.name = "esai0",
    459	.playback = {
    460		.channels_min = 1,
    461		.channels_max = 8,
    462	},
    463	.capture = {
    464		.channels_min = 1,
    465		.channels_max = 8,
    466	},
    467},
    468{
    469	.name = "sai1",
    470	.playback = {
    471		.channels_min = 1,
    472		.channels_max = 32,
    473	},
    474	.capture = {
    475		.channels_min = 1,
    476		.channels_max = 32,
    477	},
    478},
    479};
    480
    481static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev,
    482				    const struct sof_dsp_power_state *target_state)
    483{
    484	sdev->dsp_power_state = *target_state;
    485
    486	return 0;
    487}
    488
    489/* i.MX8 ops */
    490static struct snd_sof_dsp_ops sof_imx8_ops = {
    491	/* probe and remove */
    492	.probe		= imx8_probe,
    493	.remove		= imx8_remove,
    494	/* DSP core boot */
    495	.run		= imx8_run,
    496
    497	/* Block IO */
    498	.block_read	= sof_block_read,
    499	.block_write	= sof_block_write,
    500
    501	/* Mailbox IO */
    502	.mailbox_read	= sof_mailbox_read,
    503	.mailbox_write	= sof_mailbox_write,
    504
    505	/* ipc */
    506	.send_msg	= imx8_send_msg,
    507	.get_mailbox_offset	= imx8_get_mailbox_offset,
    508	.get_window_offset	= imx8_get_window_offset,
    509
    510	.ipc_msg_data	= sof_ipc_msg_data,
    511	.set_stream_data_offset = sof_set_stream_data_offset,
    512
    513	.get_bar_index	= imx8_get_bar_index,
    514
    515	/* firmware loading */
    516	.load_firmware	= snd_sof_load_firmware_memcpy,
    517
    518	/* Debug information */
    519	.dbg_dump = imx8_dump,
    520	.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
    521
    522	/* stream callbacks */
    523	.pcm_open = sof_stream_pcm_open,
    524	.pcm_close = sof_stream_pcm_close,
    525
    526	/* Firmware ops */
    527	.dsp_arch_ops = &sof_xtensa_arch_ops,
    528
    529	/* DAI drivers */
    530	.drv = imx8_dai,
    531	.num_drv = ARRAY_SIZE(imx8_dai),
    532
    533	/* ALSA HW info flags */
    534	.hw_info =	SNDRV_PCM_INFO_MMAP |
    535			SNDRV_PCM_INFO_MMAP_VALID |
    536			SNDRV_PCM_INFO_INTERLEAVED |
    537			SNDRV_PCM_INFO_PAUSE |
    538			SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
    539
    540	/* PM */
    541	.runtime_suspend	= imx8_dsp_runtime_suspend,
    542	.runtime_resume		= imx8_dsp_runtime_resume,
    543
    544	.suspend	= imx8_dsp_suspend,
    545	.resume		= imx8_dsp_resume,
    546
    547	.set_power_state	= imx8_dsp_set_power_state,
    548};
    549
    550/* i.MX8X ops */
    551static struct snd_sof_dsp_ops sof_imx8x_ops = {
    552	/* probe and remove */
    553	.probe		= imx8_probe,
    554	.remove		= imx8_remove,
    555	/* DSP core boot */
    556	.run		= imx8x_run,
    557
    558	/* Block IO */
    559	.block_read	= sof_block_read,
    560	.block_write	= sof_block_write,
    561
    562	/* Mailbox IO */
    563	.mailbox_read	= sof_mailbox_read,
    564	.mailbox_write	= sof_mailbox_write,
    565
    566	/* ipc */
    567	.send_msg	= imx8_send_msg,
    568	.get_mailbox_offset	= imx8_get_mailbox_offset,
    569	.get_window_offset	= imx8_get_window_offset,
    570
    571	.ipc_msg_data	= sof_ipc_msg_data,
    572	.set_stream_data_offset = sof_set_stream_data_offset,
    573
    574	.get_bar_index	= imx8_get_bar_index,
    575
    576	/* firmware loading */
    577	.load_firmware	= snd_sof_load_firmware_memcpy,
    578
    579	/* Debug information */
    580	.dbg_dump = imx8_dump,
    581	.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
    582
    583	/* stream callbacks */
    584	.pcm_open = sof_stream_pcm_open,
    585	.pcm_close = sof_stream_pcm_close,
    586
    587	/* Firmware ops */
    588	.dsp_arch_ops = &sof_xtensa_arch_ops,
    589
    590	/* DAI drivers */
    591	.drv = imx8_dai,
    592	.num_drv = ARRAY_SIZE(imx8_dai),
    593
    594	/* PM */
    595	.runtime_suspend	= imx8_dsp_runtime_suspend,
    596	.runtime_resume		= imx8_dsp_runtime_resume,
    597
    598	.suspend	= imx8_dsp_suspend,
    599	.resume		= imx8_dsp_resume,
    600
    601	.set_power_state	= imx8_dsp_set_power_state,
    602
    603	/* ALSA HW info flags */
    604	.hw_info =	SNDRV_PCM_INFO_MMAP |
    605			SNDRV_PCM_INFO_MMAP_VALID |
    606			SNDRV_PCM_INFO_INTERLEAVED |
    607			SNDRV_PCM_INFO_PAUSE |
    608			SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
    609};
    610
    611static struct sof_dev_desc sof_of_imx8qxp_desc = {
    612	.ipc_supported_mask	= BIT(SOF_IPC),
    613	.ipc_default		= SOF_IPC,
    614	.default_fw_path = {
    615		[SOF_IPC] = "imx/sof",
    616	},
    617	.default_tplg_path = {
    618		[SOF_IPC] = "imx/sof-tplg",
    619	},
    620	.default_fw_filename = {
    621		[SOF_IPC] = "sof-imx8x.ri",
    622	},
    623	.nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
    624	.ops = &sof_imx8x_ops,
    625};
    626
    627static struct sof_dev_desc sof_of_imx8qm_desc = {
    628	.ipc_supported_mask	= BIT(SOF_IPC),
    629	.ipc_default		= SOF_IPC,
    630	.default_fw_path = {
    631		[SOF_IPC] = "imx/sof",
    632	},
    633	.default_tplg_path = {
    634		[SOF_IPC] = "imx/sof-tplg",
    635	},
    636	.default_fw_filename = {
    637		[SOF_IPC] = "sof-imx8.ri",
    638	},
    639	.nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
    640	.ops = &sof_imx8_ops,
    641};
    642
    643static const struct of_device_id sof_of_imx8_ids[] = {
    644	{ .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc},
    645	{ .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc},
    646	{ }
    647};
    648MODULE_DEVICE_TABLE(of, sof_of_imx8_ids);
    649
    650/* DT driver definition */
    651static struct platform_driver snd_sof_of_imx8_driver = {
    652	.probe = sof_of_probe,
    653	.remove = sof_of_remove,
    654	.driver = {
    655		.name = "sof-audio-of-imx8",
    656		.pm = &sof_of_pm,
    657		.of_match_table = sof_of_imx8_ids,
    658	},
    659};
    660module_platform_driver(snd_sof_of_imx8_driver);
    661
    662MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
    663MODULE_LICENSE("Dual BSD/GPL");