~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/intel/common/sst-ipc.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Intel SST generic IPC Support
  4  *
  5  * Copyright (C) 2015, Intel Corporation
  6  */
  7 
  8 #include <linux/types.h>
  9 #include <linux/kernel.h>
 10 #include <linux/list.h>
 11 #include <linux/wait.h>
 12 #include <linux/module.h>
 13 #include <linux/spinlock.h>
 14 #include <linux/device.h>
 15 #include <linux/slab.h>
 16 #include <linux/workqueue.h>
 17 #include <linux/sched.h>
 18 #include <linux/delay.h>
 19 #include <linux/platform_device.h>
 20 #include <sound/asound.h>
 21 
 22 #include "sst-dsp.h"
 23 #include "sst-dsp-priv.h"
 24 #include "sst-ipc.h"
 25 
 26 /* IPC message timeout (msecs) */
 27 #define IPC_TIMEOUT_MSECS       300
 28 
 29 #define IPC_EMPTY_LIST_SIZE     8
 30 
 31 /* locks held by caller */
 32 static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc)
 33 {
 34         struct ipc_message *msg = NULL;
 35 
 36         if (!list_empty(&ipc->empty_list)) {
 37                 msg = list_first_entry(&ipc->empty_list, struct ipc_message,
 38                         list);
 39                 list_del(&msg->list);
 40         }
 41 
 42         return msg;
 43 }
 44 
 45 static int tx_wait_done(struct sst_generic_ipc *ipc,
 46         struct ipc_message *msg, struct sst_ipc_message *reply)
 47 {
 48         unsigned long flags;
 49         int ret;
 50 
 51         /* wait for DSP completion (in all cases atm inc pending) */
 52         ret = wait_event_timeout(msg->waitq, msg->complete,
 53                 msecs_to_jiffies(IPC_TIMEOUT_MSECS));
 54 
 55         spin_lock_irqsave(&ipc->dsp->spinlock, flags);
 56         if (ret == 0) {
 57                 if (ipc->ops.shim_dbg != NULL)
 58                         ipc->ops.shim_dbg(ipc, "message timeout");
 59 
 60                 list_del(&msg->list);
 61                 ret = -ETIMEDOUT;
 62         } else {
 63 
 64                 /* copy the data returned from DSP */
 65                 if (reply) {
 66                         reply->header = msg->rx.header;
 67                         if (reply->data)
 68                                 memcpy(reply->data, msg->rx.data, msg->rx.size);
 69                 }
 70                 ret = msg->errno;
 71         }
 72 
 73         list_add_tail(&msg->list, &ipc->empty_list);
 74         spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
 75         return ret;
 76 }
 77 
 78 static int ipc_tx_message(struct sst_generic_ipc *ipc,
 79         struct sst_ipc_message request,
 80         struct sst_ipc_message *reply, int wait)
 81 {
 82         struct ipc_message *msg;
 83         unsigned long flags;
 84 
 85         spin_lock_irqsave(&ipc->dsp->spinlock, flags);
 86 
 87         msg = msg_get_empty(ipc);
 88         if (msg == NULL) {
 89                 spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
 90                 return -EBUSY;
 91         }
 92 
 93         msg->tx.header = request.header;
 94         msg->tx.size = request.size;
 95         msg->rx.header = 0;
 96         msg->rx.size = reply ? reply->size : 0;
 97         msg->wait = wait;
 98         msg->errno = 0;
 99         msg->pending = false;
100         msg->complete = false;
101 
102         if ((request.size) && (ipc->ops.tx_data_copy != NULL))
103                 ipc->ops.tx_data_copy(msg, request.data, request.size);
104 
105         list_add_tail(&msg->list, &ipc->tx_list);
106         schedule_work(&ipc->kwork);
107         spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
108 
109         if (wait)
110                 return tx_wait_done(ipc, msg, reply);
111         else
112                 return 0;
113 }
114 
115 static int msg_empty_list_init(struct sst_generic_ipc *ipc)
116 {
117         int i;
118 
119         ipc->msg = kcalloc(IPC_EMPTY_LIST_SIZE, sizeof(struct ipc_message),
120                            GFP_KERNEL);
121         if (ipc->msg == NULL)
122                 return -ENOMEM;
123 
124         for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
125                 ipc->msg[i].tx.data = kzalloc(ipc->tx_data_max_size, GFP_KERNEL);
126                 if (ipc->msg[i].tx.data == NULL)
127                         goto free_mem;
128 
129                 ipc->msg[i].rx.data = kzalloc(ipc->rx_data_max_size, GFP_KERNEL);
130                 if (ipc->msg[i].rx.data == NULL) {
131                         kfree(ipc->msg[i].tx.data);
132                         goto free_mem;
133                 }
134 
135                 init_waitqueue_head(&ipc->msg[i].waitq);
136                 list_add(&ipc->msg[i].list, &ipc->empty_list);
137         }
138 
139         return 0;
140 
141 free_mem:
142         while (i > 0) {
143                 kfree(ipc->msg[i-1].tx.data);
144                 kfree(ipc->msg[i-1].rx.data);
145                 --i;
146         }
147         kfree(ipc->msg);
148 
149         return -ENOMEM;
150 }
151 
152 static void ipc_tx_msgs(struct work_struct *work)
153 {
154         struct sst_generic_ipc *ipc =
155                 container_of(work, struct sst_generic_ipc, kwork);
156         struct ipc_message *msg;
157 
158         spin_lock_irq(&ipc->dsp->spinlock);
159 
160         while (!list_empty(&ipc->tx_list) && !ipc->pending) {
161                 /* if the DSP is busy, we will TX messages after IRQ.
162                  * also postpone if we are in the middle of processing
163                  * completion irq
164                  */
165                 if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
166                         dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
167                         break;
168                 }
169 
170                 msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
171                 list_move(&msg->list, &ipc->rx_list);
172 
173                 if (ipc->ops.tx_msg != NULL)
174                         ipc->ops.tx_msg(ipc, msg);
175         }
176 
177         spin_unlock_irq(&ipc->dsp->spinlock);
178 }
179 
180 int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc,
181         struct sst_ipc_message request, struct sst_ipc_message *reply)
182 {
183         int ret;
184 
185         /*
186          * DSP maybe in lower power active state, so
187          * check if the DSP supports DSP lp On method
188          * if so invoke that before sending IPC
189          */
190         if (ipc->ops.check_dsp_lp_on)
191                 if (ipc->ops.check_dsp_lp_on(ipc->dsp, true))
192                         return -EIO;
193 
194         ret = ipc_tx_message(ipc, request, reply, 1);
195 
196         if (ipc->ops.check_dsp_lp_on)
197                 if (ipc->ops.check_dsp_lp_on(ipc->dsp, false))
198                         return -EIO;
199 
200         return ret;
201 }
202 EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
203 
204 int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc,
205         struct sst_ipc_message request)
206 {
207         return ipc_tx_message(ipc, request, NULL, 0);
208 }
209 EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
210 
211 int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc,
212         struct sst_ipc_message request, struct sst_ipc_message *reply)
213 {
214         return ipc_tx_message(ipc, request, reply, 1);
215 }
216 EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm);
217 
218 struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
219         u64 header)
220 {
221         struct ipc_message *msg;
222         u64 mask;
223 
224         if (ipc->ops.reply_msg_match != NULL)
225                 header = ipc->ops.reply_msg_match(header, &mask);
226         else
227                 mask = (u64)-1;
228 
229         if (list_empty(&ipc->rx_list)) {
230                 dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n",
231                         header);
232                 return NULL;
233         }
234 
235         list_for_each_entry(msg, &ipc->rx_list, list) {
236                 if ((msg->tx.header & mask) == header)
237                         return msg;
238         }
239 
240         return NULL;
241 }
242 EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg);
243 
244 /* locks held by caller */
245 void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
246         struct ipc_message *msg)
247 {
248         msg->complete = true;
249 
250         if (!msg->wait)
251                 list_add_tail(&msg->list, &ipc->empty_list);
252         else
253                 wake_up(&msg->waitq);
254 }
255 EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete);
256 
257 int sst_ipc_init(struct sst_generic_ipc *ipc)
258 {
259         int ret;
260 
261         INIT_LIST_HEAD(&ipc->tx_list);
262         INIT_LIST_HEAD(&ipc->rx_list);
263         INIT_LIST_HEAD(&ipc->empty_list);
264         init_waitqueue_head(&ipc->wait_txq);
265 
266         ret = msg_empty_list_init(ipc);
267         if (ret < 0)
268                 return -ENOMEM;
269 
270         INIT_WORK(&ipc->kwork, ipc_tx_msgs);
271         return 0;
272 }
273 EXPORT_SYMBOL_GPL(sst_ipc_init);
274 
275 void sst_ipc_fini(struct sst_generic_ipc *ipc)
276 {
277         int i;
278 
279         cancel_work_sync(&ipc->kwork);
280 
281         if (ipc->msg) {
282                 for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
283                         kfree(ipc->msg[i].tx.data);
284                         kfree(ipc->msg[i].rx.data);
285                 }
286                 kfree(ipc->msg);
287         }
288 }
289 EXPORT_SYMBOL_GPL(sst_ipc_fini);
290 
291 /* Module information */
292 MODULE_AUTHOR("Jin Yao");
293 MODULE_DESCRIPTION("Intel SST IPC generic");
294 MODULE_LICENSE("GPL v2");
295 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php