Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license.  When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2018-2021 Intel Corporation. All rights reserved.
0007 //
0008 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
0009 //
0010 
0011 /*
0012  * Hardware interface for audio DSP on Atom devices
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <sound/sof.h>
0017 #include <sound/sof/xtensa.h>
0018 #include <sound/soc-acpi.h>
0019 #include <sound/soc-acpi-intel-match.h>
0020 #include <sound/intel-dsp-config.h>
0021 #include "../ops.h"
0022 #include "shim.h"
0023 #include "atom.h"
0024 #include "../sof-acpi-dev.h"
0025 #include "../sof-audio.h"
0026 #include "../../intel/common/soc-intel-quirks.h"
0027 
0028 static void atom_host_done(struct snd_sof_dev *sdev);
0029 static void atom_dsp_done(struct snd_sof_dev *sdev);
0030 
0031 /*
0032  * Debug
0033  */
0034 
0035 static void atom_get_registers(struct snd_sof_dev *sdev,
0036                    struct sof_ipc_dsp_oops_xtensa *xoops,
0037                    struct sof_ipc_panic_info *panic_info,
0038                    u32 *stack, size_t stack_words)
0039 {
0040     u32 offset = sdev->dsp_oops_offset;
0041 
0042     /* first read regsisters */
0043     sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
0044 
0045     /* note: variable AR register array is not read */
0046 
0047     /* then get panic info */
0048     if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
0049         dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
0050             xoops->arch_hdr.totalsize);
0051         return;
0052     }
0053     offset += xoops->arch_hdr.totalsize;
0054     sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
0055 
0056     /* then get the stack */
0057     offset += sizeof(*panic_info);
0058     sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
0059 }
0060 
0061 void atom_dump(struct snd_sof_dev *sdev, u32 flags)
0062 {
0063     struct sof_ipc_dsp_oops_xtensa xoops;
0064     struct sof_ipc_panic_info panic_info;
0065     u32 stack[STACK_DUMP_SIZE];
0066     u64 status, panic, imrd, imrx;
0067 
0068     /* now try generic SOF status messages */
0069     status = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
0070     panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
0071     atom_get_registers(sdev, &xoops, &panic_info, stack,
0072                STACK_DUMP_SIZE);
0073     sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
0074                  &panic_info, stack, STACK_DUMP_SIZE);
0075 
0076     /* provide some context for firmware debug */
0077     imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);
0078     imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD);
0079     dev_err(sdev->dev,
0080         "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
0081         (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
0082         (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
0083     dev_err(sdev->dev,
0084         "error: mask host: pending %s complete %s raw 0x%llx\n",
0085         (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
0086         (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
0087     dev_err(sdev->dev,
0088         "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
0089         (status & SHIM_IPCD_BUSY) ? "yes" : "no",
0090         (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
0091     dev_err(sdev->dev,
0092         "error: mask DSP: pending %s complete %s raw 0x%llx\n",
0093         (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
0094         (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
0095 
0096 }
0097 EXPORT_SYMBOL_NS(atom_dump, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0098 
0099 /*
0100  * IPC Doorbell IRQ handler and thread.
0101  */
0102 
0103 irqreturn_t atom_irq_handler(int irq, void *context)
0104 {
0105     struct snd_sof_dev *sdev = context;
0106     u64 ipcx, ipcd;
0107     int ret = IRQ_NONE;
0108 
0109     ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
0110     ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
0111 
0112     if (ipcx & SHIM_BYT_IPCX_DONE) {
0113 
0114         /* reply message from DSP, Mask Done interrupt first */
0115         snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
0116                            SHIM_IMRX,
0117                            SHIM_IMRX_DONE,
0118                            SHIM_IMRX_DONE);
0119         ret = IRQ_WAKE_THREAD;
0120     }
0121 
0122     if (ipcd & SHIM_BYT_IPCD_BUSY) {
0123 
0124         /* new message from DSP, Mask Busy interrupt first */
0125         snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
0126                            SHIM_IMRX,
0127                            SHIM_IMRX_BUSY,
0128                            SHIM_IMRX_BUSY);
0129         ret = IRQ_WAKE_THREAD;
0130     }
0131 
0132     return ret;
0133 }
0134 EXPORT_SYMBOL_NS(atom_irq_handler, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0135 
0136 irqreturn_t atom_irq_thread(int irq, void *context)
0137 {
0138     struct snd_sof_dev *sdev = context;
0139     u64 ipcx, ipcd;
0140 
0141     ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
0142     ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
0143 
0144     /* reply message from DSP */
0145     if (ipcx & SHIM_BYT_IPCX_DONE) {
0146 
0147         spin_lock_irq(&sdev->ipc_lock);
0148 
0149         /*
0150          * handle immediate reply from DSP core. If the msg is
0151          * found, set done bit in cmd_done which is called at the
0152          * end of message processing function, else set it here
0153          * because the done bit can't be set in cmd_done function
0154          * which is triggered by msg
0155          */
0156         snd_sof_ipc_process_reply(sdev, ipcx);
0157 
0158         atom_dsp_done(sdev);
0159 
0160         spin_unlock_irq(&sdev->ipc_lock);
0161     }
0162 
0163     /* new message from DSP */
0164     if (ipcd & SHIM_BYT_IPCD_BUSY) {
0165 
0166         /* Handle messages from DSP Core */
0167         if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
0168             snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET,
0169                       true);
0170         } else {
0171             snd_sof_ipc_msgs_rx(sdev);
0172         }
0173 
0174         atom_host_done(sdev);
0175     }
0176 
0177     return IRQ_HANDLED;
0178 }
0179 EXPORT_SYMBOL_NS(atom_irq_thread, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0180 
0181 int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
0182 {
0183     /* unmask and prepare to receive Done interrupt */
0184     snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
0185                        SHIM_IMRX_DONE, 0);
0186 
0187     /* send the message */
0188     sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
0189               msg->msg_size);
0190     snd_sof_dsp_write64(sdev, DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
0191 
0192     return 0;
0193 }
0194 EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0195 
0196 int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
0197 {
0198     return MBOX_OFFSET;
0199 }
0200 EXPORT_SYMBOL_NS(atom_get_mailbox_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0201 
0202 int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id)
0203 {
0204     return MBOX_OFFSET;
0205 }
0206 EXPORT_SYMBOL_NS(atom_get_window_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0207 
0208 static void atom_host_done(struct snd_sof_dev *sdev)
0209 {
0210     /* clear BUSY bit and set DONE bit - accept new messages */
0211     snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCD,
0212                        SHIM_BYT_IPCD_BUSY |
0213                        SHIM_BYT_IPCD_DONE,
0214                        SHIM_BYT_IPCD_DONE);
0215 
0216     /* unmask and prepare to receive next new message */
0217     snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
0218                        SHIM_IMRX_BUSY, 0);
0219 }
0220 
0221 static void atom_dsp_done(struct snd_sof_dev *sdev)
0222 {
0223     /* clear DONE bit - tell DSP we have completed */
0224     snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCX,
0225                        SHIM_BYT_IPCX_DONE, 0);
0226 }
0227 
0228 /*
0229  * DSP control.
0230  */
0231 
0232 int atom_run(struct snd_sof_dev *sdev)
0233 {
0234     int tries = 10;
0235 
0236     /* release stall and wait to unstall */
0237     snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
0238                   SHIM_BYT_CSR_STALL, 0x0);
0239     while (tries--) {
0240         if (!(snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_CSR) &
0241               SHIM_BYT_CSR_PWAITMODE))
0242             break;
0243         msleep(100);
0244     }
0245     if (tries < 0)
0246         return -ENODEV;
0247 
0248     /* return init core mask */
0249     return 1;
0250 }
0251 EXPORT_SYMBOL_NS(atom_run, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0252 
0253 int atom_reset(struct snd_sof_dev *sdev)
0254 {
0255     /* put DSP into reset, set reset vector and stall */
0256     snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
0257                   SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
0258                   SHIM_BYT_CSR_STALL,
0259                   SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
0260                   SHIM_BYT_CSR_STALL);
0261 
0262     usleep_range(10, 15);
0263 
0264     /* take DSP out of reset and keep stalled for FW loading */
0265     snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
0266                   SHIM_BYT_CSR_RST, 0);
0267 
0268     return 0;
0269 }
0270 EXPORT_SYMBOL_NS(atom_reset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0271 
0272 static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
0273                    const char *sof_tplg_filename,
0274                    const char *ssp_str)
0275 {
0276     const char *tplg_filename = NULL;
0277     const char *split_ext;
0278     char *filename, *tmp;
0279 
0280     filename = kstrdup(sof_tplg_filename, GFP_KERNEL);
0281     if (!filename)
0282         return NULL;
0283 
0284     /* this assumes a .tplg extension */
0285     tmp = filename;
0286     split_ext = strsep(&tmp, ".");
0287     if (split_ext)
0288         tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
0289                            "%s-%s.tplg",
0290                            split_ext, ssp_str);
0291     kfree(filename);
0292 
0293     return tplg_filename;
0294 }
0295 
0296 struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev)
0297 {
0298     struct snd_sof_pdata *sof_pdata = sdev->pdata;
0299     const struct sof_dev_desc *desc = sof_pdata->desc;
0300     struct snd_soc_acpi_mach *mach;
0301     struct platform_device *pdev;
0302     const char *tplg_filename;
0303 
0304     mach = snd_soc_acpi_find_machine(desc->machines);
0305     if (!mach) {
0306         dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
0307         return NULL;
0308     }
0309 
0310     pdev = to_platform_device(sdev->dev);
0311     if (soc_intel_is_byt_cr(pdev)) {
0312         dev_dbg(sdev->dev,
0313             "BYT-CR detected, SSP0 used instead of SSP2\n");
0314 
0315         tplg_filename = fixup_tplg_name(sdev,
0316                         mach->sof_tplg_filename,
0317                         "ssp0");
0318     } else {
0319         tplg_filename = mach->sof_tplg_filename;
0320     }
0321 
0322     if (!tplg_filename) {
0323         dev_dbg(sdev->dev,
0324             "error: no topology filename\n");
0325         return NULL;
0326     }
0327 
0328     sof_pdata->tplg_filename = tplg_filename;
0329     mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
0330 
0331     return mach;
0332 }
0333 EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0334 
0335 /* Atom DAIs */
0336 struct snd_soc_dai_driver atom_dai[] = {
0337 {
0338     .name = "ssp0-port",
0339     .playback = {
0340         .channels_min = 1,
0341         .channels_max = 8,
0342     },
0343     .capture = {
0344         .channels_min = 1,
0345         .channels_max = 8,
0346     },
0347 },
0348 {
0349     .name = "ssp1-port",
0350     .playback = {
0351         .channels_min = 1,
0352         .channels_max = 8,
0353     },
0354     .capture = {
0355         .channels_min = 1,
0356         .channels_max = 8,
0357     },
0358 },
0359 {
0360     .name = "ssp2-port",
0361     .playback = {
0362         .channels_min = 1,
0363         .channels_max = 8,
0364     },
0365     .capture = {
0366         .channels_min = 1,
0367         .channels_max = 8,
0368     }
0369 },
0370 {
0371     .name = "ssp3-port",
0372     .playback = {
0373         .channels_min = 1,
0374         .channels_max = 8,
0375     },
0376     .capture = {
0377         .channels_min = 1,
0378         .channels_max = 8,
0379     },
0380 },
0381 {
0382     .name = "ssp4-port",
0383     .playback = {
0384         .channels_min = 1,
0385         .channels_max = 8,
0386     },
0387     .capture = {
0388         .channels_min = 1,
0389         .channels_max = 8,
0390     },
0391 },
0392 {
0393     .name = "ssp5-port",
0394     .playback = {
0395         .channels_min = 1,
0396         .channels_max = 8,
0397     },
0398     .capture = {
0399         .channels_min = 1,
0400         .channels_max = 8,
0401     },
0402 },
0403 };
0404 EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0405 
0406 void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
0407               struct snd_sof_dev *sdev)
0408 {
0409     struct snd_sof_pdata *pdata = sdev->pdata;
0410     const struct sof_dev_desc *desc = pdata->desc;
0411     struct snd_soc_acpi_mach_params *mach_params;
0412 
0413     mach_params = &mach->mach_params;
0414     mach_params->platform = dev_name(sdev->dev);
0415     mach_params->num_dai_drivers = desc->ops->num_drv;
0416     mach_params->dai_drivers = desc->ops->drv;
0417 }
0418 EXPORT_SYMBOL_NS(atom_set_mach_params, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
0419 
0420 MODULE_LICENSE("Dual BSD/GPL");