1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2020 Intel Corporation. All rights reserved.
4 //
5 // Author: Tomasz Lauda <tomasz.lauda@linux.intel.com>
6
7 #include <sof/audio/component.h>
8 #include <sof/audio/component_ext.h>
9 #include <sof/drivers/idc.h>
10 #include <sof/ipc/driver.h>
11 #include <sof/ipc/msg.h>
12 #include <sof/ipc/topology.h>
13 #include <sof/ipc/schedule.h>
14 #include <sof/drivers/timer.h>
15 #include <sof/lib/alloc.h>
16 #include <sof/lib/clk.h>
17 #include <sof/lib/cpu.h>
18 #include <sof/lib/memory.h>
19 #include <sof/lib/notifier.h>
20 #include <sof/lib/uuid.h>
21 #include <sof/platform.h>
22 #include <arch/lib/wait.h>
23 #include <sof/schedule/edf_schedule.h>
24 #include <sof/schedule/ll_schedule.h>
25 #include <sof/schedule/schedule.h>
26 #include <sof/schedule/task.h>
27 #include <sof/trace/trace.h>
28 #include <ipc/header.h>
29 #include <ipc/stream.h>
30 #include <ipc/topology.h>
31 #include <errno.h>
32 #include <stdint.h>
33
34 /** \brief IDC message payload per core. */
35 static SHARED_DATA struct idc_payload static_payload[CONFIG_CORE_COUNT];
36
37 /* 379a60ae-cedb-4777-aaf2-5659b0a85735 */
38 DECLARE_SOF_UUID("idc", idc_uuid, 0x379a60ae, 0xcedb, 0x4777,
39 0xaa, 0xf2, 0x56, 0x59, 0xb0, 0xa8, 0x57, 0x35);
40
41 DECLARE_TR_CTX(idc_tr, SOF_UUID(idc_uuid), LOG_LEVEL_INFO);
42
43 /* b90f5a4e-5537-4375-a1df-95485472ff9e */
44 DECLARE_SOF_UUID("comp-task", idc_comp_task_uuid, 0xb90f5a4e, 0x5537, 0x4375,
45 0xa1, 0xdf, 0x95, 0x48, 0x54, 0x72, 0xff, 0x9e);
46
47 #ifndef __ZEPHYR__
48 /* a5dacb0e-88dc-415c-a1b5-3e8df77f1976 */
49 DECLARE_SOF_UUID("idc-cmd-task", idc_cmd_task_uuid, 0xa5dacb0e, 0x88dc, 0x415c,
50 0xa1, 0xb5, 0x3e, 0x8d, 0xf7, 0x7f, 0x19, 0x76);
51 #endif
52
53 /**
54 * \brief Sets IDC command status after execution.
55 * \param[in] status Status to be set.
56 * \param[in] core Id of the core for this status.
57 */
idc_msg_status_set(int status,uint32_t core)58 static void idc_msg_status_set(int status, uint32_t core)
59 {
60 struct idc *idc = *idc_get();
61 struct idc_payload *payload = idc_payload_get(idc, core);
62
63 *(uint32_t *)payload->data = status;
64
65 }
66
67 /**
68 * \brief Retrieves IDC command status after sending message.
69 * \param[in] core Id of the core for this status.
70 * \return Last IDC message status.
71 */
idc_msg_status_get(uint32_t core)72 int idc_msg_status_get(uint32_t core)
73 {
74 struct idc *idc = *idc_get();
75 struct idc_payload *payload = idc_payload_get(idc, core);
76 int status;
77
78 status = *(uint32_t *)payload->data;
79
80
81 return status;
82 }
83
84 /**
85 * \brief Waits until status condition is true.
86 * \param[in] target_core Id of the core receiving the message.
87 * \param[in] cond Pointer to condition function.
88 * \return Error code.
89 */
idc_wait_in_blocking_mode(uint32_t target_core,bool (* cond)(int))90 int idc_wait_in_blocking_mode(uint32_t target_core, bool (*cond)(int))
91 {
92 struct timer *timer = timer_get();
93 uint64_t deadline;
94
95 deadline = platform_timer_get(timer) +
96 clock_us_to_ticks(PLATFORM_DEFAULT_CLOCK, IDC_TIMEOUT);
97
98 while (!cond(target_core)) {
99
100 /* spin here so other core can access IO and timers freely */
101 idelay(8192);
102
103 if (deadline < platform_timer_get(timer))
104 break;
105 }
106
107 /* safe check in case we've got preempted
108 * after read
109 */
110 if (cond(target_core))
111 return 0;
112
113 tr_err(&idc_tr, "idc_wait_in_blocking_mode() error: timeout, target_core %u",
114 target_core);
115 return -ETIME;
116 }
117
118 /**
119 * \brief Executes IDC IPC processing message.
120 */
idc_ipc(void)121 static void idc_ipc(void)
122 {
123 struct ipc *ipc = ipc_get();
124
125 ipc_cmd(ipc->comp_data);
126
127 /* Signal the host */
128 ipc_platform_complete_cmd(ipc);
129 }
130
131 /**
132 * \brief Executes IDC component params message.
133 * \param[in] comp_id Component id to have params set.
134 * \return Error code.
135 */
idc_params(uint32_t comp_id)136 static int idc_params(uint32_t comp_id)
137 {
138 struct ipc *ipc = ipc_get();
139 struct ipc_comp_dev *ipc_dev;
140 struct idc *idc = *idc_get();
141 struct idc_payload *payload = idc_payload_get(idc, cpu_get_id());
142 struct sof_ipc_stream_params *params =
143 (struct sof_ipc_stream_params *)payload;
144 int ret;
145
146 ipc_dev = ipc_get_comp_by_id(ipc, comp_id);
147 if (!ipc_dev)
148 return -ENODEV;
149
150 ret = comp_params(ipc_dev->cd, params);
151
152
153 return ret;
154 }
155
comp_task(void * data)156 static enum task_state comp_task(void *data)
157 {
158 if (comp_copy(data) < 0)
159 return SOF_TASK_STATE_COMPLETED;
160
161 return SOF_TASK_STATE_RESCHEDULE;
162 }
163
164 /**
165 * \brief Executes IDC component prepare message.
166 * \param[in] comp_id Component id to be prepared.
167 * \return Error code.
168 */
idc_prepare(uint32_t comp_id)169 static int idc_prepare(uint32_t comp_id)
170 {
171 struct ipc *ipc = ipc_get();
172 struct ipc_comp_dev *ipc_dev;
173 struct comp_dev *dev;
174 int ret;
175
176 ipc_dev = ipc_get_comp_by_id(ipc, comp_id);
177 if (!ipc_dev)
178 return -ENODEV;
179
180 dev = ipc_dev->cd;
181
182 /* we're running on different core, so allocate our own task */
183 if (!dev->task) {
184 /* allocate task for shared component */
185 dev->task = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
186 sizeof(*dev->task));
187 if (!dev->task) {
188 ret = -ENOMEM;
189 goto out;
190 }
191
192 ret = schedule_task_init_ll(dev->task,
193 SOF_UUID(idc_comp_task_uuid),
194 SOF_SCHEDULE_LL_TIMER,
195 dev->priority, comp_task, dev,
196 dev->ipc_config.core, 0);
197 if (ret < 0) {
198 rfree(dev->task);
199 goto out;
200 }
201 }
202
203 ret = comp_prepare(ipc_dev->cd);
204
205 out:
206
207 return ret;
208 }
209
210 /**
211 * \brief Executes IDC component trigger message.
212 * \param[in] comp_id Component id to be triggered.
213 * \return Error code.
214 */
idc_trigger(uint32_t comp_id)215 static int idc_trigger(uint32_t comp_id)
216 {
217 struct ipc *ipc = ipc_get();
218 struct ipc_comp_dev *ipc_dev;
219 struct idc *idc = *idc_get();
220 struct idc_payload *payload = idc_payload_get(idc, cpu_get_id());
221 uint32_t cmd = *(uint32_t *)payload;
222 int ret;
223
224 ipc_dev = ipc_get_comp_by_id(ipc, comp_id);
225 if (!ipc_dev)
226 return -ENODEV;
227
228 ret = comp_trigger(ipc_dev->cd, cmd);
229 if (ret < 0)
230 goto out;
231
232 /* schedule or cancel task */
233 switch (cmd) {
234 case COMP_TRIGGER_START:
235 case COMP_TRIGGER_RELEASE:
236 schedule_task(ipc_dev->cd->task, 0, ipc_dev->cd->period);
237 break;
238 case COMP_TRIGGER_XRUN:
239 case COMP_TRIGGER_PAUSE:
240 case COMP_TRIGGER_STOP:
241 schedule_task_cancel(ipc_dev->cd->task);
242 break;
243 }
244
245 out:
246
247 return ret;
248 }
249
250 /**
251 * \brief Executes IDC component reset message.
252 * \param[in] comp_id Component id to be reset.
253 * \return Error code.
254 */
idc_reset(uint32_t comp_id)255 static int idc_reset(uint32_t comp_id)
256 {
257 struct ipc *ipc = ipc_get();
258 struct ipc_comp_dev *ipc_dev;
259 int ret;
260
261 ipc_dev = ipc_get_comp_by_id(ipc, comp_id);
262 if (!ipc_dev)
263 return -ENODEV;
264
265 ret = comp_reset(ipc_dev->cd);
266
267
268 return ret;
269 }
270
271 /**
272 * \brief Executes IDC message based on type.
273 * \param[in,out] msg Pointer to IDC message.
274 */
idc_cmd(struct idc_msg * msg)275 void idc_cmd(struct idc_msg *msg)
276 {
277 uint32_t type = iTS(msg->header);
278 int ret = 0;
279
280 switch (type) {
281 case iTS(IDC_MSG_POWER_DOWN):
282 cpu_power_down_core();
283 break;
284 case iTS(IDC_MSG_NOTIFY):
285 notifier_notify_remote();
286 break;
287 case iTS(IDC_MSG_IPC):
288 idc_ipc();
289 break;
290 case iTS(IDC_MSG_PARAMS):
291 ret = idc_params(msg->extension);
292 break;
293 case iTS(IDC_MSG_PREPARE):
294 ret = idc_prepare(msg->extension);
295 break;
296 case iTS(IDC_MSG_TRIGGER):
297 ret = idc_trigger(msg->extension);
298 break;
299 case iTS(IDC_MSG_RESET):
300 ret = idc_reset(msg->extension);
301 break;
302 default:
303 tr_err(&idc_tr, "idc_cmd(): invalid msg->header = %u",
304 msg->header);
305 }
306
307 idc_msg_status_set(ret, cpu_get_id());
308 }
309
310 /* Runs on each CPU */
idc_init(void)311 int idc_init(void)
312 {
313 struct idc **idc = idc_get();
314 #ifndef __ZEPHYR__
315 struct task_ops ops = {
316 .run = idc_do_cmd,
317 .get_deadline = ipc_task_deadline,
318 };
319 #endif
320
321 tr_info(&idc_tr, "idc_init()");
322
323 /* initialize idc data */
324 *idc = rzalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM, sizeof(**idc));
325 (*idc)->payload = cache_to_uncache((struct idc_payload *)static_payload);
326
327 /* process task */
328 #ifndef __ZEPHYR__
329 schedule_task_init_edf(&(*idc)->idc_task, SOF_UUID(idc_cmd_task_uuid),
330 &ops, *idc, cpu_get_id(), 0);
331
332 return platform_idc_init();
333 #else
334 idc_init_thread();
335
336 return 0;
337 #endif
338 }
339