1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. |
4 | // |
5 | // Authors: Cezary Rojewski <cezary.rojewski@intel.com> |
6 | // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> |
7 | // |
8 | |
9 | #include <linux/slab.h> |
10 | #include "avs.h" |
11 | #include "messages.h" |
12 | |
13 | #define AVS_CL_TIMEOUT_MS 5000 |
14 | |
15 | int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge) |
16 | { |
17 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(ROM_CONTROL); |
18 | struct avs_ipc_msg request = {{0}}; |
19 | |
20 | msg.boot_cfg.rom_ctrl_msg_type = AVS_ROM_SET_BOOT_CONFIG; |
21 | msg.boot_cfg.dma_id = dma_id; |
22 | msg.boot_cfg.purge_request = purge; |
23 | request.header = msg.val; |
24 | |
25 | return avs_dsp_send_rom_msg(adev, request: &request, name: "set boot config" ); |
26 | } |
27 | |
28 | int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids) |
29 | { |
30 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES); |
31 | struct avs_ipc_msg request; |
32 | |
33 | msg.load_multi_mods.mod_cnt = num_mod_ids; |
34 | request.header = msg.val; |
35 | request.data = mod_ids; |
36 | request.size = sizeof(*mod_ids) * num_mod_ids; |
37 | |
38 | return avs_dsp_send_msg_timeout(adev, request: &request, NULL, AVS_CL_TIMEOUT_MS, |
39 | name: "load multiple modules" ); |
40 | } |
41 | |
42 | int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids) |
43 | { |
44 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(UNLOAD_MULTIPLE_MODULES); |
45 | struct avs_ipc_msg request; |
46 | |
47 | msg.load_multi_mods.mod_cnt = num_mod_ids; |
48 | request.header = msg.val; |
49 | request.data = mod_ids; |
50 | request.size = sizeof(*mod_ids) * num_mod_ids; |
51 | |
52 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "unload multiple modules" ); |
53 | } |
54 | |
55 | int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id) |
56 | { |
57 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY); |
58 | struct avs_ipc_msg request = {{0}}; |
59 | |
60 | msg.load_lib.dma_id = dma_id; |
61 | msg.load_lib.lib_id = lib_id; |
62 | request.header = msg.val; |
63 | |
64 | return avs_dsp_send_msg_timeout(adev, request: &request, NULL, AVS_CL_TIMEOUT_MS, name: "load library" ); |
65 | } |
66 | |
67 | int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, |
68 | u8 instance_id, bool lp, u16 attributes) |
69 | { |
70 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE); |
71 | struct avs_ipc_msg request = {{0}}; |
72 | |
73 | msg.create_ppl.ppl_mem_size = req_size; |
74 | msg.create_ppl.ppl_priority = priority; |
75 | msg.create_ppl.instance_id = instance_id; |
76 | msg.ext.create_ppl.lp = lp; |
77 | msg.ext.create_ppl.attributes = attributes; |
78 | request.header = msg.val; |
79 | |
80 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "create pipeline" ); |
81 | } |
82 | |
83 | int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id) |
84 | { |
85 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(DELETE_PIPELINE); |
86 | struct avs_ipc_msg request = {{0}}; |
87 | |
88 | msg.ppl.instance_id = instance_id; |
89 | request.header = msg.val; |
90 | |
91 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "delete pipeline" ); |
92 | } |
93 | |
94 | int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id, |
95 | enum avs_pipeline_state state) |
96 | { |
97 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(SET_PIPELINE_STATE); |
98 | struct avs_ipc_msg request = {{0}}; |
99 | |
100 | msg.set_ppl_state.ppl_id = instance_id; |
101 | msg.set_ppl_state.state = state; |
102 | request.header = msg.val; |
103 | |
104 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "set pipeline state" ); |
105 | } |
106 | |
107 | int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id, |
108 | enum avs_pipeline_state *state) |
109 | { |
110 | union avs_global_msg msg = AVS_GLOBAL_REQUEST(GET_PIPELINE_STATE); |
111 | struct avs_ipc_msg request = {{0}}; |
112 | struct avs_ipc_msg reply = {{0}}; |
113 | int ret; |
114 | |
115 | msg.get_ppl_state.ppl_id = instance_id; |
116 | request.header = msg.val; |
117 | |
118 | ret = avs_dsp_send_msg(adev, request: &request, reply: &reply, name: "get pipeline state" ); |
119 | if (!ret) |
120 | *state = reply.rsp.ext.get_ppl_state.state; |
121 | return ret; |
122 | } |
123 | |
124 | /* |
125 | * avs_ipc_init_instance - Initialize module instance |
126 | * |
127 | * @adev: Driver context |
128 | * @module_id: Module-type id |
129 | * @instance_id: Unique module instance id |
130 | * @ppl_id: Parent pipeline id |
131 | * @core_id: DSP core to allocate module on |
132 | * @domain: Processing domain (low latency or data processing) |
133 | * @param: Module-type specific configuration |
134 | * @param_size: Size of @param in bytes |
135 | * |
136 | * Argument verification, as well as pipeline state checks are done by the |
137 | * firmware. |
138 | * |
139 | * Note: @ppl_id and @core_id are independent of each other as single pipeline |
140 | * can be composed of module instances located on different DSP cores. |
141 | */ |
142 | int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id, |
143 | u8 ppl_id, u8 core_id, u8 domain, |
144 | void *param, u32 param_size) |
145 | { |
146 | union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE); |
147 | struct avs_ipc_msg request; |
148 | |
149 | msg.module_id = module_id; |
150 | msg.instance_id = instance_id; |
151 | /* firmware expects size provided in dwords */ |
152 | msg.ext.init_instance.param_block_size = DIV_ROUND_UP(param_size, sizeof(u32)); |
153 | msg.ext.init_instance.ppl_instance_id = ppl_id; |
154 | msg.ext.init_instance.core_id = core_id; |
155 | msg.ext.init_instance.proc_domain = domain; |
156 | |
157 | request.header = msg.val; |
158 | request.data = param; |
159 | request.size = param_size; |
160 | |
161 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "init instance" ); |
162 | } |
163 | |
164 | /* |
165 | * avs_ipc_delete_instance - Delete module instance |
166 | * |
167 | * @adev: Driver context |
168 | * @module_id: Module-type id |
169 | * @instance_id: Unique module instance id |
170 | * |
171 | * Argument verification, as well as pipeline state checks are done by the |
172 | * firmware. |
173 | * |
174 | * Note: only standalone modules i.e. without a parent pipeline shall be |
175 | * deleted using this IPC message. In all other cases, pipeline owning the |
176 | * modules performs cleanup automatically when it is deleted. |
177 | */ |
178 | int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id) |
179 | { |
180 | union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE); |
181 | struct avs_ipc_msg request = {{0}}; |
182 | |
183 | msg.module_id = module_id; |
184 | msg.instance_id = instance_id; |
185 | request.header = msg.val; |
186 | |
187 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "delete instance" ); |
188 | } |
189 | |
190 | /* |
191 | * avs_ipc_bind - Bind two module instances |
192 | * |
193 | * @adev: Driver context |
194 | * @module_id: Source module-type id |
195 | * @instance_id: Source module instance id |
196 | * @dst_module_id: Sink module-type id |
197 | * @dst_instance_id: Sink module instance id |
198 | * @dst_queue: Sink module pin to bind @src_queue with |
199 | * @src_queue: Source module pin to bind @dst_queue with |
200 | */ |
201 | int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id, |
202 | u16 dst_module_id, u8 dst_instance_id, |
203 | u8 dst_queue, u8 src_queue) |
204 | { |
205 | union avs_module_msg msg = AVS_MODULE_REQUEST(BIND); |
206 | struct avs_ipc_msg request = {{0}}; |
207 | |
208 | msg.module_id = module_id; |
209 | msg.instance_id = instance_id; |
210 | msg.ext.bind_unbind.dst_module_id = dst_module_id; |
211 | msg.ext.bind_unbind.dst_instance_id = dst_instance_id; |
212 | msg.ext.bind_unbind.dst_queue = dst_queue; |
213 | msg.ext.bind_unbind.src_queue = src_queue; |
214 | request.header = msg.val; |
215 | |
216 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "bind modules" ); |
217 | } |
218 | |
219 | /* |
220 | * avs_ipc_unbind - Unbind two module instances |
221 | * |
222 | * @adev: Driver context |
223 | * @module_id: Source module-type id |
224 | * @instance_id: Source module instance id |
225 | * @dst_module_id: Sink module-type id |
226 | * @dst_instance_id: Sink module instance id |
227 | * @dst_queue: Sink module pin to unbind @src_queue from |
228 | * @src_queue: Source module pin to unbind @dst_queue from |
229 | */ |
230 | int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id, |
231 | u16 dst_module_id, u8 dst_instance_id, |
232 | u8 dst_queue, u8 src_queue) |
233 | { |
234 | union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND); |
235 | struct avs_ipc_msg request = {{0}}; |
236 | |
237 | msg.module_id = module_id; |
238 | msg.instance_id = instance_id; |
239 | msg.ext.bind_unbind.dst_module_id = dst_module_id; |
240 | msg.ext.bind_unbind.dst_instance_id = dst_instance_id; |
241 | msg.ext.bind_unbind.dst_queue = dst_queue; |
242 | msg.ext.bind_unbind.src_queue = src_queue; |
243 | request.header = msg.val; |
244 | |
245 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "unbind modules" ); |
246 | } |
247 | |
248 | static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id, |
249 | u8 param_id, bool init_block, bool final_block, |
250 | u8 *request_data, size_t request_size, size_t off_size) |
251 | { |
252 | union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_SET); |
253 | struct avs_ipc_msg request; |
254 | |
255 | msg.module_id = module_id; |
256 | msg.instance_id = instance_id; |
257 | msg.ext.large_config.data_off_size = off_size; |
258 | msg.ext.large_config.large_param_id = param_id; |
259 | msg.ext.large_config.final_block = final_block; |
260 | msg.ext.large_config.init_block = init_block; |
261 | |
262 | request.header = msg.val; |
263 | request.data = request_data; |
264 | request.size = request_size; |
265 | |
266 | return avs_dsp_send_msg(adev, request: &request, NULL, name: "large config set" ); |
267 | } |
268 | |
269 | int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, |
270 | u8 instance_id, u8 param_id, |
271 | u8 *request, size_t request_size) |
272 | { |
273 | size_t remaining, tx_size; |
274 | bool final; |
275 | int ret; |
276 | |
277 | remaining = request_size; |
278 | tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining); |
279 | final = (tx_size == remaining); |
280 | |
281 | /* Initial request states total payload size. */ |
282 | ret = __avs_ipc_set_large_config(adev, module_id, instance_id, |
283 | param_id, init_block: 1, final_block: final, request_data: request, request_size: tx_size, |
284 | off_size: request_size); |
285 | if (ret) |
286 | return ret; |
287 | |
288 | remaining -= tx_size; |
289 | |
290 | /* Loop the rest only when payload exceeds mailbox's size. */ |
291 | while (remaining) { |
292 | size_t offset; |
293 | |
294 | offset = request_size - remaining; |
295 | tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining); |
296 | final = (tx_size == remaining); |
297 | |
298 | ret = __avs_ipc_set_large_config(adev, module_id, instance_id, |
299 | param_id, init_block: 0, final_block: final, |
300 | request_data: request + offset, request_size: tx_size, |
301 | off_size: offset); |
302 | if (ret) |
303 | return ret; |
304 | |
305 | remaining -= tx_size; |
306 | } |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id, |
312 | u8 param_id, u8 *request_data, size_t request_size, |
313 | u8 **reply_data, size_t *reply_size) |
314 | { |
315 | union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_GET); |
316 | struct avs_ipc_msg request; |
317 | struct avs_ipc_msg reply = {{0}}; |
318 | void *buf; |
319 | int ret; |
320 | |
321 | reply.data = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); |
322 | if (!reply.data) |
323 | return -ENOMEM; |
324 | |
325 | msg.module_id = module_id; |
326 | msg.instance_id = instance_id; |
327 | msg.ext.large_config.data_off_size = request_size; |
328 | msg.ext.large_config.large_param_id = param_id; |
329 | /* final_block is always 0 on request. Updated by fw on reply. */ |
330 | msg.ext.large_config.final_block = 0; |
331 | msg.ext.large_config.init_block = 1; |
332 | |
333 | request.header = msg.val; |
334 | request.data = request_data; |
335 | request.size = request_size; |
336 | reply.size = AVS_MAILBOX_SIZE; |
337 | |
338 | ret = avs_dsp_send_msg(adev, request: &request, reply: &reply, name: "large config get" ); |
339 | if (ret) { |
340 | kfree(objp: reply.data); |
341 | return ret; |
342 | } |
343 | |
344 | buf = krealloc(objp: reply.data, new_size: reply.size, GFP_KERNEL); |
345 | if (!buf) { |
346 | kfree(objp: reply.data); |
347 | return -ENOMEM; |
348 | } |
349 | |
350 | *reply_data = buf; |
351 | *reply_size = reply.size; |
352 | |
353 | return 0; |
354 | } |
355 | |
356 | int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup) |
357 | { |
358 | union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX); |
359 | struct avs_ipc_msg request; |
360 | struct avs_dxstate_info dx; |
361 | |
362 | dx.core_mask = core_mask; |
363 | dx.dx_mask = powerup ? core_mask : 0; |
364 | request.header = msg.val; |
365 | request.data = &dx; |
366 | request.size = sizeof(dx); |
367 | |
368 | return avs_dsp_send_pm_msg(adev, request: &request, NULL, wake_d0i0: true, name: "set dx" ); |
369 | } |
370 | |
371 | /* |
372 | * avs_ipc_set_d0ix - Set power gating policy (entering D0IX substates) |
373 | * |
374 | * @enable_pg: Whether to enable or disable power gating |
375 | * @streaming: Whether a stream is running when transitioning |
376 | */ |
377 | int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming) |
378 | { |
379 | union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX); |
380 | struct avs_ipc_msg request = {{0}}; |
381 | |
382 | msg.ext.set_d0ix.wake = enable_pg; |
383 | msg.ext.set_d0ix.streaming = streaming; |
384 | msg.ext.set_d0ix.prevent_pg = !enable_pg; |
385 | |
386 | request.header = msg.val; |
387 | |
388 | return avs_dsp_send_pm_msg(adev, request: &request, NULL, wake_d0i0: false, name: "set d0ix" ); |
389 | } |
390 | |
391 | int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg) |
392 | { |
393 | struct avs_tlv *tlv; |
394 | size_t payload_size; |
395 | size_t offset = 0; |
396 | u8 *payload; |
397 | int ret; |
398 | |
399 | ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, |
400 | param_id: AVS_BASEFW_FIRMWARE_CONFIG, NULL, request_size: 0, |
401 | reply_data: &payload, reply_size: &payload_size); |
402 | if (ret) |
403 | return ret; |
404 | /* Non-zero payload expected for FIRMWARE_CONFIG. */ |
405 | if (!payload_size) |
406 | return -EREMOTEIO; |
407 | |
408 | while (offset < payload_size) { |
409 | tlv = (struct avs_tlv *)(payload + offset); |
410 | |
411 | switch (tlv->type) { |
412 | case AVS_FW_CFG_FW_VERSION: |
413 | memcpy(&cfg->fw_version, tlv->value, sizeof(cfg->fw_version)); |
414 | break; |
415 | |
416 | case AVS_FW_CFG_MEMORY_RECLAIMED: |
417 | cfg->memory_reclaimed = *tlv->value; |
418 | break; |
419 | |
420 | case AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ: |
421 | cfg->slow_clock_freq_hz = *tlv->value; |
422 | break; |
423 | |
424 | case AVS_FW_CFG_FAST_CLOCK_FREQ_HZ: |
425 | cfg->fast_clock_freq_hz = *tlv->value; |
426 | break; |
427 | |
428 | case AVS_FW_CFG_ALH_SUPPORT_LEVEL: |
429 | cfg->alh_support = *tlv->value; |
430 | break; |
431 | |
432 | case AVS_FW_CFG_IPC_DL_MAILBOX_BYTES: |
433 | cfg->ipc_dl_mailbox_bytes = *tlv->value; |
434 | break; |
435 | |
436 | case AVS_FW_CFG_IPC_UL_MAILBOX_BYTES: |
437 | cfg->ipc_ul_mailbox_bytes = *tlv->value; |
438 | break; |
439 | |
440 | case AVS_FW_CFG_TRACE_LOG_BYTES: |
441 | cfg->trace_log_bytes = *tlv->value; |
442 | break; |
443 | |
444 | case AVS_FW_CFG_MAX_PPL_COUNT: |
445 | cfg->max_ppl_count = *tlv->value; |
446 | break; |
447 | |
448 | case AVS_FW_CFG_MAX_ASTATE_COUNT: |
449 | cfg->max_astate_count = *tlv->value; |
450 | break; |
451 | |
452 | case AVS_FW_CFG_MAX_MODULE_PIN_COUNT: |
453 | cfg->max_module_pin_count = *tlv->value; |
454 | break; |
455 | |
456 | case AVS_FW_CFG_MODULES_COUNT: |
457 | cfg->modules_count = *tlv->value; |
458 | break; |
459 | |
460 | case AVS_FW_CFG_MAX_MOD_INST_COUNT: |
461 | cfg->max_mod_inst_count = *tlv->value; |
462 | break; |
463 | |
464 | case AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT: |
465 | cfg->max_ll_tasks_per_pri_count = *tlv->value; |
466 | break; |
467 | |
468 | case AVS_FW_CFG_LL_PRI_COUNT: |
469 | cfg->ll_pri_count = *tlv->value; |
470 | break; |
471 | |
472 | case AVS_FW_CFG_MAX_DP_TASKS_COUNT: |
473 | cfg->max_dp_tasks_count = *tlv->value; |
474 | break; |
475 | |
476 | case AVS_FW_CFG_MAX_LIBS_COUNT: |
477 | cfg->max_libs_count = *tlv->value; |
478 | break; |
479 | |
480 | case AVS_FW_CFG_XTAL_FREQ_HZ: |
481 | cfg->xtal_freq_hz = *tlv->value; |
482 | break; |
483 | |
484 | case AVS_FW_CFG_POWER_GATING_POLICY: |
485 | cfg->power_gating_policy = *tlv->value; |
486 | break; |
487 | |
488 | /* Known but not useful to us. */ |
489 | case AVS_FW_CFG_DMA_BUFFER_CONFIG: |
490 | case AVS_FW_CFG_SCHEDULER_CONFIG: |
491 | case AVS_FW_CFG_CLOCKS_CONFIG: |
492 | case AVS_FW_CFG_RESERVED: |
493 | break; |
494 | |
495 | default: |
496 | dev_info(adev->dev, "Unrecognized fw param: %d\n" , tlv->type); |
497 | break; |
498 | } |
499 | |
500 | offset += sizeof(*tlv) + tlv->length; |
501 | } |
502 | |
503 | /* No longer needed, free it as it's owned by the get_large_config() caller. */ |
504 | kfree(objp: payload); |
505 | return ret; |
506 | } |
507 | |
508 | int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) |
509 | { |
510 | struct avs_tlv *tlv; |
511 | size_t payload_size; |
512 | size_t size, offset = 0; |
513 | u8 *payload; |
514 | int ret; |
515 | |
516 | ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, |
517 | param_id: AVS_BASEFW_HARDWARE_CONFIG, NULL, request_size: 0, |
518 | reply_data: &payload, reply_size: &payload_size); |
519 | if (ret) |
520 | return ret; |
521 | /* Non-zero payload expected for HARDWARE_CONFIG. */ |
522 | if (!payload_size) |
523 | return -EREMOTEIO; |
524 | |
525 | while (offset < payload_size) { |
526 | tlv = (struct avs_tlv *)(payload + offset); |
527 | |
528 | switch (tlv->type) { |
529 | case AVS_HW_CFG_AVS_VER: |
530 | cfg->avs_version = *tlv->value; |
531 | break; |
532 | |
533 | case AVS_HW_CFG_DSP_CORES: |
534 | cfg->dsp_cores = *tlv->value; |
535 | break; |
536 | |
537 | case AVS_HW_CFG_MEM_PAGE_BYTES: |
538 | cfg->mem_page_bytes = *tlv->value; |
539 | break; |
540 | |
541 | case AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES: |
542 | cfg->total_phys_mem_pages = *tlv->value; |
543 | break; |
544 | |
545 | case AVS_HW_CFG_I2S_CAPS: |
546 | cfg->i2s_caps.i2s_version = tlv->value[0]; |
547 | size = tlv->value[1]; |
548 | cfg->i2s_caps.ctrl_count = size; |
549 | if (!size) |
550 | break; |
551 | |
552 | /* Multiply to get entire array size. */ |
553 | size *= sizeof(*cfg->i2s_caps.ctrl_base_addr); |
554 | cfg->i2s_caps.ctrl_base_addr = devm_kmemdup(dev: adev->dev, |
555 | src: &tlv->value[2], |
556 | len: size, GFP_KERNEL); |
557 | if (!cfg->i2s_caps.ctrl_base_addr) { |
558 | ret = -ENOMEM; |
559 | goto exit; |
560 | } |
561 | break; |
562 | |
563 | case AVS_HW_CFG_GATEWAY_COUNT: |
564 | cfg->gateway_count = *tlv->value; |
565 | break; |
566 | |
567 | case AVS_HW_CFG_HP_EBB_COUNT: |
568 | cfg->hp_ebb_count = *tlv->value; |
569 | break; |
570 | |
571 | case AVS_HW_CFG_LP_EBB_COUNT: |
572 | cfg->lp_ebb_count = *tlv->value; |
573 | break; |
574 | |
575 | case AVS_HW_CFG_EBB_SIZE_BYTES: |
576 | cfg->ebb_size_bytes = *tlv->value; |
577 | break; |
578 | |
579 | case AVS_HW_CFG_GPDMA_CAPS: |
580 | break; |
581 | |
582 | default: |
583 | dev_info(adev->dev, "Unrecognized hw config: %d\n" , tlv->type); |
584 | break; |
585 | } |
586 | |
587 | offset += sizeof(*tlv) + tlv->length; |
588 | } |
589 | |
590 | exit: |
591 | /* No longer needed, free it as it's owned by the get_large_config() caller. */ |
592 | kfree(objp: payload); |
593 | return ret; |
594 | } |
595 | |
596 | int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) |
597 | { |
598 | size_t payload_size; |
599 | u8 *payload; |
600 | int ret; |
601 | |
602 | ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, |
603 | param_id: AVS_BASEFW_MODULES_INFO, NULL, request_size: 0, |
604 | reply_data: &payload, reply_size: &payload_size); |
605 | if (ret) |
606 | return ret; |
607 | /* Non-zero payload expected for MODULES_INFO. */ |
608 | if (!payload_size) |
609 | return -EREMOTEIO; |
610 | |
611 | *info = (struct avs_mods_info *)payload; |
612 | return 0; |
613 | } |
614 | |
615 | int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, |
616 | u8 instance_id, u32 sink_id, |
617 | const struct avs_audio_format *src_fmt, |
618 | const struct avs_audio_format *sink_fmt) |
619 | { |
620 | struct avs_copier_sink_format cpr_fmt; |
621 | |
622 | cpr_fmt.sink_id = sink_id; |
623 | /* Firmware expects driver to resend copier's input format. */ |
624 | cpr_fmt.src_fmt = *src_fmt; |
625 | cpr_fmt.sink_fmt = *sink_fmt; |
626 | |
627 | return avs_ipc_set_large_config(adev, module_id, instance_id, |
628 | param_id: AVS_COPIER_SET_SINK_FORMAT, |
629 | request: (u8 *)&cpr_fmt, request_size: sizeof(cpr_fmt)); |
630 | } |
631 | |
632 | int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, |
633 | struct avs_volume_cfg *vol) |
634 | { |
635 | return avs_ipc_set_large_config(adev, module_id, instance_id, param_id: AVS_PEAKVOL_VOLUME, request: (u8 *)vol, |
636 | request_size: sizeof(*vol)); |
637 | } |
638 | |
639 | int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, |
640 | struct avs_volume_cfg **vols, size_t *num_vols) |
641 | { |
642 | size_t payload_size; |
643 | u8 *payload; |
644 | int ret; |
645 | |
646 | ret = avs_ipc_get_large_config(adev, module_id, instance_id, param_id: AVS_PEAKVOL_VOLUME, NULL, request_size: 0, |
647 | reply_data: &payload, reply_size: &payload_size); |
648 | if (ret) |
649 | return ret; |
650 | |
651 | /* Non-zero payload expected for PEAKVOL_VOLUME. */ |
652 | if (!payload_size) |
653 | return -EREMOTEIO; |
654 | |
655 | *vols = (struct avs_volume_cfg *)payload; |
656 | *num_vols = payload_size / sizeof(**vols); |
657 | |
658 | return 0; |
659 | } |
660 | |
661 | #ifdef CONFIG_DEBUG_FS |
662 | int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) |
663 | { |
664 | return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, |
665 | param_id: AVS_BASEFW_ENABLE_LOGS, request: log_info, request_size: size); |
666 | } |
667 | |
668 | int avs_ipc_set_system_time(struct avs_dev *adev) |
669 | { |
670 | struct avs_sys_time sys_time; |
671 | u64 us; |
672 | |
673 | /* firmware expects UTC time in micro seconds */ |
674 | us = ktime_to_us(kt: ktime_get()); |
675 | sys_time.val_l = us & UINT_MAX; |
676 | sys_time.val_u = us >> 32; |
677 | |
678 | return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, |
679 | param_id: AVS_BASEFW_SYSTEM_TIME, request: (u8 *)&sys_time, request_size: sizeof(sys_time)); |
680 | } |
681 | |
682 | int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas) |
683 | { |
684 | size_t payload_size; |
685 | u32 module_id; |
686 | u8 *payload; |
687 | int ret; |
688 | |
689 | module_id = avs_get_module_id(adev, uuid: &AVS_PROBE_MOD_UUID); |
690 | |
691 | ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, param_id: AVS_PROBE_INJECTION_DMA, |
692 | NULL, request_size: 0, reply_data: &payload, reply_size: &payload_size); |
693 | if (ret) |
694 | return ret; |
695 | |
696 | *dmas = (struct avs_probe_dma *)payload; |
697 | *num_dmas = payload_size / sizeof(**dmas); |
698 | |
699 | return 0; |
700 | } |
701 | |
702 | int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas) |
703 | { |
704 | u32 module_id = avs_get_module_id(adev, uuid: &AVS_PROBE_MOD_UUID); |
705 | |
706 | return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, param_id: AVS_PROBE_INJECTION_DMA, |
707 | request: (u8 *)dmas, array_size(sizeof(*dmas), num_dmas)); |
708 | } |
709 | |
710 | int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids, |
711 | size_t num_node_ids) |
712 | { |
713 | u32 module_id = avs_get_module_id(adev, uuid: &AVS_PROBE_MOD_UUID); |
714 | |
715 | return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, |
716 | param_id: AVS_PROBE_INJECTION_DMA_DETACH, request: (u8 *)node_ids, |
717 | array_size(sizeof(*node_ids), num_node_ids)); |
718 | } |
719 | |
720 | int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs, |
721 | size_t *num_descs) |
722 | { |
723 | size_t payload_size; |
724 | u32 module_id; |
725 | u8 *payload; |
726 | int ret; |
727 | |
728 | module_id = avs_get_module_id(adev, uuid: &AVS_PROBE_MOD_UUID); |
729 | |
730 | ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, param_id: AVS_PROBE_POINTS, NULL, |
731 | request_size: 0, reply_data: &payload, reply_size: &payload_size); |
732 | if (ret) |
733 | return ret; |
734 | |
735 | *descs = (struct avs_probe_point_desc *)payload; |
736 | *num_descs = payload_size / sizeof(**descs); |
737 | |
738 | return 0; |
739 | } |
740 | |
741 | int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs, |
742 | size_t num_descs) |
743 | { |
744 | u32 module_id = avs_get_module_id(adev, uuid: &AVS_PROBE_MOD_UUID); |
745 | |
746 | return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, param_id: AVS_PROBE_POINTS, |
747 | request: (u8 *)descs, array_size(sizeof(*descs), num_descs)); |
748 | } |
749 | |
750 | int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids, |
751 | size_t num_ids) |
752 | { |
753 | u32 module_id = avs_get_module_id(adev, uuid: &AVS_PROBE_MOD_UUID); |
754 | |
755 | return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, |
756 | param_id: AVS_PROBE_POINTS_DISCONNECT, request: (u8 *)ids, |
757 | array_size(sizeof(*ids), num_ids)); |
758 | } |
759 | #endif |
760 | |