Message ID | 1658208367-24376-1-git-send-email-shengjiu.wang@nxp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [1/2] ASoC: SOF: imx: Add i.MX8ULP HW support | expand |
Hi Shengjiu, Thank you for the patch! Yet something to improve: [auto build test ERROR on shawnguo/for-next] [also build test ERROR on robh/for-next broonie-sound/for-next linus/master v5.19-rc7 next-20220718] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Shengjiu-Wang/ASoC-SOF-imx-Add-i-MX8ULP-HW-support/20220719-134348 base: https://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git for-next config: sh-allmodconfig compiler: sh4-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/7cd457928803203c18dc543f6f98c304fa601a30 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Shengjiu-Wang/ASoC-SOF-imx-Add-i-MX8ULP-HW-support/20220719-134348 git checkout 7cd457928803203c18dc543f6f98c304fa601a30 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash sound/soc/sof/imx/ If you fix the issue, kindly add following tag where applicable Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): sound/soc/sof/imx/imx8ulp.c: In function 'imx8ulp_reset': >> sound/soc/sof/imx/imx8ulp.c:186:9: error: implicit declaration of function 'arm_smccc_smc' [-Werror=implicit-function-declaration] 186 | arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource); | ^~~~~~~~~~~~~ sound/soc/sof/imx/imx8ulp.c: At top level: >> sound/soc/sof/imx/imx8ulp.c:42:33: error: storage size of 'smc_resource' isn't known 42 | struct arm_smccc_res smc_resource; | ^~~~~~~~~~~~ cc1: some warnings being treated as errors vim +/arm_smccc_smc +186 sound/soc/sof/imx/imx8ulp.c 41 > 42 struct arm_smccc_res smc_resource; 43 44 static struct clk_bulk_data imx8ulp_dsp_clks[] = { 45 { .id = "core" }, 46 { .id = "ipg" }, 47 { .id = "ocram" }, 48 { .id = "mu" }, 49 }; 50 51 struct imx8ulp_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 struct regmap *regmap; 60 struct imx_clocks *clks; 61 }; 62 63 static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv) 64 { 65 /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */ 66 regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0); 67 /* Reset HiFi4 DSP Debug logic: 1 reset, 0 not set */ 68 regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0); 69 /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ 70 regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0); 71 } 72 73 static void imx8ulp_get_reply(struct snd_sof_dev *sdev) 74 { 75 struct snd_sof_ipc_msg *msg = sdev->msg; 76 struct sof_ipc_reply reply; 77 int ret = 0; 78 79 if (!msg) { 80 dev_warn(sdev->dev, "unexpected ipc interrupt\n"); 81 return; 82 } 83 84 /* get reply */ 85 sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); 86 87 if (reply.error < 0) { 88 memcpy(msg->reply_data, &reply, sizeof(reply)); 89 ret = reply.error; 90 } else { 91 /* reply has correct size? */ 92 if (reply.hdr.size != msg->reply_size) { 93 dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", 94 msg->reply_size, reply.hdr.size); 95 ret = -EINVAL; 96 } 97 98 /* read the message */ 99 if (msg->reply_size > 0) 100 sof_mailbox_read(sdev, sdev->host_box.offset, 101 msg->reply_data, msg->reply_size); 102 } 103 104 msg->reply_error = ret; 105 } 106 107 static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev) 108 { 109 return MBOX_OFFSET; 110 } 111 112 static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id) 113 { 114 return MBOX_OFFSET; 115 } 116 117 static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) 118 { 119 struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); 120 unsigned long flags; 121 122 spin_lock_irqsave(&priv->sdev->ipc_lock, flags); 123 124 imx8ulp_get_reply(priv->sdev); 125 snd_sof_ipc_reply(priv->sdev, 0); 126 spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); 127 } 128 129 static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc) 130 { 131 struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); 132 u32 p; /* panic code */ 133 134 /* Read the message from the debug box. */ 135 sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); 136 137 /* Check to see if the message is a panic code (0x0dead***) */ 138 if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) 139 snd_sof_dsp_panic(priv->sdev, p, true); 140 else 141 snd_sof_ipc_msgs_rx(priv->sdev); 142 } 143 144 static struct imx_dsp_ops dsp_ops = { 145 .handle_reply = imx8ulp_dsp_handle_reply, 146 .handle_request = imx8ulp_dsp_handle_request, 147 }; 148 149 static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 150 { 151 struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; 152 153 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, 154 msg->msg_size); 155 imx_dsp_ring_doorbell(priv->dsp_ipc, 0); 156 157 return 0; 158 } 159 160 static int imx8ulp_run(struct snd_sof_dev *sdev) 161 { 162 struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; 163 164 imx8ulp_sim_lpav_start(priv); 165 166 return 0; 167 } 168 169 static int imx8ulp_reset(struct snd_sof_dev *sdev) 170 { 171 struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; 172 173 /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */ 174 regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT); 175 /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */ 176 regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT); 177 /* HiFi4 Clock Enable: 1 enabled, 0 disabled */ 178 regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT); 179 180 regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT); 181 usleep_range(1, 2); 182 /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ 183 regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); 184 usleep_range(1, 2); 185 > 186 arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource); 187 188 return 0; 189 } 190
On Tue, Jul 19, 2022 at 01:26:06PM +0800, Shengjiu Wang wrote: Not a thorough review, just a few nitpicks: > +#define MBOX_SIZE 0x1000 > + > +struct arm_smccc_res smc_resource; This should be static shouldn't it? > +static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) > +{ > + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); > + unsigned long flags; > + > + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); > + > + imx8ulp_get_reply(priv->sdev); > + snd_sof_ipc_reply(priv->sdev, 0); > + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); Minor nitpick but a blank line before the unlock to match the one after the lock would be a bit easier to read. > + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); > + usleep_range(1, 2); > + > + arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource); You need linux/arm-smccc.h for this (as 0day said). > + if (ret) { > + dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret); > + goto exit_pdev_unregister; > + } > + > + priv->clks->dsp_clks = imx8ulp_dsp_clks; > + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8ulp_dsp_clks); > + > + ret = imx8_parse_clocks(sdev, priv->clks); > + if (ret < 0) > + goto exit_pdev_unregister; > + > + ret = imx8_enable_clocks(sdev, priv->clks); > + if (ret < 0) > + goto exit_pdev_unregister; We're registering the platform device before we enable the clocks - is that safe? > +static int imx8ulp_remove(struct snd_sof_dev *sdev) > +{ > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > + > + platform_device_unregister(priv->ipc_dev); > + > + return 0; > +} Could we just use devm? I'm not seeing an ordering issue but I might be missing something.
> +static void imx8ulp_get_reply(struct snd_sof_dev *sdev) > +{ > + struct snd_sof_ipc_msg *msg = sdev->msg; > + struct sof_ipc_reply reply; > + int ret = 0; > + > + if (!msg) { > + dev_warn(sdev->dev, "unexpected ipc interrupt\n"); > + return; > + } > + > + /* get reply */ > + sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); > + > + if (reply.error < 0) { > + memcpy(msg->reply_data, &reply, sizeof(reply)); > + ret = reply.error; > + } else { > + /* reply has correct size? */ > + if (reply.hdr.size != msg->reply_size) { > + dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", > + msg->reply_size, reply.hdr.size); > + ret = -EINVAL; > + } > + > + /* read the message */ > + if (msg->reply_size > 0) > + sof_mailbox_read(sdev, sdev->host_box.offset, > + msg->reply_data, msg->reply_size); > + } > + > + msg->reply_error = ret; > +} Can you double-check if this helper is needed? It looks completely generic and I vaguely remember that this get_reply() was moved to common code, and it's no longer in the other existing iMX support files. > + > +static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev) > +{ > + return MBOX_OFFSET; > +} > + > +static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id) > +{ > + return MBOX_OFFSET; > +} > + > +static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) > +{ > + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); > + unsigned long flags; > + > + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); > + > + imx8ulp_get_reply(priv->sdev); > + snd_sof_ipc_reply(priv->sdev, 0); > + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); this is old stuff that wasn't updated, we now have an inline that combines this: static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id) { snd_sof_ipc_get_reply(sdev); snd_sof_ipc_reply(sdev, msg_id); } see the code in im8xm.c.... > +} > + > +static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc) > +{ > + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); > + u32 p; /* panic code */ > + > + /* Read the message from the debug box. */ > + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); > + > + /* Check to see if the message is a panic code (0x0dead***) */ > + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) > + snd_sof_dsp_panic(priv->sdev, p, true); > + else > + snd_sof_ipc_msgs_rx(priv->sdev); > +} > + > +static struct imx_dsp_ops dsp_ops = { > + .handle_reply = imx8ulp_dsp_handle_reply, > + .handle_request = imx8ulp_dsp_handle_request, > +}; > + > +static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) > +{ > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > + > + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, > + msg->msg_size); > + imx_dsp_ring_doorbell(priv->dsp_ipc, 0); > + > + return 0; > +} > + > +static int imx8ulp_run(struct snd_sof_dev *sdev) > +{ > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > + > + imx8ulp_sim_lpav_start(priv); > + > + return 0; > +} > + > +static int imx8ulp_reset(struct snd_sof_dev *sdev) > +{ > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > + > + /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */ > + regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT); > + /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */ > + regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT); > + /* HiFi4 Clock Enable: 1 enabled, 0 disabled */ > + regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT); > + > + regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT); > + usleep_range(1, 2); > + /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ > + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); > + usleep_range(1, 2); adding newlines would help make the comments more readable, this is a bit of an eyesore. > + arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource); > + > + return 0; > +} > + > +static int imx8ulp_probe(struct snd_sof_dev *sdev) > +{ > + struct platform_device *pdev = > + container_of(sdev->dev, struct platform_device, dev); > + struct device_node *np = pdev->dev.of_node; > + struct device_node *res_node; > + struct resource *mmio; > + struct imx8ulp_priv *priv; > + struct resource res; > + u32 base, size; > + int ret = 0; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); > + if (!priv->clks) > + return -ENOMEM; > + > + sdev->num_cores = 1; > + sdev->pdata->hw_pdata = priv; > + priv->dev = sdev->dev; > + priv->sdev = sdev; > + > + /* System integration module(SIM) control dsp configurtion */ typo: configuration > + priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl"); > + if (IS_ERR(priv->regmap)) > + return PTR_ERR(priv->regmap); > + > + priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", > + PLATFORM_DEVID_NONE, > + pdev, sizeof(*pdev)); > + if (IS_ERR(priv->ipc_dev)) > + return PTR_ERR(priv->ipc_dev); > + > + priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); > + if (!priv->dsp_ipc) { > + /* DSP IPC driver not probed yet, try later */ > + ret = -EPROBE_DEFER; > + dev_err(sdev->dev, "Failed to get drvdata\n"); > + goto exit_pdev_unregister; > + } > + > + imx_dsp_set_data(priv->dsp_ipc, priv); > + priv->dsp_ipc->ops = &dsp_ops; > + > + /* DSP base */ > + mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (mmio) { > + base = mmio->start; > + size = resource_size(mmio); > + } else { > + dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n"); > + ret = -EINVAL; > + goto exit_pdev_unregister; > + } > + > + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); > + if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { > + dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", > + base, size); > + ret = -ENODEV; > + goto exit_pdev_unregister; > + } > + sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; > + > + res_node = of_parse_phandle(np, "memory-reserved", 0); > + if (!res_node) { > + dev_err(&pdev->dev, "failed to get memory region node\n"); > + ret = -ENODEV; > + goto exit_pdev_unregister; > + } > + > + ret = of_address_to_resource(res_node, 0, &res); > + if (ret) { > + dev_err(&pdev->dev, "failed to get reserved region address\n"); > + goto exit_pdev_unregister; > + } > + > + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, > + resource_size(&res)); > + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { > + dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", > + base, size); > + ret = -ENOMEM; > + goto exit_pdev_unregister; > + } > + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; > + > + /* set default mailbox offset for FW ready message */ > + sdev->dsp_box.offset = MBOX_OFFSET; > + > + ret = of_reserved_mem_device_init(sdev->dev); > + if (ret) { > + dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret); > + goto exit_pdev_unregister; > + } > + > + priv->clks->dsp_clks = imx8ulp_dsp_clks; > + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8ulp_dsp_clks); > + > + ret = imx8_parse_clocks(sdev, priv->clks); > + if (ret < 0) > + goto exit_pdev_unregister; > + > + ret = imx8_enable_clocks(sdev, priv->clks); > + if (ret < 0) > + goto exit_pdev_unregister; > + > + return 0; > + > +exit_pdev_unregister: > + platform_device_unregister(priv->ipc_dev); > + > + return ret; > +} > + > +static int imx8ulp_suspend(struct snd_sof_dev *sdev) > +{ > + int i; > + struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata; > + > + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); > + > + for (i = 0; i < DSP_MU_CHAN_NUM; i++) > + imx_dsp_free_channel(priv->dsp_ipc, i); > + > + imx8_disable_clocks(sdev, priv->clks); > + > + return 0; > +} > + > +static int imx8ulp_resume(struct snd_sof_dev *sdev) > +{ > + struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata; > + int i; > + > + imx8_enable_clocks(sdev, priv->clks); > + > + for (i = 0; i < DSP_MU_CHAN_NUM; i++) > + imx_dsp_request_channel(priv->dsp_ipc, i); > + > + return 0; is the assymetry between suspend and resume intentional? You are missing the update_bit for EXECUTE_BIT? > +} > +
On Tue, Jul 19, 2022 at 7:41 PM Mark Brown <broonie@kernel.org> wrote: > On Tue, Jul 19, 2022 at 01:26:06PM +0800, Shengjiu Wang wrote: > > Not a thorough review, just a few nitpicks: > Thanks. > > > +#define MBOX_SIZE 0x1000 > > + > > +struct arm_smccc_res smc_resource; > > This should be static shouldn't it? > I will move it to function as a local variable. > > > +static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) > > +{ > > + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); > > + unsigned long flags; > > + > > + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); > > + > > + imx8ulp_get_reply(priv->sdev); > > + snd_sof_ipc_reply(priv->sdev, 0); > > + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); > > Minor nitpick but a blank line before the unlock to match the one after > the lock would be a bit easier to read. > ok, will update > > > + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, > EXECUTE_BIT); > > + usleep_range(1, 2); > > + > > + arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, > &smc_resource); > > You need linux/arm-smccc.h for this (as 0day said). > Yes, right. > > > + if (ret) { > > + dev_err(&pdev->dev, "failed to init reserved memory region > %d\n", ret); > > + goto exit_pdev_unregister; > > + } > > + > > + priv->clks->dsp_clks = imx8ulp_dsp_clks; > > + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8ulp_dsp_clks); > > + > > + ret = imx8_parse_clocks(sdev, priv->clks); > > + if (ret < 0) > > + goto exit_pdev_unregister; > > + > > + ret = imx8_enable_clocks(sdev, priv->clks); > > + if (ret < 0) > > + goto exit_pdev_unregister; > > We're registering the platform device before we enable the clocks - is > that safe? > Yes, it is safe. > > > +static int imx8ulp_remove(struct snd_sof_dev *sdev) > > +{ > > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > > + > > + platform_device_unregister(priv->ipc_dev); > > + > > + return 0; > > +} > > Could we just use devm? I'm not seeing an ordering issue but I might be > missing something. > Which devm do you mean? There seems no devm_platform_device_register_data(). best regards wang shengjiu
On Tue, Jul 19, 2022 at 10:19 PM Pierre-Louis Bossart < pierre-louis.bossart@linux.intel.com> wrote: > > > +static void imx8ulp_get_reply(struct snd_sof_dev *sdev) > > +{ > > + struct snd_sof_ipc_msg *msg = sdev->msg; > > + struct sof_ipc_reply reply; > > + int ret = 0; > > + > > + if (!msg) { > > + dev_warn(sdev->dev, "unexpected ipc interrupt\n"); > > + return; > > + } > > + > > + /* get reply */ > > + sof_mailbox_read(sdev, sdev->host_box.offset, &reply, > sizeof(reply)); > > + > > + if (reply.error < 0) { > > + memcpy(msg->reply_data, &reply, sizeof(reply)); > > + ret = reply.error; > > + } else { > > + /* reply has correct size? */ > > + if (reply.hdr.size != msg->reply_size) { > > + dev_err(sdev->dev, "error: reply expected %zu got > %u bytes\n", > > + msg->reply_size, reply.hdr.size); > > + ret = -EINVAL; > > + } > > + > > + /* read the message */ > > + if (msg->reply_size > 0) > > + sof_mailbox_read(sdev, sdev->host_box.offset, > > + msg->reply_data, msg->reply_size); > > + } > > + > > + msg->reply_error = ret; > > +} > > Can you double-check if this helper is needed? It looks completely > generic and I vaguely remember that this get_reply() was moved to common > code, and it's no longer in the other existing iMX support files. > ok, I will use the latest interface. > > > + > > +static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev) > > +{ > > + return MBOX_OFFSET; > > +} > > + > > +static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id) > > +{ > > + return MBOX_OFFSET; > > +} > > + > > +static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) > > +{ > > + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); > > + unsigned long flags; > > + > > + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); > > + > > + imx8ulp_get_reply(priv->sdev); > > + snd_sof_ipc_reply(priv->sdev, 0); > > + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); > > this is old stuff that wasn't updated, we now have an inline that > combines this: > > static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, > u32 msg_id) > { > snd_sof_ipc_get_reply(sdev); > snd_sof_ipc_reply(sdev, msg_id); > } > > see the code in im8xm.c.... > ok, I will use the latest interface. > > > +} > > + > > +static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc) > > +{ > > + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); > > + u32 p; /* panic code */ > > + > > + /* Read the message from the debug box. */ > > + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, > sizeof(p)); > > + > > + /* Check to see if the message is a panic code (0x0dead***) */ > > + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) > > + snd_sof_dsp_panic(priv->sdev, p, true); > > + else > > + snd_sof_ipc_msgs_rx(priv->sdev); > > +} > > + > > +static struct imx_dsp_ops dsp_ops = { > > + .handle_reply = imx8ulp_dsp_handle_reply, > > + .handle_request = imx8ulp_dsp_handle_request, > > +}; > > + > > +static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct > snd_sof_ipc_msg *msg) > > +{ > > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > > + > > + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, > > + msg->msg_size); > > + imx_dsp_ring_doorbell(priv->dsp_ipc, 0); > > + > > + return 0; > > +} > > + > > +static int imx8ulp_run(struct snd_sof_dev *sdev) > > +{ > > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > > + > > + imx8ulp_sim_lpav_start(priv); > > + > > + return 0; > > +} > > + > > +static int imx8ulp_reset(struct snd_sof_dev *sdev) > > +{ > > + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; > > + > > + /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */ > > + regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, > PLAT_CLK_BIT); > > + /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */ > > + regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT); > > + /* HiFi4 Clock Enable: 1 enabled, 0 disabled */ > > + regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, > HIFI4_CLK_BIT); > > + > > + regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT); > > + usleep_range(1, 2); > > + /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ > > + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, > EXECUTE_BIT); > > + usleep_range(1, 2); > > > adding newlines would help make the comments more readable, this is a > bit of an eyesore. > ok. > > > + arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, > &smc_resource); > > + > > + return 0; > > +} > > + > > +static int imx8ulp_probe(struct snd_sof_dev *sdev) > > +{ > > + struct platform_device *pdev = > > + container_of(sdev->dev, struct platform_device, dev); > > + struct device_node *np = pdev->dev.of_node; > > + struct device_node *res_node; > > + struct resource *mmio; > > + struct imx8ulp_priv *priv; > > + struct resource res; > > + u32 base, size; > > + int ret = 0; > > + > > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > > + if (!priv) > > + return -ENOMEM; > > + > > + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), > GFP_KERNEL); > > + if (!priv->clks) > > + return -ENOMEM; > > + > > + sdev->num_cores = 1; > > + sdev->pdata->hw_pdata = priv; > > + priv->dev = sdev->dev; > > + priv->sdev = sdev; > > + > > + /* System integration module(SIM) control dsp configurtion */ > > typo: configuration > > Yes, I will fix it. > > + priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl"); > > + if (IS_ERR(priv->regmap)) > > + return PTR_ERR(priv->regmap); > > + > > + priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", > > + PLATFORM_DEVID_NONE, > > + pdev, sizeof(*pdev)); > > + if (IS_ERR(priv->ipc_dev)) > > + return PTR_ERR(priv->ipc_dev); > > + > > + priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); > > + if (!priv->dsp_ipc) { > > + /* DSP IPC driver not probed yet, try later */ > > + ret = -EPROBE_DEFER; > > + dev_err(sdev->dev, "Failed to get drvdata\n"); > > + goto exit_pdev_unregister; > > + } > > + > > + imx_dsp_set_data(priv->dsp_ipc, priv); > > + priv->dsp_ipc->ops = &dsp_ops; > > + > > + /* DSP base */ > > + mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (mmio) { > > + base = mmio->start; > > + size = resource_size(mmio); > > + } else { > > + dev_err(sdev->dev, "error: failed to get DSP base at idx > 0\n"); > > + ret = -EINVAL; > > + goto exit_pdev_unregister; > > + } > > + > > + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, > size); > > + if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { > > + dev_err(sdev->dev, "failed to ioremap base 0x%x size > 0x%x\n", > > + base, size); > > + ret = -ENODEV; > > + goto exit_pdev_unregister; > > + } > > + sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; > > + > > + res_node = of_parse_phandle(np, "memory-reserved", 0); > > + if (!res_node) { > > + dev_err(&pdev->dev, "failed to get memory region node\n"); > > + ret = -ENODEV; > > + goto exit_pdev_unregister; > > + } > > + > > + ret = of_address_to_resource(res_node, 0, &res); > > + if (ret) { > > + dev_err(&pdev->dev, "failed to get reserved region > address\n"); > > + goto exit_pdev_unregister; > > + } > > + > > + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, > res.start, > > + > resource_size(&res)); > > + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { > > + dev_err(sdev->dev, "failed to ioremap mem 0x%x size > 0x%x\n", > > + base, size); > > + ret = -ENOMEM; > > + goto exit_pdev_unregister; > > + } > > + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; > > + > > + /* set default mailbox offset for FW ready message */ > > + sdev->dsp_box.offset = MBOX_OFFSET; > > + > > + ret = of_reserved_mem_device_init(sdev->dev); > > + if (ret) { > > + dev_err(&pdev->dev, "failed to init reserved memory region > %d\n", ret); > > + goto exit_pdev_unregister; > > + } > > + > > + priv->clks->dsp_clks = imx8ulp_dsp_clks; > > + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8ulp_dsp_clks); > > + > > + ret = imx8_parse_clocks(sdev, priv->clks); > > + if (ret < 0) > > + goto exit_pdev_unregister; > > + > > + ret = imx8_enable_clocks(sdev, priv->clks); > > + if (ret < 0) > > + goto exit_pdev_unregister; > > + > > + return 0; > > + > > +exit_pdev_unregister: > > + platform_device_unregister(priv->ipc_dev); > > + > > + return ret; > > +} > > + > > > +static int imx8ulp_suspend(struct snd_sof_dev *sdev) > > +{ > > + int i; > > + struct imx8ulp_priv *priv = (struct imx8ulp_priv > *)sdev->pdata->hw_pdata; > > + > > + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, > EXECUTE_BIT); > > + > > + for (i = 0; i < DSP_MU_CHAN_NUM; i++) > > + imx_dsp_free_channel(priv->dsp_ipc, i); > > + > > + imx8_disable_clocks(sdev, priv->clks); > > + > > + return 0; > > +} > > + > > +static int imx8ulp_resume(struct snd_sof_dev *sdev) > > +{ > > + struct imx8ulp_priv *priv = (struct imx8ulp_priv > *)sdev->pdata->hw_pdata; > > + int i; > > + > > + imx8_enable_clocks(sdev, priv->clks); > > + > > + for (i = 0; i < DSP_MU_CHAN_NUM; i++) > > + imx_dsp_request_channel(priv->dsp_ipc, i); > > + > > + return 0; > > is the assymetry between suspend and resume intentional? You are missing > the update_bit for EXECUTE_BIT? > Yes, intentional. After resume the firmware is reloaded and EXECUTE_BIT will be updated at trigger DSP start. best regards wang shengjiu > > > +} > > + > > >
On 7/20/22 03:57, Shengjiu Wang wrote: > > +static int imx8ulp_resume(struct snd_sof_dev *sdev) > > +{ > > + struct imx8ulp_priv *priv = (struct imx8ulp_priv > *)sdev->pdata->hw_pdata; > > + int i; > > + > > + imx8_enable_clocks(sdev, priv->clks); > > + > > + for (i = 0; i < DSP_MU_CHAN_NUM; i++) > > + imx_dsp_request_channel(priv->dsp_ipc, i); > > + > > + return 0; > > is the assymetry between suspend and resume intentional? You are missing > the update_bit for EXECUTE_BIT? > > > Yes, intentional. After resume the firmware is reloaded and EXECUTE_BIT > will be updated at trigger DSP start. That's worthy of a comment to help reviewers, thanks.
On Wed, Jul 20, 2022 at 9:25 PM Pierre-Louis Bossart < pierre-louis.bossart@linux.intel.com> wrote: > > > On 7/20/22 03:57, Shengjiu Wang wrote: > > > +static int imx8ulp_resume(struct snd_sof_dev *sdev) > > > +{ > > > + struct imx8ulp_priv *priv = (struct imx8ulp_priv > > *)sdev->pdata->hw_pdata; > > > + int i; > > > + > > > + imx8_enable_clocks(sdev, priv->clks); > > > + > > > + for (i = 0; i < DSP_MU_CHAN_NUM; i++) > > > + imx_dsp_request_channel(priv->dsp_ipc, i); > > > + > > > + return 0; > > > > is the assymetry between suspend and resume intentional? You are > missing > > the update_bit for EXECUTE_BIT? > > > > > > Yes, intentional. After resume the firmware is reloaded and EXECUTE_BIT > > will be updated at trigger DSP start. > > That's worthy of a comment to help reviewers, thanks. > ok, thanks. best regards wang shengjiu
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index cc6e695f913a..4751b04d5e6f 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -41,4 +41,13 @@ config SND_SOC_SOF_IMX8M Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_IMX8ULP + tristate "SOF support for i.MX8ULP" + depends on IMX_DSP + select SND_SOC_SOF_IMX_COMMON + help + This adds support for Sound Open Firmware for NXP i.MX8ULP platforms. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_SOF_IMX_TOPLEVEL diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile index dba93c3466ec..798b43a415bf 100644 --- a/sound/soc/sof/imx/Makefile +++ b/sound/soc/sof/imx/Makefile @@ -1,9 +1,11 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-imx8-objs := imx8.o snd-sof-imx8m-objs := imx8m.o +snd-sof-imx8ulp-objs := imx8ulp.o snd-sof-imx-common-objs := imx-common.o obj-$(CONFIG_SND_SOC_SOF_IMX8) += snd-sof-imx8.o obj-$(CONFIG_SND_SOC_SOF_IMX8M) += snd-sof-imx8m.o +obj-$(CONFIG_SND_SOC_SOF_IMX8ULP) += snd-sof-imx8ulp.o obj-$(CONFIG_SND_SOC_SOF_IMX_COMMON) += imx-common.o diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c new file mode 100644 index 000000000000..ecb8750de7ea --- /dev/null +++ b/sound/soc/sof/imx/imx8ulp.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright 2021-2022 NXP +// +// Author: Peng Zhang <peng.zhang_8@nxp.com> +// +// Hardware interface for audio DSP on i.MX8ULP + +#include <linux/clk.h> +#include <linux/firmware.h> +#include <linux/firmware/imx/dsp.h> +#include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/svc/misc.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> + +#include <sound/sof.h> +#include <sound/sof/xtensa.h> + +#include "../ops.h" +#include "../sof-of-dev.h" +#include "imx-common.h" + +#define FSL_SIP_HIFI_XRDC 0xc200000e + +/* SIM Domain register */ +#define SYSCTRL0 0x8 +#define EXECUTE_BIT BIT(13) +#define RESET_BIT BIT(16) +#define HIFI4_CLK_BIT BIT(17) +#define PB_CLK_BIT BIT(18) +#define PLAT_CLK_BIT BIT(19) +#define DEBUG_LOGIC_BIT BIT(25) + +#define MBOX_OFFSET 0x800000 +#define MBOX_SIZE 0x1000 + +struct arm_smccc_res smc_resource; + +static struct clk_bulk_data imx8ulp_dsp_clks[] = { + { .id = "core" }, + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "mu" }, +}; + +struct imx8ulp_priv { + struct device *dev; + struct snd_sof_dev *sdev; + + /* DSP IPC handler */ + struct imx_dsp_ipc *dsp_ipc; + struct platform_device *ipc_dev; + + struct regmap *regmap; + struct imx_clocks *clks; +}; + +static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv) +{ + /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */ + regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0); + /* Reset HiFi4 DSP Debug logic: 1 reset, 0 not set */ + regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0); + /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0); +} + +static void imx8ulp_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + int ret = 0; + + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt\n"); + return; + } + + /* get reply */ + sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else { + /* reply has correct size? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", + msg->reply_size, reply.hdr.size); + ret = -EINVAL; + } + + /* read the message */ + if (msg->reply_size > 0) + sof_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + msg->reply_error = ret; +} + +static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev) +{ + return MBOX_OFFSET; +} + +static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id) +{ + return MBOX_OFFSET; +} + +static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) +{ + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); + unsigned long flags; + + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); + + imx8ulp_get_reply(priv->sdev); + snd_sof_ipc_reply(priv->sdev, 0); + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); +} + +static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc) +{ + struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); + u32 p; /* panic code */ + + /* Read the message from the debug box. */ + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); + + /* Check to see if the message is a panic code (0x0dead***) */ + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) + snd_sof_dsp_panic(priv->sdev, p, true); + else + snd_sof_ipc_msgs_rx(priv->sdev); +} + +static struct imx_dsp_ops dsp_ops = { + .handle_reply = imx8ulp_dsp_handle_reply, + .handle_request = imx8ulp_dsp_handle_request, +}; + +static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; + + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + imx_dsp_ring_doorbell(priv->dsp_ipc, 0); + + return 0; +} + +static int imx8ulp_run(struct snd_sof_dev *sdev) +{ + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; + + imx8ulp_sim_lpav_start(priv); + + return 0; +} + +static int imx8ulp_reset(struct snd_sof_dev *sdev) +{ + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; + + /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */ + regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT); + /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */ + regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT); + /* HiFi4 Clock Enable: 1 enabled, 0 disabled */ + regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT); + + regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT); + usleep_range(1, 2); + /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); + usleep_range(1, 2); + + arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource); + + return 0; +} + +static int imx8ulp_probe(struct snd_sof_dev *sdev) +{ + struct platform_device *pdev = + container_of(sdev->dev, struct platform_device, dev); + struct device_node *np = pdev->dev.of_node; + struct device_node *res_node; + struct resource *mmio; + struct imx8ulp_priv *priv; + struct resource res; + u32 base, size; + int ret = 0; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + sdev->num_cores = 1; + sdev->pdata->hw_pdata = priv; + priv->dev = sdev->dev; + priv->sdev = sdev; + + /* System integration module(SIM) control dsp configurtion */ + priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl"); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", + PLATFORM_DEVID_NONE, + pdev, sizeof(*pdev)); + if (IS_ERR(priv->ipc_dev)) + return PTR_ERR(priv->ipc_dev); + + priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); + if (!priv->dsp_ipc) { + /* DSP IPC driver not probed yet, try later */ + ret = -EPROBE_DEFER; + dev_err(sdev->dev, "Failed to get drvdata\n"); + goto exit_pdev_unregister; + } + + imx_dsp_set_data(priv->dsp_ipc, priv); + priv->dsp_ipc->ops = &dsp_ops; + + /* DSP base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n"); + ret = -EINVAL; + goto exit_pdev_unregister; + } + + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); + if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { + dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto exit_pdev_unregister; + } + sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; + + res_node = of_parse_phandle(np, "memory-reserved", 0); + if (!res_node) { + dev_err(&pdev->dev, "failed to get memory region node\n"); + ret = -ENODEV; + goto exit_pdev_unregister; + } + + ret = of_address_to_resource(res_node, 0, &res); + if (ret) { + dev_err(&pdev->dev, "failed to get reserved region address\n"); + goto exit_pdev_unregister; + } + + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, + resource_size(&res)); + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { + dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", + base, size); + ret = -ENOMEM; + goto exit_pdev_unregister; + } + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + ret = of_reserved_mem_device_init(sdev->dev); + if (ret) { + dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret); + goto exit_pdev_unregister; + } + + priv->clks->dsp_clks = imx8ulp_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8ulp_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + return 0; + +exit_pdev_unregister: + platform_device_unregister(priv->ipc_dev); + + return ret; +} + +static int imx8ulp_remove(struct snd_sof_dev *sdev) +{ + struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; + + platform_device_unregister(priv->ipc_dev); + + return 0; +} + +/* on i.MX8 there is 1 to 1 match between type and BAR idx */ +static int imx8ulp_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} + +static int imx8ulp_suspend(struct snd_sof_dev *sdev) +{ + int i; + struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata; + + regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); + + return 0; +} + +static int imx8ulp_resume(struct snd_sof_dev *sdev) +{ + struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata; + int i; + + imx8_enable_clocks(sdev, priv->clks); + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + + return 0; +} + +static int imx8ulp_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + .substate = 0, + }; + + imx8ulp_resume(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8ulp_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + .substate = 0, + }; + + imx8ulp_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8ulp_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + .substate = 0, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8ulp_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8ulp_dsp_resume(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + .substate = 0, + }; + + imx8ulp_resume(sdev); + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static struct snd_soc_dai_driver imx8ulp_dai[] = { + { + .name = "sai5", + .playback = { + .channels_min = 1, + .channels_max = 32, + }, + .capture = { + .channels_min = 1, + .channels_max = 32, + }, + }, + { + .name = "sai6", + .playback = { + .channels_min = 1, + .channels_max = 32, + }, + .capture = { + .channels_min = 1, + .channels_max = 32, + }, + }, +}; + +static int imx8ulp_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + +/* i.MX8 ops */ +struct snd_sof_dsp_ops sof_imx8ulp_ops = { + /* probe and remove */ + .probe = imx8ulp_probe, + .remove = imx8ulp_remove, + /* DSP core boot */ + .run = imx8ulp_run, + .reset = imx8ulp_reset, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* Module IO */ + .read64 = sof_io_read64, + + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + + /* ipc */ + .send_msg = imx8ulp_send_msg, + .get_mailbox_offset = imx8ulp_get_mailbox_offset, + .get_window_offset = imx8ulp_get_window_offset, + + .ipc_msg_data = sof_ipc_msg_data, + .set_stream_data_offset = sof_set_stream_data_offset, + + /* stream callbacks */ + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + /* module loading */ + .get_bar_index = imx8ulp_get_bar_index, + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* Debug information */ + .dbg_dump = imx8_dump, + + /* Firmware ops */ + .dsp_arch_ops = &sof_xtensa_arch_ops, + + /* DAI drivers */ + .drv = imx8ulp_dai, + .num_drv = ARRAY_SIZE(imx8ulp_dai), + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + /* PM */ + .runtime_suspend = imx8ulp_dsp_runtime_suspend, + .runtime_resume = imx8ulp_dsp_runtime_resume, + + .suspend = imx8ulp_dsp_suspend, + .resume = imx8ulp_dsp_resume, + + .set_power_state = imx8ulp_dsp_set_power_state, +}; + +static struct sof_dev_desc sof_of_imx8ulp_desc = { + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "imx/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "imx/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-imx8ulp.ri", + }, + .nocodec_tplg_filename = "sof-imx8ulp-nocodec.tplg", + .ops = &sof_imx8ulp_ops, +}; + +static const struct of_device_id sof_of_imx8ulp_ids[] = { + { .compatible = "fsl,imx8ulp-dsp", .data = &sof_of_imx8ulp_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8ulp_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8ulp_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8ulp", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8ulp_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8ulp_driver); + +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_LICENSE("Dual BSD/GPL");