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 Intel Corporation. All rights reserved.
0007 //
0008 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
0009 //
0010 // Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided
0011 // by platform driver code.
0012 //
0013 
0014 #include <linux/mutex.h>
0015 #include <linux/types.h>
0016 
0017 #include "sof-priv.h"
0018 #include "sof-audio.h"
0019 #include "ops.h"
0020 
0021 /**
0022  * sof_ipc_send_msg - generic function to prepare and send one IPC message
0023  * @sdev:       pointer to SOF core device struct
0024  * @msg_data:       pointer to a message to send
0025  * @msg_bytes:      number of bytes in the message
0026  * @reply_bytes:    number of bytes available for the reply.
0027  *          The buffer for the reply data is not passed to this
0028  *          function, the available size is an information for the
0029  *          reply handling functions.
0030  *
0031  * On success the function returns 0, otherwise negative error number.
0032  *
0033  * Note: higher level sdev->ipc->tx_mutex must be held to make sure that
0034  *   transfers are synchronized.
0035  */
0036 int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
0037              size_t reply_bytes)
0038 {
0039     struct snd_sof_ipc *ipc = sdev->ipc;
0040     struct snd_sof_ipc_msg *msg;
0041     int ret;
0042 
0043     if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE)
0044         return -ENODEV;
0045 
0046     /*
0047      * The spin-lock is needed to protect message objects against other
0048      * atomic contexts.
0049      */
0050     spin_lock_irq(&sdev->ipc_lock);
0051 
0052     /* initialise the message */
0053     msg = &ipc->msg;
0054 
0055     /* attach message data */
0056     msg->msg_data = msg_data;
0057     msg->msg_size = msg_bytes;
0058 
0059     msg->reply_size = reply_bytes;
0060     msg->reply_error = 0;
0061 
0062     sdev->msg = msg;
0063 
0064     ret = snd_sof_dsp_send_msg(sdev, msg);
0065     /* Next reply that we receive will be related to this message */
0066     if (!ret)
0067         msg->ipc_complete = false;
0068 
0069     spin_unlock_irq(&sdev->ipc_lock);
0070 
0071     return ret;
0072 }
0073 
0074 /* send IPC message from host to DSP */
0075 int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
0076                void *reply_data, size_t reply_bytes)
0077 {
0078     if (msg_bytes > ipc->max_payload_size ||
0079         reply_bytes > ipc->max_payload_size)
0080         return -ENOBUFS;
0081 
0082     return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
0083                 reply_bytes, false);
0084 }
0085 EXPORT_SYMBOL(sof_ipc_tx_message);
0086 
0087 /*
0088  * send IPC message from host to DSP without modifying the DSP state.
0089  * This will be used for IPC's that can be handled by the DSP
0090  * even in a low-power D0 substate.
0091  */
0092 int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
0093                  void *reply_data, size_t reply_bytes)
0094 {
0095     if (msg_bytes > ipc->max_payload_size ||
0096         reply_bytes > ipc->max_payload_size)
0097         return -ENOBUFS;
0098 
0099     return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
0100                 reply_bytes, true);
0101 }
0102 EXPORT_SYMBOL(sof_ipc_tx_message_no_pm);
0103 
0104 /* Generic helper function to retrieve the reply */
0105 void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev)
0106 {
0107     /*
0108      * Sometimes, there is unexpected reply ipc arriving. The reply
0109      * ipc belongs to none of the ipcs sent from driver.
0110      * In this case, the driver must ignore the ipc.
0111      */
0112     if (!sdev->msg) {
0113         dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
0114         return;
0115     }
0116 
0117     sdev->msg->reply_error = sdev->ipc->ops->get_reply(sdev);
0118 }
0119 EXPORT_SYMBOL(snd_sof_ipc_get_reply);
0120 
0121 /* handle reply message from DSP */
0122 void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
0123 {
0124     struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
0125 
0126     if (msg->ipc_complete) {
0127         dev_dbg(sdev->dev,
0128             "no reply expected, received 0x%x, will be ignored",
0129             msg_id);
0130         return;
0131     }
0132 
0133     /* wake up and return the error if we have waiters on this message ? */
0134     msg->ipc_complete = true;
0135     wake_up(&msg->waitq);
0136 }
0137 EXPORT_SYMBOL(snd_sof_ipc_reply);
0138 
0139 struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
0140 {
0141     struct snd_sof_ipc *ipc;
0142     struct snd_sof_ipc_msg *msg;
0143     const struct sof_ipc_ops *ops;
0144 
0145     ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
0146     if (!ipc)
0147         return NULL;
0148 
0149     mutex_init(&ipc->tx_mutex);
0150     ipc->sdev = sdev;
0151     msg = &ipc->msg;
0152 
0153     /* indicate that we aren't sending a message ATM */
0154     msg->ipc_complete = true;
0155 
0156     init_waitqueue_head(&msg->waitq);
0157 
0158     switch (sdev->pdata->ipc_type) {
0159 #if defined(CONFIG_SND_SOC_SOF_IPC3)
0160     case SOF_IPC:
0161         ops = &ipc3_ops;
0162         break;
0163 #endif
0164 #if defined(CONFIG_SND_SOC_SOF_INTEL_IPC4)
0165     case SOF_INTEL_IPC4:
0166         ops = &ipc4_ops;
0167         break;
0168 #endif
0169     default:
0170         dev_err(sdev->dev, "Not supported IPC version: %d\n",
0171             sdev->pdata->ipc_type);
0172         return NULL;
0173     }
0174 
0175     /* check for mandatory ops */
0176     if (!ops->tx_msg || !ops->rx_msg || !ops->set_get_data || !ops->get_reply) {
0177         dev_err(sdev->dev, "Missing IPC message handling ops\n");
0178         return NULL;
0179     }
0180 
0181     if (!ops->fw_loader || !ops->fw_loader->validate ||
0182         !ops->fw_loader->parse_ext_manifest) {
0183         dev_err(sdev->dev, "Missing IPC firmware loading ops\n");
0184         return NULL;
0185     }
0186 
0187     if (!ops->pcm) {
0188         dev_err(sdev->dev, "Missing IPC PCM ops\n");
0189         return NULL;
0190     }
0191 
0192     if (!ops->tplg || !ops->tplg->widget || !ops->tplg->control) {
0193         dev_err(sdev->dev, "Missing IPC topology ops\n");
0194         return NULL;
0195     }
0196 
0197     if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend ||
0198                 !ops->fw_tracing->resume)) {
0199         dev_err(sdev->dev, "Missing firmware tracing ops\n");
0200         return NULL;
0201     }
0202 
0203     ipc->ops = ops;
0204 
0205     return ipc;
0206 }
0207 EXPORT_SYMBOL(snd_sof_ipc_init);
0208 
0209 void snd_sof_ipc_free(struct snd_sof_dev *sdev)
0210 {
0211     struct snd_sof_ipc *ipc = sdev->ipc;
0212 
0213     if (!ipc)
0214         return;
0215 
0216     /* disable sending of ipc's */
0217     mutex_lock(&ipc->tx_mutex);
0218     ipc->disable_ipc_tx = true;
0219     mutex_unlock(&ipc->tx_mutex);
0220 }
0221 EXPORT_SYMBOL(snd_sof_ipc_free);