Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Intel SST generic IPC Support
0004  *
0005  * Copyright (C) 2015, Intel Corporation. All rights reserved.
0006  */
0007 
0008 #include <linux/types.h>
0009 #include <linux/kernel.h>
0010 #include <linux/list.h>
0011 #include <linux/wait.h>
0012 #include <linux/module.h>
0013 #include <linux/spinlock.h>
0014 #include <linux/device.h>
0015 #include <linux/slab.h>
0016 #include <linux/workqueue.h>
0017 #include <linux/sched.h>
0018 #include <linux/delay.h>
0019 #include <linux/platform_device.h>
0020 #include <sound/asound.h>
0021 
0022 #include "sst-dsp.h"
0023 #include "sst-dsp-priv.h"
0024 #include "sst-ipc.h"
0025 
0026 /* IPC message timeout (msecs) */
0027 #define IPC_TIMEOUT_MSECS   300
0028 
0029 #define IPC_EMPTY_LIST_SIZE 8
0030 
0031 /* locks held by caller */
0032 static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc)
0033 {
0034     struct ipc_message *msg = NULL;
0035 
0036     if (!list_empty(&ipc->empty_list)) {
0037         msg = list_first_entry(&ipc->empty_list, struct ipc_message,
0038             list);
0039         list_del(&msg->list);
0040     }
0041 
0042     return msg;
0043 }
0044 
0045 static int tx_wait_done(struct sst_generic_ipc *ipc,
0046     struct ipc_message *msg, struct sst_ipc_message *reply)
0047 {
0048     unsigned long flags;
0049     int ret;
0050 
0051     /* wait for DSP completion (in all cases atm inc pending) */
0052     ret = wait_event_timeout(msg->waitq, msg->complete,
0053         msecs_to_jiffies(IPC_TIMEOUT_MSECS));
0054 
0055     spin_lock_irqsave(&ipc->dsp->spinlock, flags);
0056     if (ret == 0) {
0057         if (ipc->ops.shim_dbg != NULL)
0058             ipc->ops.shim_dbg(ipc, "message timeout");
0059 
0060         list_del(&msg->list);
0061         ret = -ETIMEDOUT;
0062     } else {
0063 
0064         /* copy the data returned from DSP */
0065         if (reply) {
0066             reply->header = msg->rx.header;
0067             if (reply->data)
0068                 memcpy(reply->data, msg->rx.data, msg->rx.size);
0069         }
0070         ret = msg->errno;
0071     }
0072 
0073     list_add_tail(&msg->list, &ipc->empty_list);
0074     spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
0075     return ret;
0076 }
0077 
0078 static int ipc_tx_message(struct sst_generic_ipc *ipc,
0079     struct sst_ipc_message request,
0080     struct sst_ipc_message *reply, int wait)
0081 {
0082     struct ipc_message *msg;
0083     unsigned long flags;
0084 
0085     spin_lock_irqsave(&ipc->dsp->spinlock, flags);
0086 
0087     msg = msg_get_empty(ipc);
0088     if (msg == NULL) {
0089         spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
0090         return -EBUSY;
0091     }
0092 
0093     msg->tx.header = request.header;
0094     msg->tx.size = request.size;
0095     msg->rx.header = 0;
0096     msg->rx.size = reply ? reply->size : 0;
0097     msg->wait = wait;
0098     msg->errno = 0;
0099     msg->pending = false;
0100     msg->complete = false;
0101 
0102     if ((request.size) && (ipc->ops.tx_data_copy != NULL))
0103         ipc->ops.tx_data_copy(msg, request.data, request.size);
0104 
0105     list_add_tail(&msg->list, &ipc->tx_list);
0106     schedule_work(&ipc->kwork);
0107     spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
0108 
0109     if (wait)
0110         return tx_wait_done(ipc, msg, reply);
0111     else
0112         return 0;
0113 }
0114 
0115 static int msg_empty_list_init(struct sst_generic_ipc *ipc)
0116 {
0117     int i;
0118 
0119     ipc->msg = kcalloc(IPC_EMPTY_LIST_SIZE, sizeof(struct ipc_message),
0120                GFP_KERNEL);
0121     if (ipc->msg == NULL)
0122         return -ENOMEM;
0123 
0124     for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
0125         ipc->msg[i].tx.data = kzalloc(ipc->tx_data_max_size, GFP_KERNEL);
0126         if (ipc->msg[i].tx.data == NULL)
0127             goto free_mem;
0128 
0129         ipc->msg[i].rx.data = kzalloc(ipc->rx_data_max_size, GFP_KERNEL);
0130         if (ipc->msg[i].rx.data == NULL) {
0131             kfree(ipc->msg[i].tx.data);
0132             goto free_mem;
0133         }
0134 
0135         init_waitqueue_head(&ipc->msg[i].waitq);
0136         list_add(&ipc->msg[i].list, &ipc->empty_list);
0137     }
0138 
0139     return 0;
0140 
0141 free_mem:
0142     while (i > 0) {
0143         kfree(ipc->msg[i-1].tx.data);
0144         kfree(ipc->msg[i-1].rx.data);
0145         --i;
0146     }
0147     kfree(ipc->msg);
0148 
0149     return -ENOMEM;
0150 }
0151 
0152 static void ipc_tx_msgs(struct work_struct *work)
0153 {
0154     struct sst_generic_ipc *ipc =
0155         container_of(work, struct sst_generic_ipc, kwork);
0156     struct ipc_message *msg;
0157 
0158     spin_lock_irq(&ipc->dsp->spinlock);
0159 
0160     while (!list_empty(&ipc->tx_list) && !ipc->pending) {
0161         /* if the DSP is busy, we will TX messages after IRQ.
0162          * also postpone if we are in the middle of processing
0163          * completion irq
0164          */
0165         if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
0166             dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
0167             break;
0168         }
0169 
0170         msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
0171         list_move(&msg->list, &ipc->rx_list);
0172 
0173         if (ipc->ops.tx_msg != NULL)
0174             ipc->ops.tx_msg(ipc, msg);
0175     }
0176 
0177     spin_unlock_irq(&ipc->dsp->spinlock);
0178 }
0179 
0180 int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc,
0181     struct sst_ipc_message request, struct sst_ipc_message *reply)
0182 {
0183     int ret;
0184 
0185     /*
0186      * DSP maybe in lower power active state, so
0187      * check if the DSP supports DSP lp On method
0188      * if so invoke that before sending IPC
0189      */
0190     if (ipc->ops.check_dsp_lp_on)
0191         if (ipc->ops.check_dsp_lp_on(ipc->dsp, true))
0192             return -EIO;
0193 
0194     ret = ipc_tx_message(ipc, request, reply, 1);
0195 
0196     if (ipc->ops.check_dsp_lp_on)
0197         if (ipc->ops.check_dsp_lp_on(ipc->dsp, false))
0198             return -EIO;
0199 
0200     return ret;
0201 }
0202 EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
0203 
0204 int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc,
0205     struct sst_ipc_message request)
0206 {
0207     return ipc_tx_message(ipc, request, NULL, 0);
0208 }
0209 EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
0210 
0211 int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc,
0212     struct sst_ipc_message request, struct sst_ipc_message *reply)
0213 {
0214     return ipc_tx_message(ipc, request, reply, 1);
0215 }
0216 EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm);
0217 
0218 struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
0219     u64 header)
0220 {
0221     struct ipc_message *msg;
0222     u64 mask;
0223 
0224     if (ipc->ops.reply_msg_match != NULL)
0225         header = ipc->ops.reply_msg_match(header, &mask);
0226     else
0227         mask = (u64)-1;
0228 
0229     if (list_empty(&ipc->rx_list)) {
0230         dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n",
0231             header);
0232         return NULL;
0233     }
0234 
0235     list_for_each_entry(msg, &ipc->rx_list, list) {
0236         if ((msg->tx.header & mask) == header)
0237             return msg;
0238     }
0239 
0240     return NULL;
0241 }
0242 EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg);
0243 
0244 /* locks held by caller */
0245 void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
0246     struct ipc_message *msg)
0247 {
0248     msg->complete = true;
0249 
0250     if (!msg->wait)
0251         list_add_tail(&msg->list, &ipc->empty_list);
0252     else
0253         wake_up(&msg->waitq);
0254 }
0255 EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete);
0256 
0257 int sst_ipc_init(struct sst_generic_ipc *ipc)
0258 {
0259     int ret;
0260 
0261     INIT_LIST_HEAD(&ipc->tx_list);
0262     INIT_LIST_HEAD(&ipc->rx_list);
0263     INIT_LIST_HEAD(&ipc->empty_list);
0264     init_waitqueue_head(&ipc->wait_txq);
0265 
0266     ret = msg_empty_list_init(ipc);
0267     if (ret < 0)
0268         return -ENOMEM;
0269 
0270     INIT_WORK(&ipc->kwork, ipc_tx_msgs);
0271     return 0;
0272 }
0273 EXPORT_SYMBOL_GPL(sst_ipc_init);
0274 
0275 void sst_ipc_fini(struct sst_generic_ipc *ipc)
0276 {
0277     int i;
0278 
0279     cancel_work_sync(&ipc->kwork);
0280 
0281     if (ipc->msg) {
0282         for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
0283             kfree(ipc->msg[i].tx.data);
0284             kfree(ipc->msg[i].rx.data);
0285         }
0286         kfree(ipc->msg);
0287     }
0288 }
0289 EXPORT_SYMBOL_GPL(sst_ipc_fini);
0290 
0291 /* Module information */
0292 MODULE_AUTHOR("Jin Yao");
0293 MODULE_DESCRIPTION("Intel SST IPC generic");
0294 MODULE_LICENSE("GPL v2");