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