diff options
author | Liam Girdwood <liam.r.girdwood@linux.intel.com> | 2014-02-20 16:48:43 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-02-21 21:22:25 -0500 |
commit | 22981243589c3468481f5cfd04176233d2c7be4b (patch) | |
tree | b498b2fe9425770297f7e2afd04ce574e34b816d | |
parent | 1e42c3e426b3f7bc61ba338dd6507a293108117c (diff) |
ASoC: Intel: Add Haswell/Broadwell IPC
Add support for Haswell and Broadwell DSP IPC. This is used by the DSP
platform PCM driver to configure the DSP for audio operations.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r-- | sound/soc/intel/sst-haswell-ipc.c | 1785 | ||||
-rw-r--r-- | sound/soc/intel/sst-haswell-ipc.h | 488 |
2 files changed, 2273 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c new file mode 100644 index 000000000000..668d486520ae --- /dev/null +++ b/sound/soc/intel/sst-haswell-ipc.c | |||
@@ -0,0 +1,1785 @@ | |||
1 | /* | ||
2 | * Intel SST Haswell/Broadwell IPC Support | ||
3 | * | ||
4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/wait.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <linux/export.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/list.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/kthread.h> | ||
31 | #include <linux/firmware.h> | ||
32 | #include <linux/dma-mapping.h> | ||
33 | #include <linux/debugfs.h> | ||
34 | |||
35 | #include "sst-haswell-ipc.h" | ||
36 | #include "sst-dsp.h" | ||
37 | #include "sst-dsp-priv.h" | ||
38 | |||
39 | /* Global Message - Generic */ | ||
40 | #define IPC_GLB_TYPE_SHIFT 24 | ||
41 | #define IPC_GLB_TYPE_MASK (0x1f << IPC_GLB_TYPE_SHIFT) | ||
42 | #define IPC_GLB_TYPE(x) (x << IPC_GLB_TYPE_SHIFT) | ||
43 | |||
44 | /* Global Message - Reply */ | ||
45 | #define IPC_GLB_REPLY_SHIFT 0 | ||
46 | #define IPC_GLB_REPLY_MASK (0x1f << IPC_GLB_REPLY_SHIFT) | ||
47 | #define IPC_GLB_REPLY_TYPE(x) (x << IPC_GLB_REPLY_TYPE_SHIFT) | ||
48 | |||
49 | /* Stream Message - Generic */ | ||
50 | #define IPC_STR_TYPE_SHIFT 20 | ||
51 | #define IPC_STR_TYPE_MASK (0xf << IPC_STR_TYPE_SHIFT) | ||
52 | #define IPC_STR_TYPE(x) (x << IPC_STR_TYPE_SHIFT) | ||
53 | #define IPC_STR_ID_SHIFT 16 | ||
54 | #define IPC_STR_ID_MASK (0xf << IPC_STR_ID_SHIFT) | ||
55 | #define IPC_STR_ID(x) (x << IPC_STR_ID_SHIFT) | ||
56 | |||
57 | /* Stream Message - Reply */ | ||
58 | #define IPC_STR_REPLY_SHIFT 0 | ||
59 | #define IPC_STR_REPLY_MASK (0x1f << IPC_STR_REPLY_SHIFT) | ||
60 | |||
61 | /* Stream Stage Message - Generic */ | ||
62 | #define IPC_STG_TYPE_SHIFT 12 | ||
63 | #define IPC_STG_TYPE_MASK (0xf << IPC_STG_TYPE_SHIFT) | ||
64 | #define IPC_STG_TYPE(x) (x << IPC_STG_TYPE_SHIFT) | ||
65 | #define IPC_STG_ID_SHIFT 10 | ||
66 | #define IPC_STG_ID_MASK (0x3 << IPC_STG_ID_SHIFT) | ||
67 | #define IPC_STG_ID(x) (x << IPC_STG_ID_SHIFT) | ||
68 | |||
69 | /* Stream Stage Message - Reply */ | ||
70 | #define IPC_STG_REPLY_SHIFT 0 | ||
71 | #define IPC_STG_REPLY_MASK (0x1f << IPC_STG_REPLY_SHIFT) | ||
72 | |||
73 | /* Debug Log Message - Generic */ | ||
74 | #define IPC_LOG_OP_SHIFT 20 | ||
75 | #define IPC_LOG_OP_MASK (0xf << IPC_LOG_OP_SHIFT) | ||
76 | #define IPC_LOG_OP_TYPE(x) (x << IPC_LOG_OP_SHIFT) | ||
77 | #define IPC_LOG_ID_SHIFT 16 | ||
78 | #define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) | ||
79 | #define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT) | ||
80 | |||
81 | /* IPC message timeout (msecs) */ | ||
82 | #define IPC_TIMEOUT_MSECS 300 | ||
83 | #define IPC_BOOT_MSECS 200 | ||
84 | #define IPC_MSG_WAIT 0 | ||
85 | #define IPC_MSG_NOWAIT 1 | ||
86 | |||
87 | /* Firmware Ready Message */ | ||
88 | #define IPC_FW_READY (0x1 << 29) | ||
89 | #define IPC_STATUS_MASK (0x3 << 30) | ||
90 | |||
91 | #define IPC_EMPTY_LIST_SIZE 8 | ||
92 | #define IPC_MAX_STREAMS 4 | ||
93 | |||
94 | /* Mailbox */ | ||
95 | #define IPC_MAX_MAILBOX_BYTES 256 | ||
96 | |||
97 | /* Global Message - Types and Replies */ | ||
98 | enum ipc_glb_type { | ||
99 | IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */ | ||
100 | IPC_GLB_PERFORMANCE_MONITOR = 1, /* Performance monitoring actions */ | ||
101 | IPC_GLB_ALLOCATE_STREAM = 3, /* Request to allocate new stream */ | ||
102 | IPC_GLB_FREE_STREAM = 4, /* Request to free stream */ | ||
103 | IPC_GLB_GET_FW_CAPABILITIES = 5, /* Retrieves firmware capabilities */ | ||
104 | IPC_GLB_STREAM_MESSAGE = 6, /* Message directed to stream or its stages */ | ||
105 | /* Request to store firmware context during D0->D3 transition */ | ||
106 | IPC_GLB_REQUEST_DUMP = 7, | ||
107 | /* Request to restore firmware context during D3->D0 transition */ | ||
108 | IPC_GLB_RESTORE_CONTEXT = 8, | ||
109 | IPC_GLB_GET_DEVICE_FORMATS = 9, /* Set device format */ | ||
110 | IPC_GLB_SET_DEVICE_FORMATS = 10, /* Get device format */ | ||
111 | IPC_GLB_SHORT_REPLY = 11, | ||
112 | IPC_GLB_ENTER_DX_STATE = 12, | ||
113 | IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ | ||
114 | IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ | ||
115 | IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ | ||
116 | IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ | ||
117 | }; | ||
118 | |||
119 | enum ipc_glb_reply { | ||
120 | IPC_GLB_REPLY_SUCCESS = 0, /* The operation was successful. */ | ||
121 | IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1, /* Invalid parameter was passed. */ | ||
122 | IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2, /* Uknown message type was resceived. */ | ||
123 | IPC_GLB_REPLY_OUT_OF_RESOURCES = 3, /* No resources to satisfy the request. */ | ||
124 | IPC_GLB_REPLY_BUSY = 4, /* The system or resource is busy. */ | ||
125 | IPC_GLB_REPLY_PENDING = 5, /* The action was scheduled for processing. */ | ||
126 | IPC_GLB_REPLY_FAILURE = 6, /* Critical error happened. */ | ||
127 | IPC_GLB_REPLY_INVALID_REQUEST = 7, /* Request can not be completed. */ | ||
128 | IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8, /* Processing stage was uninitialized. */ | ||
129 | IPC_GLB_REPLY_NOT_FOUND = 9, /* Required resource can not be found. */ | ||
130 | IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ | ||
131 | }; | ||
132 | |||
133 | /* Stream Message - Types */ | ||
134 | enum ipc_str_operation { | ||
135 | IPC_STR_RESET = 0, | ||
136 | IPC_STR_PAUSE = 1, | ||
137 | IPC_STR_RESUME = 2, | ||
138 | IPC_STR_STAGE_MESSAGE = 3, | ||
139 | IPC_STR_NOTIFICATION = 4, | ||
140 | IPC_STR_MAX_MESSAGE | ||
141 | }; | ||
142 | |||
143 | /* Stream Stage Message Types */ | ||
144 | enum ipc_stg_operation { | ||
145 | IPC_STG_GET_VOLUME = 0, | ||
146 | IPC_STG_SET_VOLUME, | ||
147 | IPC_STG_SET_WRITE_POSITION, | ||
148 | IPC_STG_SET_FX_ENABLE, | ||
149 | IPC_STG_SET_FX_DISABLE, | ||
150 | IPC_STG_SET_FX_GET_PARAM, | ||
151 | IPC_STG_SET_FX_SET_PARAM, | ||
152 | IPC_STG_SET_FX_GET_INFO, | ||
153 | IPC_STG_MUTE_LOOPBACK, | ||
154 | IPC_STG_MAX_MESSAGE | ||
155 | }; | ||
156 | |||
157 | /* Stream Stage Message Types For Notification*/ | ||
158 | enum ipc_stg_operation_notify { | ||
159 | IPC_POSITION_CHANGED = 0, | ||
160 | IPC_STG_GLITCH, | ||
161 | IPC_STG_MAX_NOTIFY | ||
162 | }; | ||
163 | |||
164 | enum ipc_glitch_type { | ||
165 | IPC_GLITCH_UNDERRUN = 1, | ||
166 | IPC_GLITCH_DECODER_ERROR, | ||
167 | IPC_GLITCH_DOUBLED_WRITE_POS, | ||
168 | IPC_GLITCH_MAX | ||
169 | }; | ||
170 | |||
171 | /* Debug Control */ | ||
172 | enum ipc_debug_operation { | ||
173 | IPC_DEBUG_ENABLE_LOG = 0, | ||
174 | IPC_DEBUG_DISABLE_LOG = 1, | ||
175 | IPC_DEBUG_REQUEST_LOG_DUMP = 2, | ||
176 | IPC_DEBUG_NOTIFY_LOG_DUMP = 3, | ||
177 | IPC_DEBUG_MAX_DEBUG_LOG | ||
178 | }; | ||
179 | |||
180 | /* Firmware Ready */ | ||
181 | struct sst_hsw_ipc_fw_ready { | ||
182 | u32 inbox_offset; | ||
183 | u32 outbox_offset; | ||
184 | u32 inbox_size; | ||
185 | u32 outbox_size; | ||
186 | u32 fw_info_size; | ||
187 | u8 fw_info[1]; | ||
188 | } __attribute__((packed)); | ||
189 | |||
190 | struct ipc_message { | ||
191 | struct list_head list; | ||
192 | u32 header; | ||
193 | |||
194 | /* direction wrt host CPU */ | ||
195 | char tx_data[IPC_MAX_MAILBOX_BYTES]; | ||
196 | size_t tx_size; | ||
197 | char rx_data[IPC_MAX_MAILBOX_BYTES]; | ||
198 | size_t rx_size; | ||
199 | |||
200 | wait_queue_head_t waitq; | ||
201 | bool pending; | ||
202 | bool complete; | ||
203 | bool wait; | ||
204 | int errno; | ||
205 | }; | ||
206 | |||
207 | struct sst_hsw_stream; | ||
208 | struct sst_hsw; | ||
209 | |||
210 | /* Stream infomation */ | ||
211 | struct sst_hsw_stream { | ||
212 | /* configuration */ | ||
213 | struct sst_hsw_ipc_stream_alloc_req request; | ||
214 | struct sst_hsw_ipc_stream_alloc_reply reply; | ||
215 | struct sst_hsw_ipc_stream_free_req free_req; | ||
216 | |||
217 | /* Mixer info */ | ||
218 | u32 mute_volume[SST_HSW_NO_CHANNELS]; | ||
219 | u32 mute[SST_HSW_NO_CHANNELS]; | ||
220 | |||
221 | /* runtime info */ | ||
222 | struct sst_hsw *hsw; | ||
223 | int host_id; | ||
224 | bool commited; | ||
225 | bool running; | ||
226 | |||
227 | /* Notification work */ | ||
228 | struct work_struct notify_work; | ||
229 | u32 header; | ||
230 | |||
231 | /* Position info from DSP */ | ||
232 | struct sst_hsw_ipc_stream_set_position wpos; | ||
233 | struct sst_hsw_ipc_stream_get_position rpos; | ||
234 | struct sst_hsw_ipc_stream_glitch_position glitch; | ||
235 | |||
236 | /* Volume info */ | ||
237 | struct sst_hsw_ipc_volume_req vol_req; | ||
238 | |||
239 | /* driver callback */ | ||
240 | u32 (*notify_position)(struct sst_hsw_stream *stream, void *data); | ||
241 | void *pdata; | ||
242 | |||
243 | struct list_head node; | ||
244 | }; | ||
245 | |||
246 | /* FW log ring information */ | ||
247 | struct sst_hsw_log_stream { | ||
248 | dma_addr_t dma_addr; | ||
249 | unsigned char *dma_area; | ||
250 | unsigned char *ring_descr; | ||
251 | int pages; | ||
252 | int size; | ||
253 | |||
254 | /* Notification work */ | ||
255 | struct work_struct notify_work; | ||
256 | wait_queue_head_t readers_wait_q; | ||
257 | struct mutex rw_mutex; | ||
258 | |||
259 | u32 last_pos; | ||
260 | u32 curr_pos; | ||
261 | u32 reader_pos; | ||
262 | |||
263 | /* fw log config */ | ||
264 | u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS]; | ||
265 | |||
266 | struct sst_hsw *hsw; | ||
267 | }; | ||
268 | |||
269 | /* SST Haswell IPC data */ | ||
270 | struct sst_hsw { | ||
271 | struct device *dev; | ||
272 | struct sst_dsp *dsp; | ||
273 | struct platform_device *pdev_pcm; | ||
274 | |||
275 | /* FW config */ | ||
276 | struct sst_hsw_ipc_fw_ready fw_ready; | ||
277 | struct sst_hsw_ipc_fw_version version; | ||
278 | struct sst_module *scratch; | ||
279 | bool fw_done; | ||
280 | |||
281 | /* stream */ | ||
282 | struct list_head stream_list; | ||
283 | |||
284 | /* global mixer */ | ||
285 | struct sst_hsw_ipc_stream_info_reply mixer_info; | ||
286 | enum sst_hsw_volume_curve curve_type; | ||
287 | u32 curve_duration; | ||
288 | u32 mute[SST_HSW_NO_CHANNELS]; | ||
289 | u32 mute_volume[SST_HSW_NO_CHANNELS]; | ||
290 | |||
291 | /* DX */ | ||
292 | struct sst_hsw_ipc_dx_reply dx; | ||
293 | |||
294 | /* boot */ | ||
295 | wait_queue_head_t boot_wait; | ||
296 | bool boot_complete; | ||
297 | bool shutdown; | ||
298 | |||
299 | /* IPC messaging */ | ||
300 | struct list_head tx_list; | ||
301 | struct list_head rx_list; | ||
302 | struct list_head empty_list; | ||
303 | wait_queue_head_t wait_txq; | ||
304 | struct task_struct *tx_thread; | ||
305 | struct kthread_worker kworker; | ||
306 | struct kthread_work kwork; | ||
307 | bool pending; | ||
308 | struct ipc_message *msg; | ||
309 | |||
310 | /* FW log stream */ | ||
311 | struct sst_hsw_log_stream log_stream; | ||
312 | }; | ||
313 | |||
314 | #define CREATE_TRACE_POINTS | ||
315 | #include <trace/events/hswadsp.h> | ||
316 | |||
317 | static inline u32 msg_get_global_type(u32 msg) | ||
318 | { | ||
319 | return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT; | ||
320 | } | ||
321 | |||
322 | static inline u32 msg_get_global_reply(u32 msg) | ||
323 | { | ||
324 | return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT; | ||
325 | } | ||
326 | |||
327 | static inline u32 msg_get_stream_type(u32 msg) | ||
328 | { | ||
329 | return (msg & IPC_STR_TYPE_MASK) >> IPC_STR_TYPE_SHIFT; | ||
330 | } | ||
331 | |||
332 | static inline u32 msg_get_stage_type(u32 msg) | ||
333 | { | ||
334 | return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; | ||
335 | } | ||
336 | |||
337 | static inline u32 msg_set_stage_type(u32 msg, u32 type) | ||
338 | { | ||
339 | return (msg & ~IPC_STG_TYPE_MASK) + | ||
340 | (type << IPC_STG_TYPE_SHIFT); | ||
341 | } | ||
342 | |||
343 | static inline u32 msg_get_stream_id(u32 msg) | ||
344 | { | ||
345 | return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT; | ||
346 | } | ||
347 | |||
348 | static inline u32 msg_get_notify_reason(u32 msg) | ||
349 | { | ||
350 | return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; | ||
351 | } | ||
352 | |||
353 | u32 create_channel_map(enum sst_hsw_channel_config config) | ||
354 | { | ||
355 | switch (config) { | ||
356 | case SST_HSW_CHANNEL_CONFIG_MONO: | ||
357 | return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER); | ||
358 | case SST_HSW_CHANNEL_CONFIG_STEREO: | ||
359 | return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT | ||
360 | | (SST_HSW_CHANNEL_RIGHT << 4)); | ||
361 | case SST_HSW_CHANNEL_CONFIG_2_POINT_1: | ||
362 | return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT | ||
363 | | (SST_HSW_CHANNEL_RIGHT << 4) | ||
364 | | (SST_HSW_CHANNEL_LFE << 8 )); | ||
365 | case SST_HSW_CHANNEL_CONFIG_3_POINT_0: | ||
366 | return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT | ||
367 | | (SST_HSW_CHANNEL_CENTER << 4) | ||
368 | | (SST_HSW_CHANNEL_RIGHT << 8)); | ||
369 | case SST_HSW_CHANNEL_CONFIG_3_POINT_1: | ||
370 | return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT | ||
371 | | (SST_HSW_CHANNEL_CENTER << 4) | ||
372 | | (SST_HSW_CHANNEL_RIGHT << 8) | ||
373 | | (SST_HSW_CHANNEL_LFE << 12)); | ||
374 | case SST_HSW_CHANNEL_CONFIG_QUATRO: | ||
375 | return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT | ||
376 | | (SST_HSW_CHANNEL_RIGHT << 4) | ||
377 | | (SST_HSW_CHANNEL_LEFT_SURROUND << 8) | ||
378 | | (SST_HSW_CHANNEL_RIGHT_SURROUND << 12)); | ||
379 | case SST_HSW_CHANNEL_CONFIG_4_POINT_0: | ||
380 | return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT | ||
381 | | (SST_HSW_CHANNEL_CENTER << 4) | ||
382 | | (SST_HSW_CHANNEL_RIGHT << 8) | ||
383 | | (SST_HSW_CHANNEL_CENTER_SURROUND << 12)); | ||
384 | case SST_HSW_CHANNEL_CONFIG_5_POINT_0: | ||
385 | return (0xFFF00000 | SST_HSW_CHANNEL_LEFT | ||
386 | | (SST_HSW_CHANNEL_CENTER << 4) | ||
387 | | (SST_HSW_CHANNEL_RIGHT << 8) | ||
388 | | (SST_HSW_CHANNEL_LEFT_SURROUND << 12) | ||
389 | | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)); | ||
390 | case SST_HSW_CHANNEL_CONFIG_5_POINT_1: | ||
391 | return (0xFF000000 | SST_HSW_CHANNEL_CENTER | ||
392 | | (SST_HSW_CHANNEL_LEFT << 4) | ||
393 | | (SST_HSW_CHANNEL_RIGHT << 8) | ||
394 | | (SST_HSW_CHANNEL_LEFT_SURROUND << 12) | ||
395 | | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16) | ||
396 | | (SST_HSW_CHANNEL_LFE << 20)); | ||
397 | case SST_HSW_CHANNEL_CONFIG_DUAL_MONO: | ||
398 | return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT | ||
399 | | (SST_HSW_CHANNEL_LEFT << 4)); | ||
400 | default: | ||
401 | return 0xFFFFFFFF; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw, | ||
406 | int stream_id) | ||
407 | { | ||
408 | struct sst_hsw_stream *stream; | ||
409 | |||
410 | list_for_each_entry(stream, &hsw->stream_list, node) { | ||
411 | if (stream->reply.stream_hw_id == stream_id) | ||
412 | return stream; | ||
413 | } | ||
414 | |||
415 | return NULL; | ||
416 | } | ||
417 | |||
418 | static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text) | ||
419 | { | ||
420 | struct sst_dsp *sst = hsw->dsp; | ||
421 | u32 isr, ipcd, imrx, ipcx; | ||
422 | |||
423 | ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); | ||
424 | isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); | ||
425 | ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); | ||
426 | imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); | ||
427 | |||
428 | dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", | ||
429 | text, ipcx, isr, ipcd, imrx); | ||
430 | } | ||
431 | |||
432 | /* locks held by caller */ | ||
433 | static struct ipc_message *msg_get_empty(struct sst_hsw *hsw) | ||
434 | { | ||
435 | struct ipc_message *msg = NULL; | ||
436 | |||
437 | if (!list_empty(&hsw->empty_list)) { | ||
438 | msg = list_first_entry(&hsw->empty_list, struct ipc_message, | ||
439 | list); | ||
440 | list_del(&msg->list); | ||
441 | } | ||
442 | |||
443 | return msg; | ||
444 | } | ||
445 | |||
446 | static void ipc_tx_msgs(struct kthread_work *work) | ||
447 | { | ||
448 | struct sst_hsw *hsw = | ||
449 | container_of(work, struct sst_hsw, kwork); | ||
450 | struct ipc_message *msg; | ||
451 | unsigned long flags; | ||
452 | u32 ipcx; | ||
453 | |||
454 | spin_lock_irqsave(&hsw->dsp->spinlock, flags); | ||
455 | |||
456 | if (list_empty(&hsw->tx_list) || hsw->pending) { | ||
457 | spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); | ||
458 | return; | ||
459 | } | ||
460 | |||
461 | /* if the DSP is busy we will TX messages after IRQ */ | ||
462 | ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX); | ||
463 | if (ipcx & SST_IPCX_BUSY) { | ||
464 | spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); | ||
465 | return; | ||
466 | } | ||
467 | |||
468 | msg = list_first_entry(&hsw->tx_list, struct ipc_message, list); | ||
469 | |||
470 | list_move(&msg->list, &hsw->rx_list); | ||
471 | |||
472 | /* send the message */ | ||
473 | sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size); | ||
474 | sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY); | ||
475 | |||
476 | spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); | ||
477 | } | ||
478 | |||
479 | /* locks held by caller */ | ||
480 | static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg) | ||
481 | { | ||
482 | msg->complete = true; | ||
483 | trace_ipc_reply("completed", msg->header); | ||
484 | |||
485 | if (!msg->wait) | ||
486 | list_add_tail(&msg->list, &hsw->empty_list); | ||
487 | else | ||
488 | wake_up(&msg->waitq); | ||
489 | } | ||
490 | |||
491 | static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg, | ||
492 | void *rx_data) | ||
493 | { | ||
494 | unsigned long flags; | ||
495 | int ret; | ||
496 | |||
497 | /* wait for DSP completion (in all cases atm inc pending) */ | ||
498 | ret = wait_event_timeout(msg->waitq, msg->complete, | ||
499 | msecs_to_jiffies(IPC_TIMEOUT_MSECS)); | ||
500 | |||
501 | spin_lock_irqsave(&hsw->dsp->spinlock, flags); | ||
502 | if (ret == 0) { | ||
503 | ipc_shim_dbg(hsw, "message timeout"); | ||
504 | |||
505 | trace_ipc_error("error message timeout for", msg->header); | ||
506 | ret = -ETIMEDOUT; | ||
507 | } else { | ||
508 | |||
509 | /* copy the data returned from DSP */ | ||
510 | if (msg->rx_size) | ||
511 | memcpy(rx_data, msg->rx_data, msg->rx_size); | ||
512 | ret = msg->errno; | ||
513 | } | ||
514 | |||
515 | list_add_tail(&msg->list, &hsw->empty_list); | ||
516 | spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data, | ||
521 | size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait) | ||
522 | { | ||
523 | struct ipc_message *msg; | ||
524 | unsigned long flags; | ||
525 | |||
526 | spin_lock_irqsave(&hsw->dsp->spinlock, flags); | ||
527 | |||
528 | msg = msg_get_empty(hsw); | ||
529 | if (msg == NULL) { | ||
530 | spin_unlock(&hsw->dsp->spinlock); | ||
531 | return -EBUSY; | ||
532 | } | ||
533 | |||
534 | if (tx_bytes) | ||
535 | memcpy(msg->tx_data, tx_data, tx_bytes); | ||
536 | |||
537 | msg->header = header; | ||
538 | msg->tx_size = tx_bytes; | ||
539 | msg->rx_size = rx_bytes; | ||
540 | msg->wait = wait; | ||
541 | msg->errno = 0; | ||
542 | msg->pending = false; | ||
543 | msg->complete = false; | ||
544 | |||
545 | list_add_tail(&msg->list, &hsw->tx_list); | ||
546 | spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); | ||
547 | |||
548 | queue_kthread_work(&hsw->kworker, &hsw->kwork); | ||
549 | |||
550 | if (wait) | ||
551 | return tx_wait_done(hsw, msg, rx_data); | ||
552 | else | ||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header, | ||
557 | void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) | ||
558 | { | ||
559 | return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data, | ||
560 | rx_bytes, 1); | ||
561 | } | ||
562 | |||
563 | static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header, | ||
564 | void *tx_data, size_t tx_bytes) | ||
565 | { | ||
566 | return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0); | ||
567 | } | ||
568 | |||
569 | static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) | ||
570 | { | ||
571 | struct sst_hsw_ipc_fw_ready fw_ready; | ||
572 | u32 offset; | ||
573 | |||
574 | offset = (header & 0x1FFFFFFF) << 3; | ||
575 | |||
576 | dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", | ||
577 | header, offset); | ||
578 | |||
579 | /* copy data from the DSP FW ready offset */ | ||
580 | sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready)); | ||
581 | |||
582 | sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset, | ||
583 | fw_ready.inbox_size, fw_ready.outbox_offset, | ||
584 | fw_ready.outbox_size); | ||
585 | |||
586 | hsw->boot_complete = true; | ||
587 | wake_up(&hsw->boot_wait); | ||
588 | |||
589 | dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n", | ||
590 | fw_ready.inbox_offset, fw_ready.inbox_size); | ||
591 | dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n", | ||
592 | fw_ready.outbox_offset, fw_ready.outbox_size); | ||
593 | } | ||
594 | |||
595 | static void hsw_notification_work(struct work_struct *work) | ||
596 | { | ||
597 | struct sst_hsw_stream *stream = container_of(work, | ||
598 | struct sst_hsw_stream, notify_work); | ||
599 | struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch; | ||
600 | struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos; | ||
601 | struct sst_hsw *hsw = stream->hsw; | ||
602 | u32 reason; | ||
603 | |||
604 | reason = msg_get_notify_reason(stream->header); | ||
605 | |||
606 | switch (reason) { | ||
607 | case IPC_STG_GLITCH: | ||
608 | trace_ipc_notification("DSP stream under/overrun", | ||
609 | stream->reply.stream_hw_id); | ||
610 | sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch)); | ||
611 | |||
612 | dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n", | ||
613 | glitch->glitch_type, glitch->present_pos, | ||
614 | glitch->write_pos); | ||
615 | break; | ||
616 | |||
617 | case IPC_POSITION_CHANGED: | ||
618 | trace_ipc_notification("DSP stream position changed for", | ||
619 | stream->reply.stream_hw_id); | ||
620 | sst_dsp_inbox_read(hsw->dsp, pos, sizeof(&pos)); | ||
621 | |||
622 | if (stream->notify_position) | ||
623 | stream->notify_position(stream, stream->pdata); | ||
624 | |||
625 | break; | ||
626 | default: | ||
627 | dev_err(hsw->dev, "error: unknown notification 0x%x\n", | ||
628 | stream->header); | ||
629 | break; | ||
630 | } | ||
631 | |||
632 | /* tell DSP that notification has been handled */ | ||
633 | sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IPCD, | ||
634 | SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); | ||
635 | |||
636 | /* unmask busy interrupt */ | ||
637 | sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); | ||
638 | } | ||
639 | |||
640 | static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header) | ||
641 | { | ||
642 | struct ipc_message *msg; | ||
643 | |||
644 | /* clear reply bits & status bits */ | ||
645 | header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); | ||
646 | |||
647 | if (list_empty(&hsw->rx_list)) { | ||
648 | dev_err(hsw->dev, "error: rx list empty but received 0x%x\n", | ||
649 | header); | ||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | list_for_each_entry(msg, &hsw->rx_list, list) { | ||
654 | if (msg->header == header) | ||
655 | return msg; | ||
656 | } | ||
657 | |||
658 | return NULL; | ||
659 | } | ||
660 | |||
661 | static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg) | ||
662 | { | ||
663 | struct sst_hsw_stream *stream; | ||
664 | u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); | ||
665 | u32 stream_id = msg_get_stream_id(header); | ||
666 | u32 stream_msg = msg_get_stream_type(header); | ||
667 | |||
668 | stream = get_stream_by_id(hsw, stream_id); | ||
669 | if (stream == NULL) | ||
670 | return; | ||
671 | |||
672 | switch (stream_msg) { | ||
673 | case IPC_STR_STAGE_MESSAGE: | ||
674 | case IPC_STR_NOTIFICATION: | ||
675 | case IPC_STR_RESET: | ||
676 | break; | ||
677 | case IPC_STR_PAUSE: | ||
678 | stream->running = false; | ||
679 | trace_ipc_notification("stream paused", | ||
680 | stream->reply.stream_hw_id); | ||
681 | break; | ||
682 | case IPC_STR_RESUME: | ||
683 | stream->running = true; | ||
684 | trace_ipc_notification("stream running", | ||
685 | stream->reply.stream_hw_id); | ||
686 | break; | ||
687 | } | ||
688 | } | ||
689 | |||
690 | static int hsw_process_reply(struct sst_hsw *hsw, u32 header) | ||
691 | { | ||
692 | struct ipc_message *msg; | ||
693 | u32 reply = msg_get_global_reply(header); | ||
694 | |||
695 | trace_ipc_reply("processing -->", header); | ||
696 | |||
697 | msg = reply_find_msg(hsw, header); | ||
698 | if (msg == NULL) { | ||
699 | trace_ipc_error("error: can't find message header", header); | ||
700 | return -EIO; | ||
701 | } | ||
702 | |||
703 | /* first process the header */ | ||
704 | switch (reply) { | ||
705 | case IPC_GLB_REPLY_PENDING: | ||
706 | trace_ipc_pending_reply("received", header); | ||
707 | msg->pending = true; | ||
708 | hsw->pending = true; | ||
709 | return 1; | ||
710 | case IPC_GLB_REPLY_SUCCESS: | ||
711 | if (msg->pending) { | ||
712 | trace_ipc_pending_reply("completed", header); | ||
713 | sst_dsp_inbox_read(hsw->dsp, msg->rx_data, | ||
714 | msg->rx_size); | ||
715 | hsw->pending = false; | ||
716 | } else { | ||
717 | /* copy data from the DSP */ | ||
718 | sst_dsp_outbox_read(hsw->dsp, msg->rx_data, | ||
719 | msg->rx_size); | ||
720 | } | ||
721 | break; | ||
722 | /* these will be rare - but useful for debug */ | ||
723 | case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE: | ||
724 | trace_ipc_error("error: unknown message type", header); | ||
725 | msg->errno = -EBADMSG; | ||
726 | break; | ||
727 | case IPC_GLB_REPLY_OUT_OF_RESOURCES: | ||
728 | trace_ipc_error("error: out of resources", header); | ||
729 | msg->errno = -ENOMEM; | ||
730 | break; | ||
731 | case IPC_GLB_REPLY_BUSY: | ||
732 | trace_ipc_error("error: reply busy", header); | ||
733 | msg->errno = -EBUSY; | ||
734 | break; | ||
735 | case IPC_GLB_REPLY_FAILURE: | ||
736 | trace_ipc_error("error: reply failure", header); | ||
737 | msg->errno = -EINVAL; | ||
738 | break; | ||
739 | case IPC_GLB_REPLY_STAGE_UNINITIALIZED: | ||
740 | trace_ipc_error("error: stage uninitialized", header); | ||
741 | msg->errno = -EINVAL; | ||
742 | break; | ||
743 | case IPC_GLB_REPLY_NOT_FOUND: | ||
744 | trace_ipc_error("error: reply not found", header); | ||
745 | msg->errno = -EINVAL; | ||
746 | break; | ||
747 | case IPC_GLB_REPLY_SOURCE_NOT_STARTED: | ||
748 | trace_ipc_error("error: source not started", header); | ||
749 | msg->errno = -EINVAL; | ||
750 | break; | ||
751 | case IPC_GLB_REPLY_INVALID_REQUEST: | ||
752 | trace_ipc_error("error: invalid request", header); | ||
753 | msg->errno = -EINVAL; | ||
754 | break; | ||
755 | case IPC_GLB_REPLY_ERROR_INVALID_PARAM: | ||
756 | trace_ipc_error("error: invalid parameter", header); | ||
757 | msg->errno = -EINVAL; | ||
758 | break; | ||
759 | default: | ||
760 | trace_ipc_error("error: unknown reply", header); | ||
761 | msg->errno = -EINVAL; | ||
762 | break; | ||
763 | } | ||
764 | |||
765 | /* update any stream states */ | ||
766 | hsw_stream_update(hsw, msg); | ||
767 | |||
768 | /* wake up and return the error if we have waiters on this message ? */ | ||
769 | list_del(&msg->list); | ||
770 | tx_msg_reply_complete(hsw, msg); | ||
771 | |||
772 | return 1; | ||
773 | } | ||
774 | |||
775 | static int hsw_stream_message(struct sst_hsw *hsw, u32 header) | ||
776 | { | ||
777 | u32 stream_msg, stream_id, stage_type; | ||
778 | struct sst_hsw_stream *stream; | ||
779 | int handled = 0; | ||
780 | |||
781 | stream_msg = msg_get_stream_type(header); | ||
782 | stream_id = msg_get_stream_id(header); | ||
783 | stage_type = msg_get_stage_type(header); | ||
784 | |||
785 | stream = get_stream_by_id(hsw, stream_id); | ||
786 | if (stream == NULL) | ||
787 | return handled; | ||
788 | |||
789 | stream->header = header; | ||
790 | |||
791 | switch (stream_msg) { | ||
792 | case IPC_STR_STAGE_MESSAGE: | ||
793 | dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n", | ||
794 | header); | ||
795 | break; | ||
796 | case IPC_STR_NOTIFICATION: | ||
797 | schedule_work(&stream->notify_work); | ||
798 | break; | ||
799 | default: | ||
800 | /* handle pending message complete request */ | ||
801 | handled = hsw_process_reply(hsw, header); | ||
802 | break; | ||
803 | } | ||
804 | |||
805 | return handled; | ||
806 | } | ||
807 | |||
808 | static int hsw_log_message(struct sst_hsw *hsw, u32 header) | ||
809 | { | ||
810 | u32 operation = (header & IPC_LOG_OP_MASK) >> IPC_LOG_OP_SHIFT; | ||
811 | struct sst_hsw_log_stream *stream = &hsw->log_stream; | ||
812 | int ret = 1; | ||
813 | |||
814 | if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) { | ||
815 | dev_err(hsw->dev, | ||
816 | "error: log msg not implemented 0x%8.8x\n", header); | ||
817 | return 0; | ||
818 | } | ||
819 | |||
820 | mutex_lock(&stream->rw_mutex); | ||
821 | stream->last_pos = stream->curr_pos; | ||
822 | sst_dsp_inbox_read( | ||
823 | hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos)); | ||
824 | mutex_unlock(&stream->rw_mutex); | ||
825 | |||
826 | schedule_work(&stream->notify_work); | ||
827 | |||
828 | return ret; | ||
829 | } | ||
830 | |||
831 | static int hsw_process_notification(struct sst_hsw *hsw) | ||
832 | { | ||
833 | struct sst_dsp *sst = hsw->dsp; | ||
834 | u32 type, header; | ||
835 | int handled = 1; | ||
836 | |||
837 | header = sst_dsp_shim_read_unlocked(sst, SST_IPCD); | ||
838 | type = msg_get_global_type(header); | ||
839 | |||
840 | trace_ipc_request("processing -->", header); | ||
841 | |||
842 | /* FW Ready is a special case */ | ||
843 | if (!hsw->boot_complete && header & IPC_FW_READY) { | ||
844 | hsw_fw_ready(hsw, header); | ||
845 | return handled; | ||
846 | } | ||
847 | |||
848 | switch (type) { | ||
849 | case IPC_GLB_GET_FW_VERSION: | ||
850 | case IPC_GLB_ALLOCATE_STREAM: | ||
851 | case IPC_GLB_FREE_STREAM: | ||
852 | case IPC_GLB_GET_FW_CAPABILITIES: | ||
853 | case IPC_GLB_REQUEST_DUMP: | ||
854 | case IPC_GLB_GET_DEVICE_FORMATS: | ||
855 | case IPC_GLB_SET_DEVICE_FORMATS: | ||
856 | case IPC_GLB_ENTER_DX_STATE: | ||
857 | case IPC_GLB_GET_MIXER_STREAM_INFO: | ||
858 | case IPC_GLB_MAX_IPC_MESSAGE_TYPE: | ||
859 | case IPC_GLB_RESTORE_CONTEXT: | ||
860 | case IPC_GLB_SHORT_REPLY: | ||
861 | dev_err(hsw->dev, "error: message type %d header 0x%x\n", | ||
862 | type, header); | ||
863 | break; | ||
864 | case IPC_GLB_STREAM_MESSAGE: | ||
865 | handled = hsw_stream_message(hsw, header); | ||
866 | break; | ||
867 | case IPC_GLB_DEBUG_LOG_MESSAGE: | ||
868 | handled = hsw_log_message(hsw, header); | ||
869 | break; | ||
870 | default: | ||
871 | dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", | ||
872 | type, header); | ||
873 | break; | ||
874 | } | ||
875 | |||
876 | return handled; | ||
877 | } | ||
878 | |||
879 | static irqreturn_t hsw_irq_thread(int irq, void *context) | ||
880 | { | ||
881 | struct sst_dsp *sst = (struct sst_dsp *) context; | ||
882 | struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); | ||
883 | u32 ipcx, ipcd; | ||
884 | int handled; | ||
885 | unsigned long flags; | ||
886 | |||
887 | spin_lock_irqsave(&sst->spinlock, flags); | ||
888 | |||
889 | ipcx = sst_dsp_ipc_msg_rx(hsw->dsp); | ||
890 | ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); | ||
891 | |||
892 | /* reply message from DSP */ | ||
893 | if (ipcx & SST_IPCX_DONE) { | ||
894 | |||
895 | /* Handle Immediate reply from DSP Core */ | ||
896 | handled = hsw_process_reply(hsw, ipcx); | ||
897 | |||
898 | if (handled) { | ||
899 | /* clear DONE bit - tell DSP we have completed */ | ||
900 | sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX, | ||
901 | SST_IPCX_DONE, 0); | ||
902 | |||
903 | /* unmask Done interrupt */ | ||
904 | sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, | ||
905 | SST_IMRX_DONE, 0); | ||
906 | } | ||
907 | } | ||
908 | |||
909 | /* new message from DSP */ | ||
910 | if (ipcd & SST_IPCD_BUSY) { | ||
911 | |||
912 | /* Handle Notification and Delayed reply from DSP Core */ | ||
913 | handled = hsw_process_notification(hsw); | ||
914 | |||
915 | /* clear BUSY bit and set DONE bit - accept new messages */ | ||
916 | if (handled) { | ||
917 | sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD, | ||
918 | SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); | ||
919 | |||
920 | /* unmask busy interrupt */ | ||
921 | sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, | ||
922 | SST_IMRX_BUSY, 0); | ||
923 | } | ||
924 | } | ||
925 | |||
926 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
927 | |||
928 | /* continue to send any remaining messages... */ | ||
929 | queue_kthread_work(&hsw->kworker, &hsw->kwork); | ||
930 | |||
931 | return IRQ_HANDLED; | ||
932 | } | ||
933 | |||
934 | int sst_hsw_fw_get_version(struct sst_hsw *hsw, | ||
935 | struct sst_hsw_ipc_fw_version *version) | ||
936 | { | ||
937 | int ret; | ||
938 | |||
939 | ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), | ||
940 | NULL, 0, version, sizeof(*version)); | ||
941 | if (ret < 0) | ||
942 | dev_err(hsw->dev, "error: get version failed\n"); | ||
943 | |||
944 | return ret; | ||
945 | } | ||
946 | |||
947 | /* Mixer Controls */ | ||
948 | int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
949 | u32 stage_id, u32 channel) | ||
950 | { | ||
951 | int ret; | ||
952 | |||
953 | ret = sst_hsw_stream_get_volume(hsw, stream, stage_id, channel, | ||
954 | &stream->mute_volume[channel]); | ||
955 | if (ret < 0) | ||
956 | return ret; | ||
957 | |||
958 | ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel, 0); | ||
959 | if (ret < 0) { | ||
960 | dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n", | ||
961 | stream->reply.stream_hw_id, channel); | ||
962 | return ret; | ||
963 | } | ||
964 | |||
965 | stream->mute[channel] = 1; | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
970 | u32 stage_id, u32 channel) | ||
971 | |||
972 | { | ||
973 | int ret; | ||
974 | |||
975 | stream->mute[channel] = 0; | ||
976 | ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel, | ||
977 | stream->mute_volume[channel]); | ||
978 | if (ret < 0) { | ||
979 | dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n", | ||
980 | stream->reply.stream_hw_id, channel); | ||
981 | return ret; | ||
982 | } | ||
983 | |||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
988 | u32 stage_id, u32 channel, u32 *volume) | ||
989 | { | ||
990 | if (channel > 1) | ||
991 | return -EINVAL; | ||
992 | |||
993 | sst_dsp_read(hsw->dsp, volume, | ||
994 | stream->reply.volume_register_address[channel], sizeof(volume)); | ||
995 | |||
996 | return 0; | ||
997 | } | ||
998 | |||
999 | int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw, | ||
1000 | struct sst_hsw_stream *stream, u64 curve_duration, | ||
1001 | enum sst_hsw_volume_curve curve) | ||
1002 | { | ||
1003 | /* curve duration in steps of 100ns */ | ||
1004 | stream->vol_req.curve_duration = curve_duration; | ||
1005 | stream->vol_req.curve_type = curve; | ||
1006 | |||
1007 | return 0; | ||
1008 | } | ||
1009 | |||
1010 | /* stream volume */ | ||
1011 | int sst_hsw_stream_set_volume(struct sst_hsw *hsw, | ||
1012 | struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume) | ||
1013 | { | ||
1014 | struct sst_hsw_ipc_volume_req *req; | ||
1015 | u32 header; | ||
1016 | int ret; | ||
1017 | |||
1018 | trace_ipc_request("set stream volume", stream->reply.stream_hw_id); | ||
1019 | |||
1020 | if (channel > 1) | ||
1021 | return -EINVAL; | ||
1022 | |||
1023 | if (stream->mute[channel]) { | ||
1024 | stream->mute_volume[channel] = volume; | ||
1025 | return 0; | ||
1026 | } | ||
1027 | |||
1028 | header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | | ||
1029 | IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); | ||
1030 | header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT); | ||
1031 | header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT); | ||
1032 | header |= (stage_id << IPC_STG_ID_SHIFT); | ||
1033 | |||
1034 | req = &stream->vol_req; | ||
1035 | req->channel = channel; | ||
1036 | req->target_volume = volume; | ||
1037 | |||
1038 | ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0); | ||
1039 | if (ret < 0) { | ||
1040 | dev_err(hsw->dev, "error: set stream volume failed\n"); | ||
1041 | return ret; | ||
1042 | } | ||
1043 | |||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1047 | int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel) | ||
1048 | { | ||
1049 | int ret; | ||
1050 | |||
1051 | ret = sst_hsw_mixer_get_volume(hsw, stage_id, channel, | ||
1052 | &hsw->mute_volume[channel]); | ||
1053 | if (ret < 0) | ||
1054 | return ret; | ||
1055 | |||
1056 | ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel, 0); | ||
1057 | if (ret < 0) { | ||
1058 | dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n", | ||
1059 | channel); | ||
1060 | return ret; | ||
1061 | } | ||
1062 | |||
1063 | hsw->mute[channel] = 1; | ||
1064 | return 0; | ||
1065 | } | ||
1066 | |||
1067 | int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel) | ||
1068 | { | ||
1069 | int ret; | ||
1070 | |||
1071 | ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel, | ||
1072 | hsw->mixer_info.volume_register_address[channel]); | ||
1073 | if (ret < 0) { | ||
1074 | dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n", | ||
1075 | channel); | ||
1076 | return ret; | ||
1077 | } | ||
1078 | |||
1079 | hsw->mute[channel] = 0; | ||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
1083 | int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, | ||
1084 | u32 *volume) | ||
1085 | { | ||
1086 | if (channel > 1) | ||
1087 | return -EINVAL; | ||
1088 | |||
1089 | sst_dsp_read(hsw->dsp, volume, | ||
1090 | hsw->mixer_info.volume_register_address[channel], | ||
1091 | sizeof(*volume)); | ||
1092 | |||
1093 | return 0; | ||
1094 | } | ||
1095 | |||
1096 | int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw, | ||
1097 | u64 curve_duration, enum sst_hsw_volume_curve curve) | ||
1098 | { | ||
1099 | /* curve duration in steps of 100ns */ | ||
1100 | hsw->curve_duration = curve_duration; | ||
1101 | hsw->curve_type = curve; | ||
1102 | |||
1103 | return 0; | ||
1104 | } | ||
1105 | |||
1106 | /* global mixer volume */ | ||
1107 | int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, | ||
1108 | u32 volume) | ||
1109 | { | ||
1110 | struct sst_hsw_ipc_volume_req req; | ||
1111 | u32 header; | ||
1112 | int ret; | ||
1113 | |||
1114 | trace_ipc_request("set mixer volume", volume); | ||
1115 | |||
1116 | /* set both at same time ? */ | ||
1117 | if (channel == 2) { | ||
1118 | if (hsw->mute[0] && hsw->mute[1]) { | ||
1119 | hsw->mute_volume[0] = hsw->mute_volume[1] = volume; | ||
1120 | return 0; | ||
1121 | } else if (hsw->mute[0]) | ||
1122 | req.channel = 1; | ||
1123 | else if (hsw->mute[1]) | ||
1124 | req.channel = 0; | ||
1125 | else | ||
1126 | req.channel = 0xffffffff; | ||
1127 | } else { | ||
1128 | /* set only 1 channel */ | ||
1129 | if (hsw->mute[channel]) { | ||
1130 | hsw->mute_volume[channel] = volume; | ||
1131 | return 0; | ||
1132 | } | ||
1133 | req.channel = channel; | ||
1134 | } | ||
1135 | |||
1136 | header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | | ||
1137 | IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); | ||
1138 | header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT); | ||
1139 | header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT); | ||
1140 | header |= (stage_id << IPC_STG_ID_SHIFT); | ||
1141 | |||
1142 | req.curve_duration = hsw->curve_duration; | ||
1143 | req.curve_type = hsw->curve_type; | ||
1144 | req.target_volume = volume; | ||
1145 | |||
1146 | ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0); | ||
1147 | if (ret < 0) { | ||
1148 | dev_err(hsw->dev, "error: set mixer volume failed\n"); | ||
1149 | return ret; | ||
1150 | } | ||
1151 | |||
1152 | return 0; | ||
1153 | } | ||
1154 | |||
1155 | /* Stream API */ | ||
1156 | struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, | ||
1157 | u32 (*notify_position)(struct sst_hsw_stream *stream, void *data), | ||
1158 | void *data) | ||
1159 | { | ||
1160 | struct sst_hsw_stream *stream; | ||
1161 | |||
1162 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | ||
1163 | if (stream == NULL) | ||
1164 | return NULL; | ||
1165 | |||
1166 | list_add(&stream->node, &hsw->stream_list); | ||
1167 | stream->notify_position = notify_position; | ||
1168 | stream->pdata = data; | ||
1169 | stream->hsw = hsw; | ||
1170 | stream->host_id = id; | ||
1171 | |||
1172 | /* work to process notification messages */ | ||
1173 | INIT_WORK(&stream->notify_work, hsw_notification_work); | ||
1174 | |||
1175 | return stream; | ||
1176 | } | ||
1177 | |||
1178 | int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) | ||
1179 | { | ||
1180 | u32 header; | ||
1181 | int ret = 0; | ||
1182 | |||
1183 | /* dont free DSP streams that are not commited */ | ||
1184 | if (!stream->commited) | ||
1185 | goto out; | ||
1186 | |||
1187 | trace_ipc_request("stream free", stream->host_id); | ||
1188 | |||
1189 | stream->free_req.stream_id = stream->reply.stream_hw_id; | ||
1190 | header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); | ||
1191 | |||
1192 | ret = ipc_tx_message_wait(hsw, header, &stream->free_req, | ||
1193 | sizeof(stream->free_req), NULL, 0); | ||
1194 | if (ret < 0) { | ||
1195 | dev_err(hsw->dev, "error: free stream %d failed\n", | ||
1196 | stream->free_req.stream_id); | ||
1197 | return -EAGAIN; | ||
1198 | } | ||
1199 | |||
1200 | trace_hsw_stream_free_req(stream, &stream->free_req); | ||
1201 | |||
1202 | out: | ||
1203 | list_del(&stream->node); | ||
1204 | kfree(stream); | ||
1205 | |||
1206 | return ret; | ||
1207 | } | ||
1208 | |||
1209 | int sst_hsw_stream_set_bits(struct sst_hsw *hsw, | ||
1210 | struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits) | ||
1211 | { | ||
1212 | if (stream->commited) { | ||
1213 | dev_err(hsw->dev, "error: stream committed for set bits\n"); | ||
1214 | return -EINVAL; | ||
1215 | } | ||
1216 | |||
1217 | stream->request.format.bitdepth = bits; | ||
1218 | return 0; | ||
1219 | } | ||
1220 | |||
1221 | int sst_hsw_stream_set_channels(struct sst_hsw *hsw, | ||
1222 | struct sst_hsw_stream *stream, int channels) | ||
1223 | { | ||
1224 | if (stream->commited) { | ||
1225 | dev_err(hsw->dev, "error: stream committed for set channels\n"); | ||
1226 | return -EINVAL; | ||
1227 | } | ||
1228 | |||
1229 | /* stereo is only supported atm */ | ||
1230 | if (channels != 2) | ||
1231 | return -EINVAL; | ||
1232 | |||
1233 | stream->request.format.ch_num = channels; | ||
1234 | return 0; | ||
1235 | } | ||
1236 | |||
1237 | int sst_hsw_stream_set_rate(struct sst_hsw *hsw, | ||
1238 | struct sst_hsw_stream *stream, int rate) | ||
1239 | { | ||
1240 | if (stream->commited) { | ||
1241 | dev_err(hsw->dev, "error: stream committed for set rate\n"); | ||
1242 | return -EINVAL; | ||
1243 | } | ||
1244 | |||
1245 | stream->request.format.frequency = rate; | ||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, | ||
1250 | struct sst_hsw_stream *stream, u32 map, | ||
1251 | enum sst_hsw_channel_config config) | ||
1252 | { | ||
1253 | if (stream->commited) { | ||
1254 | dev_err(hsw->dev, "error: stream committed for set map\n"); | ||
1255 | return -EINVAL; | ||
1256 | } | ||
1257 | |||
1258 | stream->request.format.map = map; | ||
1259 | stream->request.format.config = config; | ||
1260 | return 0; | ||
1261 | } | ||
1262 | |||
1263 | int sst_hsw_stream_set_style(struct sst_hsw *hsw, | ||
1264 | struct sst_hsw_stream *stream, enum sst_hsw_interleaving style) | ||
1265 | { | ||
1266 | if (stream->commited) { | ||
1267 | dev_err(hsw->dev, "error: stream committed for set style\n"); | ||
1268 | return -EINVAL; | ||
1269 | } | ||
1270 | |||
1271 | stream->request.format.style = style; | ||
1272 | return 0; | ||
1273 | } | ||
1274 | |||
1275 | int sst_hsw_stream_set_valid(struct sst_hsw *hsw, | ||
1276 | struct sst_hsw_stream *stream, u32 bits) | ||
1277 | { | ||
1278 | if (stream->commited) { | ||
1279 | dev_err(hsw->dev, "error: stream committed for set valid bits\n"); | ||
1280 | return -EINVAL; | ||
1281 | } | ||
1282 | |||
1283 | stream->request.format.valid_bit = bits; | ||
1284 | return 0; | ||
1285 | } | ||
1286 | |||
1287 | /* Stream Configuration */ | ||
1288 | int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
1289 | enum sst_hsw_stream_path_id path_id, | ||
1290 | enum sst_hsw_stream_type stream_type, | ||
1291 | enum sst_hsw_stream_format format_id) | ||
1292 | { | ||
1293 | if (stream->commited) { | ||
1294 | dev_err(hsw->dev, "error: stream committed for set format\n"); | ||
1295 | return -EINVAL; | ||
1296 | } | ||
1297 | |||
1298 | stream->request.path_id = path_id; | ||
1299 | stream->request.stream_type = stream_type; | ||
1300 | stream->request.format_id = format_id; | ||
1301 | |||
1302 | trace_hsw_stream_alloc_request(stream, &stream->request); | ||
1303 | |||
1304 | return 0; | ||
1305 | } | ||
1306 | |||
1307 | int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
1308 | u32 ring_pt_address, u32 num_pages, | ||
1309 | u32 ring_size, u32 ring_offset, u32 ring_first_pfn) | ||
1310 | { | ||
1311 | if (stream->commited) { | ||
1312 | dev_err(hsw->dev, "error: stream committed for buffer\n"); | ||
1313 | return -EINVAL; | ||
1314 | } | ||
1315 | |||
1316 | stream->request.ringinfo.ring_pt_address = ring_pt_address; | ||
1317 | stream->request.ringinfo.num_pages = num_pages; | ||
1318 | stream->request.ringinfo.ring_size = ring_size; | ||
1319 | stream->request.ringinfo.ring_offset = ring_offset; | ||
1320 | stream->request.ringinfo.ring_first_pfn = ring_first_pfn; | ||
1321 | |||
1322 | trace_hsw_stream_buffer(stream); | ||
1323 | |||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, | ||
1328 | struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id, | ||
1329 | u32 entry_point) | ||
1330 | { | ||
1331 | struct sst_hsw_module_map *map = &stream->request.map; | ||
1332 | |||
1333 | if (stream->commited) { | ||
1334 | dev_err(hsw->dev, "error: stream committed for set module\n"); | ||
1335 | return -EINVAL; | ||
1336 | } | ||
1337 | |||
1338 | /* only support initial module atm */ | ||
1339 | map->module_entries_count = 1; | ||
1340 | map->module_entries[0].module_id = module_id; | ||
1341 | map->module_entries[0].entry_point = entry_point; | ||
1342 | |||
1343 | return 0; | ||
1344 | } | ||
1345 | |||
1346 | int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, | ||
1347 | struct sst_hsw_stream *stream, u32 offset, u32 size) | ||
1348 | { | ||
1349 | if (stream->commited) { | ||
1350 | dev_err(hsw->dev, "error: stream committed for set pmem\n"); | ||
1351 | return -EINVAL; | ||
1352 | } | ||
1353 | |||
1354 | stream->request.persistent_mem.offset = offset; | ||
1355 | stream->request.persistent_mem.size = size; | ||
1356 | |||
1357 | return 0; | ||
1358 | } | ||
1359 | |||
1360 | int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, | ||
1361 | struct sst_hsw_stream *stream, u32 offset, u32 size) | ||
1362 | { | ||
1363 | if (stream->commited) { | ||
1364 | dev_err(hsw->dev, "error: stream committed for set smem\n"); | ||
1365 | return -EINVAL; | ||
1366 | } | ||
1367 | |||
1368 | stream->request.scratch_mem.offset = offset; | ||
1369 | stream->request.scratch_mem.size = size; | ||
1370 | |||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) | ||
1375 | { | ||
1376 | struct sst_hsw_ipc_stream_alloc_req *str_req = &stream->request; | ||
1377 | struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply; | ||
1378 | u32 header; | ||
1379 | int ret; | ||
1380 | |||
1381 | trace_ipc_request("stream alloc", stream->host_id); | ||
1382 | |||
1383 | header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); | ||
1384 | |||
1385 | ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req), | ||
1386 | reply, sizeof(*reply)); | ||
1387 | if (ret < 0) { | ||
1388 | dev_err(hsw->dev, "error: stream commit failed\n"); | ||
1389 | return ret; | ||
1390 | } | ||
1391 | |||
1392 | stream->commited = 1; | ||
1393 | trace_hsw_stream_alloc_reply(stream); | ||
1394 | |||
1395 | return 0; | ||
1396 | } | ||
1397 | |||
1398 | /* Stream Information - these calls could be inline but we want the IPC | ||
1399 | ABI to be opaque to client PCM drivers to cope with any future ABI changes */ | ||
1400 | int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw, | ||
1401 | struct sst_hsw_stream *stream) | ||
1402 | { | ||
1403 | return stream->reply.stream_hw_id; | ||
1404 | } | ||
1405 | |||
1406 | int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw, | ||
1407 | struct sst_hsw_stream *stream) | ||
1408 | { | ||
1409 | return stream->reply.mixer_hw_id; | ||
1410 | } | ||
1411 | |||
1412 | u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw, | ||
1413 | struct sst_hsw_stream *stream) | ||
1414 | { | ||
1415 | return stream->reply.read_position_register_address; | ||
1416 | } | ||
1417 | |||
1418 | u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw, | ||
1419 | struct sst_hsw_stream *stream) | ||
1420 | { | ||
1421 | return stream->reply.presentation_position_register_address; | ||
1422 | } | ||
1423 | |||
1424 | u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw, | ||
1425 | struct sst_hsw_stream *stream, u32 channel) | ||
1426 | { | ||
1427 | if (channel >= 2) | ||
1428 | return 0; | ||
1429 | |||
1430 | return stream->reply.peak_meter_register_address[channel]; | ||
1431 | } | ||
1432 | |||
1433 | u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw, | ||
1434 | struct sst_hsw_stream *stream, u32 channel) | ||
1435 | { | ||
1436 | if (channel >= 2) | ||
1437 | return 0; | ||
1438 | |||
1439 | return stream->reply.volume_register_address[channel]; | ||
1440 | } | ||
1441 | |||
1442 | int sst_hsw_mixer_get_info(struct sst_hsw *hsw) | ||
1443 | { | ||
1444 | struct sst_hsw_ipc_stream_info_reply *reply; | ||
1445 | u32 header; | ||
1446 | int ret; | ||
1447 | |||
1448 | reply = &hsw->mixer_info; | ||
1449 | header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO); | ||
1450 | |||
1451 | trace_ipc_request("get global mixer info", 0); | ||
1452 | |||
1453 | ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply)); | ||
1454 | if (ret < 0) { | ||
1455 | dev_err(hsw->dev, "error: get stream info failed\n"); | ||
1456 | return ret; | ||
1457 | } | ||
1458 | |||
1459 | trace_hsw_mixer_info_reply(reply); | ||
1460 | |||
1461 | return 0; | ||
1462 | } | ||
1463 | |||
1464 | /* Send stream command */ | ||
1465 | static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, | ||
1466 | int stream_id, int wait) | ||
1467 | { | ||
1468 | u32 header; | ||
1469 | |||
1470 | header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type); | ||
1471 | header |= (stream_id << IPC_STR_ID_SHIFT); | ||
1472 | |||
1473 | if (wait) | ||
1474 | return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0); | ||
1475 | else | ||
1476 | return ipc_tx_message_nowait(hsw, header, NULL, 0); | ||
1477 | } | ||
1478 | |||
1479 | /* Stream ALSA trigger operations */ | ||
1480 | int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
1481 | int wait) | ||
1482 | { | ||
1483 | int ret; | ||
1484 | |||
1485 | trace_ipc_request("stream pause", stream->reply.stream_hw_id); | ||
1486 | |||
1487 | ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE, | ||
1488 | stream->reply.stream_hw_id, wait); | ||
1489 | if (ret < 0) | ||
1490 | dev_err(hsw->dev, "error: failed to pause stream %d\n", | ||
1491 | stream->reply.stream_hw_id); | ||
1492 | |||
1493 | return ret; | ||
1494 | } | ||
1495 | |||
1496 | int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
1497 | int wait) | ||
1498 | { | ||
1499 | int ret; | ||
1500 | |||
1501 | trace_ipc_request("stream resume", stream->reply.stream_hw_id); | ||
1502 | |||
1503 | ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME, | ||
1504 | stream->reply.stream_hw_id, wait); | ||
1505 | if (ret < 0) | ||
1506 | dev_err(hsw->dev, "error: failed to resume stream %d\n", | ||
1507 | stream->reply.stream_hw_id); | ||
1508 | |||
1509 | return ret; | ||
1510 | } | ||
1511 | |||
1512 | int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) | ||
1513 | { | ||
1514 | int ret, tries = 10; | ||
1515 | |||
1516 | /* dont reset streams that are not commited */ | ||
1517 | if (!stream->commited) | ||
1518 | return 0; | ||
1519 | |||
1520 | /* wait for pause to complete before we reset the stream */ | ||
1521 | while (stream->running && tries--) | ||
1522 | msleep(1); | ||
1523 | if (!tries) { | ||
1524 | dev_err(hsw->dev, "error: reset stream %d still running\n", | ||
1525 | stream->reply.stream_hw_id); | ||
1526 | return -EINVAL; | ||
1527 | } | ||
1528 | |||
1529 | trace_ipc_request("stream reset", stream->reply.stream_hw_id); | ||
1530 | |||
1531 | ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET, | ||
1532 | stream->reply.stream_hw_id, 1); | ||
1533 | if (ret < 0) | ||
1534 | dev_err(hsw->dev, "error: failed to reset stream %d\n", | ||
1535 | stream->reply.stream_hw_id); | ||
1536 | return ret; | ||
1537 | } | ||
1538 | |||
1539 | /* Stream pointer positions */ | ||
1540 | int sst_hsw_get_dsp_position(struct sst_hsw *hsw, | ||
1541 | struct sst_hsw_stream *stream) | ||
1542 | { | ||
1543 | return stream->rpos.position; | ||
1544 | } | ||
1545 | |||
1546 | int sst_hsw_stream_set_write_position(struct sst_hsw *hsw, | ||
1547 | struct sst_hsw_stream *stream, u32 stage_id, u32 position) | ||
1548 | { | ||
1549 | u32 header; | ||
1550 | int ret; | ||
1551 | |||
1552 | trace_stream_write_position(stream->reply.stream_hw_id, position); | ||
1553 | |||
1554 | header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | | ||
1555 | IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); | ||
1556 | header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT); | ||
1557 | header |= (IPC_STG_SET_WRITE_POSITION << IPC_STG_TYPE_SHIFT); | ||
1558 | header |= (stage_id << IPC_STG_ID_SHIFT); | ||
1559 | stream->wpos.position = position; | ||
1560 | |||
1561 | ret = ipc_tx_message_nowait(hsw, header, &stream->wpos, | ||
1562 | sizeof(stream->wpos)); | ||
1563 | if (ret < 0) | ||
1564 | dev_err(hsw->dev, "error: stream %d set position %d failed\n", | ||
1565 | stream->reply.stream_hw_id, position); | ||
1566 | |||
1567 | return ret; | ||
1568 | } | ||
1569 | |||
1570 | /* physical BE config */ | ||
1571 | int sst_hsw_device_set_config(struct sst_hsw *hsw, | ||
1572 | enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk, | ||
1573 | enum sst_hsw_device_mode mode, u32 clock_divider) | ||
1574 | { | ||
1575 | struct sst_hsw_ipc_device_config_req config; | ||
1576 | u32 header; | ||
1577 | int ret; | ||
1578 | |||
1579 | trace_ipc_request("set device config", dev); | ||
1580 | |||
1581 | config.ssp_interface = dev; | ||
1582 | config.clock_frequency = mclk; | ||
1583 | config.mode = mode; | ||
1584 | config.clock_divider = clock_divider; | ||
1585 | |||
1586 | trace_hsw_device_config_req(&config); | ||
1587 | |||
1588 | header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); | ||
1589 | |||
1590 | ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config), | ||
1591 | NULL, 0); | ||
1592 | if (ret < 0) | ||
1593 | dev_err(hsw->dev, "error: set device formats failed\n"); | ||
1594 | |||
1595 | return ret; | ||
1596 | } | ||
1597 | EXPORT_SYMBOL_GPL(sst_hsw_device_set_config); | ||
1598 | |||
1599 | /* DX Config */ | ||
1600 | int sst_hsw_dx_set_state(struct sst_hsw *hsw, | ||
1601 | enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx) | ||
1602 | { | ||
1603 | u32 header, state_; | ||
1604 | int ret; | ||
1605 | |||
1606 | header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE); | ||
1607 | state_ = state; | ||
1608 | |||
1609 | trace_ipc_request("PM enter Dx state", state); | ||
1610 | |||
1611 | ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_), | ||
1612 | dx, sizeof(dx)); | ||
1613 | if (ret < 0) { | ||
1614 | dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); | ||
1615 | return ret; | ||
1616 | } | ||
1617 | |||
1618 | dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n", | ||
1619 | dx->entries_no, state); | ||
1620 | |||
1621 | memcpy(&hsw->dx, dx, sizeof(*dx)); | ||
1622 | return 0; | ||
1623 | } | ||
1624 | |||
1625 | /* Used to save state into hsw->dx_reply */ | ||
1626 | int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item, | ||
1627 | u32 *offset, u32 *size, u32 *source) | ||
1628 | { | ||
1629 | struct sst_hsw_ipc_dx_memory_item *dx_mem; | ||
1630 | struct sst_hsw_ipc_dx_reply *dx_reply; | ||
1631 | int entry_no; | ||
1632 | |||
1633 | dx_reply = &hsw->dx; | ||
1634 | entry_no = dx_reply->entries_no; | ||
1635 | |||
1636 | trace_ipc_request("PM get Dx state", entry_no); | ||
1637 | |||
1638 | if (item >= entry_no) | ||
1639 | return -EINVAL; | ||
1640 | |||
1641 | dx_mem = &dx_reply->mem_info[item]; | ||
1642 | *offset = dx_mem->offset; | ||
1643 | *size = dx_mem->size; | ||
1644 | *source = dx_mem->source; | ||
1645 | |||
1646 | return 0; | ||
1647 | } | ||
1648 | |||
1649 | static int msg_empty_list_init(struct sst_hsw *hsw) | ||
1650 | { | ||
1651 | int i; | ||
1652 | |||
1653 | hsw->msg = kzalloc(sizeof(struct ipc_message) * | ||
1654 | IPC_EMPTY_LIST_SIZE, GFP_KERNEL); | ||
1655 | if (hsw->msg == NULL) | ||
1656 | return -ENOMEM; | ||
1657 | |||
1658 | for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { | ||
1659 | init_waitqueue_head(&hsw->msg[i].waitq); | ||
1660 | list_add(&hsw->msg[i].list, &hsw->empty_list); | ||
1661 | } | ||
1662 | |||
1663 | return 0; | ||
1664 | } | ||
1665 | |||
1666 | void sst_hsw_set_scratch_module(struct sst_hsw *hsw, | ||
1667 | struct sst_module *scratch) | ||
1668 | { | ||
1669 | hsw->scratch = scratch; | ||
1670 | } | ||
1671 | |||
1672 | struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) | ||
1673 | { | ||
1674 | return hsw->dsp; | ||
1675 | } | ||
1676 | |||
1677 | static struct sst_dsp_device hsw_dev = { | ||
1678 | .thread = hsw_irq_thread, | ||
1679 | .ops = &haswell_ops, | ||
1680 | }; | ||
1681 | |||
1682 | int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) | ||
1683 | { | ||
1684 | struct sst_hsw_ipc_fw_version version; | ||
1685 | struct sst_hsw *hsw; | ||
1686 | struct sst_fw *hsw_sst_fw; | ||
1687 | int ret; | ||
1688 | |||
1689 | dev_dbg(dev, "initialising Audio DSP IPC\n"); | ||
1690 | |||
1691 | hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL); | ||
1692 | if (hsw == NULL) | ||
1693 | return -ENOMEM; | ||
1694 | |||
1695 | hsw->dev = dev; | ||
1696 | INIT_LIST_HEAD(&hsw->stream_list); | ||
1697 | INIT_LIST_HEAD(&hsw->tx_list); | ||
1698 | INIT_LIST_HEAD(&hsw->rx_list); | ||
1699 | INIT_LIST_HEAD(&hsw->empty_list); | ||
1700 | init_waitqueue_head(&hsw->boot_wait); | ||
1701 | init_waitqueue_head(&hsw->wait_txq); | ||
1702 | |||
1703 | ret = msg_empty_list_init(hsw); | ||
1704 | if (ret < 0) | ||
1705 | goto list_err; | ||
1706 | |||
1707 | /* start the IPC message thread */ | ||
1708 | init_kthread_worker(&hsw->kworker); | ||
1709 | hsw->tx_thread = kthread_run(kthread_worker_fn, | ||
1710 | &hsw->kworker, | ||
1711 | dev_name(hsw->dev)); | ||
1712 | if (IS_ERR(hsw->tx_thread)) { | ||
1713 | ret = PTR_ERR(hsw->tx_thread); | ||
1714 | dev_err(hsw->dev, "error: failed to create message TX task\n"); | ||
1715 | goto list_err; | ||
1716 | } | ||
1717 | init_kthread_work(&hsw->kwork, ipc_tx_msgs); | ||
1718 | |||
1719 | hsw_dev.thread_context = hsw; | ||
1720 | |||
1721 | /* init SST shim */ | ||
1722 | hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata); | ||
1723 | if (hsw->dsp == NULL) { | ||
1724 | ret = -ENODEV; | ||
1725 | goto list_err; | ||
1726 | } | ||
1727 | |||
1728 | /* keep the DSP in reset state for base FW loading */ | ||
1729 | sst_dsp_reset(hsw->dsp); | ||
1730 | |||
1731 | hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); | ||
1732 | |||
1733 | if (hsw_sst_fw == NULL) { | ||
1734 | ret = -ENODEV; | ||
1735 | dev_err(dev, "error: failed to load firmware\n"); | ||
1736 | goto fw_err; | ||
1737 | } | ||
1738 | |||
1739 | /* wait for DSP boot completion */ | ||
1740 | sst_dsp_boot(hsw->dsp); | ||
1741 | ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, | ||
1742 | msecs_to_jiffies(IPC_BOOT_MSECS)); | ||
1743 | if (ret == 0) { | ||
1744 | ret = -EIO; | ||
1745 | dev_err(hsw->dev, "error: ADSP boot timeout\n"); | ||
1746 | goto boot_err; | ||
1747 | } | ||
1748 | |||
1749 | /* get the FW version */ | ||
1750 | sst_hsw_fw_get_version(hsw, &version); | ||
1751 | dev_info(hsw->dev, "FW loaded: type %d - version: %d.%d build %d\n", | ||
1752 | version.type, version.major, version.minor, version.build); | ||
1753 | |||
1754 | /* get the globalmixer */ | ||
1755 | ret = sst_hsw_mixer_get_info(hsw); | ||
1756 | if (ret < 0) { | ||
1757 | dev_err(hsw->dev, "error: failed to get stream info\n"); | ||
1758 | goto boot_err; | ||
1759 | } | ||
1760 | |||
1761 | pdata->dsp = hsw; | ||
1762 | return 0; | ||
1763 | |||
1764 | boot_err: | ||
1765 | sst_dsp_reset(hsw->dsp); | ||
1766 | sst_fw_free(hsw_sst_fw); | ||
1767 | fw_err: | ||
1768 | sst_dsp_free(hsw->dsp); | ||
1769 | kfree(hsw->msg); | ||
1770 | list_err: | ||
1771 | return ret; | ||
1772 | } | ||
1773 | EXPORT_SYMBOL_GPL(sst_hsw_dsp_init); | ||
1774 | |||
1775 | void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) | ||
1776 | { | ||
1777 | struct sst_hsw *hsw = pdata->dsp; | ||
1778 | |||
1779 | sst_dsp_reset(hsw->dsp); | ||
1780 | sst_fw_free_all(hsw->dsp); | ||
1781 | sst_dsp_free(hsw->dsp); | ||
1782 | kfree(hsw->scratch); | ||
1783 | kfree(hsw->msg); | ||
1784 | } | ||
1785 | EXPORT_SYMBOL_GPL(sst_hsw_dsp_free); | ||
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h new file mode 100644 index 000000000000..d517929ccc38 --- /dev/null +++ b/sound/soc/intel/sst-haswell-ipc.h | |||
@@ -0,0 +1,488 @@ | |||
1 | /* | ||
2 | * Intel SST Haswell/Broadwell IPC Support | ||
3 | * | ||
4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef __SST_HASWELL_IPC_H | ||
18 | #define __SST_HASWELL_IPC_H | ||
19 | |||
20 | #include <linux/types.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | |||
24 | #define SST_HSW_NO_CHANNELS 2 | ||
25 | #define SST_HSW_MAX_DX_REGIONS 14 | ||
26 | |||
27 | #define SST_HSW_FW_LOG_CONFIG_DWORDS 12 | ||
28 | #define SST_HSW_GLOBAL_LOG 15 | ||
29 | |||
30 | /** | ||
31 | * Upfront defined maximum message size that is | ||
32 | * expected by the in/out communication pipes in FW. | ||
33 | */ | ||
34 | #define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 | ||
35 | #define SST_HSW_MAX_INFO_SIZE 64 | ||
36 | #define SST_HSW_BUILD_HASH_LENGTH 40 | ||
37 | |||
38 | struct sst_hsw; | ||
39 | struct sst_hsw_stream; | ||
40 | struct sst_hsw_log_stream; | ||
41 | struct sst_pdata; | ||
42 | struct sst_module; | ||
43 | extern struct sst_ops haswell_ops; | ||
44 | |||
45 | /* Stream Allocate Path ID */ | ||
46 | enum sst_hsw_stream_path_id { | ||
47 | SST_HSW_STREAM_PATH_SSP0_OUT = 0, | ||
48 | SST_HSW_STREAM_PATH_SSP0_IN = 1, | ||
49 | SST_HSW_STREAM_PATH_MAX_PATH_ID = 2, | ||
50 | }; | ||
51 | |||
52 | /* Stream Allocate Stream Type */ | ||
53 | enum sst_hsw_stream_type { | ||
54 | SST_HSW_STREAM_TYPE_RENDER = 0, | ||
55 | SST_HSW_STREAM_TYPE_SYSTEM = 1, | ||
56 | SST_HSW_STREAM_TYPE_CAPTURE = 2, | ||
57 | SST_HSW_STREAM_TYPE_LOOPBACK = 3, | ||
58 | SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4, | ||
59 | }; | ||
60 | |||
61 | /* Stream Allocate Stream Format */ | ||
62 | enum sst_hsw_stream_format { | ||
63 | SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0, | ||
64 | SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1, | ||
65 | SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2, | ||
66 | SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3, | ||
67 | }; | ||
68 | |||
69 | /* Device ID */ | ||
70 | enum sst_hsw_device_id { | ||
71 | SST_HSW_DEVICE_SSP_0 = 0, | ||
72 | SST_HSW_DEVICE_SSP_1 = 1, | ||
73 | }; | ||
74 | |||
75 | /* Device Master Clock Frequency */ | ||
76 | enum sst_hsw_device_mclk { | ||
77 | SST_HSW_DEVICE_MCLK_OFF = 0, | ||
78 | SST_HSW_DEVICE_MCLK_FREQ_6_MHZ = 1, | ||
79 | SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2, | ||
80 | SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3, | ||
81 | }; | ||
82 | |||
83 | /* Device Clock Master */ | ||
84 | enum sst_hsw_device_mode { | ||
85 | SST_HSW_DEVICE_CLOCK_SLAVE = 0, | ||
86 | SST_HSW_DEVICE_CLOCK_MASTER = 1, | ||
87 | }; | ||
88 | |||
89 | /* DX Power State */ | ||
90 | enum sst_hsw_dx_state { | ||
91 | SST_HSW_DX_STATE_D0 = 0, | ||
92 | SST_HSW_DX_STATE_D1 = 1, | ||
93 | SST_HSW_DX_STATE_D3 = 3, | ||
94 | SST_HSW_DX_STATE_MAX = 3, | ||
95 | }; | ||
96 | |||
97 | /* Audio stream stage IDs */ | ||
98 | enum sst_hsw_fx_stage_id { | ||
99 | SST_HSW_STAGE_ID_WAVES = 0, | ||
100 | SST_HSW_STAGE_ID_DTS = 1, | ||
101 | SST_HSW_STAGE_ID_DOLBY = 2, | ||
102 | SST_HSW_STAGE_ID_BOOST = 3, | ||
103 | SST_HSW_STAGE_ID_MAX_FX_ID | ||
104 | }; | ||
105 | |||
106 | /* DX State Type */ | ||
107 | enum sst_hsw_dx_type { | ||
108 | SST_HSW_DX_TYPE_FW_IMAGE = 0, | ||
109 | SST_HSW_DX_TYPE_MEMORY_DUMP = 1 | ||
110 | }; | ||
111 | |||
112 | /* Volume Curve Type*/ | ||
113 | enum sst_hsw_volume_curve { | ||
114 | SST_HSW_VOLUME_CURVE_NONE = 0, | ||
115 | SST_HSW_VOLUME_CURVE_FADE = 1 | ||
116 | }; | ||
117 | |||
118 | /* Sample ordering */ | ||
119 | enum sst_hsw_interleaving { | ||
120 | SST_HSW_INTERLEAVING_PER_CHANNEL = 0, | ||
121 | SST_HSW_INTERLEAVING_PER_SAMPLE = 1, | ||
122 | }; | ||
123 | |||
124 | /* Channel indices */ | ||
125 | enum sst_hsw_channel_index { | ||
126 | SST_HSW_CHANNEL_LEFT = 0, | ||
127 | SST_HSW_CHANNEL_CENTER = 1, | ||
128 | SST_HSW_CHANNEL_RIGHT = 2, | ||
129 | SST_HSW_CHANNEL_LEFT_SURROUND = 3, | ||
130 | SST_HSW_CHANNEL_CENTER_SURROUND = 3, | ||
131 | SST_HSW_CHANNEL_RIGHT_SURROUND = 4, | ||
132 | SST_HSW_CHANNEL_LFE = 7, | ||
133 | SST_HSW_CHANNEL_INVALID = 0xF, | ||
134 | }; | ||
135 | |||
136 | /* List of supported channel maps. */ | ||
137 | enum sst_hsw_channel_config { | ||
138 | SST_HSW_CHANNEL_CONFIG_MONO = 0, /* mono only. */ | ||
139 | SST_HSW_CHANNEL_CONFIG_STEREO = 1, /* L & R. */ | ||
140 | SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */ | ||
141 | SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */ | ||
142 | SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */ | ||
143 | SST_HSW_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */ | ||
144 | SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */ | ||
145 | SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */ | ||
146 | SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */ | ||
147 | SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */ | ||
148 | SST_HSW_CHANNEL_CONFIG_INVALID, | ||
149 | }; | ||
150 | |||
151 | /* List of supported bit depths. */ | ||
152 | enum sst_hsw_bitdepth { | ||
153 | SST_HSW_DEPTH_8BIT = 8, | ||
154 | SST_HSW_DEPTH_16BIT = 16, | ||
155 | SST_HSW_DEPTH_24BIT = 24, /* Default. */ | ||
156 | SST_HSW_DEPTH_32BIT = 32, | ||
157 | SST_HSW_DEPTH_INVALID = 33, | ||
158 | }; | ||
159 | |||
160 | enum sst_hsw_module_id { | ||
161 | SST_HSW_MODULE_BASE_FW = 0x0, | ||
162 | SST_HSW_MODULE_MP3 = 0x1, | ||
163 | SST_HSW_MODULE_AAC_5_1 = 0x2, | ||
164 | SST_HSW_MODULE_AAC_2_0 = 0x3, | ||
165 | SST_HSW_MODULE_SRC = 0x4, | ||
166 | SST_HSW_MODULE_WAVES = 0x5, | ||
167 | SST_HSW_MODULE_DOLBY = 0x6, | ||
168 | SST_HSW_MODULE_BOOST = 0x7, | ||
169 | SST_HSW_MODULE_LPAL = 0x8, | ||
170 | SST_HSW_MODULE_DTS = 0x9, | ||
171 | SST_HSW_MODULE_PCM_CAPTURE = 0xA, | ||
172 | SST_HSW_MODULE_PCM_SYSTEM = 0xB, | ||
173 | SST_HSW_MODULE_PCM_REFERENCE = 0xC, | ||
174 | SST_HSW_MODULE_PCM = 0xD, | ||
175 | SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE, | ||
176 | SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF, | ||
177 | SST_HSW_MAX_MODULE_ID, | ||
178 | }; | ||
179 | |||
180 | enum sst_hsw_performance_action { | ||
181 | SST_HSW_PERF_START = 0, | ||
182 | SST_HSW_PERF_STOP = 1, | ||
183 | }; | ||
184 | |||
185 | /* SST firmware module info */ | ||
186 | struct sst_hsw_module_info { | ||
187 | u8 name[SST_HSW_MAX_INFO_SIZE]; | ||
188 | u8 version[SST_HSW_MAX_INFO_SIZE]; | ||
189 | } __attribute__((packed)); | ||
190 | |||
191 | /* Module entry point */ | ||
192 | struct sst_hsw_module_entry { | ||
193 | enum sst_hsw_module_id module_id; | ||
194 | u32 entry_point; | ||
195 | } __attribute__((packed)); | ||
196 | |||
197 | /* Module map - alignement matches DSP */ | ||
198 | struct sst_hsw_module_map { | ||
199 | u8 module_entries_count; | ||
200 | struct sst_hsw_module_entry module_entries[1]; | ||
201 | } __attribute__((packed)); | ||
202 | |||
203 | struct sst_hsw_memory_info { | ||
204 | u32 offset; | ||
205 | u32 size; | ||
206 | } __attribute__((packed)); | ||
207 | |||
208 | struct sst_hsw_fx_enable { | ||
209 | struct sst_hsw_module_map module_map; | ||
210 | struct sst_hsw_memory_info persistent_mem; | ||
211 | } __attribute__((packed)); | ||
212 | |||
213 | struct sst_hsw_get_fx_param { | ||
214 | u32 parameter_id; | ||
215 | u32 param_size; | ||
216 | } __attribute__((packed)); | ||
217 | |||
218 | struct sst_hsw_perf_action { | ||
219 | u32 action; | ||
220 | } __attribute__((packed)); | ||
221 | |||
222 | struct sst_hsw_perf_data { | ||
223 | u64 timestamp; | ||
224 | u64 cycles; | ||
225 | u64 datatime; | ||
226 | } __attribute__((packed)); | ||
227 | |||
228 | /* FW version */ | ||
229 | struct sst_hsw_ipc_fw_version { | ||
230 | u8 build; | ||
231 | u8 minor; | ||
232 | u8 major; | ||
233 | u8 type; | ||
234 | u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH]; | ||
235 | u32 fw_log_providers_hash; | ||
236 | } __attribute__((packed)); | ||
237 | |||
238 | /* Stream ring info */ | ||
239 | struct sst_hsw_ipc_stream_ring { | ||
240 | u32 ring_pt_address; | ||
241 | u32 num_pages; | ||
242 | u32 ring_size; | ||
243 | u32 ring_offset; | ||
244 | u32 ring_first_pfn; | ||
245 | } __attribute__((packed)); | ||
246 | |||
247 | /* Debug Dump Log Enable Request */ | ||
248 | struct sst_hsw_ipc_debug_log_enable_req { | ||
249 | struct sst_hsw_ipc_stream_ring ringinfo; | ||
250 | u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS]; | ||
251 | } __attribute__((packed)); | ||
252 | |||
253 | /* Debug Dump Log Reply */ | ||
254 | struct sst_hsw_ipc_debug_log_reply { | ||
255 | u32 log_buffer_begining; | ||
256 | u32 log_buffer_size; | ||
257 | } __attribute__((packed)); | ||
258 | |||
259 | /* Stream glitch position */ | ||
260 | struct sst_hsw_ipc_stream_glitch_position { | ||
261 | u32 glitch_type; | ||
262 | u32 present_pos; | ||
263 | u32 write_pos; | ||
264 | } __attribute__((packed)); | ||
265 | |||
266 | /* Stream get position */ | ||
267 | struct sst_hsw_ipc_stream_get_position { | ||
268 | u32 position; | ||
269 | u32 fw_cycle_count; | ||
270 | } __attribute__((packed)); | ||
271 | |||
272 | /* Stream set position */ | ||
273 | struct sst_hsw_ipc_stream_set_position { | ||
274 | u32 position; | ||
275 | u32 end_of_buffer; | ||
276 | } __attribute__((packed)); | ||
277 | |||
278 | /* Stream Free Request */ | ||
279 | struct sst_hsw_ipc_stream_free_req { | ||
280 | u8 stream_id; | ||
281 | u8 reserved[3]; | ||
282 | } __attribute__((packed)); | ||
283 | |||
284 | /* Set Volume Request */ | ||
285 | struct sst_hsw_ipc_volume_req { | ||
286 | u32 channel; | ||
287 | u32 target_volume; | ||
288 | u64 curve_duration; | ||
289 | u32 curve_type; | ||
290 | } __attribute__((packed)); | ||
291 | |||
292 | /* Device Configuration Request */ | ||
293 | struct sst_hsw_ipc_device_config_req { | ||
294 | u32 ssp_interface; | ||
295 | u32 clock_frequency; | ||
296 | u32 mode; | ||
297 | u16 clock_divider; | ||
298 | u16 reserved; | ||
299 | } __attribute__((packed)); | ||
300 | |||
301 | /* Audio Data formats */ | ||
302 | struct sst_hsw_audio_data_format_ipc { | ||
303 | u32 frequency; | ||
304 | u32 bitdepth; | ||
305 | u32 map; | ||
306 | u32 config; | ||
307 | u32 style; | ||
308 | u8 ch_num; | ||
309 | u8 valid_bit; | ||
310 | u8 reserved[2]; | ||
311 | } __attribute__((packed)); | ||
312 | |||
313 | /* Stream Allocate Request */ | ||
314 | struct sst_hsw_ipc_stream_alloc_req { | ||
315 | u8 path_id; | ||
316 | u8 stream_type; | ||
317 | u8 format_id; | ||
318 | u8 reserved; | ||
319 | struct sst_hsw_audio_data_format_ipc format; | ||
320 | struct sst_hsw_ipc_stream_ring ringinfo; | ||
321 | struct sst_hsw_module_map map; | ||
322 | struct sst_hsw_memory_info persistent_mem; | ||
323 | struct sst_hsw_memory_info scratch_mem; | ||
324 | u32 number_of_notifications; | ||
325 | } __attribute__((packed)); | ||
326 | |||
327 | /* Stream Allocate Reply */ | ||
328 | struct sst_hsw_ipc_stream_alloc_reply { | ||
329 | u32 stream_hw_id; | ||
330 | u32 mixer_hw_id; // returns rate ???? | ||
331 | u32 read_position_register_address; | ||
332 | u32 presentation_position_register_address; | ||
333 | u32 peak_meter_register_address[SST_HSW_NO_CHANNELS]; | ||
334 | u32 volume_register_address[SST_HSW_NO_CHANNELS]; | ||
335 | } __attribute__((packed)); | ||
336 | |||
337 | /* Get Mixer Stream Info */ | ||
338 | struct sst_hsw_ipc_stream_info_reply { | ||
339 | u32 mixer_hw_id; | ||
340 | u32 peak_meter_register_address[SST_HSW_NO_CHANNELS]; | ||
341 | u32 volume_register_address[SST_HSW_NO_CHANNELS]; | ||
342 | } __attribute__((packed)); | ||
343 | |||
344 | /* DX State Request */ | ||
345 | struct sst_hsw_ipc_dx_req { | ||
346 | u8 state; | ||
347 | u8 reserved[3]; | ||
348 | } __attribute__((packed)); | ||
349 | |||
350 | /* DX State Reply Memory Info Item */ | ||
351 | struct sst_hsw_ipc_dx_memory_item { | ||
352 | u32 offset; | ||
353 | u32 size; | ||
354 | u32 source; | ||
355 | } __attribute__((packed)); | ||
356 | |||
357 | /* DX State Reply */ | ||
358 | struct sst_hsw_ipc_dx_reply { | ||
359 | u32 entries_no; | ||
360 | struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS]; | ||
361 | } __attribute__((packed)); | ||
362 | |||
363 | struct sst_hsw_ipc_fw_version; | ||
364 | |||
365 | /* SST Init & Free */ | ||
366 | struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length, | ||
367 | u32 fw_offset); | ||
368 | void sst_hsw_free(struct sst_hsw *hsw); | ||
369 | int sst_hsw_fw_get_version(struct sst_hsw *hsw, | ||
370 | struct sst_hsw_ipc_fw_version *version); | ||
371 | u32 create_channel_map(enum sst_hsw_channel_config config); | ||
372 | |||
373 | /* Stream Mixer Controls - */ | ||
374 | int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
375 | u32 stage_id, u32 channel); | ||
376 | int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
377 | u32 stage_id, u32 channel); | ||
378 | |||
379 | int sst_hsw_stream_set_volume(struct sst_hsw *hsw, | ||
380 | struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume); | ||
381 | int sst_hsw_stream_get_volume(struct sst_hsw *hsw, | ||
382 | struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume); | ||
383 | |||
384 | int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw, | ||
385 | struct sst_hsw_stream *stream, u64 curve_duration, | ||
386 | enum sst_hsw_volume_curve curve); | ||
387 | |||
388 | /* Global Mixer Controls - */ | ||
389 | int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel); | ||
390 | int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel); | ||
391 | |||
392 | int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, | ||
393 | u32 volume); | ||
394 | int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, | ||
395 | u32 *volume); | ||
396 | |||
397 | int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw, | ||
398 | u64 curve_duration, enum sst_hsw_volume_curve curve); | ||
399 | |||
400 | /* Stream API */ | ||
401 | struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, | ||
402 | u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data), | ||
403 | void *data); | ||
404 | |||
405 | int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream); | ||
406 | |||
407 | /* Stream Configuration */ | ||
408 | int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
409 | enum sst_hsw_stream_path_id path_id, | ||
410 | enum sst_hsw_stream_type stream_type, | ||
411 | enum sst_hsw_stream_format format_id); | ||
412 | |||
413 | int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
414 | u32 ring_pt_address, u32 num_pages, | ||
415 | u32 ring_size, u32 ring_offset, u32 ring_first_pfn); | ||
416 | |||
417 | int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream); | ||
418 | |||
419 | int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
420 | u32 bits); | ||
421 | int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
422 | int rate); | ||
423 | int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
424 | enum sst_hsw_bitdepth bits); | ||
425 | int sst_hsw_stream_set_channels(struct sst_hsw *hsw, | ||
426 | struct sst_hsw_stream *stream, int channels); | ||
427 | int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, | ||
428 | struct sst_hsw_stream *stream, u32 map, | ||
429 | enum sst_hsw_channel_config config); | ||
430 | int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
431 | enum sst_hsw_interleaving style); | ||
432 | int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, | ||
433 | struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id, | ||
434 | u32 entry_point); | ||
435 | int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, | ||
436 | struct sst_hsw_stream *stream, u32 offset, u32 size); | ||
437 | int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, | ||
438 | struct sst_hsw_stream *stream, u32 offset, u32 size); | ||
439 | int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw, | ||
440 | struct sst_hsw_stream *stream); | ||
441 | int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw, | ||
442 | struct sst_hsw_stream *stream); | ||
443 | u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw, | ||
444 | struct sst_hsw_stream *stream); | ||
445 | u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw, | ||
446 | struct sst_hsw_stream *stream); | ||
447 | u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw, | ||
448 | struct sst_hsw_stream *stream, u32 channel); | ||
449 | u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw, | ||
450 | struct sst_hsw_stream *stream, u32 channel); | ||
451 | int sst_hsw_mixer_get_info(struct sst_hsw *hsw); | ||
452 | |||
453 | /* Stream ALSA trigger operations */ | ||
454 | int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
455 | int wait); | ||
456 | int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, | ||
457 | int wait); | ||
458 | int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream); | ||
459 | |||
460 | /* Stream pointer positions */ | ||
461 | int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw, | ||
462 | struct sst_hsw_stream *stream, u32 *position); | ||
463 | int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw, | ||
464 | struct sst_hsw_stream *stream, u32 *position); | ||
465 | int sst_hsw_stream_set_write_position(struct sst_hsw *hsw, | ||
466 | struct sst_hsw_stream *stream, u32 stage_id, u32 position); | ||
467 | int sst_hsw_get_dsp_position(struct sst_hsw *hsw, | ||
468 | struct sst_hsw_stream *stream); | ||
469 | |||
470 | /* HW port config */ | ||
471 | int sst_hsw_device_set_config(struct sst_hsw *hsw, | ||
472 | enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk, | ||
473 | enum sst_hsw_device_mode mode, u32 clock_divider); | ||
474 | |||
475 | /* DX Config */ | ||
476 | int sst_hsw_dx_set_state(struct sst_hsw *hsw, | ||
477 | enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx); | ||
478 | int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item, | ||
479 | u32 *offset, u32 *size, u32 *source); | ||
480 | |||
481 | /* init */ | ||
482 | int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); | ||
483 | void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); | ||
484 | struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); | ||
485 | void sst_hsw_set_scratch_module(struct sst_hsw *hsw, | ||
486 | struct sst_module *scratch); | ||
487 | |||
488 | #endif | ||