1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2016 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6
7 #include <sof/audio/component_ext.h>
8 #include <sof/common.h>
9 #include <sof/debug/panic.h>
10 #include <sof/drivers/interrupt.h>
11 #include <sof/ipc/msg.h>
12 #include <sof/lib/alloc.h>
13 #include <sof/lib/cache.h>
14 #include <sof/lib/memory.h>
15 #include <sof/list.h>
16 #include <sof/sof.h>
17 #include <sof/string.h>
18 #include <ipc/topology.h>
19 #include <errno.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23
24 static SHARED_DATA struct comp_driver_list cd;
25
26 /* 7c42ce8b-0108-43d0-9137-56d660478c5f */
27 DECLARE_SOF_UUID("component", comp_uuid, 0x7c42ce8b, 0x0108, 0x43d0,
28 0x91, 0x37, 0x56, 0xd6, 0x60, 0x47, 0x8c, 0x5f);
29
30 DECLARE_TR_CTX(comp_tr, SOF_UUID(comp_uuid), LOG_LEVEL_INFO);
31
comp_register(struct comp_driver_info * drv)32 int comp_register(struct comp_driver_info *drv)
33 {
34 struct comp_driver_list *drivers = comp_drivers_get();
35
36 spin_lock(&drivers->lock);
37 list_item_prepend(&drv->list, &drivers->list);
38 spin_unlock(&drivers->lock);
39
40 return 0;
41 }
42
comp_unregister(struct comp_driver_info * drv)43 void comp_unregister(struct comp_driver_info *drv)
44 {
45 struct comp_driver_list *drivers = comp_drivers_get();
46
47 spin_lock(&drivers->lock);
48 list_item_del(&drv->list);
49 spin_unlock(&drivers->lock);
50 }
51
52 /* NOTE: Keep the component state diagram up to date:
53 * sof-docs/developer_guides/firmware/components/images/comp-dev-states.pu
54 */
55
comp_set_state(struct comp_dev * dev,int cmd)56 int comp_set_state(struct comp_dev *dev, int cmd)
57 {
58 int requested_state = comp_get_requested_state(cmd);
59 int ret = 0;
60
61 if (dev->state == requested_state) {
62 comp_info(dev, "comp_set_state(), state already set to %u",
63 dev->state);
64 return COMP_STATUS_STATE_ALREADY_SET;
65 }
66
67 switch (cmd) {
68 case COMP_TRIGGER_START:
69 if (dev->state == COMP_STATE_PREPARE) {
70 dev->state = COMP_STATE_ACTIVE;
71 } else {
72 comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_START",
73 dev->state);
74 ret = -EINVAL;
75 }
76 break;
77 case COMP_TRIGGER_RELEASE:
78 if (dev->state == COMP_STATE_PAUSED) {
79 dev->state = COMP_STATE_ACTIVE;
80 } else {
81 comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_RELEASE",
82 dev->state);
83 ret = -EINVAL;
84 }
85 break;
86 case COMP_TRIGGER_STOP:
87 if (dev->state == COMP_STATE_ACTIVE ||
88 dev->state == COMP_STATE_PAUSED) {
89 dev->state = COMP_STATE_PREPARE;
90 } else {
91 comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_STOP",
92 dev->state);
93 ret = -EINVAL;
94 }
95 break;
96 case COMP_TRIGGER_PAUSE:
97 /* only support pausing for running */
98 if (dev->state == COMP_STATE_ACTIVE) {
99 dev->state = COMP_STATE_PAUSED;
100 } else {
101 comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_PAUSE",
102 dev->state);
103 ret = -EINVAL;
104 }
105 break;
106 case COMP_TRIGGER_RESET:
107 /* reset always succeeds */
108 if (dev->state == COMP_STATE_ACTIVE ||
109 dev->state == COMP_STATE_PAUSED) {
110 comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_RESET",
111 dev->state);
112 ret = 0;
113 }
114 dev->state = COMP_STATE_READY;
115 break;
116 case COMP_TRIGGER_PREPARE:
117 if (dev->state == COMP_STATE_READY) {
118 dev->state = COMP_STATE_PREPARE;
119 } else {
120 comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_PREPARE",
121 dev->state);
122 ret = -EINVAL;
123 }
124 break;
125 default:
126 break;
127 }
128
129 comp_writeback(dev);
130
131 return ret;
132 }
133
sys_comp_init(struct sof * sof)134 void sys_comp_init(struct sof *sof)
135 {
136 sof->comp_drivers = platform_shared_get(&cd, sizeof(cd));
137
138 list_init(&sof->comp_drivers->list);
139 spinlock_init(&sof->comp_drivers->lock);
140 }
141
comp_get_copy_limits(struct comp_buffer * source,struct comp_buffer * sink,struct comp_copy_limits * cl)142 void comp_get_copy_limits(struct comp_buffer *source, struct comp_buffer *sink,
143 struct comp_copy_limits *cl)
144 {
145 cl->frames = audio_stream_avail_frames(&source->stream, &sink->stream);
146 cl->source_frame_bytes = audio_stream_frame_bytes(&source->stream);
147 cl->sink_frame_bytes = audio_stream_frame_bytes(&sink->stream);
148 cl->source_bytes = cl->frames * cl->source_frame_bytes;
149 cl->sink_bytes = cl->frames * cl->sink_frame_bytes;
150 }
151
152 /** \brief Struct handler for large component configs */
153 struct comp_data_blob_handler {
154 struct comp_dev *dev; /**< audio component device */
155 uint32_t data_size; /**< size of component's data blob */
156 void *data; /**< pointer to data blob */
157 void *data_new; /**< pointer to new data blob */
158 bool data_ready; /**< set when data blob is fully received */
159 uint32_t data_pos; /**< indicates a data position in data
160 * sending/receiving process
161 */
162 };
163
comp_free_data_blob(struct comp_data_blob_handler * blob_handler)164 static void comp_free_data_blob(struct comp_data_blob_handler *blob_handler)
165 {
166 assert(blob_handler);
167
168 if (!blob_handler->data)
169 return;
170
171 rfree(blob_handler->data);
172 rfree(blob_handler->data_new);
173 blob_handler->data = NULL;
174 blob_handler->data_new = NULL;
175 blob_handler->data_size = 0;
176 }
177
comp_get_data_blob(struct comp_data_blob_handler * blob_handler,size_t * size,uint32_t * crc)178 void *comp_get_data_blob(struct comp_data_blob_handler *blob_handler,
179 size_t *size, uint32_t *crc)
180 {
181 assert(blob_handler);
182
183 comp_dbg(blob_handler->dev, "comp_get_data_blob()");
184
185 if (size)
186 *size = 0;
187
188 /* Function returns new data blob if available */
189 if (comp_is_new_data_blob_available(blob_handler)) {
190 comp_dbg(blob_handler->dev, "comp_get_data_blob(): new data available");
191
192 /* Free "old" data blob and set data to data_new pointer */
193 rfree(blob_handler->data);
194 blob_handler->data = blob_handler->data_new;
195 blob_handler->data_new = NULL;
196 blob_handler->data_ready = false;
197 }
198
199 /* If data is available we calculate crc32 when crc pointer is given */
200 if (blob_handler->data) {
201 if (crc)
202 *crc = crc32(0, blob_handler->data,
203 blob_handler->data_size);
204 } else {
205 /* If blob_handler->data is equal to NULL and there is no new
206 * data blob it means that component hasn't got any config yet.
207 * Function returns NULL in that case.
208 */
209 comp_warn(blob_handler->dev, "comp_get_data_blob(): blob_handler->data is not set.");
210 }
211
212 if (size)
213 *size = blob_handler->data_size;
214
215 return blob_handler->data;
216 }
217
comp_is_new_data_blob_available(struct comp_data_blob_handler * blob_handler)218 bool comp_is_new_data_blob_available(struct comp_data_blob_handler
219 *blob_handler)
220 {
221 assert(blob_handler);
222
223 comp_dbg(blob_handler->dev, "comp_is_new_data_blob_available()");
224
225 /* New data blob is available when new data blob is allocated (data_new
226 * is not NULL) nd component received all required chunks of data
227 * (data_ready is set to TRUE)
228 */
229 if (blob_handler->data_new && blob_handler->data_ready)
230 return true;
231
232 return false;
233 }
234
comp_init_data_blob(struct comp_data_blob_handler * blob_handler,uint32_t size,void * init_data)235 int comp_init_data_blob(struct comp_data_blob_handler *blob_handler,
236 uint32_t size, void *init_data)
237 {
238 int ret;
239
240 assert(blob_handler);
241
242 comp_free_data_blob(blob_handler);
243
244 if (!size)
245 return 0;
246
247 /* Data blob allocation */
248 blob_handler->data = rballoc(0, SOF_MEM_CAPS_RAM, size);
249 if (!blob_handler->data) {
250 comp_err(blob_handler->dev, "comp_init_data_blob(): model->data rballoc failed");
251 return -ENOMEM;
252 }
253
254 /* If init_data is given, data will be initialized with it. In other
255 * case, data will be set to zero.
256 */
257 if (init_data) {
258 ret = memcpy_s(blob_handler->data, size, init_data, size);
259 assert(!ret);
260 } else {
261 bzero(blob_handler->data, size);
262 }
263
264 blob_handler->data_new = NULL;
265 blob_handler->data_size = size;
266 blob_handler->data_ready = true;
267
268 return 0;
269 }
270
comp_data_blob_set_cmd(struct comp_data_blob_handler * blob_handler,struct sof_ipc_ctrl_data * cdata)271 int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler,
272 struct sof_ipc_ctrl_data *cdata)
273 {
274 int ret = 0;
275
276 assert(blob_handler);
277
278 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd() msg_index = %d, num_elems = %d, remaining = %d ",
279 cdata->msg_index, cdata->num_elems,
280 cdata->elems_remaining);
281
282 /* Check that there is no work-in-progress previous request */
283 if (blob_handler->data_new && cdata->msg_index == 0) {
284 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), busy with previous request");
285 return -EBUSY;
286 }
287
288 /* in case when the current package is the first, we should allocate
289 * memory for whole model data
290 */
291 if (!cdata->msg_index) {
292 /* in case when required model size is equal to zero we do not
293 * allocate memory and should just return 0.
294 *
295 * Set cmd with cdata->data->size equal to 0 is possible in
296 * following situation:
297 * 1. At first boot and topology parsing stage, the driver will
298 * read all initial values of DSP kcontrols via IPC. Driver send
299 * get_model() cmd to components. If we do not initialize
300 * component earlier driver will get "model" with size 0.
301 * 2. When resuming from runtime suspended, the driver will
302 * restore all pipelines and kcontrols, for the tlv binary
303 * kcontrols, it will call the set_model() with the cached value
304 * and size (0 if it is not updated by any actual end user
305 * sof-ctl settings) - basically driver will send set_model()
306 * command with size equal to 0.
307 */
308 if (!cdata->data->size)
309 return 0;
310
311 blob_handler->data_new = rballoc(0, SOF_MEM_CAPS_RAM,
312 cdata->data->size);
313 if (!blob_handler->data_new) {
314 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): blob_handler->data_new allocation failed.");
315 return -ENOMEM;
316 }
317
318 blob_handler->data_size = cdata->data->size;
319 blob_handler->data_ready = false;
320 blob_handler->data_pos = 0;
321 }
322
323 /* return an error in case when we do not have allocated memory for
324 * model data
325 */
326 if (!blob_handler->data_new) {
327 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): buffer not allocated");
328 return -ENOMEM;
329 }
330
331 ret = memcpy_s((char *)blob_handler->data_new + blob_handler->data_pos,
332 blob_handler->data_size - blob_handler->data_pos,
333 cdata->data->data, cdata->num_elems);
334 assert(!ret);
335
336 blob_handler->data_pos += cdata->num_elems;
337
338 if (!cdata->elems_remaining) {
339 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd(): final package received");
340
341 /* The new configuration is OK to be applied */
342 blob_handler->data_ready = true;
343
344 /* If component state is READY we can omit old
345 * configuration immediately. When in playback/capture
346 * the new configuration presence is checked in copy().
347 */
348 if (blob_handler->dev->state == COMP_STATE_READY) {
349 rfree(blob_handler->data);
350 blob_handler->data = NULL;
351 }
352
353 /* If there is no existing configuration the received
354 * can be set to current immediately. It will be
355 * applied in prepare() when streaming starts.
356 */
357 if (!blob_handler->data) {
358 blob_handler->data = blob_handler->data_new;
359 blob_handler->data_new = NULL;
360 }
361 }
362
363 return 0;
364 }
365
comp_data_blob_get_cmd(struct comp_data_blob_handler * blob_handler,struct sof_ipc_ctrl_data * cdata,int size)366 int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler,
367 struct sof_ipc_ctrl_data *cdata, int size)
368 {
369 int ret = 0;
370
371 assert(blob_handler);
372
373 comp_dbg(blob_handler->dev, "comp_data_blob_get_cmd() msg_index = %d, num_elems = %d, remaining = %d ",
374 cdata->msg_index, cdata->num_elems,
375 cdata->elems_remaining);
376
377 /* Copy back to user space */
378 if (blob_handler->data) {
379 /* reset data_pos variable in case of copying first element */
380 if (!cdata->msg_index) {
381 blob_handler->data_pos = 0;
382 comp_dbg(blob_handler->dev, "comp_data_blob_get_cmd() model data_size = 0x%x",
383 blob_handler->data_size);
384 }
385
386 /* return an error in case of mismatch between num_elems and
387 * required size
388 */
389 if (cdata->num_elems > size) {
390 comp_err(blob_handler->dev, "comp_data_blob_get_cmd(): invalid cdata->num_elems %d",
391 cdata->num_elems);
392 return -EINVAL;
393 }
394
395 /* copy required size of data */
396 ret = memcpy_s(cdata->data->data, size,
397 (char *)blob_handler->data + blob_handler->data_pos,
398 cdata->num_elems);
399 assert(!ret);
400
401 cdata->data->abi = SOF_ABI_VERSION;
402 cdata->data->size = blob_handler->data_size;
403 blob_handler->data_pos += cdata->num_elems;
404 } else {
405 comp_warn(blob_handler->dev, "comp_data_blob_get_cmd(): model->data not allocated yet.");
406 cdata->data->abi = SOF_ABI_VERSION;
407 cdata->data->size = 0;
408 }
409
410 return ret;
411 }
412
comp_data_blob_handler_new(struct comp_dev * dev)413 struct comp_data_blob_handler *comp_data_blob_handler_new(struct comp_dev *dev)
414 {
415 struct comp_data_blob_handler *handler;
416
417 comp_dbg(dev, "comp_data_blob_handler_new()");
418
419 handler = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
420 sizeof(struct comp_data_blob_handler));
421
422 if (handler)
423 handler->dev = dev;
424
425 return handler;
426 }
427
comp_data_blob_handler_free(struct comp_data_blob_handler * blob_handler)428 void comp_data_blob_handler_free(struct comp_data_blob_handler *blob_handler)
429 {
430 if (!blob_handler)
431 return;
432
433 comp_free_data_blob(blob_handler);
434
435 rfree(blob_handler);
436 }
437
comp_make_shared(struct comp_dev * dev)438 struct comp_dev *comp_make_shared(struct comp_dev *dev)
439 {
440 struct list_item *old_bsource_list = &dev->bsource_list;
441 struct list_item *old_bsink_list = &dev->bsink_list;
442
443 /* flush cache to share */
444 dcache_writeback_region(dev, dev->size);
445
446 dev = platform_shared_get(dev, dev->size);
447
448 /* re-link lists with the new heads addresses, init would cut
449 * links to existing items, local already connected buffers
450 */
451 list_relink(&dev->bsource_list, old_bsource_list);
452 list_relink(&dev->bsink_list, old_bsink_list);
453 dev->is_shared = true;
454
455 return dev;
456 }
457