Commit | Line | Data |
---|---|---|
a4b12990 MB |
1 | /* |
2 | * Intel Baytrail SST IPC Support | |
3 | * Copyright (c) 2014, Intel Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | */ | |
14 | ||
15 | #include <linux/types.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/list.h> | |
18 | #include <linux/device.h> | |
19 | #include <linux/wait.h> | |
20 | #include <linux/spinlock.h> | |
21 | #include <linux/workqueue.h> | |
22 | #include <linux/export.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/delay.h> | |
a4b12990 MB |
25 | #include <linux/platform_device.h> |
26 | #include <linux/kthread.h> | |
27 | #include <linux/firmware.h> | |
28 | #include <linux/io.h> | |
29 | #include <asm/div64.h> | |
30 | ||
31 | #include "sst-baytrail-ipc.h" | |
66a6fd98 JY |
32 | #include "../common/sst-dsp.h" |
33 | #include "../common/sst-dsp-priv.h" | |
48cec59b | 34 | #include "../common/sst-ipc.h" |
a4b12990 MB |
35 | |
36 | /* IPC message timeout */ | |
37 | #define IPC_TIMEOUT_MSECS 300 | |
38 | #define IPC_BOOT_MSECS 200 | |
39 | ||
40 | #define IPC_EMPTY_LIST_SIZE 8 | |
41 | ||
42 | /* IPC header bits */ | |
43 | #define IPC_HEADER_MSG_ID_MASK 0xff | |
44 | #define IPC_HEADER_MSG_ID(x) ((x) & IPC_HEADER_MSG_ID_MASK) | |
45 | #define IPC_HEADER_STR_ID_SHIFT 8 | |
46 | #define IPC_HEADER_STR_ID_MASK 0x1f | |
47 | #define IPC_HEADER_STR_ID(x) (((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT) | |
48 | #define IPC_HEADER_LARGE_SHIFT 13 | |
49 | #define IPC_HEADER_LARGE(x) (((x) & 0x1) << IPC_HEADER_LARGE_SHIFT) | |
50 | #define IPC_HEADER_DATA_SHIFT 16 | |
51 | #define IPC_HEADER_DATA_MASK 0x3fff | |
52 | #define IPC_HEADER_DATA(x) (((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT) | |
53 | ||
54 | /* mask for differentiating between notification and reply message */ | |
55 | #define IPC_NOTIFICATION (0x1 << 7) | |
56 | ||
57 | /* I2L Stream config/control msgs */ | |
58 | #define IPC_IA_ALLOC_STREAM 0x20 | |
59 | #define IPC_IA_FREE_STREAM 0x21 | |
60 | #define IPC_IA_PAUSE_STREAM 0x24 | |
61 | #define IPC_IA_RESUME_STREAM 0x25 | |
62 | #define IPC_IA_DROP_STREAM 0x26 | |
63 | #define IPC_IA_START_STREAM 0x30 | |
64 | ||
65 | /* notification messages */ | |
66 | #define IPC_IA_FW_INIT_CMPLT 0x81 | |
67 | #define IPC_SST_PERIOD_ELAPSED 0x97 | |
68 | ||
69 | /* IPC messages between host and ADSP */ | |
70 | struct sst_byt_address_info { | |
71 | u32 addr; | |
72 | u32 size; | |
73 | } __packed; | |
74 | ||
75 | struct sst_byt_str_type { | |
76 | u8 codec_type; | |
77 | u8 str_type; | |
78 | u8 operation; | |
79 | u8 protected_str; | |
80 | u8 time_slots; | |
81 | u8 reserved; | |
82 | u16 result; | |
83 | } __packed; | |
84 | ||
85 | struct sst_byt_pcm_params { | |
86 | u8 num_chan; | |
87 | u8 pcm_wd_sz; | |
88 | u8 use_offload_path; | |
89 | u8 reserved; | |
90 | u32 sfreq; | |
91 | u8 channel_map[8]; | |
92 | } __packed; | |
93 | ||
94 | struct sst_byt_frames_info { | |
95 | u16 num_entries; | |
96 | u16 rsrvd; | |
97 | u32 frag_size; | |
98 | struct sst_byt_address_info ring_buf_info[8]; | |
99 | } __packed; | |
100 | ||
101 | struct sst_byt_alloc_params { | |
102 | struct sst_byt_str_type str_type; | |
103 | struct sst_byt_pcm_params pcm_params; | |
104 | struct sst_byt_frames_info frame_info; | |
105 | } __packed; | |
106 | ||
107 | struct sst_byt_alloc_response { | |
108 | struct sst_byt_str_type str_type; | |
109 | u8 reserved[88]; | |
110 | } __packed; | |
111 | ||
112 | struct sst_byt_start_stream_params { | |
113 | u32 byte_offset; | |
114 | } __packed; | |
115 | ||
116 | struct sst_byt_tstamp { | |
117 | u64 ring_buffer_counter; | |
118 | u64 hardware_counter; | |
119 | u64 frames_decoded; | |
120 | u64 bytes_decoded; | |
121 | u64 bytes_copied; | |
122 | u32 sampling_frequency; | |
123 | u32 channel_peak[8]; | |
124 | } __packed; | |
125 | ||
4131eceb JN |
126 | struct sst_byt_fw_version { |
127 | u8 build; | |
128 | u8 minor; | |
129 | u8 major; | |
130 | u8 type; | |
131 | } __packed; | |
132 | ||
133 | struct sst_byt_fw_build_info { | |
134 | u8 date[16]; | |
135 | u8 time[16]; | |
136 | } __packed; | |
137 | ||
138 | struct sst_byt_fw_init { | |
139 | struct sst_byt_fw_version fw_version; | |
140 | struct sst_byt_fw_build_info build_info; | |
141 | u16 result; | |
142 | u8 module_id; | |
143 | u8 debug_info; | |
144 | } __packed; | |
145 | ||
a4b12990 MB |
146 | struct sst_byt_stream; |
147 | struct sst_byt; | |
148 | ||
149 | /* stream infomation */ | |
150 | struct sst_byt_stream { | |
151 | struct list_head node; | |
152 | ||
153 | /* configuration */ | |
154 | struct sst_byt_alloc_params request; | |
155 | struct sst_byt_alloc_response reply; | |
156 | ||
157 | /* runtime info */ | |
158 | struct sst_byt *byt; | |
159 | int str_id; | |
160 | bool commited; | |
161 | bool running; | |
162 | ||
163 | /* driver callback */ | |
164 | u32 (*notify_position)(struct sst_byt_stream *stream, void *data); | |
165 | void *pdata; | |
166 | }; | |
167 | ||
168 | /* SST Baytrail IPC data */ | |
169 | struct sst_byt { | |
170 | struct device *dev; | |
171 | struct sst_dsp *dsp; | |
172 | ||
173 | /* stream */ | |
174 | struct list_head stream_list; | |
175 | ||
176 | /* boot */ | |
177 | wait_queue_head_t boot_wait; | |
178 | bool boot_complete; | |
af94aa55 | 179 | struct sst_fw *fw; |
a4b12990 MB |
180 | |
181 | /* IPC messaging */ | |
48cec59b | 182 | struct sst_generic_ipc ipc; |
a4b12990 MB |
183 | }; |
184 | ||
185 | static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) | |
186 | { | |
44cc4a01 MY |
187 | return IPC_HEADER_MSG_ID(msg_id) | IPC_HEADER_STR_ID(str_id) | |
188 | IPC_HEADER_LARGE(large) | IPC_HEADER_DATA(data) | | |
189 | SST_BYT_IPCX_BUSY; | |
a4b12990 MB |
190 | } |
191 | ||
192 | static inline u16 sst_byt_header_msg_id(u64 header) | |
193 | { | |
194 | return header & IPC_HEADER_MSG_ID_MASK; | |
195 | } | |
196 | ||
197 | static inline u8 sst_byt_header_str_id(u64 header) | |
198 | { | |
199 | return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK; | |
200 | } | |
201 | ||
202 | static inline u16 sst_byt_header_data(u64 header) | |
203 | { | |
204 | return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK; | |
205 | } | |
206 | ||
207 | static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, | |
208 | int stream_id) | |
209 | { | |
210 | struct sst_byt_stream *stream; | |
211 | ||
212 | list_for_each_entry(stream, &byt->stream_list, node) { | |
213 | if (stream->str_id == stream_id) | |
214 | return stream; | |
215 | } | |
216 | ||
217 | return NULL; | |
218 | } | |
219 | ||
a4b12990 MB |
220 | static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) |
221 | { | |
222 | struct sst_byt_stream *stream; | |
223 | u64 header = msg->header; | |
224 | u8 stream_id = sst_byt_header_str_id(header); | |
225 | u8 stream_msg = sst_byt_header_msg_id(header); | |
226 | ||
227 | stream = sst_byt_get_stream(byt, stream_id); | |
228 | if (stream == NULL) | |
229 | return; | |
230 | ||
231 | switch (stream_msg) { | |
232 | case IPC_IA_DROP_STREAM: | |
233 | case IPC_IA_PAUSE_STREAM: | |
234 | case IPC_IA_FREE_STREAM: | |
235 | stream->running = false; | |
236 | break; | |
237 | case IPC_IA_START_STREAM: | |
238 | case IPC_IA_RESUME_STREAM: | |
239 | stream->running = true; | |
240 | break; | |
241 | } | |
242 | } | |
243 | ||
244 | static int sst_byt_process_reply(struct sst_byt *byt, u64 header) | |
245 | { | |
246 | struct ipc_message *msg; | |
247 | ||
48cec59b | 248 | msg = sst_ipc_reply_find_msg(&byt->ipc, header); |
a4b12990 MB |
249 | if (msg == NULL) |
250 | return 1; | |
251 | ||
252 | if (header & IPC_HEADER_LARGE(true)) { | |
253 | msg->rx_size = sst_byt_header_data(header); | |
254 | sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size); | |
255 | } | |
256 | ||
257 | /* update any stream states */ | |
258 | sst_byt_stream_update(byt, msg); | |
259 | ||
260 | list_del(&msg->list); | |
261 | /* wake up */ | |
48cec59b | 262 | sst_ipc_tx_msg_reply_complete(&byt->ipc, msg); |
a4b12990 MB |
263 | |
264 | return 1; | |
265 | } | |
266 | ||
267 | static void sst_byt_fw_ready(struct sst_byt *byt, u64 header) | |
268 | { | |
269 | dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header); | |
270 | ||
271 | byt->boot_complete = true; | |
272 | wake_up(&byt->boot_wait); | |
273 | } | |
274 | ||
275 | static int sst_byt_process_notification(struct sst_byt *byt, | |
276 | unsigned long *flags) | |
277 | { | |
278 | struct sst_dsp *sst = byt->dsp; | |
279 | struct sst_byt_stream *stream; | |
280 | u64 header; | |
281 | u8 msg_id, stream_id; | |
282 | int handled = 1; | |
283 | ||
284 | header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); | |
285 | msg_id = sst_byt_header_msg_id(header); | |
286 | ||
287 | switch (msg_id) { | |
288 | case IPC_SST_PERIOD_ELAPSED: | |
289 | stream_id = sst_byt_header_str_id(header); | |
290 | stream = sst_byt_get_stream(byt, stream_id); | |
291 | if (stream && stream->running && stream->notify_position) { | |
292 | spin_unlock_irqrestore(&sst->spinlock, *flags); | |
293 | stream->notify_position(stream, stream->pdata); | |
294 | spin_lock_irqsave(&sst->spinlock, *flags); | |
295 | } | |
296 | break; | |
297 | case IPC_IA_FW_INIT_CMPLT: | |
298 | sst_byt_fw_ready(byt, header); | |
299 | break; | |
300 | } | |
301 | ||
302 | return handled; | |
303 | } | |
304 | ||
305 | static irqreturn_t sst_byt_irq_thread(int irq, void *context) | |
306 | { | |
307 | struct sst_dsp *sst = (struct sst_dsp *) context; | |
308 | struct sst_byt *byt = sst_dsp_get_thread_context(sst); | |
48cec59b | 309 | struct sst_generic_ipc *ipc = &byt->ipc; |
a4b12990 MB |
310 | u64 header; |
311 | unsigned long flags; | |
312 | ||
313 | spin_lock_irqsave(&sst->spinlock, flags); | |
314 | ||
315 | header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); | |
316 | if (header & SST_BYT_IPCD_BUSY) { | |
317 | if (header & IPC_NOTIFICATION) { | |
318 | /* message from ADSP */ | |
319 | sst_byt_process_notification(byt, &flags); | |
320 | } else { | |
321 | /* reply from ADSP */ | |
322 | sst_byt_process_reply(byt, header); | |
323 | } | |
324 | /* | |
325 | * clear IPCD BUSY bit and set DONE bit. Tell DSP we have | |
326 | * processed the message and can accept new. Clear data part | |
327 | * of the header | |
328 | */ | |
329 | sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD, | |
330 | SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY | | |
331 | IPC_HEADER_DATA(IPC_HEADER_DATA_MASK), | |
332 | SST_BYT_IPCD_DONE); | |
333 | /* unmask message request interrupts */ | |
334 | sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX, | |
335 | SST_BYT_IMRX_REQUEST, 0); | |
336 | } | |
337 | ||
338 | spin_unlock_irqrestore(&sst->spinlock, flags); | |
339 | ||
340 | /* continue to send any remaining messages... */ | |
8c03cbe6 | 341 | kthread_queue_work(&ipc->kworker, &ipc->kwork); |
a4b12990 MB |
342 | |
343 | return IRQ_HANDLED; | |
344 | } | |
345 | ||
346 | /* stream API */ | |
347 | struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, | |
348 | u32 (*notify_position)(struct sst_byt_stream *stream, void *data), | |
349 | void *data) | |
350 | { | |
351 | struct sst_byt_stream *stream; | |
d132cb0a WD |
352 | struct sst_dsp *sst = byt->dsp; |
353 | unsigned long flags; | |
a4b12990 MB |
354 | |
355 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | |
356 | if (stream == NULL) | |
357 | return NULL; | |
358 | ||
d132cb0a | 359 | spin_lock_irqsave(&sst->spinlock, flags); |
a4b12990 MB |
360 | list_add(&stream->node, &byt->stream_list); |
361 | stream->notify_position = notify_position; | |
362 | stream->pdata = data; | |
363 | stream->byt = byt; | |
364 | stream->str_id = id; | |
d132cb0a | 365 | spin_unlock_irqrestore(&sst->spinlock, flags); |
a4b12990 MB |
366 | |
367 | return stream; | |
368 | } | |
369 | ||
370 | int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream, | |
371 | int bits) | |
372 | { | |
373 | stream->request.pcm_params.pcm_wd_sz = bits; | |
374 | return 0; | |
375 | } | |
376 | ||
377 | int sst_byt_stream_set_channels(struct sst_byt *byt, | |
378 | struct sst_byt_stream *stream, u8 channels) | |
379 | { | |
380 | stream->request.pcm_params.num_chan = channels; | |
381 | return 0; | |
382 | } | |
383 | ||
384 | int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream, | |
385 | unsigned int rate) | |
386 | { | |
387 | stream->request.pcm_params.sfreq = rate; | |
388 | return 0; | |
389 | } | |
390 | ||
391 | /* stream sonfiguration */ | |
392 | int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream, | |
393 | int codec_type, int stream_type, int operation) | |
394 | { | |
395 | stream->request.str_type.codec_type = codec_type; | |
396 | stream->request.str_type.str_type = stream_type; | |
397 | stream->request.str_type.operation = operation; | |
398 | stream->request.str_type.time_slots = 0xc; | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream, | |
404 | uint32_t buffer_addr, uint32_t buffer_size) | |
405 | { | |
406 | stream->request.frame_info.num_entries = 1; | |
407 | stream->request.frame_info.ring_buf_info[0].addr = buffer_addr; | |
408 | stream->request.frame_info.ring_buf_info[0].size = buffer_size; | |
409 | /* calculate bytes per 4 ms fragment */ | |
410 | stream->request.frame_info.frag_size = | |
411 | stream->request.pcm_params.sfreq * | |
412 | stream->request.pcm_params.num_chan * | |
413 | stream->request.pcm_params.pcm_wd_sz / 8 * | |
414 | 4 / 1000; | |
415 | return 0; | |
416 | } | |
417 | ||
418 | int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) | |
419 | { | |
420 | struct sst_byt_alloc_params *str_req = &stream->request; | |
421 | struct sst_byt_alloc_response *reply = &stream->reply; | |
422 | u64 header; | |
423 | int ret; | |
424 | ||
425 | header = sst_byt_header(IPC_IA_ALLOC_STREAM, | |
426 | sizeof(*str_req) + sizeof(u32), | |
427 | true, stream->str_id); | |
48cec59b JY |
428 | ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req, |
429 | sizeof(*str_req), | |
a4b12990 MB |
430 | reply, sizeof(*reply)); |
431 | if (ret < 0) { | |
432 | dev_err(byt->dev, "ipc: error stream commit failed\n"); | |
433 | return ret; | |
434 | } | |
435 | ||
436 | stream->commited = true; | |
437 | ||
438 | return 0; | |
439 | } | |
440 | ||
441 | int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) | |
442 | { | |
443 | u64 header; | |
444 | int ret = 0; | |
d132cb0a WD |
445 | struct sst_dsp *sst = byt->dsp; |
446 | unsigned long flags; | |
a4b12990 MB |
447 | |
448 | if (!stream->commited) | |
449 | goto out; | |
450 | ||
451 | header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); | |
48cec59b | 452 | ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0); |
a4b12990 MB |
453 | if (ret < 0) { |
454 | dev_err(byt->dev, "ipc: free stream %d failed\n", | |
455 | stream->str_id); | |
456 | return -EAGAIN; | |
457 | } | |
458 | ||
459 | stream->commited = false; | |
460 | out: | |
d132cb0a | 461 | spin_lock_irqsave(&sst->spinlock, flags); |
a4b12990 MB |
462 | list_del(&stream->node); |
463 | kfree(stream); | |
d132cb0a | 464 | spin_unlock_irqrestore(&sst->spinlock, flags); |
a4b12990 MB |
465 | |
466 | return ret; | |
467 | } | |
468 | ||
469 | static int sst_byt_stream_operations(struct sst_byt *byt, int type, | |
470 | int stream_id, int wait) | |
471 | { | |
a4b12990 | 472 | u64 header; |
a4b12990 | 473 | |
65ee9e8f | 474 | header = sst_byt_header(type, 0, false, stream_id); |
a4b12990 | 475 | if (wait) |
48cec59b JY |
476 | return sst_ipc_tx_message_wait(&byt->ipc, header, NULL, |
477 | 0, NULL, 0); | |
a4b12990 | 478 | else |
48cec59b JY |
479 | return sst_ipc_tx_message_nowait(&byt->ipc, header, |
480 | NULL, 0); | |
a4b12990 MB |
481 | } |
482 | ||
483 | /* stream ALSA trigger operations */ | |
a6686ed5 JN |
484 | int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, |
485 | u32 start_offset) | |
a4b12990 | 486 | { |
65ee9e8f JN |
487 | struct sst_byt_start_stream_params start_stream; |
488 | void *tx_msg; | |
489 | size_t size; | |
490 | u64 header; | |
a4b12990 MB |
491 | int ret; |
492 | ||
a6686ed5 | 493 | start_stream.byte_offset = start_offset; |
65ee9e8f JN |
494 | header = sst_byt_header(IPC_IA_START_STREAM, |
495 | sizeof(start_stream) + sizeof(u32), | |
496 | true, stream->str_id); | |
497 | tx_msg = &start_stream; | |
498 | size = sizeof(start_stream); | |
499 | ||
48cec59b | 500 | ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size); |
a4b12990 MB |
501 | if (ret < 0) |
502 | dev_err(byt->dev, "ipc: error failed to start stream %d\n", | |
503 | stream->str_id); | |
504 | ||
505 | return ret; | |
506 | } | |
507 | ||
508 | int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream) | |
509 | { | |
510 | int ret; | |
511 | ||
512 | /* don't stop streams that are not commited */ | |
513 | if (!stream->commited) | |
514 | return 0; | |
515 | ||
516 | ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM, | |
517 | stream->str_id, 0); | |
518 | if (ret < 0) | |
519 | dev_err(byt->dev, "ipc: error failed to stop stream %d\n", | |
520 | stream->str_id); | |
521 | return ret; | |
522 | } | |
523 | ||
524 | int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream) | |
525 | { | |
526 | int ret; | |
527 | ||
528 | ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM, | |
529 | stream->str_id, 0); | |
530 | if (ret < 0) | |
531 | dev_err(byt->dev, "ipc: error failed to pause stream %d\n", | |
532 | stream->str_id); | |
533 | ||
534 | return ret; | |
535 | } | |
536 | ||
537 | int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream) | |
538 | { | |
539 | int ret; | |
540 | ||
541 | ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM, | |
542 | stream->str_id, 0); | |
543 | if (ret < 0) | |
544 | dev_err(byt->dev, "ipc: error failed to resume stream %d\n", | |
545 | stream->str_id); | |
546 | ||
547 | return ret; | |
548 | } | |
549 | ||
550 | int sst_byt_get_dsp_position(struct sst_byt *byt, | |
551 | struct sst_byt_stream *stream, int buffer_size) | |
552 | { | |
553 | struct sst_dsp *sst = byt->dsp; | |
554 | struct sst_byt_tstamp fw_tstamp; | |
555 | u8 str_id = stream->str_id; | |
556 | u32 tstamp_offset; | |
557 | ||
558 | tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp); | |
559 | memcpy_fromio(&fw_tstamp, | |
560 | sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp)); | |
561 | ||
562 | return do_div(fw_tstamp.ring_buffer_counter, buffer_size); | |
563 | } | |
564 | ||
a4b12990 MB |
565 | struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) |
566 | { | |
567 | return byt->dsp; | |
568 | } | |
569 | ||
570 | static struct sst_dsp_device byt_dev = { | |
571 | .thread = sst_byt_irq_thread, | |
572 | .ops = &sst_byt_ops, | |
573 | }; | |
574 | ||
27d3f026 | 575 | int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) |
af94aa55 LG |
576 | { |
577 | struct sst_byt *byt = pdata->dsp; | |
578 | ||
579 | dev_dbg(byt->dev, "dsp reset\n"); | |
580 | sst_dsp_reset(byt->dsp); | |
48cec59b | 581 | sst_ipc_drop_all(&byt->ipc); |
af94aa55 LG |
582 | dev_dbg(byt->dev, "dsp in reset\n"); |
583 | ||
af94aa55 LG |
584 | dev_dbg(byt->dev, "free all blocks and unload fw\n"); |
585 | sst_fw_unload(byt->fw); | |
586 | ||
587 | return 0; | |
588 | } | |
589 | EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late); | |
590 | ||
591 | int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata) | |
592 | { | |
593 | struct sst_byt *byt = pdata->dsp; | |
594 | int ret; | |
595 | ||
596 | dev_dbg(byt->dev, "reload dsp fw\n"); | |
597 | ||
598 | sst_dsp_reset(byt->dsp); | |
599 | ||
600 | ret = sst_fw_reload(byt->fw); | |
601 | if (ret < 0) { | |
602 | dev_err(dev, "error: failed to reload firmware\n"); | |
603 | return ret; | |
604 | } | |
605 | ||
606 | /* wait for DSP boot completion */ | |
607 | byt->boot_complete = false; | |
608 | sst_dsp_boot(byt->dsp); | |
609 | dev_dbg(byt->dev, "dsp booting...\n"); | |
610 | ||
611 | return 0; | |
612 | } | |
613 | EXPORT_SYMBOL_GPL(sst_byt_dsp_boot); | |
614 | ||
615 | int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) | |
616 | { | |
617 | struct sst_byt *byt = pdata->dsp; | |
618 | int err; | |
619 | ||
620 | dev_dbg(byt->dev, "wait for dsp reboot\n"); | |
621 | ||
622 | err = wait_event_timeout(byt->boot_wait, byt->boot_complete, | |
623 | msecs_to_jiffies(IPC_BOOT_MSECS)); | |
624 | if (err == 0) { | |
625 | dev_err(byt->dev, "ipc: error DSP boot timeout\n"); | |
626 | return -EIO; | |
627 | } | |
628 | ||
629 | dev_dbg(byt->dev, "dsp rebooted\n"); | |
630 | return 0; | |
631 | } | |
632 | EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); | |
633 | ||
48cec59b JY |
634 | static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) |
635 | { | |
636 | if (msg->header & IPC_HEADER_LARGE(true)) | |
637 | sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); | |
638 | ||
639 | sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header); | |
640 | } | |
641 | ||
642 | static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text) | |
643 | { | |
644 | struct sst_dsp *sst = ipc->dsp; | |
645 | u64 isr, ipcd, imrx, ipcx; | |
646 | ||
647 | ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); | |
648 | isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); | |
649 | ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); | |
650 | imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); | |
651 | ||
652 | dev_err(ipc->dev, | |
653 | "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", | |
654 | text, ipcx, isr, ipcd, imrx); | |
655 | } | |
656 | ||
657 | static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data, | |
658 | size_t tx_size) | |
659 | { | |
660 | /* msg content = lower 32-bit of the header + data */ | |
661 | *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1); | |
662 | memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size); | |
663 | msg->tx_size += sizeof(u32); | |
664 | } | |
665 | ||
666 | static u64 byt_reply_msg_match(u64 header, u64 *mask) | |
667 | { | |
668 | /* match reply to message sent based on msg and stream IDs */ | |
669 | *mask = IPC_HEADER_MSG_ID_MASK | | |
670 | IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; | |
671 | header &= *mask; | |
672 | ||
673 | return header; | |
674 | } | |
675 | ||
2709bdbc SP |
676 | static bool byt_is_dsp_busy(struct sst_dsp *dsp) |
677 | { | |
678 | u64 ipcx; | |
679 | ||
680 | ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); | |
681 | return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)); | |
682 | } | |
683 | ||
a4b12990 MB |
684 | int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) |
685 | { | |
686 | struct sst_byt *byt; | |
48cec59b | 687 | struct sst_generic_ipc *ipc; |
a4b12990 | 688 | struct sst_fw *byt_sst_fw; |
4131eceb | 689 | struct sst_byt_fw_init init; |
a4b12990 MB |
690 | int err; |
691 | ||
692 | dev_dbg(dev, "initialising Byt DSP IPC\n"); | |
693 | ||
694 | byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL); | |
695 | if (byt == NULL) | |
696 | return -ENOMEM; | |
697 | ||
45f503df JY |
698 | byt->dev = dev; |
699 | ||
48cec59b JY |
700 | ipc = &byt->ipc; |
701 | ipc->dev = dev; | |
702 | ipc->ops.tx_msg = byt_tx_msg; | |
703 | ipc->ops.shim_dbg = byt_shim_dbg; | |
704 | ipc->ops.tx_data_copy = byt_tx_data_copy; | |
705 | ipc->ops.reply_msg_match = byt_reply_msg_match; | |
2709bdbc | 706 | ipc->ops.is_dsp_busy = byt_is_dsp_busy; |
f99b26f0 SP |
707 | ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES; |
708 | ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES; | |
a4b12990 | 709 | |
48cec59b JY |
710 | err = sst_ipc_init(ipc); |
711 | if (err != 0) | |
712 | goto ipc_init_err; | |
a4b12990 | 713 | |
48cec59b JY |
714 | INIT_LIST_HEAD(&byt->stream_list); |
715 | init_waitqueue_head(&byt->boot_wait); | |
a4b12990 MB |
716 | byt_dev.thread_context = byt; |
717 | ||
718 | /* init SST shim */ | |
719 | byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); | |
720 | if (byt->dsp == NULL) { | |
721 | err = -ENODEV; | |
48cec59b | 722 | goto dsp_new_err; |
a4b12990 MB |
723 | } |
724 | ||
48cec59b JY |
725 | ipc->dsp = byt->dsp; |
726 | ||
a4b12990 MB |
727 | /* keep the DSP in reset state for base FW loading */ |
728 | sst_dsp_reset(byt->dsp); | |
729 | ||
730 | byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt); | |
731 | if (byt_sst_fw == NULL) { | |
732 | err = -ENODEV; | |
733 | dev_err(dev, "error: failed to load firmware\n"); | |
734 | goto fw_err; | |
735 | } | |
736 | ||
737 | /* wait for DSP boot completion */ | |
738 | sst_dsp_boot(byt->dsp); | |
739 | err = wait_event_timeout(byt->boot_wait, byt->boot_complete, | |
740 | msecs_to_jiffies(IPC_BOOT_MSECS)); | |
741 | if (err == 0) { | |
742 | err = -EIO; | |
743 | dev_err(byt->dev, "ipc: error DSP boot timeout\n"); | |
744 | goto boot_err; | |
745 | } | |
746 | ||
4131eceb JN |
747 | /* show firmware information */ |
748 | sst_dsp_inbox_read(byt->dsp, &init, sizeof(init)); | |
749 | dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n", | |
750 | init.fw_version.major, init.fw_version.minor, | |
751 | init.fw_version.build, init.fw_version.type); | |
752 | dev_info(byt->dev, "Build type: %x\n", init.fw_version.type); | |
753 | dev_info(byt->dev, "Build date: %s %s\n", | |
754 | init.build_info.date, init.build_info.time); | |
755 | ||
a4b12990 | 756 | pdata->dsp = byt; |
af94aa55 | 757 | byt->fw = byt_sst_fw; |
a4b12990 MB |
758 | |
759 | return 0; | |
760 | ||
761 | boot_err: | |
762 | sst_dsp_reset(byt->dsp); | |
763 | sst_fw_free(byt_sst_fw); | |
764 | fw_err: | |
765 | sst_dsp_free(byt->dsp); | |
48cec59b JY |
766 | dsp_new_err: |
767 | sst_ipc_fini(ipc); | |
768 | ipc_init_err: | |
a4b12990 MB |
769 | |
770 | return err; | |
771 | } | |
772 | EXPORT_SYMBOL_GPL(sst_byt_dsp_init); | |
773 | ||
774 | void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) | |
775 | { | |
776 | struct sst_byt *byt = pdata->dsp; | |
777 | ||
778 | sst_dsp_reset(byt->dsp); | |
779 | sst_fw_free_all(byt->dsp); | |
780 | sst_dsp_free(byt->dsp); | |
48cec59b | 781 | sst_ipc_fini(&byt->ipc); |
a4b12990 MB |
782 | } |
783 | EXPORT_SYMBOL_GPL(sst_byt_dsp_free); |