xref: /linux/sound/soc/sof/imx/imx8ulp.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1fb5319afSZhang Peng // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2fb5319afSZhang Peng //
3fb5319afSZhang Peng // Copyright 2021-2022 NXP
4fb5319afSZhang Peng //
5fb5319afSZhang Peng // Author: Peng Zhang <peng.zhang_8@nxp.com>
6fb5319afSZhang Peng //
7fb5319afSZhang Peng // Hardware interface for audio DSP on i.MX8ULP
8fb5319afSZhang Peng 
9fb5319afSZhang Peng #include <linux/arm-smccc.h>
10fb5319afSZhang Peng #include <linux/clk.h>
11fb5319afSZhang Peng #include <linux/firmware.h>
12fb5319afSZhang Peng #include <linux/firmware/imx/dsp.h>
13fb5319afSZhang Peng #include <linux/firmware/imx/ipc.h>
14fb5319afSZhang Peng #include <linux/firmware/imx/svc/misc.h>
15fb5319afSZhang Peng #include <linux/mfd/syscon.h>
16fb5319afSZhang Peng #include <linux/module.h>
17fb5319afSZhang Peng #include <linux/of_address.h>
18fb5319afSZhang Peng #include <linux/of_irq.h>
19fb5319afSZhang Peng #include <linux/of_platform.h>
20fb5319afSZhang Peng #include <linux/of_reserved_mem.h>
21fb5319afSZhang Peng 
22fb5319afSZhang Peng #include <sound/sof.h>
23fb5319afSZhang Peng #include <sound/sof/xtensa.h>
24fb5319afSZhang Peng 
25fb5319afSZhang Peng #include "../ops.h"
26fb5319afSZhang Peng #include "../sof-of-dev.h"
27fb5319afSZhang Peng #include "imx-common.h"
28fb5319afSZhang Peng 
29fb5319afSZhang Peng #define FSL_SIP_HIFI_XRDC	0xc200000e
30fb5319afSZhang Peng 
31fb5319afSZhang Peng /* SIM Domain register */
32fb5319afSZhang Peng #define SYSCTRL0		0x8
33fb5319afSZhang Peng #define EXECUTE_BIT		BIT(13)
34fb5319afSZhang Peng #define RESET_BIT		BIT(16)
35fb5319afSZhang Peng #define HIFI4_CLK_BIT		BIT(17)
36fb5319afSZhang Peng #define PB_CLK_BIT		BIT(18)
37fb5319afSZhang Peng #define PLAT_CLK_BIT		BIT(19)
38fb5319afSZhang Peng #define DEBUG_LOGIC_BIT		BIT(25)
39fb5319afSZhang Peng 
40fb5319afSZhang Peng #define MBOX_OFFSET		0x800000
41fb5319afSZhang Peng #define MBOX_SIZE		0x1000
42fb5319afSZhang Peng 
43fb5319afSZhang Peng struct imx8ulp_priv {
44fb5319afSZhang Peng 	struct device *dev;
45fb5319afSZhang Peng 	struct snd_sof_dev *sdev;
46fb5319afSZhang Peng 
47fb5319afSZhang Peng 	/* DSP IPC handler */
48fb5319afSZhang Peng 	struct imx_dsp_ipc *dsp_ipc;
49fb5319afSZhang Peng 	struct platform_device *ipc_dev;
50fb5319afSZhang Peng 
51fb5319afSZhang Peng 	struct regmap *regmap;
52a358f67dSLaurentiu Mihalcea 	struct clk_bulk_data *clks;
53a358f67dSLaurentiu Mihalcea 	int clk_num;
54fb5319afSZhang Peng };
55fb5319afSZhang Peng 
imx8ulp_sim_lpav_start(struct imx8ulp_priv * priv)56fb5319afSZhang Peng static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv)
57fb5319afSZhang Peng {
58fb5319afSZhang Peng 	/* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
59fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0);
60fb5319afSZhang Peng 
61fb5319afSZhang Peng 	/* Reset HiFi4 DSP Debug logic: 1 debug reset, 0  out of reset*/
62fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
63fb5319afSZhang Peng 
64fb5319afSZhang Peng 	/* Stall HIFI4 DSP Execution: 1 stall, 0 run */
65fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0);
66fb5319afSZhang Peng }
67fb5319afSZhang Peng 
imx8ulp_get_mailbox_offset(struct snd_sof_dev * sdev)68fb5319afSZhang Peng static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev)
69fb5319afSZhang Peng {
70fb5319afSZhang Peng 	return MBOX_OFFSET;
71fb5319afSZhang Peng }
72fb5319afSZhang Peng 
imx8ulp_get_window_offset(struct snd_sof_dev * sdev,u32 id)73fb5319afSZhang Peng static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id)
74fb5319afSZhang Peng {
75fb5319afSZhang Peng 	return MBOX_OFFSET;
76fb5319afSZhang Peng }
77fb5319afSZhang Peng 
imx8ulp_dsp_handle_reply(struct imx_dsp_ipc * ipc)78fb5319afSZhang Peng static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc)
79fb5319afSZhang Peng {
80fb5319afSZhang Peng 	struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
81fb5319afSZhang Peng 	unsigned long flags;
82fb5319afSZhang Peng 
83fb5319afSZhang Peng 	spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
84fb5319afSZhang Peng 
85fb5319afSZhang Peng 	snd_sof_ipc_process_reply(priv->sdev, 0);
86fb5319afSZhang Peng 
87fb5319afSZhang Peng 	spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
88fb5319afSZhang Peng }
89fb5319afSZhang Peng 
imx8ulp_dsp_handle_request(struct imx_dsp_ipc * ipc)90fb5319afSZhang Peng static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc)
91fb5319afSZhang Peng {
92fb5319afSZhang Peng 	struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
93fb5319afSZhang Peng 	u32 p; /* panic code */
94fb5319afSZhang Peng 
95fb5319afSZhang Peng 	/* Read the message from the debug box. */
96fb5319afSZhang Peng 	sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
97fb5319afSZhang Peng 
98fb5319afSZhang Peng 	/* Check to see if the message is a panic code (0x0dead***) */
99fb5319afSZhang Peng 	if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
100fb5319afSZhang Peng 		snd_sof_dsp_panic(priv->sdev, p, true);
101fb5319afSZhang Peng 	else
102fb5319afSZhang Peng 		snd_sof_ipc_msgs_rx(priv->sdev);
103fb5319afSZhang Peng }
104fb5319afSZhang Peng 
105fb5319afSZhang Peng static struct imx_dsp_ops dsp_ops = {
106fb5319afSZhang Peng 	.handle_reply		= imx8ulp_dsp_handle_reply,
107fb5319afSZhang Peng 	.handle_request		= imx8ulp_dsp_handle_request,
108fb5319afSZhang Peng };
109fb5319afSZhang Peng 
imx8ulp_send_msg(struct snd_sof_dev * sdev,struct snd_sof_ipc_msg * msg)110fb5319afSZhang Peng static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
111fb5319afSZhang Peng {
112fb5319afSZhang Peng 	struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
113fb5319afSZhang Peng 
114fb5319afSZhang Peng 	sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
115fb5319afSZhang Peng 			  msg->msg_size);
116fb5319afSZhang Peng 	imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
117fb5319afSZhang Peng 
118fb5319afSZhang Peng 	return 0;
119fb5319afSZhang Peng }
120fb5319afSZhang Peng 
imx8ulp_run(struct snd_sof_dev * sdev)121fb5319afSZhang Peng static int imx8ulp_run(struct snd_sof_dev *sdev)
122fb5319afSZhang Peng {
123fb5319afSZhang Peng 	struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
124fb5319afSZhang Peng 
125fb5319afSZhang Peng 	imx8ulp_sim_lpav_start(priv);
126fb5319afSZhang Peng 
127fb5319afSZhang Peng 	return 0;
128fb5319afSZhang Peng }
129fb5319afSZhang Peng 
imx8ulp_reset(struct snd_sof_dev * sdev)130fb5319afSZhang Peng static int imx8ulp_reset(struct snd_sof_dev *sdev)
131fb5319afSZhang Peng {
132fb5319afSZhang Peng 	struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
133fb5319afSZhang Peng 	struct arm_smccc_res smc_resource;
134fb5319afSZhang Peng 
135fb5319afSZhang Peng 	/* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
136fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
137fb5319afSZhang Peng 
138fb5319afSZhang Peng 	/* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
139fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
140fb5319afSZhang Peng 
141fb5319afSZhang Peng 	/* HiFi4 Clock Enable: 1 enabled, 0 disabled */
142fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
143fb5319afSZhang Peng 
144fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
145fb5319afSZhang Peng 	usleep_range(1, 2);
146fb5319afSZhang Peng 
147fb5319afSZhang Peng 	/* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
148fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
149fb5319afSZhang Peng 	usleep_range(1, 2);
150fb5319afSZhang Peng 
151fb5319afSZhang Peng 	arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource);
152fb5319afSZhang Peng 
153fb5319afSZhang Peng 	return 0;
154fb5319afSZhang Peng }
155fb5319afSZhang Peng 
imx8ulp_probe(struct snd_sof_dev * sdev)156fb5319afSZhang Peng static int imx8ulp_probe(struct snd_sof_dev *sdev)
157fb5319afSZhang Peng {
158fb5319afSZhang Peng 	struct platform_device *pdev =
159fb5319afSZhang Peng 		container_of(sdev->dev, struct platform_device, dev);
160fb5319afSZhang Peng 	struct device_node *np = pdev->dev.of_node;
161fb5319afSZhang Peng 	struct device_node *res_node;
162fb5319afSZhang Peng 	struct resource *mmio;
163fb5319afSZhang Peng 	struct imx8ulp_priv *priv;
164fb5319afSZhang Peng 	struct resource res;
165fb5319afSZhang Peng 	u32 base, size;
166fb5319afSZhang Peng 	int ret = 0;
167fb5319afSZhang Peng 
168fb5319afSZhang Peng 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
169fb5319afSZhang Peng 	if (!priv)
170fb5319afSZhang Peng 		return -ENOMEM;
171fb5319afSZhang Peng 
172fb5319afSZhang Peng 	sdev->num_cores = 1;
173fb5319afSZhang Peng 	sdev->pdata->hw_pdata = priv;
174fb5319afSZhang Peng 	priv->dev = sdev->dev;
175fb5319afSZhang Peng 	priv->sdev = sdev;
176fb5319afSZhang Peng 
177fb5319afSZhang Peng 	/* System integration module(SIM) control dsp configuration */
178fb5319afSZhang Peng 	priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl");
179fb5319afSZhang Peng 	if (IS_ERR(priv->regmap))
180fb5319afSZhang Peng 		return PTR_ERR(priv->regmap);
181fb5319afSZhang Peng 
182fb5319afSZhang Peng 	priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
183fb5319afSZhang Peng 						      PLATFORM_DEVID_NONE,
184fb5319afSZhang Peng 						      pdev, sizeof(*pdev));
185fb5319afSZhang Peng 	if (IS_ERR(priv->ipc_dev))
186fb5319afSZhang Peng 		return PTR_ERR(priv->ipc_dev);
187fb5319afSZhang Peng 
188fb5319afSZhang Peng 	priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
189fb5319afSZhang Peng 	if (!priv->dsp_ipc) {
190fb5319afSZhang Peng 		/* DSP IPC driver not probed yet, try later */
191fb5319afSZhang Peng 		ret = -EPROBE_DEFER;
192fb5319afSZhang Peng 		dev_err(sdev->dev, "Failed to get drvdata\n");
193fb5319afSZhang Peng 		goto exit_pdev_unregister;
194fb5319afSZhang Peng 	}
195fb5319afSZhang Peng 
196fb5319afSZhang Peng 	imx_dsp_set_data(priv->dsp_ipc, priv);
197fb5319afSZhang Peng 	priv->dsp_ipc->ops = &dsp_ops;
198fb5319afSZhang Peng 
199fb5319afSZhang Peng 	/* DSP base */
200fb5319afSZhang Peng 	mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
201fb5319afSZhang Peng 	if (mmio) {
202fb5319afSZhang Peng 		base = mmio->start;
203fb5319afSZhang Peng 		size = resource_size(mmio);
204fb5319afSZhang Peng 	} else {
205fb5319afSZhang Peng 		dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
206fb5319afSZhang Peng 		ret = -EINVAL;
207fb5319afSZhang Peng 		goto exit_pdev_unregister;
208fb5319afSZhang Peng 	}
209fb5319afSZhang Peng 
210fb5319afSZhang Peng 	sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
211fb5319afSZhang Peng 	if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
212fb5319afSZhang Peng 		dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
213fb5319afSZhang Peng 			base, size);
214fb5319afSZhang Peng 		ret = -ENODEV;
215fb5319afSZhang Peng 		goto exit_pdev_unregister;
216fb5319afSZhang Peng 	}
217fb5319afSZhang Peng 	sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
218fb5319afSZhang Peng 
219fb5319afSZhang Peng 	res_node = of_parse_phandle(np, "memory-reserved", 0);
220fb5319afSZhang Peng 	if (!res_node) {
221fb5319afSZhang Peng 		dev_err(&pdev->dev, "failed to get memory region node\n");
222fb5319afSZhang Peng 		ret = -ENODEV;
223fb5319afSZhang Peng 		goto exit_pdev_unregister;
224fb5319afSZhang Peng 	}
225fb5319afSZhang Peng 
226fb5319afSZhang Peng 	ret = of_address_to_resource(res_node, 0, &res);
227adc641f1SYang Yingliang 	of_node_put(res_node);
228fb5319afSZhang Peng 	if (ret) {
229fb5319afSZhang Peng 		dev_err(&pdev->dev, "failed to get reserved region address\n");
230fb5319afSZhang Peng 		goto exit_pdev_unregister;
231fb5319afSZhang Peng 	}
232fb5319afSZhang Peng 
233fb5319afSZhang Peng 	sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
234fb5319afSZhang Peng 							  resource_size(&res));
235fb5319afSZhang Peng 	if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
236fb5319afSZhang Peng 		dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
237fb5319afSZhang Peng 			base, size);
238fb5319afSZhang Peng 		ret = -ENOMEM;
239fb5319afSZhang Peng 		goto exit_pdev_unregister;
240fb5319afSZhang Peng 	}
241fb5319afSZhang Peng 	sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
242fb5319afSZhang Peng 
243fb5319afSZhang Peng 	/* set default mailbox offset for FW ready message */
244fb5319afSZhang Peng 	sdev->dsp_box.offset = MBOX_OFFSET;
245fb5319afSZhang Peng 
246fb5319afSZhang Peng 	ret = of_reserved_mem_device_init(sdev->dev);
247fb5319afSZhang Peng 	if (ret) {
248fb5319afSZhang Peng 		dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret);
249fb5319afSZhang Peng 		goto exit_pdev_unregister;
250fb5319afSZhang Peng 	}
251fb5319afSZhang Peng 
252a358f67dSLaurentiu Mihalcea 	ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks);
253a358f67dSLaurentiu Mihalcea 	if (ret < 0) {
254a358f67dSLaurentiu Mihalcea 		dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret);
255fb5319afSZhang Peng 		goto exit_pdev_unregister;
256a358f67dSLaurentiu Mihalcea 	}
257a358f67dSLaurentiu Mihalcea 	priv->clk_num = ret;
258fb5319afSZhang Peng 
259a358f67dSLaurentiu Mihalcea 	ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
260a358f67dSLaurentiu Mihalcea 	if (ret < 0) {
261a358f67dSLaurentiu Mihalcea 		dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
262fb5319afSZhang Peng 		goto exit_pdev_unregister;
263a358f67dSLaurentiu Mihalcea 	}
264fb5319afSZhang Peng 
265fb5319afSZhang Peng 	return 0;
266fb5319afSZhang Peng 
267fb5319afSZhang Peng exit_pdev_unregister:
268fb5319afSZhang Peng 	platform_device_unregister(priv->ipc_dev);
269fb5319afSZhang Peng 
270fb5319afSZhang Peng 	return ret;
271fb5319afSZhang Peng }
272fb5319afSZhang Peng 
imx8ulp_remove(struct snd_sof_dev * sdev)273e4d09de3SPierre-Louis Bossart static void imx8ulp_remove(struct snd_sof_dev *sdev)
274fb5319afSZhang Peng {
275fb5319afSZhang Peng 	struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
276fb5319afSZhang Peng 
277a358f67dSLaurentiu Mihalcea 	clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
278fb5319afSZhang Peng 	platform_device_unregister(priv->ipc_dev);
279fb5319afSZhang Peng }
280fb5319afSZhang Peng 
281fb5319afSZhang Peng /* on i.MX8 there is 1 to 1 match between type and BAR idx */
imx8ulp_get_bar_index(struct snd_sof_dev * sdev,u32 type)282fb5319afSZhang Peng static int imx8ulp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
283fb5319afSZhang Peng {
284fb5319afSZhang Peng 	return type;
285fb5319afSZhang Peng }
286fb5319afSZhang Peng 
imx8ulp_suspend(struct snd_sof_dev * sdev)287fb5319afSZhang Peng static int imx8ulp_suspend(struct snd_sof_dev *sdev)
288fb5319afSZhang Peng {
289fb5319afSZhang Peng 	int i;
290fb5319afSZhang Peng 	struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
291fb5319afSZhang Peng 
292fb5319afSZhang Peng 	/*Stall DSP,  release in .run() */
293fb5319afSZhang Peng 	regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
294fb5319afSZhang Peng 
295fb5319afSZhang Peng 	for (i = 0; i < DSP_MU_CHAN_NUM; i++)
296fb5319afSZhang Peng 		imx_dsp_free_channel(priv->dsp_ipc, i);
297fb5319afSZhang Peng 
298a358f67dSLaurentiu Mihalcea 	clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
299fb5319afSZhang Peng 
300fb5319afSZhang Peng 	return 0;
301fb5319afSZhang Peng }
302fb5319afSZhang Peng 
imx8ulp_resume(struct snd_sof_dev * sdev)303fb5319afSZhang Peng static int imx8ulp_resume(struct snd_sof_dev *sdev)
304fb5319afSZhang Peng {
305fb5319afSZhang Peng 	struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
306a358f67dSLaurentiu Mihalcea 	int i, ret;
307fb5319afSZhang Peng 
308a358f67dSLaurentiu Mihalcea 	ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
309a358f67dSLaurentiu Mihalcea 	if (ret < 0) {
310a358f67dSLaurentiu Mihalcea 		dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
311a358f67dSLaurentiu Mihalcea 		return ret;
312a358f67dSLaurentiu Mihalcea 	}
313fb5319afSZhang Peng 
314fb5319afSZhang Peng 	for (i = 0; i < DSP_MU_CHAN_NUM; i++)
315fb5319afSZhang Peng 		imx_dsp_request_channel(priv->dsp_ipc, i);
316fb5319afSZhang Peng 
317fb5319afSZhang Peng 	return 0;
318fb5319afSZhang Peng }
319fb5319afSZhang Peng 
imx8ulp_dsp_runtime_resume(struct snd_sof_dev * sdev)320fb5319afSZhang Peng static int imx8ulp_dsp_runtime_resume(struct snd_sof_dev *sdev)
321fb5319afSZhang Peng {
322fb5319afSZhang Peng 	const struct sof_dsp_power_state target_dsp_state = {
323fb5319afSZhang Peng 		.state = SOF_DSP_PM_D0,
324fb5319afSZhang Peng 		.substate = 0,
325fb5319afSZhang Peng 	};
326fb5319afSZhang Peng 
327fb5319afSZhang Peng 	imx8ulp_resume(sdev);
328fb5319afSZhang Peng 
329fb5319afSZhang Peng 	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
330fb5319afSZhang Peng }
331fb5319afSZhang Peng 
imx8ulp_dsp_runtime_suspend(struct snd_sof_dev * sdev)332fb5319afSZhang Peng static int imx8ulp_dsp_runtime_suspend(struct snd_sof_dev *sdev)
333fb5319afSZhang Peng {
334fb5319afSZhang Peng 	const struct sof_dsp_power_state target_dsp_state = {
335fb5319afSZhang Peng 		.state = SOF_DSP_PM_D3,
336fb5319afSZhang Peng 		.substate = 0,
337fb5319afSZhang Peng 	};
338fb5319afSZhang Peng 
339fb5319afSZhang Peng 	imx8ulp_suspend(sdev);
340fb5319afSZhang Peng 
341fb5319afSZhang Peng 	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
342fb5319afSZhang Peng }
343fb5319afSZhang Peng 
imx8ulp_dsp_suspend(struct snd_sof_dev * sdev,unsigned int target_state)344fb5319afSZhang Peng static int imx8ulp_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
345fb5319afSZhang Peng {
346fb5319afSZhang Peng 	const struct sof_dsp_power_state target_dsp_state = {
347fb5319afSZhang Peng 		.state = target_state,
348fb5319afSZhang Peng 		.substate = 0,
349fb5319afSZhang Peng 	};
350fb5319afSZhang Peng 
351fb5319afSZhang Peng 	if (!pm_runtime_suspended(sdev->dev))
352fb5319afSZhang Peng 		imx8ulp_suspend(sdev);
353fb5319afSZhang Peng 
354fb5319afSZhang Peng 	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
355fb5319afSZhang Peng }
356fb5319afSZhang Peng 
imx8ulp_dsp_resume(struct snd_sof_dev * sdev)357fb5319afSZhang Peng static int imx8ulp_dsp_resume(struct snd_sof_dev *sdev)
358fb5319afSZhang Peng {
359fb5319afSZhang Peng 	const struct sof_dsp_power_state target_dsp_state = {
360fb5319afSZhang Peng 		.state = SOF_DSP_PM_D0,
361fb5319afSZhang Peng 		.substate = 0,
362fb5319afSZhang Peng 	};
363fb5319afSZhang Peng 
364fb5319afSZhang Peng 	imx8ulp_resume(sdev);
365fb5319afSZhang Peng 
366fb5319afSZhang Peng 	if (pm_runtime_suspended(sdev->dev)) {
367fb5319afSZhang Peng 		pm_runtime_disable(sdev->dev);
368fb5319afSZhang Peng 		pm_runtime_set_active(sdev->dev);
369fb5319afSZhang Peng 		pm_runtime_mark_last_busy(sdev->dev);
370fb5319afSZhang Peng 		pm_runtime_enable(sdev->dev);
371fb5319afSZhang Peng 		pm_runtime_idle(sdev->dev);
372fb5319afSZhang Peng 	}
373fb5319afSZhang Peng 
374fb5319afSZhang Peng 	return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
375fb5319afSZhang Peng }
376fb5319afSZhang Peng 
377fb5319afSZhang Peng static struct snd_soc_dai_driver imx8ulp_dai[] = {
378fb5319afSZhang Peng 	{
379fb5319afSZhang Peng 		.name = "sai5",
380fb5319afSZhang Peng 		.playback = {
381fb5319afSZhang Peng 			.channels_min = 1,
382fb5319afSZhang Peng 			.channels_max = 32,
383fb5319afSZhang Peng 		},
384fb5319afSZhang Peng 		.capture = {
385fb5319afSZhang Peng 			.channels_min = 1,
386fb5319afSZhang Peng 			.channels_max = 32,
387fb5319afSZhang Peng 		},
388fb5319afSZhang Peng 	},
389fb5319afSZhang Peng 	{
390fb5319afSZhang Peng 		.name = "sai6",
391fb5319afSZhang Peng 		.playback = {
392fb5319afSZhang Peng 			.channels_min = 1,
393fb5319afSZhang Peng 			.channels_max = 32,
394fb5319afSZhang Peng 		},
395fb5319afSZhang Peng 		.capture = {
396fb5319afSZhang Peng 			.channels_min = 1,
397fb5319afSZhang Peng 			.channels_max = 32,
398fb5319afSZhang Peng 		},
399fb5319afSZhang Peng 	},
400fb5319afSZhang Peng };
401fb5319afSZhang Peng 
imx8ulp_dsp_set_power_state(struct snd_sof_dev * sdev,const struct sof_dsp_power_state * target_state)402fb5319afSZhang Peng static int imx8ulp_dsp_set_power_state(struct snd_sof_dev *sdev,
403fb5319afSZhang Peng 				       const struct sof_dsp_power_state *target_state)
404fb5319afSZhang Peng {
405fb5319afSZhang Peng 	sdev->dsp_power_state = *target_state;
406fb5319afSZhang Peng 
407fb5319afSZhang Peng 	return 0;
408fb5319afSZhang Peng }
409fb5319afSZhang Peng 
410fb5319afSZhang Peng /* i.MX8 ops */
411232e0da9SKrzysztof Kozlowski static const struct snd_sof_dsp_ops sof_imx8ulp_ops = {
412fb5319afSZhang Peng 	/* probe and remove */
413fb5319afSZhang Peng 	.probe		= imx8ulp_probe,
414fb5319afSZhang Peng 	.remove		= imx8ulp_remove,
415fb5319afSZhang Peng 	/* DSP core boot */
416fb5319afSZhang Peng 	.run		= imx8ulp_run,
417fb5319afSZhang Peng 	.reset		= imx8ulp_reset,
418fb5319afSZhang Peng 
419fb5319afSZhang Peng 	/* Block IO */
420fb5319afSZhang Peng 	.block_read	= sof_block_read,
421fb5319afSZhang Peng 	.block_write	= sof_block_write,
422fb5319afSZhang Peng 
423fb5319afSZhang Peng 	/* Module IO */
424fb5319afSZhang Peng 	.read64		= sof_io_read64,
425fb5319afSZhang Peng 
426fb5319afSZhang Peng 	/* Mailbox IO */
427fb5319afSZhang Peng 	.mailbox_read	= sof_mailbox_read,
428fb5319afSZhang Peng 	.mailbox_write	= sof_mailbox_write,
429fb5319afSZhang Peng 
430fb5319afSZhang Peng 	/* ipc */
431fb5319afSZhang Peng 	.send_msg	= imx8ulp_send_msg,
432fb5319afSZhang Peng 	.get_mailbox_offset	= imx8ulp_get_mailbox_offset,
433fb5319afSZhang Peng 	.get_window_offset	= imx8ulp_get_window_offset,
434fb5319afSZhang Peng 
435fb5319afSZhang Peng 	.ipc_msg_data	= sof_ipc_msg_data,
436fb5319afSZhang Peng 	.set_stream_data_offset = sof_set_stream_data_offset,
437fb5319afSZhang Peng 
438fb5319afSZhang Peng 	/* stream callbacks */
439fb5319afSZhang Peng 	.pcm_open	= sof_stream_pcm_open,
440fb5319afSZhang Peng 	.pcm_close	= sof_stream_pcm_close,
441fb5319afSZhang Peng 
442fb5319afSZhang Peng 	/* module loading */
443fb5319afSZhang Peng 	.get_bar_index	= imx8ulp_get_bar_index,
444fb5319afSZhang Peng 	/* firmware loading */
445fb5319afSZhang Peng 	.load_firmware	= snd_sof_load_firmware_memcpy,
446fb5319afSZhang Peng 
447fb5319afSZhang Peng 	/* Debug information */
448fb5319afSZhang Peng 	.dbg_dump	= imx8_dump,
449fb5319afSZhang Peng 
450fb5319afSZhang Peng 	/* Firmware ops */
451fb5319afSZhang Peng 	.dsp_arch_ops	= &sof_xtensa_arch_ops,
452fb5319afSZhang Peng 
453fb5319afSZhang Peng 	/* DAI drivers */
454fb5319afSZhang Peng 	.drv		= imx8ulp_dai,
455fb5319afSZhang Peng 	.num_drv	= ARRAY_SIZE(imx8ulp_dai),
456fb5319afSZhang Peng 
457fb5319afSZhang Peng 	/* ALSA HW info flags */
458fb5319afSZhang Peng 	.hw_info	= SNDRV_PCM_INFO_MMAP |
459fb5319afSZhang Peng 			SNDRV_PCM_INFO_MMAP_VALID |
460fb5319afSZhang Peng 			SNDRV_PCM_INFO_INTERLEAVED |
461fb5319afSZhang Peng 			SNDRV_PCM_INFO_PAUSE |
462b6190c45SShengjiu Wang 			SNDRV_PCM_INFO_BATCH |
463fb5319afSZhang Peng 			SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
464fb5319afSZhang Peng 
465fb5319afSZhang Peng 	/* PM */
466fb5319afSZhang Peng 	.runtime_suspend	= imx8ulp_dsp_runtime_suspend,
467fb5319afSZhang Peng 	.runtime_resume		= imx8ulp_dsp_runtime_resume,
468fb5319afSZhang Peng 
469fb5319afSZhang Peng 	.suspend	= imx8ulp_dsp_suspend,
470fb5319afSZhang Peng 	.resume		= imx8ulp_dsp_resume,
471fb5319afSZhang Peng 
472fb5319afSZhang Peng 	.set_power_state	= imx8ulp_dsp_set_power_state,
473fb5319afSZhang Peng };
474fb5319afSZhang Peng 
4752b9cdef1SDaniel Baluta static struct snd_sof_of_mach sof_imx8ulp_machs[] = {
4762b9cdef1SDaniel Baluta 	{
4772b9cdef1SDaniel Baluta 		.compatible = "fsl,imx8ulp-evk",
4782b9cdef1SDaniel Baluta 		.sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
4792b9cdef1SDaniel Baluta 		.drv_name = "asoc-audio-graph-card2",
4802b9cdef1SDaniel Baluta 	},
4812b9cdef1SDaniel Baluta 	{}
4822b9cdef1SDaniel Baluta };
4832b9cdef1SDaniel Baluta 
484fb5319afSZhang Peng static struct sof_dev_desc sof_of_imx8ulp_desc = {
4852b9cdef1SDaniel Baluta 	.of_machines = sof_imx8ulp_machs,
4866a645a55SPeter Ujfalusi 	.ipc_supported_mask     = BIT(SOF_IPC_TYPE_3),
4876a645a55SPeter Ujfalusi 	.ipc_default            = SOF_IPC_TYPE_3,
488fb5319afSZhang Peng 	.default_fw_path = {
4896a645a55SPeter Ujfalusi 		[SOF_IPC_TYPE_3] = "imx/sof",
490fb5319afSZhang Peng 	},
491fb5319afSZhang Peng 	.default_tplg_path = {
4926a645a55SPeter Ujfalusi 		[SOF_IPC_TYPE_3] = "imx/sof-tplg",
493fb5319afSZhang Peng 	},
494fb5319afSZhang Peng 	.default_fw_filename = {
4956a645a55SPeter Ujfalusi 		[SOF_IPC_TYPE_3] = "sof-imx8ulp.ri",
496fb5319afSZhang Peng 	},
497fb5319afSZhang Peng 	.nocodec_tplg_filename = "sof-imx8ulp-nocodec.tplg",
498fb5319afSZhang Peng 	.ops = &sof_imx8ulp_ops,
499fb5319afSZhang Peng };
500fb5319afSZhang Peng 
501fb5319afSZhang Peng static const struct of_device_id sof_of_imx8ulp_ids[] = {
502fb5319afSZhang Peng 	{ .compatible = "fsl,imx8ulp-dsp", .data = &sof_of_imx8ulp_desc},
503fb5319afSZhang Peng 	{ }
504fb5319afSZhang Peng };
505fb5319afSZhang Peng MODULE_DEVICE_TABLE(of, sof_of_imx8ulp_ids);
506fb5319afSZhang Peng 
507fb5319afSZhang Peng /* DT driver definition */
508fb5319afSZhang Peng static struct platform_driver snd_sof_of_imx8ulp_driver = {
509fb5319afSZhang Peng 	.probe = sof_of_probe,
510*130af75bSUwe Kleine-König 	.remove = sof_of_remove,
511fb5319afSZhang Peng 	.driver = {
512fb5319afSZhang Peng 		.name = "sof-audio-of-imx8ulp",
513fb5319afSZhang Peng 		.pm = &sof_of_pm,
514fb5319afSZhang Peng 		.of_match_table = sof_of_imx8ulp_ids,
515fb5319afSZhang Peng 	},
516fb5319afSZhang Peng };
517fb5319afSZhang Peng module_platform_driver(snd_sof_of_imx8ulp_driver);
518fb5319afSZhang Peng 
519fb5319afSZhang Peng MODULE_LICENSE("Dual BSD/GPL");
5203ff78451SPierre-Louis Bossart MODULE_DESCRIPTION("SOF support for IMX8ULP platforms");
5213ff78451SPierre-Louis Bossart MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
522