]>
Commit | Line | Data |
---|---|---|
b81fd263 SP |
1 | /* |
2 | * skl-sst-ipc.c - Intel skl IPC Support | |
3 | * | |
4 | * Copyright (C) 2014-15, Intel Corporation. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | */ | |
15 | #include <linux/device.h> | |
16 | ||
17 | #include "../common/sst-dsp.h" | |
18 | #include "../common/sst-dsp-priv.h" | |
721c3e36 | 19 | #include "skl.h" |
b81fd263 SP |
20 | #include "skl-sst-dsp.h" |
21 | #include "skl-sst-ipc.h" | |
721c3e36 | 22 | #include "sound/hdaudio_ext.h" |
b81fd263 SP |
23 | |
24 | ||
25 | #define IPC_IXC_STATUS_BITS 24 | |
26 | ||
27 | /* Global Message - Generic */ | |
28 | #define IPC_GLB_TYPE_SHIFT 24 | |
29 | #define IPC_GLB_TYPE_MASK (0xf << IPC_GLB_TYPE_SHIFT) | |
30 | #define IPC_GLB_TYPE(x) ((x) << IPC_GLB_TYPE_SHIFT) | |
31 | ||
32 | /* Global Message - Reply */ | |
33 | #define IPC_GLB_REPLY_STATUS_SHIFT 24 | |
34 | #define IPC_GLB_REPLY_STATUS_MASK ((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1) | |
35 | #define IPC_GLB_REPLY_STATUS(x) ((x) << IPC_GLB_REPLY_STATUS_SHIFT) | |
36 | ||
37 | #define IPC_TIMEOUT_MSECS 3000 | |
38 | ||
39 | #define IPC_EMPTY_LIST_SIZE 8 | |
40 | ||
41 | #define IPC_MSG_TARGET_SHIFT 30 | |
42 | #define IPC_MSG_TARGET_MASK 0x1 | |
43 | #define IPC_MSG_TARGET(x) (((x) & IPC_MSG_TARGET_MASK) \ | |
44 | << IPC_MSG_TARGET_SHIFT) | |
45 | ||
46 | #define IPC_MSG_DIR_SHIFT 29 | |
47 | #define IPC_MSG_DIR_MASK 0x1 | |
48 | #define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ | |
49 | << IPC_MSG_DIR_SHIFT) | |
50 | /* Global Notification Message */ | |
51 | #define IPC_GLB_NOTIFY_TYPE_SHIFT 16 | |
52 | #define IPC_GLB_NOTIFY_TYPE_MASK 0xFF | |
53 | #define IPC_GLB_NOTIFY_TYPE(x) (((x) >> IPC_GLB_NOTIFY_TYPE_SHIFT) \ | |
54 | & IPC_GLB_NOTIFY_TYPE_MASK) | |
55 | ||
56 | #define IPC_GLB_NOTIFY_MSG_TYPE_SHIFT 24 | |
57 | #define IPC_GLB_NOTIFY_MSG_TYPE_MASK 0x1F | |
58 | #define IPC_GLB_NOTIFY_MSG_TYPE(x) (((x) >> IPC_GLB_NOTIFY_MSG_TYPE_SHIFT) \ | |
59 | & IPC_GLB_NOTIFY_MSG_TYPE_MASK) | |
60 | ||
61 | #define IPC_GLB_NOTIFY_RSP_SHIFT 29 | |
62 | #define IPC_GLB_NOTIFY_RSP_MASK 0x1 | |
63 | #define IPC_GLB_NOTIFY_RSP_TYPE(x) (((x) >> IPC_GLB_NOTIFY_RSP_SHIFT) \ | |
64 | & IPC_GLB_NOTIFY_RSP_MASK) | |
65 | ||
66 | /* Pipeline operations */ | |
67 | ||
68 | /* Create pipeline message */ | |
69 | #define IPC_PPL_MEM_SIZE_SHIFT 0 | |
70 | #define IPC_PPL_MEM_SIZE_MASK 0x7FF | |
71 | #define IPC_PPL_MEM_SIZE(x) (((x) & IPC_PPL_MEM_SIZE_MASK) \ | |
72 | << IPC_PPL_MEM_SIZE_SHIFT) | |
73 | ||
74 | #define IPC_PPL_TYPE_SHIFT 11 | |
75 | #define IPC_PPL_TYPE_MASK 0x1F | |
76 | #define IPC_PPL_TYPE(x) (((x) & IPC_PPL_TYPE_MASK) \ | |
77 | << IPC_PPL_TYPE_SHIFT) | |
78 | ||
79 | #define IPC_INSTANCE_ID_SHIFT 16 | |
80 | #define IPC_INSTANCE_ID_MASK 0xFF | |
81 | #define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \ | |
82 | << IPC_INSTANCE_ID_SHIFT) | |
83 | ||
8a0cb236 VK |
84 | #define IPC_PPL_LP_MODE_SHIFT 0 |
85 | #define IPC_PPL_LP_MODE_MASK 0x1 | |
86 | #define IPC_PPL_LP_MODE(x) (((x) & IPC_PPL_LP_MODE_MASK) \ | |
87 | << IPC_PPL_LP_MODE_SHIFT) | |
88 | ||
b81fd263 SP |
89 | /* Set pipeline state message */ |
90 | #define IPC_PPL_STATE_SHIFT 0 | |
91 | #define IPC_PPL_STATE_MASK 0x1F | |
92 | #define IPC_PPL_STATE(x) (((x) & IPC_PPL_STATE_MASK) \ | |
93 | << IPC_PPL_STATE_SHIFT) | |
94 | ||
95 | /* Module operations primary register */ | |
96 | #define IPC_MOD_ID_SHIFT 0 | |
97 | #define IPC_MOD_ID_MASK 0xFFFF | |
98 | #define IPC_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \ | |
99 | << IPC_MOD_ID_SHIFT) | |
100 | ||
101 | #define IPC_MOD_INSTANCE_ID_SHIFT 16 | |
102 | #define IPC_MOD_INSTANCE_ID_MASK 0xFF | |
103 | #define IPC_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \ | |
104 | << IPC_MOD_INSTANCE_ID_SHIFT) | |
105 | ||
106 | /* Init instance message extension register */ | |
107 | #define IPC_PARAM_BLOCK_SIZE_SHIFT 0 | |
108 | #define IPC_PARAM_BLOCK_SIZE_MASK 0xFFFF | |
109 | #define IPC_PARAM_BLOCK_SIZE(x) (((x) & IPC_PARAM_BLOCK_SIZE_MASK) \ | |
110 | << IPC_PARAM_BLOCK_SIZE_SHIFT) | |
111 | ||
112 | #define IPC_PPL_INSTANCE_ID_SHIFT 16 | |
113 | #define IPC_PPL_INSTANCE_ID_MASK 0xFF | |
114 | #define IPC_PPL_INSTANCE_ID(x) (((x) & IPC_PPL_INSTANCE_ID_MASK) \ | |
115 | << IPC_PPL_INSTANCE_ID_SHIFT) | |
116 | ||
117 | #define IPC_CORE_ID_SHIFT 24 | |
118 | #define IPC_CORE_ID_MASK 0x1F | |
119 | #define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \ | |
120 | << IPC_CORE_ID_SHIFT) | |
121 | ||
3d4006cd SV |
122 | #define IPC_DOMAIN_SHIFT 28 |
123 | #define IPC_DOMAIN_MASK 0x1 | |
124 | #define IPC_DOMAIN(x) (((x) & IPC_DOMAIN_MASK) \ | |
125 | << IPC_DOMAIN_SHIFT) | |
126 | ||
b81fd263 SP |
127 | /* Bind/Unbind message extension register */ |
128 | #define IPC_DST_MOD_ID_SHIFT 0 | |
129 | #define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \ | |
130 | << IPC_DST_MOD_ID_SHIFT) | |
131 | ||
132 | #define IPC_DST_MOD_INSTANCE_ID_SHIFT 16 | |
133 | #define IPC_DST_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \ | |
134 | << IPC_DST_MOD_INSTANCE_ID_SHIFT) | |
135 | ||
136 | #define IPC_DST_QUEUE_SHIFT 24 | |
137 | #define IPC_DST_QUEUE_MASK 0x7 | |
138 | #define IPC_DST_QUEUE(x) (((x) & IPC_DST_QUEUE_MASK) \ | |
139 | << IPC_DST_QUEUE_SHIFT) | |
140 | ||
141 | #define IPC_SRC_QUEUE_SHIFT 27 | |
142 | #define IPC_SRC_QUEUE_MASK 0x7 | |
143 | #define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \ | |
144 | << IPC_SRC_QUEUE_SHIFT) | |
6c5768b3 D |
145 | /* Load Module count */ |
146 | #define IPC_LOAD_MODULE_SHIFT 0 | |
147 | #define IPC_LOAD_MODULE_MASK 0xFF | |
148 | #define IPC_LOAD_MODULE_CNT(x) (((x) & IPC_LOAD_MODULE_MASK) \ | |
149 | << IPC_LOAD_MODULE_SHIFT) | |
b81fd263 SP |
150 | |
151 | /* Save pipeline messgae extension register */ | |
152 | #define IPC_DMA_ID_SHIFT 0 | |
153 | #define IPC_DMA_ID_MASK 0x1F | |
154 | #define IPC_DMA_ID(x) (((x) & IPC_DMA_ID_MASK) \ | |
155 | << IPC_DMA_ID_SHIFT) | |
156 | /* Large Config message extension register */ | |
157 | #define IPC_DATA_OFFSET_SZ_SHIFT 0 | |
158 | #define IPC_DATA_OFFSET_SZ_MASK 0xFFFFF | |
159 | #define IPC_DATA_OFFSET_SZ(x) (((x) & IPC_DATA_OFFSET_SZ_MASK) \ | |
160 | << IPC_DATA_OFFSET_SZ_SHIFT) | |
161 | #define IPC_DATA_OFFSET_SZ_CLEAR ~(IPC_DATA_OFFSET_SZ_MASK \ | |
162 | << IPC_DATA_OFFSET_SZ_SHIFT) | |
163 | ||
164 | #define IPC_LARGE_PARAM_ID_SHIFT 20 | |
165 | #define IPC_LARGE_PARAM_ID_MASK 0xFF | |
166 | #define IPC_LARGE_PARAM_ID(x) (((x) & IPC_LARGE_PARAM_ID_MASK) \ | |
167 | << IPC_LARGE_PARAM_ID_SHIFT) | |
168 | ||
169 | #define IPC_FINAL_BLOCK_SHIFT 28 | |
170 | #define IPC_FINAL_BLOCK_MASK 0x1 | |
171 | #define IPC_FINAL_BLOCK(x) (((x) & IPC_FINAL_BLOCK_MASK) \ | |
172 | << IPC_FINAL_BLOCK_SHIFT) | |
173 | ||
174 | #define IPC_INITIAL_BLOCK_SHIFT 29 | |
175 | #define IPC_INITIAL_BLOCK_MASK 0x1 | |
176 | #define IPC_INITIAL_BLOCK(x) (((x) & IPC_INITIAL_BLOCK_MASK) \ | |
177 | << IPC_INITIAL_BLOCK_SHIFT) | |
178 | #define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \ | |
179 | << IPC_INITIAL_BLOCK_SHIFT) | |
41b7523f PS |
180 | /* Set D0ix IPC extension register */ |
181 | #define IPC_D0IX_WAKE_SHIFT 0 | |
182 | #define IPC_D0IX_WAKE_MASK 0x1 | |
183 | #define IPC_D0IX_WAKE(x) (((x) & IPC_D0IX_WAKE_MASK) \ | |
184 | << IPC_D0IX_WAKE_SHIFT) | |
185 | ||
186 | #define IPC_D0IX_STREAMING_SHIFT 1 | |
187 | #define IPC_D0IX_STREAMING_MASK 0x1 | |
188 | #define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \ | |
189 | << IPC_D0IX_STREAMING_SHIFT) | |
190 | ||
b81fd263 SP |
191 | |
192 | enum skl_ipc_msg_target { | |
193 | IPC_FW_GEN_MSG = 0, | |
194 | IPC_MOD_MSG = 1 | |
195 | }; | |
196 | ||
197 | enum skl_ipc_msg_direction { | |
198 | IPC_MSG_REQUEST = 0, | |
199 | IPC_MSG_REPLY = 1 | |
200 | }; | |
201 | ||
202 | /* Global Message Types */ | |
203 | enum skl_ipc_glb_type { | |
204 | IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */ | |
205 | IPC_GLB_LOAD_MULTIPLE_MODS = 15, | |
206 | IPC_GLB_UNLOAD_MULTIPLE_MODS = 16, | |
207 | IPC_GLB_CREATE_PPL = 17, | |
208 | IPC_GLB_DELETE_PPL = 18, | |
209 | IPC_GLB_SET_PPL_STATE = 19, | |
210 | IPC_GLB_GET_PPL_STATE = 20, | |
211 | IPC_GLB_GET_PPL_CONTEXT_SIZE = 21, | |
212 | IPC_GLB_SAVE_PPL = 22, | |
213 | IPC_GLB_RESTORE_PPL = 23, | |
20fb2fbd | 214 | IPC_GLB_LOAD_LIBRARY = 24, |
b81fd263 SP |
215 | IPC_GLB_NOTIFY = 26, |
216 | IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ | |
217 | }; | |
218 | ||
219 | enum skl_ipc_glb_reply { | |
220 | IPC_GLB_REPLY_SUCCESS = 0, | |
221 | ||
222 | IPC_GLB_REPLY_UNKNOWN_MSG_TYPE = 1, | |
223 | IPC_GLB_REPLY_ERROR_INVALID_PARAM = 2, | |
224 | ||
225 | IPC_GLB_REPLY_BUSY = 3, | |
226 | IPC_GLB_REPLY_PENDING = 4, | |
227 | IPC_GLB_REPLY_FAILURE = 5, | |
228 | IPC_GLB_REPLY_INVALID_REQUEST = 6, | |
229 | ||
230 | IPC_GLB_REPLY_OUT_OF_MEMORY = 7, | |
231 | IPC_GLB_REPLY_OUT_OF_MIPS = 8, | |
232 | ||
233 | IPC_GLB_REPLY_INVALID_RESOURCE_ID = 9, | |
234 | IPC_GLB_REPLY_INVALID_RESOURCE_STATE = 10, | |
235 | ||
236 | IPC_GLB_REPLY_MOD_MGMT_ERROR = 100, | |
237 | IPC_GLB_REPLY_MOD_LOAD_CL_FAILED = 101, | |
238 | IPC_GLB_REPLY_MOD_LOAD_INVALID_HASH = 102, | |
239 | ||
240 | IPC_GLB_REPLY_MOD_UNLOAD_INST_EXIST = 103, | |
241 | IPC_GLB_REPLY_MOD_NOT_INITIALIZED = 104, | |
242 | ||
243 | IPC_GLB_REPLY_INVALID_CONFIG_PARAM_ID = 120, | |
244 | IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121, | |
245 | IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140, | |
246 | IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141, | |
247 | ||
248 | IPC_GLB_REPLY_PPL_NOT_INITIALIZED = 160, | |
249 | IPC_GLB_REPLY_PPL_NOT_EXIST = 161, | |
250 | IPC_GLB_REPLY_PPL_SAVE_FAILED = 162, | |
251 | IPC_GLB_REPLY_PPL_RESTORE_FAILED = 163, | |
252 | ||
253 | IPC_MAX_STATUS = ((1<<IPC_IXC_STATUS_BITS)-1) | |
254 | }; | |
255 | ||
256 | enum skl_ipc_notification_type { | |
257 | IPC_GLB_NOTIFY_GLITCH = 0, | |
258 | IPC_GLB_NOTIFY_OVERRUN = 1, | |
259 | IPC_GLB_NOTIFY_UNDERRUN = 2, | |
260 | IPC_GLB_NOTIFY_END_STREAM = 3, | |
261 | IPC_GLB_NOTIFY_PHRASE_DETECTED = 4, | |
262 | IPC_GLB_NOTIFY_RESOURCE_EVENT = 5, | |
263 | IPC_GLB_NOTIFY_LOG_BUFFER_STATUS = 6, | |
264 | IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7, | |
265 | IPC_GLB_NOTIFY_FW_READY = 8 | |
266 | }; | |
267 | ||
268 | /* Module Message Types */ | |
269 | enum skl_ipc_module_msg { | |
270 | IPC_MOD_INIT_INSTANCE = 0, | |
271 | IPC_MOD_CONFIG_GET = 1, | |
272 | IPC_MOD_CONFIG_SET = 2, | |
273 | IPC_MOD_LARGE_CONFIG_GET = 3, | |
274 | IPC_MOD_LARGE_CONFIG_SET = 4, | |
275 | IPC_MOD_BIND = 5, | |
276 | IPC_MOD_UNBIND = 6, | |
41b7523f PS |
277 | IPC_MOD_SET_DX = 7, |
278 | IPC_MOD_SET_D0IX = 8 | |
b81fd263 SP |
279 | }; |
280 | ||
281 | static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, | |
282 | size_t tx_size) | |
283 | { | |
284 | if (tx_size) | |
285 | memcpy(msg->tx_data, tx_data, tx_size); | |
286 | } | |
287 | ||
288 | static bool skl_ipc_is_dsp_busy(struct sst_dsp *dsp) | |
289 | { | |
290 | u32 hipci; | |
291 | ||
292 | hipci = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCI); | |
293 | return (hipci & SKL_ADSP_REG_HIPCI_BUSY); | |
294 | } | |
295 | ||
296 | /* Lock to be held by caller */ | |
297 | static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) | |
298 | { | |
299 | struct skl_ipc_header *header = (struct skl_ipc_header *)(&msg->header); | |
300 | ||
301 | if (msg->tx_size) | |
302 | sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); | |
303 | sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCIE, | |
304 | header->extension); | |
305 | sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCI, | |
306 | header->primary | SKL_ADSP_REG_HIPCI_BUSY); | |
307 | } | |
308 | ||
a83e3b4c VK |
309 | int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state) |
310 | { | |
311 | int ret; | |
312 | ||
313 | /* check D0i3 support */ | |
314 | if (!dsp->fw_ops.set_state_D0i0) | |
315 | return 0; | |
316 | ||
317 | /* Attempt D0i0 or D0i3 based on state */ | |
318 | if (state) | |
319 | ret = dsp->fw_ops.set_state_D0i0(dsp); | |
320 | else | |
321 | ret = dsp->fw_ops.set_state_D0i3(dsp); | |
322 | ||
323 | return ret; | |
324 | } | |
325 | ||
b81fd263 SP |
326 | static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, |
327 | u64 ipc_header) | |
328 | { | |
329 | struct ipc_message *msg = NULL; | |
330 | struct skl_ipc_header *header = (struct skl_ipc_header *)(&ipc_header); | |
331 | ||
332 | if (list_empty(&ipc->rx_list)) { | |
333 | dev_err(ipc->dev, "ipc: rx list is empty but received 0x%x\n", | |
334 | header->primary); | |
335 | goto out; | |
336 | } | |
337 | ||
338 | msg = list_first_entry(&ipc->rx_list, struct ipc_message, list); | |
339 | ||
340 | out: | |
341 | return msg; | |
342 | ||
343 | } | |
344 | ||
345 | static int skl_ipc_process_notification(struct sst_generic_ipc *ipc, | |
346 | struct skl_ipc_header header) | |
347 | { | |
348 | struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc); | |
349 | ||
350 | if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { | |
351 | switch (IPC_GLB_NOTIFY_TYPE(header.primary)) { | |
352 | ||
353 | case IPC_GLB_NOTIFY_UNDERRUN: | |
354 | dev_err(ipc->dev, "FW Underrun %x\n", header.primary); | |
355 | break; | |
356 | ||
357 | case IPC_GLB_NOTIFY_RESOURCE_EVENT: | |
358 | dev_err(ipc->dev, "MCPS Budget Violation: %x\n", | |
359 | header.primary); | |
360 | break; | |
361 | ||
362 | case IPC_GLB_NOTIFY_FW_READY: | |
363 | skl->boot_complete = true; | |
364 | wake_up(&skl->boot_wait); | |
365 | break; | |
366 | ||
721c3e36 D |
367 | case IPC_GLB_NOTIFY_PHRASE_DETECTED: |
368 | dev_dbg(ipc->dev, "***** Phrase Detected **********\n"); | |
369 | ||
370 | /* | |
371 | * Per HW recomendation, After phrase detection, | |
372 | * clear the CGCTL.MISCBDCGE. | |
373 | * | |
374 | * This will be set back on stream closure | |
375 | */ | |
376 | skl->enable_miscbdcge(ipc->dev, false); | |
377 | skl->miscbdcg_disabled = true; | |
378 | break; | |
379 | ||
b81fd263 | 380 | default: |
ecd286a9 | 381 | dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", |
b81fd263 SP |
382 | header.primary); |
383 | break; | |
384 | } | |
385 | } | |
386 | ||
387 | return 0; | |
388 | } | |
389 | ||
390 | static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, | |
391 | struct skl_ipc_header header) | |
392 | { | |
393 | struct ipc_message *msg; | |
394 | u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK; | |
395 | u64 *ipc_header = (u64 *)(&header); | |
396 | ||
397 | msg = skl_ipc_reply_get_msg(ipc, *ipc_header); | |
398 | if (msg == NULL) { | |
399 | dev_dbg(ipc->dev, "ipc: rx list is empty\n"); | |
400 | return; | |
401 | } | |
402 | ||
403 | /* first process the header */ | |
404 | switch (reply) { | |
405 | case IPC_GLB_REPLY_SUCCESS: | |
91c18325 | 406 | dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary); |
cce1c7f3 MJ |
407 | /* copy the rx data from the mailbox */ |
408 | sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size); | |
b81fd263 SP |
409 | break; |
410 | ||
411 | case IPC_GLB_REPLY_OUT_OF_MEMORY: | |
412 | dev_err(ipc->dev, "ipc fw reply: %x: no memory\n", header.primary); | |
413 | msg->errno = -ENOMEM; | |
414 | break; | |
415 | ||
416 | case IPC_GLB_REPLY_BUSY: | |
417 | dev_err(ipc->dev, "ipc fw reply: %x: Busy\n", header.primary); | |
418 | msg->errno = -EBUSY; | |
419 | break; | |
420 | ||
421 | default: | |
ecd286a9 | 422 | dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", reply); |
b81fd263 SP |
423 | msg->errno = -EINVAL; |
424 | break; | |
425 | } | |
426 | ||
28f3b6f1 | 427 | if (reply != IPC_GLB_REPLY_SUCCESS) { |
ecd286a9 | 428 | dev_err(ipc->dev, "ipc FW reply: reply=%d\n", reply); |
28f3b6f1 OA |
429 | dev_err(ipc->dev, "FW Error Code: %u\n", |
430 | ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); | |
431 | } | |
432 | ||
b81fd263 SP |
433 | list_del(&msg->list); |
434 | sst_ipc_tx_msg_reply_complete(ipc, msg); | |
435 | } | |
436 | ||
437 | irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) | |
438 | { | |
439 | struct sst_dsp *dsp = context; | |
440 | struct skl_sst *skl = sst_dsp_get_thread_context(dsp); | |
441 | struct sst_generic_ipc *ipc = &skl->ipc; | |
442 | struct skl_ipc_header header = {0}; | |
443 | u32 hipcie, hipct, hipcte; | |
444 | int ipc_irq = 0; | |
445 | ||
6cb00333 SP |
446 | if (dsp->intr_status & SKL_ADSPIS_CL_DMA) |
447 | skl_cldma_process_intr(dsp); | |
448 | ||
b81fd263 SP |
449 | /* Here we handle IPC interrupts only */ |
450 | if (!(dsp->intr_status & SKL_ADSPIS_IPC)) | |
451 | return IRQ_NONE; | |
452 | ||
453 | hipcie = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCIE); | |
454 | hipct = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCT); | |
455 | ||
456 | /* reply message from DSP */ | |
457 | if (hipcie & SKL_ADSP_REG_HIPCIE_DONE) { | |
458 | sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL, | |
459 | SKL_ADSP_REG_HIPCCTL_DONE, 0); | |
460 | ||
461 | /* clear DONE bit - tell DSP we have completed the operation */ | |
462 | sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCIE, | |
463 | SKL_ADSP_REG_HIPCIE_DONE, SKL_ADSP_REG_HIPCIE_DONE); | |
464 | ||
465 | ipc_irq = 1; | |
466 | ||
467 | /* unmask Done interrupt */ | |
468 | sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL, | |
469 | SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE); | |
470 | } | |
471 | ||
472 | /* New message from DSP */ | |
473 | if (hipct & SKL_ADSP_REG_HIPCT_BUSY) { | |
474 | hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE); | |
475 | header.primary = hipct; | |
476 | header.extension = hipcte; | |
ecd286a9 | 477 | dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x\n", |
b81fd263 | 478 | header.primary); |
ecd286a9 | 479 | dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x\n", |
b81fd263 SP |
480 | header.extension); |
481 | ||
482 | if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) { | |
483 | /* Handle Immediate reply from DSP Core */ | |
484 | skl_ipc_process_reply(ipc, header); | |
485 | } else { | |
486 | dev_dbg(dsp->dev, "IPC irq: Notification from firmware\n"); | |
487 | skl_ipc_process_notification(ipc, header); | |
488 | } | |
489 | /* clear busy interrupt */ | |
490 | sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCT, | |
491 | SKL_ADSP_REG_HIPCT_BUSY, SKL_ADSP_REG_HIPCT_BUSY); | |
492 | ipc_irq = 1; | |
493 | } | |
494 | ||
495 | if (ipc_irq == 0) | |
496 | return IRQ_NONE; | |
497 | ||
498 | skl_ipc_int_enable(dsp); | |
499 | ||
500 | /* continue to send any remaining messages... */ | |
786e1c37 | 501 | schedule_work(&ipc->kwork); |
b81fd263 SP |
502 | |
503 | return IRQ_HANDLED; | |
504 | } | |
505 | ||
506 | void skl_ipc_int_enable(struct sst_dsp *ctx) | |
507 | { | |
508 | sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_ADSPIC, | |
509 | SKL_ADSPIC_IPC, SKL_ADSPIC_IPC); | |
510 | } | |
511 | ||
512 | void skl_ipc_int_disable(struct sst_dsp *ctx) | |
513 | { | |
514 | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC, | |
515 | SKL_ADSPIC_IPC, 0); | |
516 | } | |
517 | ||
518 | void skl_ipc_op_int_enable(struct sst_dsp *ctx) | |
519 | { | |
520 | /* enable IPC DONE interrupt */ | |
521 | sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL, | |
522 | SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE); | |
523 | ||
524 | /* Enable IPC BUSY interrupt */ | |
525 | sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL, | |
526 | SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY); | |
527 | } | |
528 | ||
84c9e283 JK |
529 | void skl_ipc_op_int_disable(struct sst_dsp *ctx) |
530 | { | |
531 | /* disable IPC DONE interrupt */ | |
532 | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, | |
533 | SKL_ADSP_REG_HIPCCTL_DONE, 0); | |
534 | ||
535 | /* Disable IPC BUSY interrupt */ | |
536 | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, | |
537 | SKL_ADSP_REG_HIPCCTL_BUSY, 0); | |
538 | ||
539 | } | |
540 | ||
b81fd263 SP |
541 | bool skl_ipc_int_status(struct sst_dsp *ctx) |
542 | { | |
543 | return sst_dsp_shim_read_unlocked(ctx, | |
544 | SKL_ADSP_REG_ADSPIS) & SKL_ADSPIS_IPC; | |
545 | } | |
546 | ||
547 | int skl_ipc_init(struct device *dev, struct skl_sst *skl) | |
548 | { | |
549 | struct sst_generic_ipc *ipc; | |
550 | int err; | |
551 | ||
552 | ipc = &skl->ipc; | |
553 | ipc->dsp = skl->dsp; | |
554 | ipc->dev = dev; | |
555 | ||
556 | ipc->tx_data_max_size = SKL_ADSP_W1_SZ; | |
557 | ipc->rx_data_max_size = SKL_ADSP_W0_UP_SZ; | |
558 | ||
559 | err = sst_ipc_init(ipc); | |
560 | if (err) | |
561 | return err; | |
562 | ||
563 | ipc->ops.tx_msg = skl_ipc_tx_msg; | |
564 | ipc->ops.tx_data_copy = skl_ipc_tx_data_copy; | |
565 | ipc->ops.is_dsp_busy = skl_ipc_is_dsp_busy; | |
566 | ||
567 | return 0; | |
568 | } | |
569 | ||
570 | void skl_ipc_free(struct sst_generic_ipc *ipc) | |
571 | { | |
572 | /* Disable IPC DONE interrupt */ | |
573 | sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL, | |
574 | SKL_ADSP_REG_HIPCCTL_DONE, 0); | |
575 | ||
576 | /* Disable IPC BUSY interrupt */ | |
577 | sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL, | |
578 | SKL_ADSP_REG_HIPCCTL_BUSY, 0); | |
a750ba5f SP |
579 | |
580 | sst_ipc_fini(ipc); | |
b81fd263 SP |
581 | } |
582 | ||
583 | int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, | |
8a0cb236 | 584 | u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode) |
b81fd263 SP |
585 | { |
586 | struct skl_ipc_header header = {0}; | |
587 | u64 *ipc_header = (u64 *)(&header); | |
588 | int ret; | |
589 | ||
590 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
591 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
592 | header.primary |= IPC_GLB_TYPE(IPC_GLB_CREATE_PPL); | |
593 | header.primary |= IPC_INSTANCE_ID(instance_id); | |
594 | header.primary |= IPC_PPL_TYPE(ppl_type); | |
595 | header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size); | |
596 | ||
8a0cb236 VK |
597 | header.extension = IPC_PPL_LP_MODE(lp_mode); |
598 | ||
b81fd263 SP |
599 | dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); |
600 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
601 | if (ret < 0) { | |
602 | dev_err(ipc->dev, "ipc: create pipeline fail, err: %d\n", ret); | |
603 | return ret; | |
604 | } | |
605 | ||
606 | return ret; | |
607 | } | |
608 | EXPORT_SYMBOL_GPL(skl_ipc_create_pipeline); | |
609 | ||
610 | int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) | |
611 | { | |
612 | struct skl_ipc_header header = {0}; | |
613 | u64 *ipc_header = (u64 *)(&header); | |
614 | int ret; | |
615 | ||
616 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
617 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
618 | header.primary |= IPC_GLB_TYPE(IPC_GLB_DELETE_PPL); | |
619 | header.primary |= IPC_INSTANCE_ID(instance_id); | |
620 | ||
621 | dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); | |
622 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
623 | if (ret < 0) { | |
624 | dev_err(ipc->dev, "ipc: delete pipeline failed, err %d\n", ret); | |
625 | return ret; | |
626 | } | |
627 | ||
628 | return 0; | |
629 | } | |
630 | EXPORT_SYMBOL_GPL(skl_ipc_delete_pipeline); | |
631 | ||
632 | int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc, | |
633 | u8 instance_id, enum skl_ipc_pipeline_state state) | |
634 | { | |
635 | struct skl_ipc_header header = {0}; | |
636 | u64 *ipc_header = (u64 *)(&header); | |
637 | int ret; | |
638 | ||
639 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
640 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
641 | header.primary |= IPC_GLB_TYPE(IPC_GLB_SET_PPL_STATE); | |
642 | header.primary |= IPC_INSTANCE_ID(instance_id); | |
643 | header.primary |= IPC_PPL_STATE(state); | |
644 | ||
645 | dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); | |
646 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
647 | if (ret < 0) { | |
648 | dev_err(ipc->dev, "ipc: set pipeline state failed, err: %d\n", ret); | |
649 | return ret; | |
650 | } | |
651 | return ret; | |
652 | } | |
653 | EXPORT_SYMBOL_GPL(skl_ipc_set_pipeline_state); | |
654 | ||
655 | int | |
656 | skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, u8 instance_id, int dma_id) | |
657 | { | |
658 | struct skl_ipc_header header = {0}; | |
659 | u64 *ipc_header = (u64 *)(&header); | |
660 | int ret; | |
661 | ||
662 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
663 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
664 | header.primary |= IPC_GLB_TYPE(IPC_GLB_SAVE_PPL); | |
665 | header.primary |= IPC_INSTANCE_ID(instance_id); | |
666 | ||
667 | header.extension = IPC_DMA_ID(dma_id); | |
668 | dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); | |
669 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
670 | if (ret < 0) { | |
671 | dev_err(ipc->dev, "ipc: save pipeline failed, err: %d\n", ret); | |
672 | return ret; | |
673 | } | |
674 | ||
675 | return ret; | |
676 | } | |
677 | EXPORT_SYMBOL_GPL(skl_ipc_save_pipeline); | |
678 | ||
679 | int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) | |
680 | { | |
681 | struct skl_ipc_header header = {0}; | |
682 | u64 *ipc_header = (u64 *)(&header); | |
683 | int ret; | |
684 | ||
685 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
686 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
687 | header.primary |= IPC_GLB_TYPE(IPC_GLB_RESTORE_PPL); | |
688 | header.primary |= IPC_INSTANCE_ID(instance_id); | |
689 | ||
690 | dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); | |
691 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
692 | if (ret < 0) { | |
693 | dev_err(ipc->dev, "ipc: restore pipeline failed, err: %d\n", ret); | |
694 | return ret; | |
695 | } | |
696 | ||
697 | return ret; | |
698 | } | |
699 | EXPORT_SYMBOL_GPL(skl_ipc_restore_pipeline); | |
700 | ||
701 | int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, | |
702 | u16 module_id, struct skl_ipc_dxstate_info *dx) | |
703 | { | |
704 | struct skl_ipc_header header = {0}; | |
705 | u64 *ipc_header = (u64 *)(&header); | |
706 | int ret; | |
707 | ||
708 | header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); | |
709 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
710 | header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_DX); | |
711 | header.primary |= IPC_MOD_INSTANCE_ID(instance_id); | |
712 | header.primary |= IPC_MOD_ID(module_id); | |
713 | ||
714 | dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, | |
715 | header.primary, header.extension); | |
716 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, | |
aaec7e9f | 717 | dx, sizeof(*dx), NULL, 0); |
b81fd263 SP |
718 | if (ret < 0) { |
719 | dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret); | |
720 | return ret; | |
721 | } | |
722 | ||
723 | return ret; | |
724 | } | |
725 | EXPORT_SYMBOL_GPL(skl_ipc_set_dx); | |
726 | ||
727 | int skl_ipc_init_instance(struct sst_generic_ipc *ipc, | |
728 | struct skl_ipc_init_instance_msg *msg, void *param_data) | |
729 | { | |
730 | struct skl_ipc_header header = {0}; | |
731 | u64 *ipc_header = (u64 *)(&header); | |
732 | int ret; | |
733 | u32 *buffer = (u32 *)param_data; | |
734 | /* param_block_size must be in dwords */ | |
735 | u16 param_block_size = msg->param_data_size / sizeof(u32); | |
736 | ||
96bd6033 | 737 | print_hex_dump_debug("Param data:", DUMP_PREFIX_NONE, |
b81fd263 SP |
738 | 16, 4, buffer, param_block_size, false); |
739 | ||
740 | header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); | |
741 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
742 | header.primary |= IPC_GLB_TYPE(IPC_MOD_INIT_INSTANCE); | |
743 | header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); | |
744 | header.primary |= IPC_MOD_ID(msg->module_id); | |
745 | ||
746 | header.extension = IPC_CORE_ID(msg->core_id); | |
747 | header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id); | |
748 | header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size); | |
3d4006cd | 749 | header.extension |= IPC_DOMAIN(msg->domain); |
b81fd263 SP |
750 | |
751 | dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, | |
752 | header.primary, header.extension); | |
753 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, param_data, | |
754 | msg->param_data_size, NULL, 0); | |
755 | ||
756 | if (ret < 0) { | |
757 | dev_err(ipc->dev, "ipc: init instance failed\n"); | |
758 | return ret; | |
759 | } | |
760 | ||
761 | return ret; | |
762 | } | |
763 | EXPORT_SYMBOL_GPL(skl_ipc_init_instance); | |
764 | ||
765 | int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, | |
766 | struct skl_ipc_bind_unbind_msg *msg) | |
767 | { | |
768 | struct skl_ipc_header header = {0}; | |
769 | u64 *ipc_header = (u64 *)(&header); | |
770 | u8 bind_unbind = msg->bind ? IPC_MOD_BIND : IPC_MOD_UNBIND; | |
771 | int ret; | |
772 | ||
773 | header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); | |
774 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
775 | header.primary |= IPC_GLB_TYPE(bind_unbind); | |
776 | header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); | |
777 | header.primary |= IPC_MOD_ID(msg->module_id); | |
778 | ||
779 | header.extension = IPC_DST_MOD_ID(msg->dst_module_id); | |
780 | header.extension |= IPC_DST_MOD_INSTANCE_ID(msg->dst_instance_id); | |
781 | header.extension |= IPC_DST_QUEUE(msg->dst_queue); | |
782 | header.extension |= IPC_SRC_QUEUE(msg->src_queue); | |
783 | ||
784 | dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary, | |
785 | header.extension); | |
786 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
787 | if (ret < 0) { | |
ecd286a9 | 788 | dev_err(ipc->dev, "ipc: bind/unbind failed\n"); |
b81fd263 SP |
789 | return ret; |
790 | } | |
791 | ||
792 | return ret; | |
793 | } | |
794 | EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind); | |
795 | ||
6c5768b3 D |
796 | /* |
797 | * In order to load a module we need to send IPC to initiate that. DMA will | |
798 | * performed to load the module memory. The FW supports multiple module load | |
799 | * at single shot, so we can send IPC with N modules represented by | |
800 | * module_cnt | |
801 | */ | |
802 | int skl_ipc_load_modules(struct sst_generic_ipc *ipc, | |
803 | u8 module_cnt, void *data) | |
804 | { | |
805 | struct skl_ipc_header header = {0}; | |
806 | u64 *ipc_header = (u64 *)(&header); | |
807 | int ret; | |
808 | ||
809 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
810 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
811 | header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS); | |
812 | header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); | |
813 | ||
814 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, | |
815 | (sizeof(u16) * module_cnt), NULL, 0); | |
816 | if (ret < 0) | |
817 | dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret); | |
818 | ||
819 | return ret; | |
820 | } | |
821 | EXPORT_SYMBOL_GPL(skl_ipc_load_modules); | |
822 | ||
823 | int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt, | |
824 | void *data) | |
825 | { | |
826 | struct skl_ipc_header header = {0}; | |
827 | u64 *ipc_header = (u64 *)(&header); | |
828 | int ret; | |
829 | ||
830 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
831 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
832 | header.primary |= IPC_GLB_TYPE(IPC_GLB_UNLOAD_MULTIPLE_MODS); | |
833 | header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); | |
834 | ||
835 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, | |
836 | (sizeof(u16) * module_cnt), NULL, 0); | |
837 | if (ret < 0) | |
838 | dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret); | |
839 | ||
840 | return ret; | |
841 | } | |
842 | EXPORT_SYMBOL_GPL(skl_ipc_unload_modules); | |
843 | ||
b81fd263 SP |
844 | int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, |
845 | struct skl_ipc_large_config_msg *msg, u32 *param) | |
846 | { | |
847 | struct skl_ipc_header header = {0}; | |
848 | u64 *ipc_header = (u64 *)(&header); | |
849 | int ret = 0; | |
850 | size_t sz_remaining, tx_size, data_offset; | |
851 | ||
852 | header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); | |
853 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
854 | header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_SET); | |
855 | header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); | |
856 | header.primary |= IPC_MOD_ID(msg->module_id); | |
857 | ||
858 | header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); | |
859 | header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); | |
860 | header.extension |= IPC_FINAL_BLOCK(0); | |
861 | header.extension |= IPC_INITIAL_BLOCK(1); | |
862 | ||
863 | sz_remaining = msg->param_data_size; | |
864 | data_offset = 0; | |
865 | while (sz_remaining != 0) { | |
866 | tx_size = sz_remaining > SKL_ADSP_W1_SZ | |
867 | ? SKL_ADSP_W1_SZ : sz_remaining; | |
868 | if (tx_size == sz_remaining) | |
869 | header.extension |= IPC_FINAL_BLOCK(1); | |
870 | ||
871 | dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__, | |
872 | header.primary, header.extension); | |
873 | dev_dbg(ipc->dev, "transmitting offset: %#x, size: %#x\n", | |
874 | (unsigned)data_offset, (unsigned)tx_size); | |
875 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, | |
876 | ((char *)param) + data_offset, | |
877 | tx_size, NULL, 0); | |
878 | if (ret < 0) { | |
879 | dev_err(ipc->dev, | |
880 | "ipc: set large config fail, err: %d\n", ret); | |
881 | return ret; | |
882 | } | |
883 | sz_remaining -= tx_size; | |
884 | data_offset = msg->param_data_size - sz_remaining; | |
885 | ||
886 | /* clear the fields */ | |
887 | header.extension &= IPC_INITIAL_BLOCK_CLEAR; | |
888 | header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; | |
889 | /* fill the fields */ | |
890 | header.extension |= IPC_INITIAL_BLOCK(0); | |
891 | header.extension |= IPC_DATA_OFFSET_SZ(data_offset); | |
892 | } | |
893 | ||
894 | return ret; | |
895 | } | |
896 | EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); | |
cce1c7f3 MJ |
897 | |
898 | int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, | |
899 | struct skl_ipc_large_config_msg *msg, u32 *param) | |
900 | { | |
901 | struct skl_ipc_header header = {0}; | |
902 | u64 *ipc_header = (u64 *)(&header); | |
903 | int ret = 0; | |
904 | size_t sz_remaining, rx_size, data_offset; | |
905 | ||
906 | header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); | |
907 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
908 | header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_GET); | |
909 | header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); | |
910 | header.primary |= IPC_MOD_ID(msg->module_id); | |
911 | ||
912 | header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); | |
913 | header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); | |
914 | header.extension |= IPC_FINAL_BLOCK(1); | |
915 | header.extension |= IPC_INITIAL_BLOCK(1); | |
916 | ||
917 | sz_remaining = msg->param_data_size; | |
918 | data_offset = 0; | |
919 | ||
920 | while (sz_remaining != 0) { | |
921 | rx_size = sz_remaining > SKL_ADSP_W1_SZ | |
922 | ? SKL_ADSP_W1_SZ : sz_remaining; | |
923 | if (rx_size == sz_remaining) | |
924 | header.extension |= IPC_FINAL_BLOCK(1); | |
925 | ||
926 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, | |
927 | ((char *)param) + data_offset, | |
928 | msg->param_data_size); | |
929 | if (ret < 0) { | |
930 | dev_err(ipc->dev, | |
931 | "ipc: get large config fail, err: %d\n", ret); | |
932 | return ret; | |
933 | } | |
934 | sz_remaining -= rx_size; | |
935 | data_offset = msg->param_data_size - sz_remaining; | |
936 | ||
937 | /* clear the fields */ | |
938 | header.extension &= IPC_INITIAL_BLOCK_CLEAR; | |
939 | header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; | |
940 | /* fill the fields */ | |
941 | header.extension |= IPC_INITIAL_BLOCK(1); | |
942 | header.extension |= IPC_DATA_OFFSET_SZ(data_offset); | |
943 | } | |
944 | ||
945 | return ret; | |
946 | } | |
947 | EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); | |
20fb2fbd RB |
948 | |
949 | int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, | |
950 | u8 dma_id, u8 table_id) | |
951 | { | |
952 | struct skl_ipc_header header = {0}; | |
953 | u64 *ipc_header = (u64 *)(&header); | |
954 | int ret = 0; | |
955 | ||
956 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | |
957 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
958 | header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_LIBRARY); | |
959 | header.primary |= IPC_MOD_INSTANCE_ID(table_id); | |
960 | header.primary |= IPC_MOD_ID(dma_id); | |
961 | ||
962 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | |
963 | ||
964 | if (ret < 0) | |
965 | dev_err(ipc->dev, "ipc: load lib failed\n"); | |
966 | ||
967 | return ret; | |
968 | } | |
969 | EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library); | |
41b7523f PS |
970 | |
971 | int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg) | |
972 | { | |
973 | struct skl_ipc_header header = {0}; | |
974 | u64 *ipc_header = (u64 *)(&header); | |
975 | int ret; | |
976 | ||
977 | header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); | |
978 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | |
979 | header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_D0IX); | |
980 | header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); | |
981 | header.primary |= IPC_MOD_ID(msg->module_id); | |
982 | ||
983 | header.extension = IPC_D0IX_WAKE(msg->wake); | |
984 | header.extension |= IPC_D0IX_STREAMING(msg->streaming); | |
985 | ||
986 | dev_dbg(ipc->dev, "In %s primary=%x ext=%x\n", __func__, | |
987 | header.primary, header.extension); | |
988 | ||
989 | /* | |
990 | * Use the nopm IPC here as we dont want it checking for D0iX | |
991 | */ | |
992 | ret = sst_ipc_tx_message_nopm(ipc, *ipc_header, NULL, 0, NULL, 0); | |
993 | if (ret < 0) | |
994 | dev_err(ipc->dev, "ipc: set d0ix failed, err %d\n", ret); | |
995 | ||
996 | return ret; | |
997 | } | |
998 | EXPORT_SYMBOL_GPL(skl_ipc_set_d0ix); |