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 | { | |
187 | u64 header; | |
188 | ||
189 | header = IPC_HEADER_MSG_ID(msg_id) | | |
190 | IPC_HEADER_STR_ID(str_id) | | |
191 | IPC_HEADER_LARGE(large) | | |
192 | IPC_HEADER_DATA(data) | | |
193 | SST_BYT_IPCX_BUSY; | |
194 | ||
195 | return header; | |
196 | } | |
197 | ||
198 | static inline u16 sst_byt_header_msg_id(u64 header) | |
199 | { | |
200 | return header & IPC_HEADER_MSG_ID_MASK; | |
201 | } | |
202 | ||
203 | static inline u8 sst_byt_header_str_id(u64 header) | |
204 | { | |
205 | return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK; | |
206 | } | |
207 | ||
208 | static inline u16 sst_byt_header_data(u64 header) | |
209 | { | |
210 | return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK; | |
211 | } | |
212 | ||
213 | static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, | |
214 | int stream_id) | |
215 | { | |
216 | struct sst_byt_stream *stream; | |
217 | ||
218 | list_for_each_entry(stream, &byt->stream_list, node) { | |
219 | if (stream->str_id == stream_id) | |
220 | return stream; | |
221 | } | |
222 | ||
223 | return NULL; | |
224 | } | |
225 | ||
a4b12990 MB |
226 | static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) |
227 | { | |
228 | struct sst_byt_stream *stream; | |
229 | u64 header = msg->header; | |
230 | u8 stream_id = sst_byt_header_str_id(header); | |
231 | u8 stream_msg = sst_byt_header_msg_id(header); | |
232 | ||
233 | stream = sst_byt_get_stream(byt, stream_id); | |
234 | if (stream == NULL) | |
235 | return; | |
236 | ||
237 | switch (stream_msg) { | |
238 | case IPC_IA_DROP_STREAM: | |
239 | case IPC_IA_PAUSE_STREAM: | |
240 | case IPC_IA_FREE_STREAM: | |
241 | stream->running = false; | |
242 | break; | |
243 | case IPC_IA_START_STREAM: | |
244 | case IPC_IA_RESUME_STREAM: | |
245 | stream->running = true; | |
246 | break; | |
247 | } | |
248 | } | |
249 | ||
250 | static int sst_byt_process_reply(struct sst_byt *byt, u64 header) | |
251 | { | |
252 | struct ipc_message *msg; | |
253 | ||
48cec59b | 254 | msg = sst_ipc_reply_find_msg(&byt->ipc, header); |
a4b12990 MB |
255 | if (msg == NULL) |
256 | return 1; | |
257 | ||
258 | if (header & IPC_HEADER_LARGE(true)) { | |
259 | msg->rx_size = sst_byt_header_data(header); | |
260 | sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size); | |
261 | } | |
262 | ||
263 | /* update any stream states */ | |
264 | sst_byt_stream_update(byt, msg); | |
265 | ||
266 | list_del(&msg->list); | |
267 | /* wake up */ | |
48cec59b | 268 | sst_ipc_tx_msg_reply_complete(&byt->ipc, msg); |
a4b12990 MB |
269 | |
270 | return 1; | |
271 | } | |
272 | ||
273 | static void sst_byt_fw_ready(struct sst_byt *byt, u64 header) | |
274 | { | |
275 | dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header); | |
276 | ||
277 | byt->boot_complete = true; | |
278 | wake_up(&byt->boot_wait); | |
279 | } | |
280 | ||
281 | static int sst_byt_process_notification(struct sst_byt *byt, | |
282 | unsigned long *flags) | |
283 | { | |
284 | struct sst_dsp *sst = byt->dsp; | |
285 | struct sst_byt_stream *stream; | |
286 | u64 header; | |
287 | u8 msg_id, stream_id; | |
288 | int handled = 1; | |
289 | ||
290 | header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); | |
291 | msg_id = sst_byt_header_msg_id(header); | |
292 | ||
293 | switch (msg_id) { | |
294 | case IPC_SST_PERIOD_ELAPSED: | |
295 | stream_id = sst_byt_header_str_id(header); | |
296 | stream = sst_byt_get_stream(byt, stream_id); | |
297 | if (stream && stream->running && stream->notify_position) { | |
298 | spin_unlock_irqrestore(&sst->spinlock, *flags); | |
299 | stream->notify_position(stream, stream->pdata); | |
300 | spin_lock_irqsave(&sst->spinlock, *flags); | |
301 | } | |
302 | break; | |
303 | case IPC_IA_FW_INIT_CMPLT: | |
304 | sst_byt_fw_ready(byt, header); | |
305 | break; | |
306 | } | |
307 | ||
308 | return handled; | |
309 | } | |
310 | ||
311 | static irqreturn_t sst_byt_irq_thread(int irq, void *context) | |
312 | { | |
313 | struct sst_dsp *sst = (struct sst_dsp *) context; | |
314 | struct sst_byt *byt = sst_dsp_get_thread_context(sst); | |
48cec59b | 315 | struct sst_generic_ipc *ipc = &byt->ipc; |
a4b12990 MB |
316 | u64 header; |
317 | unsigned long flags; | |
318 | ||
319 | spin_lock_irqsave(&sst->spinlock, flags); | |
320 | ||
321 | header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); | |
322 | if (header & SST_BYT_IPCD_BUSY) { | |
323 | if (header & IPC_NOTIFICATION) { | |
324 | /* message from ADSP */ | |
325 | sst_byt_process_notification(byt, &flags); | |
326 | } else { | |
327 | /* reply from ADSP */ | |
328 | sst_byt_process_reply(byt, header); | |
329 | } | |
330 | /* | |
331 | * clear IPCD BUSY bit and set DONE bit. Tell DSP we have | |
332 | * processed the message and can accept new. Clear data part | |
333 | * of the header | |
334 | */ | |
335 | sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD, | |
336 | SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY | | |
337 | IPC_HEADER_DATA(IPC_HEADER_DATA_MASK), | |
338 | SST_BYT_IPCD_DONE); | |
339 | /* unmask message request interrupts */ | |
340 | sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX, | |
341 | SST_BYT_IMRX_REQUEST, 0); | |
342 | } | |
343 | ||
344 | spin_unlock_irqrestore(&sst->spinlock, flags); | |
345 | ||
346 | /* continue to send any remaining messages... */ | |
48cec59b | 347 | queue_kthread_work(&ipc->kworker, &ipc->kwork); |
a4b12990 MB |
348 | |
349 | return IRQ_HANDLED; | |
350 | } | |
351 | ||
352 | /* stream API */ | |
353 | struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, | |
354 | u32 (*notify_position)(struct sst_byt_stream *stream, void *data), | |
355 | void *data) | |
356 | { | |
357 | struct sst_byt_stream *stream; | |
d132cb0a WD |
358 | struct sst_dsp *sst = byt->dsp; |
359 | unsigned long flags; | |
a4b12990 MB |
360 | |
361 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | |
362 | if (stream == NULL) | |
363 | return NULL; | |
364 | ||
d132cb0a | 365 | spin_lock_irqsave(&sst->spinlock, flags); |
a4b12990 MB |
366 | list_add(&stream->node, &byt->stream_list); |
367 | stream->notify_position = notify_position; | |
368 | stream->pdata = data; | |
369 | stream->byt = byt; | |
370 | stream->str_id = id; | |
d132cb0a | 371 | spin_unlock_irqrestore(&sst->spinlock, flags); |
a4b12990 MB |
372 | |
373 | return stream; | |
374 | } | |
375 | ||
376 | int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream, | |
377 | int bits) | |
378 | { | |
379 | stream->request.pcm_params.pcm_wd_sz = bits; | |
380 | return 0; | |
381 | } | |
382 | ||
383 | int sst_byt_stream_set_channels(struct sst_byt *byt, | |
384 | struct sst_byt_stream *stream, u8 channels) | |
385 | { | |
386 | stream->request.pcm_params.num_chan = channels; | |
387 | return 0; | |
388 | } | |
389 | ||
390 | int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream, | |
391 | unsigned int rate) | |
392 | { | |
393 | stream->request.pcm_params.sfreq = rate; | |
394 | return 0; | |
395 | } | |
396 | ||
397 | /* stream sonfiguration */ | |
398 | int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream, | |
399 | int codec_type, int stream_type, int operation) | |
400 | { | |
401 | stream->request.str_type.codec_type = codec_type; | |
402 | stream->request.str_type.str_type = stream_type; | |
403 | stream->request.str_type.operation = operation; | |
404 | stream->request.str_type.time_slots = 0xc; | |
405 | ||
406 | return 0; | |
407 | } | |
408 | ||
409 | int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream, | |
410 | uint32_t buffer_addr, uint32_t buffer_size) | |
411 | { | |
412 | stream->request.frame_info.num_entries = 1; | |
413 | stream->request.frame_info.ring_buf_info[0].addr = buffer_addr; | |
414 | stream->request.frame_info.ring_buf_info[0].size = buffer_size; | |
415 | /* calculate bytes per 4 ms fragment */ | |
416 | stream->request.frame_info.frag_size = | |
417 | stream->request.pcm_params.sfreq * | |
418 | stream->request.pcm_params.num_chan * | |
419 | stream->request.pcm_params.pcm_wd_sz / 8 * | |
420 | 4 / 1000; | |
421 | return 0; | |
422 | } | |
423 | ||
424 | int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) | |
425 | { | |
426 | struct sst_byt_alloc_params *str_req = &stream->request; | |
427 | struct sst_byt_alloc_response *reply = &stream->reply; | |
428 | u64 header; | |
429 | int ret; | |
430 | ||
431 | header = sst_byt_header(IPC_IA_ALLOC_STREAM, | |
432 | sizeof(*str_req) + sizeof(u32), | |
433 | true, stream->str_id); | |
48cec59b JY |
434 | ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req, |
435 | sizeof(*str_req), | |
a4b12990 MB |
436 | reply, sizeof(*reply)); |
437 | if (ret < 0) { | |
438 | dev_err(byt->dev, "ipc: error stream commit failed\n"); | |
439 | return ret; | |
440 | } | |
441 | ||
442 | stream->commited = true; | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
447 | int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) | |
448 | { | |
449 | u64 header; | |
450 | int ret = 0; | |
d132cb0a WD |
451 | struct sst_dsp *sst = byt->dsp; |
452 | unsigned long flags; | |
a4b12990 MB |
453 | |
454 | if (!stream->commited) | |
455 | goto out; | |
456 | ||
457 | header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); | |
48cec59b | 458 | ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0); |
a4b12990 MB |
459 | if (ret < 0) { |
460 | dev_err(byt->dev, "ipc: free stream %d failed\n", | |
461 | stream->str_id); | |
462 | return -EAGAIN; | |
463 | } | |
464 | ||
465 | stream->commited = false; | |
466 | out: | |
d132cb0a | 467 | spin_lock_irqsave(&sst->spinlock, flags); |
a4b12990 MB |
468 | list_del(&stream->node); |
469 | kfree(stream); | |
d132cb0a | 470 | spin_unlock_irqrestore(&sst->spinlock, flags); |
a4b12990 MB |
471 | |
472 | return ret; | |
473 | } | |
474 | ||
475 | static int sst_byt_stream_operations(struct sst_byt *byt, int type, | |
476 | int stream_id, int wait) | |
477 | { | |
a4b12990 | 478 | u64 header; |
a4b12990 | 479 | |
65ee9e8f | 480 | header = sst_byt_header(type, 0, false, stream_id); |
a4b12990 | 481 | if (wait) |
48cec59b JY |
482 | return sst_ipc_tx_message_wait(&byt->ipc, header, NULL, |
483 | 0, NULL, 0); | |
a4b12990 | 484 | else |
48cec59b JY |
485 | return sst_ipc_tx_message_nowait(&byt->ipc, header, |
486 | NULL, 0); | |
a4b12990 MB |
487 | } |
488 | ||
489 | /* stream ALSA trigger operations */ | |
a6686ed5 JN |
490 | int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, |
491 | u32 start_offset) | |
a4b12990 | 492 | { |
65ee9e8f JN |
493 | struct sst_byt_start_stream_params start_stream; |
494 | void *tx_msg; | |
495 | size_t size; | |
496 | u64 header; | |
a4b12990 MB |
497 | int ret; |
498 | ||
a6686ed5 | 499 | start_stream.byte_offset = start_offset; |
65ee9e8f JN |
500 | header = sst_byt_header(IPC_IA_START_STREAM, |
501 | sizeof(start_stream) + sizeof(u32), | |
502 | true, stream->str_id); | |
503 | tx_msg = &start_stream; | |
504 | size = sizeof(start_stream); | |
505 | ||
48cec59b | 506 | ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size); |
a4b12990 MB |
507 | if (ret < 0) |
508 | dev_err(byt->dev, "ipc: error failed to start stream %d\n", | |
509 | stream->str_id); | |
510 | ||
511 | return ret; | |
512 | } | |
513 | ||
514 | int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream) | |
515 | { | |
516 | int ret; | |
517 | ||
518 | /* don't stop streams that are not commited */ | |
519 | if (!stream->commited) | |
520 | return 0; | |
521 | ||
522 | ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM, | |
523 | stream->str_id, 0); | |
524 | if (ret < 0) | |
525 | dev_err(byt->dev, "ipc: error failed to stop stream %d\n", | |
526 | stream->str_id); | |
527 | return ret; | |
528 | } | |
529 | ||
530 | int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream) | |
531 | { | |
532 | int ret; | |
533 | ||
534 | ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM, | |
535 | stream->str_id, 0); | |
536 | if (ret < 0) | |
537 | dev_err(byt->dev, "ipc: error failed to pause stream %d\n", | |
538 | stream->str_id); | |
539 | ||
540 | return ret; | |
541 | } | |
542 | ||
543 | int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream) | |
544 | { | |
545 | int ret; | |
546 | ||
547 | ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM, | |
548 | stream->str_id, 0); | |
549 | if (ret < 0) | |
550 | dev_err(byt->dev, "ipc: error failed to resume stream %d\n", | |
551 | stream->str_id); | |
552 | ||
553 | return ret; | |
554 | } | |
555 | ||
556 | int sst_byt_get_dsp_position(struct sst_byt *byt, | |
557 | struct sst_byt_stream *stream, int buffer_size) | |
558 | { | |
559 | struct sst_dsp *sst = byt->dsp; | |
560 | struct sst_byt_tstamp fw_tstamp; | |
561 | u8 str_id = stream->str_id; | |
562 | u32 tstamp_offset; | |
563 | ||
564 | tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp); | |
565 | memcpy_fromio(&fw_tstamp, | |
566 | sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp)); | |
567 | ||
568 | return do_div(fw_tstamp.ring_buffer_counter, buffer_size); | |
569 | } | |
570 | ||
a4b12990 MB |
571 | struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) |
572 | { | |
573 | return byt->dsp; | |
574 | } | |
575 | ||
576 | static struct sst_dsp_device byt_dev = { | |
577 | .thread = sst_byt_irq_thread, | |
578 | .ops = &sst_byt_ops, | |
579 | }; | |
580 | ||
27d3f026 | 581 | int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) |
af94aa55 LG |
582 | { |
583 | struct sst_byt *byt = pdata->dsp; | |
584 | ||
585 | dev_dbg(byt->dev, "dsp reset\n"); | |
586 | sst_dsp_reset(byt->dsp); | |
48cec59b | 587 | sst_ipc_drop_all(&byt->ipc); |
af94aa55 LG |
588 | dev_dbg(byt->dev, "dsp in reset\n"); |
589 | ||
af94aa55 LG |
590 | dev_dbg(byt->dev, "free all blocks and unload fw\n"); |
591 | sst_fw_unload(byt->fw); | |
592 | ||
593 | return 0; | |
594 | } | |
595 | EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late); | |
596 | ||
597 | int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata) | |
598 | { | |
599 | struct sst_byt *byt = pdata->dsp; | |
600 | int ret; | |
601 | ||
602 | dev_dbg(byt->dev, "reload dsp fw\n"); | |
603 | ||
604 | sst_dsp_reset(byt->dsp); | |
605 | ||
606 | ret = sst_fw_reload(byt->fw); | |
607 | if (ret < 0) { | |
608 | dev_err(dev, "error: failed to reload firmware\n"); | |
609 | return ret; | |
610 | } | |
611 | ||
612 | /* wait for DSP boot completion */ | |
613 | byt->boot_complete = false; | |
614 | sst_dsp_boot(byt->dsp); | |
615 | dev_dbg(byt->dev, "dsp booting...\n"); | |
616 | ||
617 | return 0; | |
618 | } | |
619 | EXPORT_SYMBOL_GPL(sst_byt_dsp_boot); | |
620 | ||
621 | int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) | |
622 | { | |
623 | struct sst_byt *byt = pdata->dsp; | |
624 | int err; | |
625 | ||
626 | dev_dbg(byt->dev, "wait for dsp reboot\n"); | |
627 | ||
628 | err = wait_event_timeout(byt->boot_wait, byt->boot_complete, | |
629 | msecs_to_jiffies(IPC_BOOT_MSECS)); | |
630 | if (err == 0) { | |
631 | dev_err(byt->dev, "ipc: error DSP boot timeout\n"); | |
632 | return -EIO; | |
633 | } | |
634 | ||
635 | dev_dbg(byt->dev, "dsp rebooted\n"); | |
636 | return 0; | |
637 | } | |
638 | EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); | |
639 | ||
48cec59b JY |
640 | static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) |
641 | { | |
642 | if (msg->header & IPC_HEADER_LARGE(true)) | |
643 | sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); | |
644 | ||
645 | sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header); | |
646 | } | |
647 | ||
648 | static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text) | |
649 | { | |
650 | struct sst_dsp *sst = ipc->dsp; | |
651 | u64 isr, ipcd, imrx, ipcx; | |
652 | ||
653 | ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); | |
654 | isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); | |
655 | ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); | |
656 | imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); | |
657 | ||
658 | dev_err(ipc->dev, | |
659 | "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", | |
660 | text, ipcx, isr, ipcd, imrx); | |
661 | } | |
662 | ||
663 | static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data, | |
664 | size_t tx_size) | |
665 | { | |
666 | /* msg content = lower 32-bit of the header + data */ | |
667 | *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1); | |
668 | memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size); | |
669 | msg->tx_size += sizeof(u32); | |
670 | } | |
671 | ||
672 | static u64 byt_reply_msg_match(u64 header, u64 *mask) | |
673 | { | |
674 | /* match reply to message sent based on msg and stream IDs */ | |
675 | *mask = IPC_HEADER_MSG_ID_MASK | | |
676 | IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; | |
677 | header &= *mask; | |
678 | ||
679 | return header; | |
680 | } | |
681 | ||
2709bdbc SP |
682 | static bool byt_is_dsp_busy(struct sst_dsp *dsp) |
683 | { | |
684 | u64 ipcx; | |
685 | ||
686 | ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); | |
687 | return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)); | |
688 | } | |
689 | ||
a4b12990 MB |
690 | int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) |
691 | { | |
692 | struct sst_byt *byt; | |
48cec59b | 693 | struct sst_generic_ipc *ipc; |
a4b12990 | 694 | struct sst_fw *byt_sst_fw; |
4131eceb | 695 | struct sst_byt_fw_init init; |
a4b12990 MB |
696 | int err; |
697 | ||
698 | dev_dbg(dev, "initialising Byt DSP IPC\n"); | |
699 | ||
700 | byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL); | |
701 | if (byt == NULL) | |
702 | return -ENOMEM; | |
703 | ||
48cec59b JY |
704 | ipc = &byt->ipc; |
705 | ipc->dev = dev; | |
706 | ipc->ops.tx_msg = byt_tx_msg; | |
707 | ipc->ops.shim_dbg = byt_shim_dbg; | |
708 | ipc->ops.tx_data_copy = byt_tx_data_copy; | |
709 | ipc->ops.reply_msg_match = byt_reply_msg_match; | |
2709bdbc | 710 | ipc->ops.is_dsp_busy = byt_is_dsp_busy; |
f99b26f0 SP |
711 | ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES; |
712 | ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES; | |
a4b12990 | 713 | |
48cec59b JY |
714 | err = sst_ipc_init(ipc); |
715 | if (err != 0) | |
716 | goto ipc_init_err; | |
a4b12990 | 717 | |
48cec59b JY |
718 | INIT_LIST_HEAD(&byt->stream_list); |
719 | init_waitqueue_head(&byt->boot_wait); | |
a4b12990 MB |
720 | byt_dev.thread_context = byt; |
721 | ||
722 | /* init SST shim */ | |
723 | byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); | |
724 | if (byt->dsp == NULL) { | |
725 | err = -ENODEV; | |
48cec59b | 726 | goto dsp_new_err; |
a4b12990 MB |
727 | } |
728 | ||
48cec59b JY |
729 | ipc->dsp = byt->dsp; |
730 | ||
a4b12990 MB |
731 | /* keep the DSP in reset state for base FW loading */ |
732 | sst_dsp_reset(byt->dsp); | |
733 | ||
734 | byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt); | |
735 | if (byt_sst_fw == NULL) { | |
736 | err = -ENODEV; | |
737 | dev_err(dev, "error: failed to load firmware\n"); | |
738 | goto fw_err; | |
739 | } | |
740 | ||
741 | /* wait for DSP boot completion */ | |
742 | sst_dsp_boot(byt->dsp); | |
743 | err = wait_event_timeout(byt->boot_wait, byt->boot_complete, | |
744 | msecs_to_jiffies(IPC_BOOT_MSECS)); | |
745 | if (err == 0) { | |
746 | err = -EIO; | |
747 | dev_err(byt->dev, "ipc: error DSP boot timeout\n"); | |
748 | goto boot_err; | |
749 | } | |
750 | ||
4131eceb JN |
751 | /* show firmware information */ |
752 | sst_dsp_inbox_read(byt->dsp, &init, sizeof(init)); | |
753 | dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n", | |
754 | init.fw_version.major, init.fw_version.minor, | |
755 | init.fw_version.build, init.fw_version.type); | |
756 | dev_info(byt->dev, "Build type: %x\n", init.fw_version.type); | |
757 | dev_info(byt->dev, "Build date: %s %s\n", | |
758 | init.build_info.date, init.build_info.time); | |
759 | ||
a4b12990 | 760 | pdata->dsp = byt; |
af94aa55 | 761 | byt->fw = byt_sst_fw; |
a4b12990 MB |
762 | |
763 | return 0; | |
764 | ||
765 | boot_err: | |
766 | sst_dsp_reset(byt->dsp); | |
767 | sst_fw_free(byt_sst_fw); | |
768 | fw_err: | |
769 | sst_dsp_free(byt->dsp); | |
48cec59b JY |
770 | dsp_new_err: |
771 | sst_ipc_fini(ipc); | |
772 | ipc_init_err: | |
a4b12990 MB |
773 | |
774 | return err; | |
775 | } | |
776 | EXPORT_SYMBOL_GPL(sst_byt_dsp_init); | |
777 | ||
778 | void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) | |
779 | { | |
780 | struct sst_byt *byt = pdata->dsp; | |
781 | ||
782 | sst_dsp_reset(byt->dsp); | |
783 | sst_fw_free_all(byt->dsp); | |
784 | sst_dsp_free(byt->dsp); | |
48cec59b | 785 | sst_ipc_fini(&byt->ipc); |
a4b12990 MB |
786 | } |
787 | EXPORT_SYMBOL_GPL(sst_byt_dsp_free); |