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