diff options
Diffstat (limited to 'sound/soc/intel/atom/sst')
-rw-r--r-- | sound/soc/intel/atom/sst/Makefile | 7 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst.c | 557 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst.h | 559 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_acpi.c | 384 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_drv_interface.c | 741 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_ipc.c | 373 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_loader.c | 463 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_pci.c | 209 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_pvt.c | 425 | ||||
-rw-r--r-- | sound/soc/intel/atom/sst/sst_stream.c | 437 |
10 files changed, 4155 insertions, 0 deletions
diff --git a/sound/soc/intel/atom/sst/Makefile b/sound/soc/intel/atom/sst/Makefile new file mode 100644 index 000000000000..fd21726361b5 --- /dev/null +++ b/sound/soc/intel/atom/sst/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o | ||
2 | snd-intel-sst-pci-objs += sst_pci.o | ||
3 | snd-intel-sst-acpi-objs += sst_acpi.o | ||
4 | |||
5 | obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o | ||
6 | obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o | ||
7 | obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o | ||
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c new file mode 100644 index 000000000000..96c2e420cce6 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst.c | |||
@@ -0,0 +1,557 @@ | |||
1 | /* | ||
2 | * sst.c - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corp | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | */ | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <linux/pm_qos.h> | ||
28 | #include <linux/async.h> | ||
29 | #include <linux/acpi.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/soc.h> | ||
32 | #include <asm/platform_sst_audio.h> | ||
33 | #include "../sst-mfld-platform.h" | ||
34 | #include "sst.h" | ||
35 | #include "../../common/sst-dsp.h" | ||
36 | |||
37 | MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); | ||
38 | MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); | ||
39 | MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver"); | ||
40 | MODULE_LICENSE("GPL v2"); | ||
41 | |||
42 | static inline bool sst_is_process_reply(u32 msg_id) | ||
43 | { | ||
44 | return ((msg_id & PROCESS_MSG) ? true : false); | ||
45 | } | ||
46 | |||
47 | static inline bool sst_validate_mailbox_size(unsigned int size) | ||
48 | { | ||
49 | return ((size <= SST_MAILBOX_SIZE) ? true : false); | ||
50 | } | ||
51 | |||
52 | static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) | ||
53 | { | ||
54 | union interrupt_reg_mrfld isr; | ||
55 | union ipc_header_mrfld header; | ||
56 | union sst_imr_reg_mrfld imr; | ||
57 | struct ipc_post *msg = NULL; | ||
58 | unsigned int size = 0; | ||
59 | struct intel_sst_drv *drv = (struct intel_sst_drv *) context; | ||
60 | irqreturn_t retval = IRQ_HANDLED; | ||
61 | |||
62 | /* Interrupt arrived, check src */ | ||
63 | isr.full = sst_shim_read64(drv->shim, SST_ISRX); | ||
64 | |||
65 | if (isr.part.done_interrupt) { | ||
66 | /* Clear done bit */ | ||
67 | spin_lock(&drv->ipc_spin_lock); | ||
68 | header.full = sst_shim_read64(drv->shim, | ||
69 | drv->ipc_reg.ipcx); | ||
70 | header.p.header_high.part.done = 0; | ||
71 | sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full); | ||
72 | |||
73 | /* write 1 to clear status register */; | ||
74 | isr.part.done_interrupt = 1; | ||
75 | sst_shim_write64(drv->shim, SST_ISRX, isr.full); | ||
76 | spin_unlock(&drv->ipc_spin_lock); | ||
77 | |||
78 | /* we can send more messages to DSP so trigger work */ | ||
79 | queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq); | ||
80 | retval = IRQ_HANDLED; | ||
81 | } | ||
82 | |||
83 | if (isr.part.busy_interrupt) { | ||
84 | /* message from dsp so copy that */ | ||
85 | spin_lock(&drv->ipc_spin_lock); | ||
86 | imr.full = sst_shim_read64(drv->shim, SST_IMRX); | ||
87 | imr.part.busy_interrupt = 1; | ||
88 | sst_shim_write64(drv->shim, SST_IMRX, imr.full); | ||
89 | spin_unlock(&drv->ipc_spin_lock); | ||
90 | header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd); | ||
91 | |||
92 | if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) { | ||
93 | drv->ops->clear_interrupt(drv); | ||
94 | return IRQ_HANDLED; | ||
95 | } | ||
96 | |||
97 | if (header.p.header_high.part.large) { | ||
98 | size = header.p.header_low_payload; | ||
99 | if (sst_validate_mailbox_size(size)) { | ||
100 | memcpy_fromio(msg->mailbox_data, | ||
101 | drv->mailbox + drv->mailbox_recv_offset, size); | ||
102 | } else { | ||
103 | dev_err(drv->dev, | ||
104 | "Mailbox not copied, payload size is: %u\n", size); | ||
105 | header.p.header_low_payload = 0; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | msg->mrfld_header = header; | ||
110 | msg->is_process_reply = | ||
111 | sst_is_process_reply(header.p.header_high.part.msg_id); | ||
112 | spin_lock(&drv->rx_msg_lock); | ||
113 | list_add_tail(&msg->node, &drv->rx_list); | ||
114 | spin_unlock(&drv->rx_msg_lock); | ||
115 | drv->ops->clear_interrupt(drv); | ||
116 | retval = IRQ_WAKE_THREAD; | ||
117 | } | ||
118 | return retval; | ||
119 | } | ||
120 | |||
121 | static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context) | ||
122 | { | ||
123 | struct intel_sst_drv *drv = (struct intel_sst_drv *) context; | ||
124 | struct ipc_post *__msg, *msg = NULL; | ||
125 | unsigned long irq_flags; | ||
126 | |||
127 | spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); | ||
128 | if (list_empty(&drv->rx_list)) { | ||
129 | spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); | ||
130 | return IRQ_HANDLED; | ||
131 | } | ||
132 | |||
133 | list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) { | ||
134 | list_del(&msg->node); | ||
135 | spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); | ||
136 | if (msg->is_process_reply) | ||
137 | drv->ops->process_message(msg); | ||
138 | else | ||
139 | drv->ops->process_reply(drv, msg); | ||
140 | |||
141 | if (msg->is_large) | ||
142 | kfree(msg->mailbox_data); | ||
143 | kfree(msg); | ||
144 | spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); | ||
145 | } | ||
146 | spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); | ||
147 | return IRQ_HANDLED; | ||
148 | } | ||
149 | |||
150 | static int sst_save_dsp_context_v2(struct intel_sst_drv *sst) | ||
151 | { | ||
152 | int ret = 0; | ||
153 | |||
154 | ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD, | ||
155 | IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL, | ||
156 | true, true, false, true); | ||
157 | |||
158 | if (ret < 0) { | ||
159 | dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret); | ||
160 | return -EIO; | ||
161 | } | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | |||
167 | static struct intel_sst_ops mrfld_ops = { | ||
168 | .interrupt = intel_sst_interrupt_mrfld, | ||
169 | .irq_thread = intel_sst_irq_thread_mrfld, | ||
170 | .clear_interrupt = intel_sst_clear_intr_mrfld, | ||
171 | .start = sst_start_mrfld, | ||
172 | .reset = intel_sst_reset_dsp_mrfld, | ||
173 | .post_message = sst_post_message_mrfld, | ||
174 | .process_reply = sst_process_reply_mrfld, | ||
175 | .save_dsp_context = sst_save_dsp_context_v2, | ||
176 | .alloc_stream = sst_alloc_stream_mrfld, | ||
177 | .post_download = sst_post_download_mrfld, | ||
178 | }; | ||
179 | |||
180 | int sst_driver_ops(struct intel_sst_drv *sst) | ||
181 | { | ||
182 | |||
183 | switch (sst->dev_id) { | ||
184 | case SST_MRFLD_PCI_ID: | ||
185 | case SST_BYT_ACPI_ID: | ||
186 | case SST_CHV_ACPI_ID: | ||
187 | sst->tstamp = SST_TIME_STAMP_MRFLD; | ||
188 | sst->ops = &mrfld_ops; | ||
189 | return 0; | ||
190 | |||
191 | default: | ||
192 | dev_err(sst->dev, | ||
193 | "SST Driver capablities missing for dev_id: %x", sst->dev_id); | ||
194 | return -EINVAL; | ||
195 | }; | ||
196 | } | ||
197 | |||
198 | void sst_process_pending_msg(struct work_struct *work) | ||
199 | { | ||
200 | struct intel_sst_drv *ctx = container_of(work, | ||
201 | struct intel_sst_drv, ipc_post_msg_wq); | ||
202 | |||
203 | ctx->ops->post_message(ctx, NULL, false); | ||
204 | } | ||
205 | |||
206 | static int sst_workqueue_init(struct intel_sst_drv *ctx) | ||
207 | { | ||
208 | INIT_LIST_HEAD(&ctx->memcpy_list); | ||
209 | INIT_LIST_HEAD(&ctx->rx_list); | ||
210 | INIT_LIST_HEAD(&ctx->ipc_dispatch_list); | ||
211 | INIT_LIST_HEAD(&ctx->block_list); | ||
212 | INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg); | ||
213 | init_waitqueue_head(&ctx->wait_queue); | ||
214 | |||
215 | ctx->post_msg_wq = | ||
216 | create_singlethread_workqueue("sst_post_msg_wq"); | ||
217 | if (!ctx->post_msg_wq) | ||
218 | return -EBUSY; | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static void sst_init_locks(struct intel_sst_drv *ctx) | ||
223 | { | ||
224 | mutex_init(&ctx->sst_lock); | ||
225 | spin_lock_init(&ctx->rx_msg_lock); | ||
226 | spin_lock_init(&ctx->ipc_spin_lock); | ||
227 | spin_lock_init(&ctx->block_lock); | ||
228 | } | ||
229 | |||
230 | int sst_alloc_drv_context(struct intel_sst_drv **ctx, | ||
231 | struct device *dev, unsigned int dev_id) | ||
232 | { | ||
233 | *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL); | ||
234 | if (!(*ctx)) | ||
235 | return -ENOMEM; | ||
236 | |||
237 | (*ctx)->dev = dev; | ||
238 | (*ctx)->dev_id = dev_id; | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | EXPORT_SYMBOL_GPL(sst_alloc_drv_context); | ||
243 | |||
244 | int sst_context_init(struct intel_sst_drv *ctx) | ||
245 | { | ||
246 | int ret = 0, i; | ||
247 | |||
248 | if (!ctx->pdata) | ||
249 | return -EINVAL; | ||
250 | |||
251 | if (!ctx->pdata->probe_data) | ||
252 | return -EINVAL; | ||
253 | |||
254 | memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info)); | ||
255 | |||
256 | ret = sst_driver_ops(ctx); | ||
257 | if (ret != 0) | ||
258 | return -EINVAL; | ||
259 | |||
260 | sst_init_locks(ctx); | ||
261 | sst_set_fw_state_locked(ctx, SST_RESET); | ||
262 | |||
263 | /* pvt_id 0 reserved for async messages */ | ||
264 | ctx->pvt_id = 1; | ||
265 | ctx->stream_cnt = 0; | ||
266 | ctx->fw_in_mem = NULL; | ||
267 | /* we use memcpy, so set to 0 */ | ||
268 | ctx->use_dma = 0; | ||
269 | ctx->use_lli = 0; | ||
270 | |||
271 | if (sst_workqueue_init(ctx)) | ||
272 | return -EINVAL; | ||
273 | |||
274 | ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off; | ||
275 | ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset; | ||
276 | ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset; | ||
277 | |||
278 | dev_info(ctx->dev, "Got drv data max stream %d\n", | ||
279 | ctx->info.max_streams); | ||
280 | |||
281 | for (i = 1; i <= ctx->info.max_streams; i++) { | ||
282 | struct stream_info *stream = &ctx->streams[i]; | ||
283 | |||
284 | memset(stream, 0, sizeof(*stream)); | ||
285 | stream->pipe_id = PIPE_RSVD; | ||
286 | mutex_init(&stream->lock); | ||
287 | } | ||
288 | |||
289 | /* Register the ISR */ | ||
290 | ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt, | ||
291 | ctx->ops->irq_thread, 0, SST_DRV_NAME, | ||
292 | ctx); | ||
293 | if (ret) | ||
294 | goto do_free_mem; | ||
295 | |||
296 | dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num); | ||
297 | |||
298 | /* default intr are unmasked so set this as masked */ | ||
299 | sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038); | ||
300 | |||
301 | ctx->qos = devm_kzalloc(ctx->dev, | ||
302 | sizeof(struct pm_qos_request), GFP_KERNEL); | ||
303 | if (!ctx->qos) { | ||
304 | ret = -ENOMEM; | ||
305 | goto do_free_mem; | ||
306 | } | ||
307 | pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, | ||
308 | PM_QOS_DEFAULT_VALUE); | ||
309 | |||
310 | dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name); | ||
311 | ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name, | ||
312 | ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb); | ||
313 | if (ret) { | ||
314 | dev_err(ctx->dev, "Firmware download failed:%d\n", ret); | ||
315 | goto do_free_mem; | ||
316 | } | ||
317 | sst_register(ctx->dev); | ||
318 | return 0; | ||
319 | |||
320 | do_free_mem: | ||
321 | destroy_workqueue(ctx->post_msg_wq); | ||
322 | return ret; | ||
323 | } | ||
324 | EXPORT_SYMBOL_GPL(sst_context_init); | ||
325 | |||
326 | void sst_context_cleanup(struct intel_sst_drv *ctx) | ||
327 | { | ||
328 | pm_runtime_get_noresume(ctx->dev); | ||
329 | pm_runtime_disable(ctx->dev); | ||
330 | sst_unregister(ctx->dev); | ||
331 | sst_set_fw_state_locked(ctx, SST_SHUTDOWN); | ||
332 | flush_scheduled_work(); | ||
333 | destroy_workqueue(ctx->post_msg_wq); | ||
334 | pm_qos_remove_request(ctx->qos); | ||
335 | kfree(ctx->fw_sg_list.src); | ||
336 | kfree(ctx->fw_sg_list.dst); | ||
337 | ctx->fw_sg_list.list_len = 0; | ||
338 | kfree(ctx->fw_in_mem); | ||
339 | ctx->fw_in_mem = NULL; | ||
340 | sst_memcpy_free_resources(ctx); | ||
341 | ctx = NULL; | ||
342 | } | ||
343 | EXPORT_SYMBOL_GPL(sst_context_cleanup); | ||
344 | |||
345 | static inline void sst_save_shim64(struct intel_sst_drv *ctx, | ||
346 | void __iomem *shim, | ||
347 | struct sst_shim_regs64 *shim_regs) | ||
348 | { | ||
349 | unsigned long irq_flags; | ||
350 | |||
351 | spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); | ||
352 | |||
353 | shim_regs->imrx = sst_shim_read64(shim, SST_IMRX); | ||
354 | shim_regs->csr = sst_shim_read64(shim, SST_CSR); | ||
355 | |||
356 | |||
357 | spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); | ||
358 | } | ||
359 | |||
360 | static inline void sst_restore_shim64(struct intel_sst_drv *ctx, | ||
361 | void __iomem *shim, | ||
362 | struct sst_shim_regs64 *shim_regs) | ||
363 | { | ||
364 | unsigned long irq_flags; | ||
365 | |||
366 | /* | ||
367 | * we only need to restore IMRX for this case, rest will be | ||
368 | * initialize by FW or driver when firmware is loaded | ||
369 | */ | ||
370 | spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); | ||
371 | sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), | ||
372 | sst_shim_write64(shim, SST_CSR, shim_regs->csr), | ||
373 | spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); | ||
374 | } | ||
375 | |||
376 | void sst_configure_runtime_pm(struct intel_sst_drv *ctx) | ||
377 | { | ||
378 | pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); | ||
379 | pm_runtime_use_autosuspend(ctx->dev); | ||
380 | /* | ||
381 | * For acpi devices, the actual physical device state is | ||
382 | * initially active. So change the state to active before | ||
383 | * enabling the pm | ||
384 | */ | ||
385 | |||
386 | if (!acpi_disabled) | ||
387 | pm_runtime_set_active(ctx->dev); | ||
388 | |||
389 | pm_runtime_enable(ctx->dev); | ||
390 | |||
391 | if (acpi_disabled) | ||
392 | pm_runtime_set_active(ctx->dev); | ||
393 | else | ||
394 | pm_runtime_put_noidle(ctx->dev); | ||
395 | |||
396 | sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); | ||
397 | } | ||
398 | EXPORT_SYMBOL_GPL(sst_configure_runtime_pm); | ||
399 | |||
400 | static int intel_sst_runtime_suspend(struct device *dev) | ||
401 | { | ||
402 | int ret = 0; | ||
403 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
404 | |||
405 | if (ctx->sst_state == SST_RESET) { | ||
406 | dev_dbg(dev, "LPE is already in RESET state, No action\n"); | ||
407 | return 0; | ||
408 | } | ||
409 | /* save fw context */ | ||
410 | if (ctx->ops->save_dsp_context(ctx)) | ||
411 | return -EBUSY; | ||
412 | |||
413 | /* Move the SST state to Reset */ | ||
414 | sst_set_fw_state_locked(ctx, SST_RESET); | ||
415 | |||
416 | synchronize_irq(ctx->irq_num); | ||
417 | flush_workqueue(ctx->post_msg_wq); | ||
418 | |||
419 | ctx->ops->reset(ctx); | ||
420 | /* save the shim registers because PMC doesn't save state */ | ||
421 | sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); | ||
422 | |||
423 | return ret; | ||
424 | } | ||
425 | |||
426 | static int intel_sst_suspend(struct device *dev) | ||
427 | { | ||
428 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
429 | struct sst_fw_save *fw_save; | ||
430 | int i, ret = 0; | ||
431 | |||
432 | /* check first if we are already in SW reset */ | ||
433 | if (ctx->sst_state == SST_RESET) | ||
434 | return 0; | ||
435 | |||
436 | /* | ||
437 | * check if any stream is active and running | ||
438 | * they should already by suspend by soc_suspend | ||
439 | */ | ||
440 | for (i = 1; i <= ctx->info.max_streams; i++) { | ||
441 | struct stream_info *stream = &ctx->streams[i]; | ||
442 | |||
443 | if (stream->status == STREAM_RUNNING) { | ||
444 | dev_err(dev, "stream %d is running, cant susupend, abort\n", i); | ||
445 | return -EBUSY; | ||
446 | } | ||
447 | } | ||
448 | synchronize_irq(ctx->irq_num); | ||
449 | flush_workqueue(ctx->post_msg_wq); | ||
450 | |||
451 | /* Move the SST state to Reset */ | ||
452 | sst_set_fw_state_locked(ctx, SST_RESET); | ||
453 | |||
454 | /* tell DSP we are suspending */ | ||
455 | if (ctx->ops->save_dsp_context(ctx)) | ||
456 | return -EBUSY; | ||
457 | |||
458 | /* save the memories */ | ||
459 | fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL); | ||
460 | if (!fw_save) | ||
461 | return -ENOMEM; | ||
462 | fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); | ||
463 | if (!fw_save->iram) { | ||
464 | ret = -ENOMEM; | ||
465 | goto iram; | ||
466 | } | ||
467 | fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); | ||
468 | if (!fw_save->dram) { | ||
469 | ret = -ENOMEM; | ||
470 | goto dram; | ||
471 | } | ||
472 | fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); | ||
473 | if (!fw_save->sram) { | ||
474 | ret = -ENOMEM; | ||
475 | goto sram; | ||
476 | } | ||
477 | |||
478 | fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); | ||
479 | if (!fw_save->ddr) { | ||
480 | ret = -ENOMEM; | ||
481 | goto ddr; | ||
482 | } | ||
483 | |||
484 | memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base); | ||
485 | memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base); | ||
486 | memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE); | ||
487 | memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base); | ||
488 | |||
489 | ctx->fw_save = fw_save; | ||
490 | ctx->ops->reset(ctx); | ||
491 | return 0; | ||
492 | ddr: | ||
493 | kfree(fw_save->sram); | ||
494 | sram: | ||
495 | kfree(fw_save->dram); | ||
496 | dram: | ||
497 | kfree(fw_save->iram); | ||
498 | iram: | ||
499 | kfree(fw_save); | ||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | static int intel_sst_resume(struct device *dev) | ||
504 | { | ||
505 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
506 | struct sst_fw_save *fw_save = ctx->fw_save; | ||
507 | int ret = 0; | ||
508 | struct sst_block *block; | ||
509 | |||
510 | if (!fw_save) | ||
511 | return 0; | ||
512 | |||
513 | sst_set_fw_state_locked(ctx, SST_FW_LOADING); | ||
514 | |||
515 | /* we have to restore the memory saved */ | ||
516 | ctx->ops->reset(ctx); | ||
517 | |||
518 | ctx->fw_save = NULL; | ||
519 | |||
520 | memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base); | ||
521 | memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base); | ||
522 | memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE); | ||
523 | memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base); | ||
524 | |||
525 | kfree(fw_save->sram); | ||
526 | kfree(fw_save->dram); | ||
527 | kfree(fw_save->iram); | ||
528 | kfree(fw_save->ddr); | ||
529 | kfree(fw_save); | ||
530 | |||
531 | block = sst_create_block(ctx, 0, FW_DWNL_ID); | ||
532 | if (block == NULL) | ||
533 | return -ENOMEM; | ||
534 | |||
535 | |||
536 | /* start and wait for ack */ | ||
537 | ctx->ops->start(ctx); | ||
538 | ret = sst_wait_timeout(ctx, block); | ||
539 | if (ret) { | ||
540 | dev_err(ctx->dev, "fw download failed %d\n", ret); | ||
541 | /* FW download failed due to timeout */ | ||
542 | ret = -EBUSY; | ||
543 | |||
544 | } else { | ||
545 | sst_set_fw_state_locked(ctx, SST_FW_RUNNING); | ||
546 | } | ||
547 | |||
548 | sst_free_block(ctx, block); | ||
549 | return ret; | ||
550 | } | ||
551 | |||
552 | const struct dev_pm_ops intel_sst_pm = { | ||
553 | .suspend = intel_sst_suspend, | ||
554 | .resume = intel_sst_resume, | ||
555 | .runtime_suspend = intel_sst_runtime_suspend, | ||
556 | }; | ||
557 | EXPORT_SYMBOL_GPL(intel_sst_pm); | ||
diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h new file mode 100644 index 000000000000..3f493862e98d --- /dev/null +++ b/sound/soc/intel/atom/sst/sst.h | |||
@@ -0,0 +1,559 @@ | |||
1 | /* | ||
2 | * sst.h - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corporation | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | * | ||
22 | * Common private declarations for SST | ||
23 | */ | ||
24 | #ifndef __SST_H__ | ||
25 | #define __SST_H__ | ||
26 | |||
27 | #include <linux/firmware.h> | ||
28 | |||
29 | /* driver names */ | ||
30 | #define SST_DRV_NAME "intel_sst_driver" | ||
31 | #define SST_MRFLD_PCI_ID 0x119A | ||
32 | #define SST_BYT_ACPI_ID 0x80860F28 | ||
33 | #define SST_CHV_ACPI_ID 0x808622A8 | ||
34 | |||
35 | #define SST_SUSPEND_DELAY 2000 | ||
36 | #define FW_CONTEXT_MEM (64*1024) | ||
37 | #define SST_ICCM_BOUNDARY 4 | ||
38 | #define SST_CONFIG_SSP_SIGN 0x7ffe8001 | ||
39 | |||
40 | #define MRFLD_FW_VIRTUAL_BASE 0xC0000000 | ||
41 | #define MRFLD_FW_DDR_BASE_OFFSET 0x0 | ||
42 | #define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 | ||
43 | #define MRFLD_FW_BSS_RESET_BIT 0 | ||
44 | |||
45 | extern const struct dev_pm_ops intel_sst_pm; | ||
46 | enum sst_states { | ||
47 | SST_FW_LOADING = 1, | ||
48 | SST_FW_RUNNING, | ||
49 | SST_RESET, | ||
50 | SST_SHUTDOWN, | ||
51 | }; | ||
52 | |||
53 | enum sst_algo_ops { | ||
54 | SST_SET_ALGO = 0, | ||
55 | SST_GET_ALGO = 1, | ||
56 | }; | ||
57 | |||
58 | #define SST_BLOCK_TIMEOUT 1000 | ||
59 | |||
60 | #define FW_SIGNATURE_SIZE 4 | ||
61 | #define FW_NAME_SIZE 32 | ||
62 | |||
63 | /* stream states */ | ||
64 | enum sst_stream_states { | ||
65 | STREAM_UN_INIT = 0, /* Freed/Not used stream */ | ||
66 | STREAM_RUNNING = 1, /* Running */ | ||
67 | STREAM_PAUSED = 2, /* Paused stream */ | ||
68 | STREAM_DECODE = 3, /* stream is in decoding only state */ | ||
69 | STREAM_INIT = 4, /* stream init, waiting for data */ | ||
70 | STREAM_RESET = 5, /* force reset on recovery */ | ||
71 | }; | ||
72 | |||
73 | enum sst_ram_type { | ||
74 | SST_IRAM = 1, | ||
75 | SST_DRAM = 2, | ||
76 | SST_DDR = 5, | ||
77 | SST_CUSTOM_INFO = 7, /* consists of FW binary information */ | ||
78 | }; | ||
79 | |||
80 | /* SST shim registers to structure mapping */ | ||
81 | union interrupt_reg { | ||
82 | struct { | ||
83 | u64 done_interrupt:1; | ||
84 | u64 busy_interrupt:1; | ||
85 | u64 rsvd:62; | ||
86 | } part; | ||
87 | u64 full; | ||
88 | }; | ||
89 | |||
90 | union sst_pisr_reg { | ||
91 | struct { | ||
92 | u32 pssp0:1; | ||
93 | u32 pssp1:1; | ||
94 | u32 rsvd0:3; | ||
95 | u32 dmac:1; | ||
96 | u32 rsvd1:26; | ||
97 | } part; | ||
98 | u32 full; | ||
99 | }; | ||
100 | |||
101 | union sst_pimr_reg { | ||
102 | struct { | ||
103 | u32 ssp0:1; | ||
104 | u32 ssp1:1; | ||
105 | u32 rsvd0:3; | ||
106 | u32 dmac:1; | ||
107 | u32 rsvd1:10; | ||
108 | u32 ssp0_sc:1; | ||
109 | u32 ssp1_sc:1; | ||
110 | u32 rsvd2:3; | ||
111 | u32 dmac_sc:1; | ||
112 | u32 rsvd3:10; | ||
113 | } part; | ||
114 | u32 full; | ||
115 | }; | ||
116 | |||
117 | union config_status_reg_mrfld { | ||
118 | struct { | ||
119 | u64 lpe_reset:1; | ||
120 | u64 lpe_reset_vector:1; | ||
121 | u64 runstall:1; | ||
122 | u64 pwaitmode:1; | ||
123 | u64 clk_sel:3; | ||
124 | u64 rsvd2:1; | ||
125 | u64 sst_clk:3; | ||
126 | u64 xt_snoop:1; | ||
127 | u64 rsvd3:4; | ||
128 | u64 clk_sel1:6; | ||
129 | u64 clk_enable:3; | ||
130 | u64 rsvd4:6; | ||
131 | u64 slim0baseclk:1; | ||
132 | u64 rsvd:32; | ||
133 | } part; | ||
134 | u64 full; | ||
135 | }; | ||
136 | |||
137 | union interrupt_reg_mrfld { | ||
138 | struct { | ||
139 | u64 done_interrupt:1; | ||
140 | u64 busy_interrupt:1; | ||
141 | u64 rsvd:62; | ||
142 | } part; | ||
143 | u64 full; | ||
144 | }; | ||
145 | |||
146 | union sst_imr_reg_mrfld { | ||
147 | struct { | ||
148 | u64 done_interrupt:1; | ||
149 | u64 busy_interrupt:1; | ||
150 | u64 rsvd:62; | ||
151 | } part; | ||
152 | u64 full; | ||
153 | }; | ||
154 | |||
155 | /** | ||
156 | * struct sst_block - This structure is used to block a user/fw data call to another | ||
157 | * fw/user call | ||
158 | * | ||
159 | * @condition: condition for blocking check | ||
160 | * @ret_code: ret code when block is released | ||
161 | * @data: data ptr | ||
162 | * @size: size of data | ||
163 | * @on: block condition | ||
164 | * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL | ||
165 | * @drv_id: str_id in mfld/ctp, = drv_id in mrfld | ||
166 | * @node: list head node | ||
167 | */ | ||
168 | struct sst_block { | ||
169 | bool condition; | ||
170 | int ret_code; | ||
171 | void *data; | ||
172 | u32 size; | ||
173 | bool on; | ||
174 | u32 msg_id; | ||
175 | u32 drv_id; | ||
176 | struct list_head node; | ||
177 | }; | ||
178 | |||
179 | /** | ||
180 | * struct stream_info - structure that holds the stream information | ||
181 | * | ||
182 | * @status : stream current state | ||
183 | * @prev : stream prev state | ||
184 | * @ops : stream operation pb/cp/drm... | ||
185 | * @bufs: stream buffer list | ||
186 | * @lock : stream mutex for protecting state | ||
187 | * @pcm_substream : PCM substream | ||
188 | * @period_elapsed : PCM period elapsed callback | ||
189 | * @sfreq : stream sampling freq | ||
190 | * @str_type : stream type | ||
191 | * @cumm_bytes : cummulative bytes decoded | ||
192 | * @str_type : stream type | ||
193 | * @src : stream source | ||
194 | */ | ||
195 | struct stream_info { | ||
196 | unsigned int status; | ||
197 | unsigned int prev; | ||
198 | unsigned int ops; | ||
199 | struct mutex lock; | ||
200 | |||
201 | void *pcm_substream; | ||
202 | void (*period_elapsed)(void *pcm_substream); | ||
203 | |||
204 | unsigned int sfreq; | ||
205 | u32 cumm_bytes; | ||
206 | |||
207 | void *compr_cb_param; | ||
208 | void (*compr_cb)(void *compr_cb_param); | ||
209 | |||
210 | void *drain_cb_param; | ||
211 | void (*drain_notify)(void *drain_cb_param); | ||
212 | |||
213 | unsigned int num_ch; | ||
214 | unsigned int pipe_id; | ||
215 | unsigned int str_id; | ||
216 | unsigned int task_id; | ||
217 | }; | ||
218 | |||
219 | #define SST_FW_SIGN "$SST" | ||
220 | #define SST_FW_LIB_SIGN "$LIB" | ||
221 | |||
222 | /** | ||
223 | * struct sst_fw_header - FW file headers | ||
224 | * | ||
225 | * @signature : FW signature | ||
226 | * @file_size: size of fw image | ||
227 | * @modules : # of modules | ||
228 | * @file_format : version of header format | ||
229 | * @reserved : reserved fields | ||
230 | */ | ||
231 | struct sst_fw_header { | ||
232 | unsigned char signature[FW_SIGNATURE_SIZE]; | ||
233 | u32 file_size; | ||
234 | u32 modules; | ||
235 | u32 file_format; | ||
236 | u32 reserved[4]; | ||
237 | }; | ||
238 | |||
239 | /** | ||
240 | * struct fw_module_header - module header in FW | ||
241 | * | ||
242 | * @signature: module signature | ||
243 | * @mod_size: size of module | ||
244 | * @blocks: block count | ||
245 | * @type: block type | ||
246 | * @entry_point: module netry point | ||
247 | */ | ||
248 | struct fw_module_header { | ||
249 | unsigned char signature[FW_SIGNATURE_SIZE]; | ||
250 | u32 mod_size; | ||
251 | u32 blocks; | ||
252 | u32 type; | ||
253 | u32 entry_point; | ||
254 | }; | ||
255 | |||
256 | /** | ||
257 | * struct fw_block_info - block header for FW | ||
258 | * | ||
259 | * @type: block ram type I/D | ||
260 | * @size: size of block | ||
261 | * @ram_offset: offset in ram | ||
262 | */ | ||
263 | struct fw_block_info { | ||
264 | enum sst_ram_type type; | ||
265 | u32 size; | ||
266 | u32 ram_offset; | ||
267 | u32 rsvd; | ||
268 | }; | ||
269 | |||
270 | struct sst_runtime_param { | ||
271 | struct snd_sst_runtime_params param; | ||
272 | }; | ||
273 | |||
274 | struct sst_sg_list { | ||
275 | struct scatterlist *src; | ||
276 | struct scatterlist *dst; | ||
277 | int list_len; | ||
278 | unsigned int sg_idx; | ||
279 | }; | ||
280 | |||
281 | struct sst_memcpy_list { | ||
282 | struct list_head memcpylist; | ||
283 | void *dstn; | ||
284 | const void *src; | ||
285 | u32 size; | ||
286 | bool is_io; | ||
287 | }; | ||
288 | |||
289 | /*Firmware Module Information*/ | ||
290 | enum sst_lib_dwnld_status { | ||
291 | SST_LIB_NOT_FOUND = 0, | ||
292 | SST_LIB_FOUND, | ||
293 | SST_LIB_DOWNLOADED, | ||
294 | }; | ||
295 | |||
296 | struct sst_module_info { | ||
297 | const char *name; /*Library name*/ | ||
298 | u32 id; /*Module ID*/ | ||
299 | u32 entry_pt; /*Module entry point*/ | ||
300 | u8 status; /*module status*/ | ||
301 | u8 rsvd1; | ||
302 | u16 rsvd2; | ||
303 | }; | ||
304 | |||
305 | /* | ||
306 | * Structure for managing the Library Region(1.5MB) | ||
307 | * in DDR in Merrifield | ||
308 | */ | ||
309 | struct sst_mem_mgr { | ||
310 | phys_addr_t current_base; | ||
311 | int avail; | ||
312 | unsigned int count; | ||
313 | }; | ||
314 | |||
315 | struct sst_ipc_reg { | ||
316 | int ipcx; | ||
317 | int ipcd; | ||
318 | }; | ||
319 | |||
320 | struct sst_shim_regs64 { | ||
321 | u64 csr; | ||
322 | u64 pisr; | ||
323 | u64 pimr; | ||
324 | u64 isrx; | ||
325 | u64 isrd; | ||
326 | u64 imrx; | ||
327 | u64 imrd; | ||
328 | u64 ipcx; | ||
329 | u64 ipcd; | ||
330 | u64 isrsc; | ||
331 | u64 isrlpesc; | ||
332 | u64 imrsc; | ||
333 | u64 imrlpesc; | ||
334 | u64 ipcsc; | ||
335 | u64 ipclpesc; | ||
336 | u64 clkctl; | ||
337 | u64 csr2; | ||
338 | }; | ||
339 | |||
340 | struct sst_fw_save { | ||
341 | void *iram; | ||
342 | void *dram; | ||
343 | void *sram; | ||
344 | void *ddr; | ||
345 | }; | ||
346 | |||
347 | /** | ||
348 | * struct intel_sst_drv - driver ops | ||
349 | * | ||
350 | * @sst_state : current sst device state | ||
351 | * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi | ||
352 | * devices | ||
353 | * @shim : SST shim pointer | ||
354 | * @mailbox : SST mailbox pointer | ||
355 | * @iram : SST IRAM pointer | ||
356 | * @dram : SST DRAM pointer | ||
357 | * @pdata : SST info passed as a part of pci platform data | ||
358 | * @shim_phy_add : SST shim phy addr | ||
359 | * @shim_regs64: Struct to save shim registers | ||
360 | * @ipc_dispatch_list : ipc messages dispatched | ||
361 | * @rx_list : to copy the process_reply/process_msg from DSP | ||
362 | * @ipc_post_msg_wq : wq to post IPC messages context | ||
363 | * @mad_ops : MAD driver operations registered | ||
364 | * @mad_wq : MAD driver wq | ||
365 | * @post_msg_wq : wq to post IPC messages | ||
366 | * @streams : sst stream contexts | ||
367 | * @list_lock : sst driver list lock (deprecated) | ||
368 | * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue | ||
369 | * @block_lock : spin lock to add block to block_list and assign pvt_id | ||
370 | * @rx_msg_lock : spin lock to handle the rx messages from the DSP | ||
371 | * @scard_ops : sst card ops | ||
372 | * @pci : sst pci device struture | ||
373 | * @dev : pointer to current device struct | ||
374 | * @sst_lock : sst device lock | ||
375 | * @pvt_id : sst private id | ||
376 | * @stream_cnt : total sst active stream count | ||
377 | * @pb_streams : total active pb streams | ||
378 | * @cp_streams : total active cp streams | ||
379 | * @audio_start : audio status | ||
380 | * @qos : PM Qos struct | ||
381 | * firmware_name : Firmware / Library name | ||
382 | */ | ||
383 | struct intel_sst_drv { | ||
384 | int sst_state; | ||
385 | int irq_num; | ||
386 | unsigned int dev_id; | ||
387 | void __iomem *ddr; | ||
388 | void __iomem *shim; | ||
389 | void __iomem *mailbox; | ||
390 | void __iomem *iram; | ||
391 | void __iomem *dram; | ||
392 | unsigned int mailbox_add; | ||
393 | unsigned int iram_base; | ||
394 | unsigned int dram_base; | ||
395 | unsigned int shim_phy_add; | ||
396 | unsigned int iram_end; | ||
397 | unsigned int dram_end; | ||
398 | unsigned int ddr_end; | ||
399 | unsigned int ddr_base; | ||
400 | unsigned int mailbox_recv_offset; | ||
401 | struct sst_shim_regs64 *shim_regs64; | ||
402 | struct list_head block_list; | ||
403 | struct list_head ipc_dispatch_list; | ||
404 | struct sst_platform_info *pdata; | ||
405 | struct list_head rx_list; | ||
406 | struct work_struct ipc_post_msg_wq; | ||
407 | wait_queue_head_t wait_queue; | ||
408 | struct workqueue_struct *post_msg_wq; | ||
409 | unsigned int tstamp; | ||
410 | /* str_id 0 is not used */ | ||
411 | struct stream_info streams[MAX_NUM_STREAMS+1]; | ||
412 | spinlock_t ipc_spin_lock; | ||
413 | spinlock_t block_lock; | ||
414 | spinlock_t rx_msg_lock; | ||
415 | struct pci_dev *pci; | ||
416 | struct device *dev; | ||
417 | volatile long unsigned pvt_id; | ||
418 | struct mutex sst_lock; | ||
419 | unsigned int stream_cnt; | ||
420 | unsigned int csr_value; | ||
421 | void *fw_in_mem; | ||
422 | struct sst_sg_list fw_sg_list, library_list; | ||
423 | struct intel_sst_ops *ops; | ||
424 | struct sst_info info; | ||
425 | struct pm_qos_request *qos; | ||
426 | unsigned int use_dma; | ||
427 | unsigned int use_lli; | ||
428 | atomic_t fw_clear_context; | ||
429 | bool lib_dwnld_reqd; | ||
430 | struct list_head memcpy_list; | ||
431 | struct sst_ipc_reg ipc_reg; | ||
432 | struct sst_mem_mgr lib_mem_mgr; | ||
433 | /* | ||
434 | * Holder for firmware name. Due to async call it needs to be | ||
435 | * persistent till worker thread gets called | ||
436 | */ | ||
437 | char firmware_name[FW_NAME_SIZE]; | ||
438 | |||
439 | struct sst_fw_save *fw_save; | ||
440 | }; | ||
441 | |||
442 | /* misc definitions */ | ||
443 | #define FW_DWNL_ID 0x01 | ||
444 | |||
445 | struct intel_sst_ops { | ||
446 | irqreturn_t (*interrupt)(int, void *); | ||
447 | irqreturn_t (*irq_thread)(int, void *); | ||
448 | void (*clear_interrupt)(struct intel_sst_drv *ctx); | ||
449 | int (*start)(struct intel_sst_drv *ctx); | ||
450 | int (*reset)(struct intel_sst_drv *ctx); | ||
451 | void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg); | ||
452 | int (*post_message)(struct intel_sst_drv *ctx, | ||
453 | struct ipc_post *msg, bool sync); | ||
454 | void (*process_message)(struct ipc_post *msg); | ||
455 | void (*set_bypass)(bool set); | ||
456 | int (*save_dsp_context)(struct intel_sst_drv *sst); | ||
457 | void (*restore_dsp_context)(void); | ||
458 | int (*alloc_stream)(struct intel_sst_drv *ctx, void *params); | ||
459 | void (*post_download)(struct intel_sst_drv *sst); | ||
460 | }; | ||
461 | |||
462 | int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id); | ||
463 | int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id); | ||
464 | int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id); | ||
465 | int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id); | ||
466 | int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); | ||
467 | int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx, | ||
468 | struct snd_sst_bytes_v2 *sbytes); | ||
469 | int sst_set_stream_param(int str_id, struct snd_sst_params *str_param); | ||
470 | int sst_set_metadata(int str_id, char *params); | ||
471 | int sst_get_stream(struct intel_sst_drv *sst_drv_ctx, | ||
472 | struct snd_sst_params *str_param); | ||
473 | int sst_get_stream_allocated(struct intel_sst_drv *ctx, | ||
474 | struct snd_sst_params *str_param, | ||
475 | struct snd_sst_lib_download **lib_dnld); | ||
476 | int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx, | ||
477 | int str_id, bool partial_drain); | ||
478 | int sst_post_message_mrfld(struct intel_sst_drv *ctx, | ||
479 | struct ipc_post *msg, bool sync); | ||
480 | void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg); | ||
481 | int sst_start_mrfld(struct intel_sst_drv *ctx); | ||
482 | int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx); | ||
483 | void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx); | ||
484 | |||
485 | int sst_load_fw(struct intel_sst_drv *ctx); | ||
486 | int sst_load_library(struct snd_sst_lib_download *lib, u8 ops); | ||
487 | void sst_post_download_mrfld(struct intel_sst_drv *ctx); | ||
488 | int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); | ||
489 | void sst_memcpy_free_resources(struct intel_sst_drv *ctx); | ||
490 | |||
491 | int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, | ||
492 | struct sst_block *block); | ||
493 | int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, | ||
494 | struct sst_block *block); | ||
495 | int sst_create_ipc_msg(struct ipc_post **arg, bool large); | ||
496 | int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id); | ||
497 | void sst_clean_stream(struct stream_info *stream); | ||
498 | int intel_sst_register_compress(struct intel_sst_drv *sst); | ||
499 | int intel_sst_remove_compress(struct intel_sst_drv *sst); | ||
500 | void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id); | ||
501 | int sst_send_sync_msg(int ipc, int str_id); | ||
502 | int sst_get_num_channel(struct snd_sst_params *str_param); | ||
503 | int sst_get_sfreq(struct snd_sst_params *str_param); | ||
504 | int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params); | ||
505 | void sst_restore_fw_context(void); | ||
506 | struct sst_block *sst_create_block(struct intel_sst_drv *ctx, | ||
507 | u32 msg_id, u32 drv_id); | ||
508 | int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, | ||
509 | struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, | ||
510 | u32 msg_id, u32 drv_id); | ||
511 | int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed); | ||
512 | int sst_wake_up_block(struct intel_sst_drv *ctx, int result, | ||
513 | u32 drv_id, u32 ipc, void *data, u32 size); | ||
514 | int sst_request_firmware_async(struct intel_sst_drv *ctx); | ||
515 | int sst_driver_ops(struct intel_sst_drv *sst); | ||
516 | struct sst_platform_info *sst_get_acpi_driver_data(const char *hid); | ||
517 | void sst_firmware_load_cb(const struct firmware *fw, void *context); | ||
518 | int sst_prepare_and_post_msg(struct intel_sst_drv *sst, | ||
519 | int task_id, int ipc_msg, int cmd_id, int pipe_id, | ||
520 | size_t mbox_data_len, const void *mbox_data, void **data, | ||
521 | bool large, bool fill_dsp, bool sync, bool response); | ||
522 | |||
523 | void sst_process_pending_msg(struct work_struct *work); | ||
524 | int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx); | ||
525 | void sst_init_stream(struct stream_info *stream, | ||
526 | int codec, int sst_id, int ops, u8 slot); | ||
527 | int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id); | ||
528 | struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx, | ||
529 | int str_id); | ||
530 | int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx, | ||
531 | u32 pipe_id); | ||
532 | u32 relocate_imr_addr_mrfld(u32 base_addr); | ||
533 | void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, | ||
534 | struct ipc_post *msg); | ||
535 | int sst_pm_runtime_put(struct intel_sst_drv *sst_drv); | ||
536 | int sst_shim_write(void __iomem *addr, int offset, int value); | ||
537 | u32 sst_shim_read(void __iomem *addr, int offset); | ||
538 | u64 sst_reg_read64(void __iomem *addr, int offset); | ||
539 | int sst_shim_write64(void __iomem *addr, int offset, u64 value); | ||
540 | u64 sst_shim_read64(void __iomem *addr, int offset); | ||
541 | void sst_set_fw_state_locked( | ||
542 | struct intel_sst_drv *sst_drv_ctx, int sst_state); | ||
543 | void sst_fill_header_mrfld(union ipc_header_mrfld *header, | ||
544 | int msg, int task_id, int large, int drv_id); | ||
545 | void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, | ||
546 | int pipe_id, int len); | ||
547 | |||
548 | int sst_register(struct device *); | ||
549 | int sst_unregister(struct device *); | ||
550 | |||
551 | int sst_alloc_drv_context(struct intel_sst_drv **ctx, | ||
552 | struct device *dev, unsigned int dev_id); | ||
553 | int sst_context_init(struct intel_sst_drv *ctx); | ||
554 | void sst_context_cleanup(struct intel_sst_drv *ctx); | ||
555 | void sst_configure_runtime_pm(struct intel_sst_drv *ctx); | ||
556 | void memcpy32_toio(void __iomem *dst, const void *src, int count); | ||
557 | void memcpy32_fromio(void *dst, const void __iomem *src, int count); | ||
558 | |||
559 | #endif | ||
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c new file mode 100644 index 000000000000..05f693083911 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_acpi.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /* | ||
2 | * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. | ||
3 | * | ||
4 | * Copyright (c) 2013, Intel Corporation. | ||
5 | * | ||
6 | * Authors: Ramesh Babu K V <Ramesh.Babu@intel.com> | ||
7 | * Authors: Omair Mohammed Abdullah <omair.m.abdullah@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms and conditions of the GNU General Public License, | ||
11 | * version 2, as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/fs.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/miscdevice.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/firmware.h> | ||
29 | #include <linux/pm_runtime.h> | ||
30 | #include <linux/pm_qos.h> | ||
31 | #include <linux/acpi.h> | ||
32 | #include <asm/platform_sst_audio.h> | ||
33 | #include <sound/core.h> | ||
34 | #include <sound/soc.h> | ||
35 | #include <sound/compress_driver.h> | ||
36 | #include <acpi/acbuffer.h> | ||
37 | #include <acpi/platform/acenv.h> | ||
38 | #include <acpi/platform/aclinux.h> | ||
39 | #include <acpi/actypes.h> | ||
40 | #include <acpi/acpi_bus.h> | ||
41 | #include "../sst-mfld-platform.h" | ||
42 | #include "../../common/sst-dsp.h" | ||
43 | #include "sst.h" | ||
44 | |||
45 | struct sst_machines { | ||
46 | char *codec_id; | ||
47 | char board[32]; | ||
48 | char machine[32]; | ||
49 | void (*machine_quirk)(void); | ||
50 | char firmware[FW_NAME_SIZE]; | ||
51 | struct sst_platform_info *pdata; | ||
52 | |||
53 | }; | ||
54 | |||
55 | /* LPE viewpoint addresses */ | ||
56 | #define SST_BYT_IRAM_PHY_START 0xff2c0000 | ||
57 | #define SST_BYT_IRAM_PHY_END 0xff2d4000 | ||
58 | #define SST_BYT_DRAM_PHY_START 0xff300000 | ||
59 | #define SST_BYT_DRAM_PHY_END 0xff320000 | ||
60 | #define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */ | ||
61 | #define SST_BYT_IMR_VIRT_END 0xc01fffff | ||
62 | #define SST_BYT_SHIM_PHY_ADDR 0xff340000 | ||
63 | #define SST_BYT_MBOX_PHY_ADDR 0xff344000 | ||
64 | #define SST_BYT_DMA0_PHY_ADDR 0xff298000 | ||
65 | #define SST_BYT_DMA1_PHY_ADDR 0xff29c000 | ||
66 | #define SST_BYT_SSP0_PHY_ADDR 0xff2a0000 | ||
67 | #define SST_BYT_SSP2_PHY_ADDR 0xff2a2000 | ||
68 | |||
69 | #define BYT_FW_MOD_TABLE_OFFSET 0x80000 | ||
70 | #define BYT_FW_MOD_TABLE_SIZE 0x100 | ||
71 | #define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE) | ||
72 | |||
73 | static const struct sst_info byt_fwparse_info = { | ||
74 | .use_elf = false, | ||
75 | .max_streams = 25, | ||
76 | .iram_start = SST_BYT_IRAM_PHY_START, | ||
77 | .iram_end = SST_BYT_IRAM_PHY_END, | ||
78 | .iram_use = true, | ||
79 | .dram_start = SST_BYT_DRAM_PHY_START, | ||
80 | .dram_end = SST_BYT_DRAM_PHY_END, | ||
81 | .dram_use = true, | ||
82 | .imr_start = SST_BYT_IMR_VIRT_START, | ||
83 | .imr_end = SST_BYT_IMR_VIRT_END, | ||
84 | .imr_use = true, | ||
85 | .mailbox_start = SST_BYT_MBOX_PHY_ADDR, | ||
86 | .num_probes = 0, | ||
87 | .lpe_viewpt_rqd = true, | ||
88 | }; | ||
89 | |||
90 | static const struct sst_ipc_info byt_ipc_info = { | ||
91 | .ipc_offset = 0, | ||
92 | .mbox_recv_off = 0x400, | ||
93 | }; | ||
94 | |||
95 | static const struct sst_lib_dnld_info byt_lib_dnld_info = { | ||
96 | .mod_base = SST_BYT_IMR_VIRT_START, | ||
97 | .mod_end = SST_BYT_IMR_VIRT_END, | ||
98 | .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET, | ||
99 | .mod_table_size = BYT_FW_MOD_TABLE_SIZE, | ||
100 | .mod_ddr_dnld = false, | ||
101 | }; | ||
102 | |||
103 | static const struct sst_res_info byt_rvp_res_info = { | ||
104 | .shim_offset = 0x140000, | ||
105 | .shim_size = 0x000100, | ||
106 | .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR, | ||
107 | .ssp0_offset = 0xa0000, | ||
108 | .ssp0_size = 0x1000, | ||
109 | .dma0_offset = 0x98000, | ||
110 | .dma0_size = 0x4000, | ||
111 | .dma1_offset = 0x9c000, | ||
112 | .dma1_size = 0x4000, | ||
113 | .iram_offset = 0x0c0000, | ||
114 | .iram_size = 0x14000, | ||
115 | .dram_offset = 0x100000, | ||
116 | .dram_size = 0x28000, | ||
117 | .mbox_offset = 0x144000, | ||
118 | .mbox_size = 0x1000, | ||
119 | .acpi_lpe_res_index = 0, | ||
120 | .acpi_ddr_index = 2, | ||
121 | .acpi_ipc_irq_index = 5, | ||
122 | }; | ||
123 | |||
124 | static struct sst_platform_info byt_rvp_platform_data = { | ||
125 | .probe_data = &byt_fwparse_info, | ||
126 | .ipc_info = &byt_ipc_info, | ||
127 | .lib_info = &byt_lib_dnld_info, | ||
128 | .res_info = &byt_rvp_res_info, | ||
129 | .platform = "sst-mfld-platform", | ||
130 | }; | ||
131 | |||
132 | /* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail, | ||
133 | * so pdata is same as Baytrail. | ||
134 | */ | ||
135 | static struct sst_platform_info chv_platform_data = { | ||
136 | .probe_data = &byt_fwparse_info, | ||
137 | .ipc_info = &byt_ipc_info, | ||
138 | .lib_info = &byt_lib_dnld_info, | ||
139 | .res_info = &byt_rvp_res_info, | ||
140 | .platform = "sst-mfld-platform", | ||
141 | }; | ||
142 | |||
143 | static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||
144 | { | ||
145 | struct resource *rsrc; | ||
146 | struct platform_device *pdev = to_platform_device(ctx->dev); | ||
147 | |||
148 | /* All ACPI resource request here */ | ||
149 | /* Get Shim addr */ | ||
150 | rsrc = platform_get_resource(pdev, IORESOURCE_MEM, | ||
151 | ctx->pdata->res_info->acpi_lpe_res_index); | ||
152 | if (!rsrc) { | ||
153 | dev_err(ctx->dev, "Invalid SHIM base from IFWI"); | ||
154 | return -EIO; | ||
155 | } | ||
156 | dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start, | ||
157 | (unsigned int)resource_size(rsrc)); | ||
158 | |||
159 | ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset; | ||
160 | ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1; | ||
161 | dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base); | ||
162 | ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, | ||
163 | ctx->pdata->res_info->iram_size); | ||
164 | if (!ctx->iram) { | ||
165 | dev_err(ctx->dev, "unable to map IRAM"); | ||
166 | return -EIO; | ||
167 | } | ||
168 | |||
169 | ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset; | ||
170 | ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1; | ||
171 | dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base); | ||
172 | ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, | ||
173 | ctx->pdata->res_info->dram_size); | ||
174 | if (!ctx->dram) { | ||
175 | dev_err(ctx->dev, "unable to map DRAM"); | ||
176 | return -EIO; | ||
177 | } | ||
178 | |||
179 | ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset; | ||
180 | dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add); | ||
181 | ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, | ||
182 | ctx->pdata->res_info->shim_size); | ||
183 | if (!ctx->shim) { | ||
184 | dev_err(ctx->dev, "unable to map SHIM"); | ||
185 | return -EIO; | ||
186 | } | ||
187 | |||
188 | /* reassign physical address to LPE viewpoint address */ | ||
189 | ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr; | ||
190 | |||
191 | /* Get mailbox addr */ | ||
192 | ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset; | ||
193 | dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add); | ||
194 | ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, | ||
195 | ctx->pdata->res_info->mbox_size); | ||
196 | if (!ctx->mailbox) { | ||
197 | dev_err(ctx->dev, "unable to map mailbox"); | ||
198 | return -EIO; | ||
199 | } | ||
200 | |||
201 | /* reassign physical address to LPE viewpoint address */ | ||
202 | ctx->mailbox_add = ctx->info.mailbox_start; | ||
203 | |||
204 | rsrc = platform_get_resource(pdev, IORESOURCE_MEM, | ||
205 | ctx->pdata->res_info->acpi_ddr_index); | ||
206 | if (!rsrc) { | ||
207 | dev_err(ctx->dev, "Invalid DDR base from IFWI"); | ||
208 | return -EIO; | ||
209 | } | ||
210 | ctx->ddr_base = rsrc->start; | ||
211 | ctx->ddr_end = rsrc->end; | ||
212 | dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base); | ||
213 | ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, | ||
214 | resource_size(rsrc)); | ||
215 | if (!ctx->ddr) { | ||
216 | dev_err(ctx->dev, "unable to map DDR"); | ||
217 | return -EIO; | ||
218 | } | ||
219 | |||
220 | /* Find the IRQ */ | ||
221 | ctx->irq_num = platform_get_irq(pdev, | ||
222 | ctx->pdata->res_info->acpi_ipc_irq_index); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, | ||
227 | void *context, void **ret) | ||
228 | { | ||
229 | *(bool *)context = true; | ||
230 | return AE_OK; | ||
231 | } | ||
232 | |||
233 | static struct sst_machines *sst_acpi_find_machine( | ||
234 | struct sst_machines *machines) | ||
235 | { | ||
236 | struct sst_machines *mach; | ||
237 | bool found = false; | ||
238 | |||
239 | for (mach = machines; mach->codec_id; mach++) | ||
240 | if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, | ||
241 | sst_acpi_mach_match, | ||
242 | &found, NULL)) && found) | ||
243 | return mach; | ||
244 | |||
245 | return NULL; | ||
246 | } | ||
247 | |||
248 | static int sst_acpi_probe(struct platform_device *pdev) | ||
249 | { | ||
250 | struct device *dev = &pdev->dev; | ||
251 | int ret = 0; | ||
252 | struct intel_sst_drv *ctx; | ||
253 | const struct acpi_device_id *id; | ||
254 | struct sst_machines *mach; | ||
255 | struct platform_device *mdev; | ||
256 | struct platform_device *plat_dev; | ||
257 | unsigned int dev_id; | ||
258 | |||
259 | id = acpi_match_device(dev->driver->acpi_match_table, dev); | ||
260 | if (!id) | ||
261 | return -ENODEV; | ||
262 | dev_dbg(dev, "for %s", id->id); | ||
263 | |||
264 | mach = (struct sst_machines *)id->driver_data; | ||
265 | mach = sst_acpi_find_machine(mach); | ||
266 | if (mach == NULL) { | ||
267 | dev_err(dev, "No matching machine driver found\n"); | ||
268 | return -ENODEV; | ||
269 | } | ||
270 | |||
271 | ret = kstrtouint(id->id, 16, &dev_id); | ||
272 | if (ret < 0) { | ||
273 | dev_err(dev, "Unique device id conversion error: %d\n", ret); | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | dev_dbg(dev, "ACPI device id: %x\n", dev_id); | ||
278 | |||
279 | plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); | ||
280 | if (IS_ERR(plat_dev)) { | ||
281 | dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); | ||
282 | return PTR_ERR(plat_dev); | ||
283 | } | ||
284 | |||
285 | /* Create platform device for sst machine driver */ | ||
286 | mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); | ||
287 | if (IS_ERR(mdev)) { | ||
288 | dev_err(dev, "Failed to create machine device: %s\n", mach->machine); | ||
289 | return PTR_ERR(mdev); | ||
290 | } | ||
291 | |||
292 | ret = sst_alloc_drv_context(&ctx, dev, dev_id); | ||
293 | if (ret < 0) | ||
294 | return ret; | ||
295 | |||
296 | /* Fill sst platform data */ | ||
297 | ctx->pdata = mach->pdata; | ||
298 | strcpy(ctx->firmware_name, mach->firmware); | ||
299 | |||
300 | ret = sst_platform_get_resources(ctx); | ||
301 | if (ret) | ||
302 | return ret; | ||
303 | |||
304 | ret = sst_context_init(ctx); | ||
305 | if (ret < 0) | ||
306 | return ret; | ||
307 | |||
308 | /* need to save shim registers in BYT */ | ||
309 | ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), | ||
310 | GFP_KERNEL); | ||
311 | if (!ctx->shim_regs64) { | ||
312 | ret = -ENOMEM; | ||
313 | goto do_sst_cleanup; | ||
314 | } | ||
315 | |||
316 | sst_configure_runtime_pm(ctx); | ||
317 | platform_set_drvdata(pdev, ctx); | ||
318 | return ret; | ||
319 | |||
320 | do_sst_cleanup: | ||
321 | sst_context_cleanup(ctx); | ||
322 | platform_set_drvdata(pdev, NULL); | ||
323 | dev_err(ctx->dev, "failed with %d\n", ret); | ||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * intel_sst_remove - remove function | ||
329 | * | ||
330 | * @pdev: platform device structure | ||
331 | * | ||
332 | * This function is called by OS when a device is unloaded | ||
333 | * This frees the interrupt etc | ||
334 | */ | ||
335 | static int sst_acpi_remove(struct platform_device *pdev) | ||
336 | { | ||
337 | struct intel_sst_drv *ctx; | ||
338 | |||
339 | ctx = platform_get_drvdata(pdev); | ||
340 | sst_context_cleanup(ctx); | ||
341 | platform_set_drvdata(pdev, NULL); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static struct sst_machines sst_acpi_bytcr[] = { | ||
346 | {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", | ||
347 | &byt_rvp_platform_data }, | ||
348 | {}, | ||
349 | }; | ||
350 | |||
351 | /* Cherryview-based platforms: CherryTrail and Braswell */ | ||
352 | static struct sst_machines sst_acpi_chv[] = { | ||
353 | {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin", | ||
354 | &chv_platform_data }, | ||
355 | {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", | ||
356 | &chv_platform_data }, | ||
357 | {}, | ||
358 | }; | ||
359 | |||
360 | static const struct acpi_device_id sst_acpi_ids[] = { | ||
361 | { "80860F28", (unsigned long)&sst_acpi_bytcr}, | ||
362 | { "808622A8", (unsigned long) &sst_acpi_chv}, | ||
363 | { }, | ||
364 | }; | ||
365 | |||
366 | MODULE_DEVICE_TABLE(acpi, sst_acpi_ids); | ||
367 | |||
368 | static struct platform_driver sst_acpi_driver = { | ||
369 | .driver = { | ||
370 | .name = "intel_sst_acpi", | ||
371 | .acpi_match_table = ACPI_PTR(sst_acpi_ids), | ||
372 | .pm = &intel_sst_pm, | ||
373 | }, | ||
374 | .probe = sst_acpi_probe, | ||
375 | .remove = sst_acpi_remove, | ||
376 | }; | ||
377 | |||
378 | module_platform_driver(sst_acpi_driver); | ||
379 | |||
380 | MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver"); | ||
381 | MODULE_AUTHOR("Ramesh Babu K V"); | ||
382 | MODULE_AUTHOR("Omair Mohammed Abdullah"); | ||
383 | MODULE_LICENSE("GPL v2"); | ||
384 | MODULE_ALIAS("sst"); | ||
diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c new file mode 100644 index 000000000000..7b50a9d17ec1 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c | |||
@@ -0,0 +1,741 @@ | |||
1 | /* | ||
2 | * sst_drv_interface.c - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corp | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com) | ||
8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; version 2 of the License. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/firmware.h> | ||
25 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/pm_qos.h> | ||
27 | #include <linux/math64.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/pcm.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <sound/compress_driver.h> | ||
32 | #include <asm/platform_sst_audio.h> | ||
33 | #include "../sst-mfld-platform.h" | ||
34 | #include "sst.h" | ||
35 | #include "../../common/sst-dsp.h" | ||
36 | |||
37 | |||
38 | |||
39 | #define NUM_CODEC 2 | ||
40 | #define MIN_FRAGMENT 2 | ||
41 | #define MAX_FRAGMENT 4 | ||
42 | #define MIN_FRAGMENT_SIZE (50 * 1024) | ||
43 | #define MAX_FRAGMENT_SIZE (1024 * 1024) | ||
44 | #define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1) | ||
45 | |||
46 | int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id) | ||
47 | { | ||
48 | struct stream_info *stream; | ||
49 | int ret = 0; | ||
50 | |||
51 | stream = get_stream_info(ctx, str_id); | ||
52 | if (stream) { | ||
53 | /* str_id is valid, so stream is alloacted */ | ||
54 | ret = sst_free_stream(ctx, str_id); | ||
55 | if (ret) | ||
56 | sst_clean_stream(&ctx->streams[str_id]); | ||
57 | return ret; | ||
58 | } else { | ||
59 | dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id); | ||
60 | } | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | int sst_get_stream_allocated(struct intel_sst_drv *ctx, | ||
65 | struct snd_sst_params *str_param, | ||
66 | struct snd_sst_lib_download **lib_dnld) | ||
67 | { | ||
68 | int retval; | ||
69 | |||
70 | retval = ctx->ops->alloc_stream(ctx, str_param); | ||
71 | if (retval > 0) | ||
72 | dev_dbg(ctx->dev, "Stream allocated %d\n", retval); | ||
73 | return retval; | ||
74 | |||
75 | } | ||
76 | |||
77 | /* | ||
78 | * sst_get_sfreq - this function returns the frequency of the stream | ||
79 | * | ||
80 | * @str_param : stream params | ||
81 | */ | ||
82 | int sst_get_sfreq(struct snd_sst_params *str_param) | ||
83 | { | ||
84 | switch (str_param->codec) { | ||
85 | case SST_CODEC_TYPE_PCM: | ||
86 | return str_param->sparams.uc.pcm_params.sfreq; | ||
87 | case SST_CODEC_TYPE_AAC: | ||
88 | return str_param->sparams.uc.aac_params.externalsr; | ||
89 | case SST_CODEC_TYPE_MP3: | ||
90 | return 0; | ||
91 | default: | ||
92 | return -EINVAL; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * sst_get_num_channel - get number of channels for the stream | ||
98 | * | ||
99 | * @str_param : stream params | ||
100 | */ | ||
101 | int sst_get_num_channel(struct snd_sst_params *str_param) | ||
102 | { | ||
103 | switch (str_param->codec) { | ||
104 | case SST_CODEC_TYPE_PCM: | ||
105 | return str_param->sparams.uc.pcm_params.num_chan; | ||
106 | case SST_CODEC_TYPE_MP3: | ||
107 | return str_param->sparams.uc.mp3_params.num_chan; | ||
108 | case SST_CODEC_TYPE_AAC: | ||
109 | return str_param->sparams.uc.aac_params.num_chan; | ||
110 | default: | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * sst_get_stream - this function prepares for stream allocation | ||
117 | * | ||
118 | * @str_param : stream param | ||
119 | */ | ||
120 | int sst_get_stream(struct intel_sst_drv *ctx, | ||
121 | struct snd_sst_params *str_param) | ||
122 | { | ||
123 | int retval; | ||
124 | struct stream_info *str_info; | ||
125 | |||
126 | /* stream is not allocated, we are allocating */ | ||
127 | retval = ctx->ops->alloc_stream(ctx, str_param); | ||
128 | if (retval <= 0) { | ||
129 | return -EIO; | ||
130 | } | ||
131 | /* store sampling freq */ | ||
132 | str_info = &ctx->streams[retval]; | ||
133 | str_info->sfreq = sst_get_sfreq(str_param); | ||
134 | |||
135 | return retval; | ||
136 | } | ||
137 | |||
138 | static int sst_power_control(struct device *dev, bool state) | ||
139 | { | ||
140 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
141 | int ret = 0; | ||
142 | int usage_count = 0; | ||
143 | |||
144 | #ifdef CONFIG_PM | ||
145 | usage_count = atomic_read(&dev->power.usage_count); | ||
146 | #else | ||
147 | usage_count = 1; | ||
148 | #endif | ||
149 | |||
150 | if (state == true) { | ||
151 | ret = pm_runtime_get_sync(dev); | ||
152 | |||
153 | dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); | ||
154 | if (ret < 0) { | ||
155 | dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret); | ||
156 | return ret; | ||
157 | } | ||
158 | if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) { | ||
159 | ret = sst_load_fw(ctx); | ||
160 | if (ret) { | ||
161 | dev_err(dev, "FW download fail %d\n", ret); | ||
162 | sst_set_fw_state_locked(ctx, SST_RESET); | ||
163 | ret = sst_pm_runtime_put(ctx); | ||
164 | } | ||
165 | } | ||
166 | } else { | ||
167 | dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count); | ||
168 | return sst_pm_runtime_put(ctx); | ||
169 | } | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * sst_open_pcm_stream - Open PCM interface | ||
175 | * | ||
176 | * @str_param: parameters of pcm stream | ||
177 | * | ||
178 | * This function is called by MID sound card driver to open | ||
179 | * a new pcm interface | ||
180 | */ | ||
181 | static int sst_open_pcm_stream(struct device *dev, | ||
182 | struct snd_sst_params *str_param) | ||
183 | { | ||
184 | int retval; | ||
185 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
186 | |||
187 | if (!str_param) | ||
188 | return -EINVAL; | ||
189 | |||
190 | retval = sst_get_stream(ctx, str_param); | ||
191 | if (retval > 0) | ||
192 | ctx->stream_cnt++; | ||
193 | else | ||
194 | dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval); | ||
195 | |||
196 | return retval; | ||
197 | } | ||
198 | |||
199 | static int sst_cdev_open(struct device *dev, | ||
200 | struct snd_sst_params *str_params, struct sst_compress_cb *cb) | ||
201 | { | ||
202 | int str_id, retval; | ||
203 | struct stream_info *stream; | ||
204 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
205 | |||
206 | retval = pm_runtime_get_sync(ctx->dev); | ||
207 | if (retval < 0) | ||
208 | return retval; | ||
209 | |||
210 | str_id = sst_get_stream(ctx, str_params); | ||
211 | if (str_id > 0) { | ||
212 | dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id); | ||
213 | stream = &ctx->streams[str_id]; | ||
214 | stream->compr_cb = cb->compr_cb; | ||
215 | stream->compr_cb_param = cb->param; | ||
216 | stream->drain_notify = cb->drain_notify; | ||
217 | stream->drain_cb_param = cb->drain_cb_param; | ||
218 | } else { | ||
219 | dev_err(dev, "stream encountered error during alloc %d\n", str_id); | ||
220 | str_id = -EINVAL; | ||
221 | sst_pm_runtime_put(ctx); | ||
222 | } | ||
223 | return str_id; | ||
224 | } | ||
225 | |||
226 | static int sst_cdev_close(struct device *dev, unsigned int str_id) | ||
227 | { | ||
228 | int retval; | ||
229 | struct stream_info *stream; | ||
230 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
231 | |||
232 | stream = get_stream_info(ctx, str_id); | ||
233 | if (!stream) { | ||
234 | dev_err(dev, "stream info is NULL for str %d!!!\n", str_id); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | |||
238 | if (stream->status == STREAM_RESET) { | ||
239 | dev_dbg(dev, "stream in reset state...\n"); | ||
240 | stream->status = STREAM_UN_INIT; | ||
241 | |||
242 | retval = 0; | ||
243 | goto put; | ||
244 | } | ||
245 | |||
246 | retval = sst_free_stream(ctx, str_id); | ||
247 | put: | ||
248 | stream->compr_cb_param = NULL; | ||
249 | stream->compr_cb = NULL; | ||
250 | |||
251 | if (retval) | ||
252 | dev_err(dev, "free stream returned err %d\n", retval); | ||
253 | |||
254 | dev_dbg(dev, "End\n"); | ||
255 | return retval; | ||
256 | |||
257 | } | ||
258 | |||
259 | static int sst_cdev_ack(struct device *dev, unsigned int str_id, | ||
260 | unsigned long bytes) | ||
261 | { | ||
262 | struct stream_info *stream; | ||
263 | struct snd_sst_tstamp fw_tstamp = {0,}; | ||
264 | int offset; | ||
265 | void __iomem *addr; | ||
266 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
267 | |||
268 | stream = get_stream_info(ctx, str_id); | ||
269 | if (!stream) | ||
270 | return -EINVAL; | ||
271 | |||
272 | /* update bytes sent */ | ||
273 | stream->cumm_bytes += bytes; | ||
274 | dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); | ||
275 | |||
276 | memcpy_fromio(&fw_tstamp, | ||
277 | ((void *)(ctx->mailbox + ctx->tstamp) | ||
278 | +(str_id * sizeof(fw_tstamp))), | ||
279 | sizeof(fw_tstamp)); | ||
280 | |||
281 | fw_tstamp.bytes_copied = stream->cumm_bytes; | ||
282 | dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n", | ||
283 | fw_tstamp.bytes_copied, bytes); | ||
284 | |||
285 | addr = ((void *)(ctx->mailbox + ctx->tstamp)) + | ||
286 | (str_id * sizeof(fw_tstamp)); | ||
287 | offset = offsetof(struct snd_sst_tstamp, bytes_copied); | ||
288 | sst_shim_write(addr, offset, fw_tstamp.bytes_copied); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int sst_cdev_set_metadata(struct device *dev, | ||
293 | unsigned int str_id, struct snd_compr_metadata *metadata) | ||
294 | { | ||
295 | int retval = 0; | ||
296 | struct stream_info *str_info; | ||
297 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
298 | |||
299 | dev_dbg(dev, "set metadata for stream %d\n", str_id); | ||
300 | |||
301 | str_info = get_stream_info(ctx, str_id); | ||
302 | if (!str_info) | ||
303 | return -EINVAL; | ||
304 | |||
305 | dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id); | ||
306 | retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD, | ||
307 | IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id, | ||
308 | sizeof(*metadata), metadata, NULL, | ||
309 | true, true, true, false); | ||
310 | |||
311 | return retval; | ||
312 | } | ||
313 | |||
314 | static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id) | ||
315 | { | ||
316 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
317 | |||
318 | return sst_pause_stream(ctx, str_id); | ||
319 | } | ||
320 | |||
321 | static int sst_cdev_stream_pause_release(struct device *dev, | ||
322 | unsigned int str_id) | ||
323 | { | ||
324 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
325 | |||
326 | return sst_resume_stream(ctx, str_id); | ||
327 | } | ||
328 | |||
329 | static int sst_cdev_stream_start(struct device *dev, unsigned int str_id) | ||
330 | { | ||
331 | struct stream_info *str_info; | ||
332 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
333 | |||
334 | str_info = get_stream_info(ctx, str_id); | ||
335 | if (!str_info) | ||
336 | return -EINVAL; | ||
337 | str_info->prev = str_info->status; | ||
338 | str_info->status = STREAM_RUNNING; | ||
339 | return sst_start_stream(ctx, str_id); | ||
340 | } | ||
341 | |||
342 | static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id) | ||
343 | { | ||
344 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
345 | |||
346 | return sst_drop_stream(ctx, str_id); | ||
347 | } | ||
348 | |||
349 | static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id) | ||
350 | { | ||
351 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
352 | |||
353 | return sst_drain_stream(ctx, str_id, false); | ||
354 | } | ||
355 | |||
356 | static int sst_cdev_stream_partial_drain(struct device *dev, | ||
357 | unsigned int str_id) | ||
358 | { | ||
359 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
360 | |||
361 | return sst_drain_stream(ctx, str_id, true); | ||
362 | } | ||
363 | |||
364 | static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, | ||
365 | struct snd_compr_tstamp *tstamp) | ||
366 | { | ||
367 | struct snd_sst_tstamp fw_tstamp = {0,}; | ||
368 | struct stream_info *stream; | ||
369 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
370 | |||
371 | memcpy_fromio(&fw_tstamp, | ||
372 | ((void *)(ctx->mailbox + ctx->tstamp) | ||
373 | +(str_id * sizeof(fw_tstamp))), | ||
374 | sizeof(fw_tstamp)); | ||
375 | |||
376 | stream = get_stream_info(ctx, str_id); | ||
377 | if (!stream) | ||
378 | return -EINVAL; | ||
379 | dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter); | ||
380 | |||
381 | tstamp->copied_total = fw_tstamp.ring_buffer_counter; | ||
382 | tstamp->pcm_frames = fw_tstamp.frames_decoded; | ||
383 | tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, | ||
384 | (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24)); | ||
385 | tstamp->sampling_rate = fw_tstamp.sampling_frequency; | ||
386 | |||
387 | dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); | ||
388 | dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n", | ||
389 | str_id, tstamp->copied_total, tstamp->pcm_frames); | ||
390 | dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames); | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int sst_cdev_caps(struct snd_compr_caps *caps) | ||
396 | { | ||
397 | caps->num_codecs = NUM_CODEC; | ||
398 | caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ | ||
399 | caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ | ||
400 | caps->min_fragments = MIN_FRAGMENT; | ||
401 | caps->max_fragments = MAX_FRAGMENT; | ||
402 | caps->codecs[0] = SND_AUDIOCODEC_MP3; | ||
403 | caps->codecs[1] = SND_AUDIOCODEC_AAC; | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static struct snd_compr_codec_caps caps_mp3 = { | ||
408 | .num_descriptors = 1, | ||
409 | .descriptor[0].max_ch = 2, | ||
410 | .descriptor[0].sample_rates[0] = 48000, | ||
411 | .descriptor[0].sample_rates[1] = 44100, | ||
412 | .descriptor[0].sample_rates[2] = 32000, | ||
413 | .descriptor[0].sample_rates[3] = 16000, | ||
414 | .descriptor[0].sample_rates[4] = 8000, | ||
415 | .descriptor[0].num_sample_rates = 5, | ||
416 | .descriptor[0].bit_rate[0] = 320, | ||
417 | .descriptor[0].bit_rate[1] = 192, | ||
418 | .descriptor[0].num_bitrates = 2, | ||
419 | .descriptor[0].profiles = 0, | ||
420 | .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, | ||
421 | .descriptor[0].formats = 0, | ||
422 | }; | ||
423 | |||
424 | static struct snd_compr_codec_caps caps_aac = { | ||
425 | .num_descriptors = 2, | ||
426 | .descriptor[1].max_ch = 2, | ||
427 | .descriptor[0].sample_rates[0] = 48000, | ||
428 | .descriptor[0].sample_rates[1] = 44100, | ||
429 | .descriptor[0].sample_rates[2] = 32000, | ||
430 | .descriptor[0].sample_rates[3] = 16000, | ||
431 | .descriptor[0].sample_rates[4] = 8000, | ||
432 | .descriptor[0].num_sample_rates = 5, | ||
433 | .descriptor[1].bit_rate[0] = 320, | ||
434 | .descriptor[1].bit_rate[1] = 192, | ||
435 | .descriptor[1].num_bitrates = 2, | ||
436 | .descriptor[1].profiles = 0, | ||
437 | .descriptor[1].modes = 0, | ||
438 | .descriptor[1].formats = | ||
439 | (SND_AUDIOSTREAMFORMAT_MP4ADTS | | ||
440 | SND_AUDIOSTREAMFORMAT_RAW), | ||
441 | }; | ||
442 | |||
443 | static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec) | ||
444 | { | ||
445 | if (codec->codec == SND_AUDIOCODEC_MP3) | ||
446 | *codec = caps_mp3; | ||
447 | else if (codec->codec == SND_AUDIOCODEC_AAC) | ||
448 | *codec = caps_aac; | ||
449 | else | ||
450 | return -EINVAL; | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id) | ||
456 | { | ||
457 | struct stream_info *stream; | ||
458 | |||
459 | dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n", | ||
460 | str_id); | ||
461 | stream = &ctx->streams[str_id]; | ||
462 | if (stream->compr_cb) | ||
463 | stream->compr_cb(stream->compr_cb_param); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * sst_close_pcm_stream - Close PCM interface | ||
468 | * | ||
469 | * @str_id: stream id to be closed | ||
470 | * | ||
471 | * This function is called by MID sound card driver to close | ||
472 | * an existing pcm interface | ||
473 | */ | ||
474 | static int sst_close_pcm_stream(struct device *dev, unsigned int str_id) | ||
475 | { | ||
476 | struct stream_info *stream; | ||
477 | int retval = 0; | ||
478 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
479 | |||
480 | stream = get_stream_info(ctx, str_id); | ||
481 | if (!stream) { | ||
482 | dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id); | ||
483 | return -EINVAL; | ||
484 | } | ||
485 | |||
486 | if (stream->status == STREAM_RESET) { | ||
487 | /* silently fail here as we have cleaned the stream earlier */ | ||
488 | dev_dbg(ctx->dev, "stream in reset state...\n"); | ||
489 | |||
490 | retval = 0; | ||
491 | goto put; | ||
492 | } | ||
493 | |||
494 | retval = free_stream_context(ctx, str_id); | ||
495 | put: | ||
496 | stream->pcm_substream = NULL; | ||
497 | stream->status = STREAM_UN_INIT; | ||
498 | stream->period_elapsed = NULL; | ||
499 | ctx->stream_cnt--; | ||
500 | |||
501 | if (retval) | ||
502 | dev_err(ctx->dev, "free stream returned err %d\n", retval); | ||
503 | |||
504 | dev_dbg(ctx->dev, "Exit\n"); | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static inline int sst_calc_tstamp(struct intel_sst_drv *ctx, | ||
509 | struct pcm_stream_info *info, | ||
510 | struct snd_pcm_substream *substream, | ||
511 | struct snd_sst_tstamp *fw_tstamp) | ||
512 | { | ||
513 | size_t delay_bytes, delay_frames; | ||
514 | size_t buffer_sz; | ||
515 | u32 pointer_bytes, pointer_samples; | ||
516 | |||
517 | dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n", | ||
518 | fw_tstamp->ring_buffer_counter); | ||
519 | dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n", | ||
520 | fw_tstamp->hardware_counter); | ||
521 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
522 | delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter - | ||
523 | fw_tstamp->hardware_counter); | ||
524 | else | ||
525 | delay_bytes = (size_t) (fw_tstamp->hardware_counter - | ||
526 | fw_tstamp->ring_buffer_counter); | ||
527 | delay_frames = bytes_to_frames(substream->runtime, delay_bytes); | ||
528 | buffer_sz = snd_pcm_lib_buffer_bytes(substream); | ||
529 | div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes); | ||
530 | pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes); | ||
531 | |||
532 | dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes); | ||
533 | |||
534 | info->buffer_ptr = pointer_samples / substream->runtime->channels; | ||
535 | |||
536 | info->pcm_delay = delay_frames / substream->runtime->channels; | ||
537 | dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n", | ||
538 | info->buffer_ptr, info->pcm_delay); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) | ||
543 | { | ||
544 | struct stream_info *stream; | ||
545 | struct snd_pcm_substream *substream; | ||
546 | struct snd_sst_tstamp fw_tstamp; | ||
547 | unsigned int str_id; | ||
548 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
549 | |||
550 | str_id = info->str_id; | ||
551 | stream = get_stream_info(ctx, str_id); | ||
552 | if (!stream) | ||
553 | return -EINVAL; | ||
554 | |||
555 | if (!stream->pcm_substream) | ||
556 | return -EINVAL; | ||
557 | substream = stream->pcm_substream; | ||
558 | |||
559 | memcpy_fromio(&fw_tstamp, | ||
560 | ((void *)(ctx->mailbox + ctx->tstamp) | ||
561 | + (str_id * sizeof(fw_tstamp))), | ||
562 | sizeof(fw_tstamp)); | ||
563 | return sst_calc_tstamp(ctx, info, substream, &fw_tstamp); | ||
564 | } | ||
565 | |||
566 | static int sst_stream_start(struct device *dev, int str_id) | ||
567 | { | ||
568 | struct stream_info *str_info; | ||
569 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
570 | |||
571 | if (ctx->sst_state != SST_FW_RUNNING) | ||
572 | return 0; | ||
573 | str_info = get_stream_info(ctx, str_id); | ||
574 | if (!str_info) | ||
575 | return -EINVAL; | ||
576 | str_info->prev = str_info->status; | ||
577 | str_info->status = STREAM_RUNNING; | ||
578 | sst_start_stream(ctx, str_id); | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int sst_stream_drop(struct device *dev, int str_id) | ||
584 | { | ||
585 | struct stream_info *str_info; | ||
586 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
587 | |||
588 | if (ctx->sst_state != SST_FW_RUNNING) | ||
589 | return 0; | ||
590 | |||
591 | str_info = get_stream_info(ctx, str_id); | ||
592 | if (!str_info) | ||
593 | return -EINVAL; | ||
594 | str_info->prev = STREAM_UN_INIT; | ||
595 | str_info->status = STREAM_INIT; | ||
596 | return sst_drop_stream(ctx, str_id); | ||
597 | } | ||
598 | |||
599 | static int sst_stream_pause(struct device *dev, int str_id) | ||
600 | { | ||
601 | struct stream_info *str_info; | ||
602 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
603 | |||
604 | if (ctx->sst_state != SST_FW_RUNNING) | ||
605 | return 0; | ||
606 | |||
607 | str_info = get_stream_info(ctx, str_id); | ||
608 | if (!str_info) | ||
609 | return -EINVAL; | ||
610 | |||
611 | return sst_pause_stream(ctx, str_id); | ||
612 | } | ||
613 | |||
614 | static int sst_stream_resume(struct device *dev, int str_id) | ||
615 | { | ||
616 | struct stream_info *str_info; | ||
617 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
618 | |||
619 | if (ctx->sst_state != SST_FW_RUNNING) | ||
620 | return 0; | ||
621 | |||
622 | str_info = get_stream_info(ctx, str_id); | ||
623 | if (!str_info) | ||
624 | return -EINVAL; | ||
625 | return sst_resume_stream(ctx, str_id); | ||
626 | } | ||
627 | |||
628 | static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info) | ||
629 | { | ||
630 | int str_id = 0; | ||
631 | struct stream_info *stream; | ||
632 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
633 | |||
634 | str_id = str_info->str_id; | ||
635 | |||
636 | if (ctx->sst_state != SST_FW_RUNNING) | ||
637 | return 0; | ||
638 | |||
639 | stream = get_stream_info(ctx, str_id); | ||
640 | if (!stream) | ||
641 | return -EINVAL; | ||
642 | |||
643 | dev_dbg(ctx->dev, "setting the period ptrs\n"); | ||
644 | stream->pcm_substream = str_info->arg; | ||
645 | stream->period_elapsed = str_info->period_elapsed; | ||
646 | stream->sfreq = str_info->sfreq; | ||
647 | stream->prev = stream->status; | ||
648 | stream->status = STREAM_INIT; | ||
649 | dev_dbg(ctx->dev, | ||
650 | "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n", | ||
651 | stream->pcm_substream, stream->period_elapsed, | ||
652 | stream->sfreq, stream->status); | ||
653 | |||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | /* | ||
658 | * sst_set_byte_stream - Set generic params | ||
659 | * | ||
660 | * @cmd: control cmd to be set | ||
661 | * @arg: command argument | ||
662 | * | ||
663 | * This function is called by MID sound card driver to configure | ||
664 | * SST runtime params. | ||
665 | */ | ||
666 | static int sst_send_byte_stream(struct device *dev, | ||
667 | struct snd_sst_bytes_v2 *bytes) | ||
668 | { | ||
669 | int ret_val = 0; | ||
670 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
671 | |||
672 | if (NULL == bytes) | ||
673 | return -EINVAL; | ||
674 | ret_val = pm_runtime_get_sync(ctx->dev); | ||
675 | if (ret_val < 0) | ||
676 | return ret_val; | ||
677 | |||
678 | ret_val = sst_send_byte_stream_mrfld(ctx, bytes); | ||
679 | sst_pm_runtime_put(ctx); | ||
680 | |||
681 | return ret_val; | ||
682 | } | ||
683 | |||
684 | static struct sst_ops pcm_ops = { | ||
685 | .open = sst_open_pcm_stream, | ||
686 | .stream_init = sst_stream_init, | ||
687 | .stream_start = sst_stream_start, | ||
688 | .stream_drop = sst_stream_drop, | ||
689 | .stream_pause = sst_stream_pause, | ||
690 | .stream_pause_release = sst_stream_resume, | ||
691 | .stream_read_tstamp = sst_read_timestamp, | ||
692 | .send_byte_stream = sst_send_byte_stream, | ||
693 | .close = sst_close_pcm_stream, | ||
694 | .power = sst_power_control, | ||
695 | }; | ||
696 | |||
697 | static struct compress_sst_ops compr_ops = { | ||
698 | .open = sst_cdev_open, | ||
699 | .close = sst_cdev_close, | ||
700 | .stream_pause = sst_cdev_stream_pause, | ||
701 | .stream_pause_release = sst_cdev_stream_pause_release, | ||
702 | .stream_start = sst_cdev_stream_start, | ||
703 | .stream_drop = sst_cdev_stream_drop, | ||
704 | .stream_drain = sst_cdev_stream_drain, | ||
705 | .stream_partial_drain = sst_cdev_stream_partial_drain, | ||
706 | .tstamp = sst_cdev_tstamp, | ||
707 | .ack = sst_cdev_ack, | ||
708 | .get_caps = sst_cdev_caps, | ||
709 | .get_codec_caps = sst_cdev_codec_caps, | ||
710 | .set_metadata = sst_cdev_set_metadata, | ||
711 | .power = sst_power_control, | ||
712 | }; | ||
713 | |||
714 | static struct sst_device sst_dsp_device = { | ||
715 | .name = "Intel(R) SST LPE", | ||
716 | .dev = NULL, | ||
717 | .ops = &pcm_ops, | ||
718 | .compr_ops = &compr_ops, | ||
719 | }; | ||
720 | |||
721 | /* | ||
722 | * sst_register - function to register DSP | ||
723 | * | ||
724 | * This functions registers DSP with the platform driver | ||
725 | */ | ||
726 | int sst_register(struct device *dev) | ||
727 | { | ||
728 | int ret_val; | ||
729 | |||
730 | sst_dsp_device.dev = dev; | ||
731 | ret_val = sst_register_dsp(&sst_dsp_device); | ||
732 | if (ret_val) | ||
733 | dev_err(dev, "Unable to register DSP with platform driver\n"); | ||
734 | |||
735 | return ret_val; | ||
736 | } | ||
737 | |||
738 | int sst_unregister(struct device *dev) | ||
739 | { | ||
740 | return sst_unregister_dsp(&sst_dsp_device); | ||
741 | } | ||
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c new file mode 100644 index 000000000000..5a278618466c --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_ipc.c | |||
@@ -0,0 +1,373 @@ | |||
1 | /* | ||
2 | * sst_ipc.c - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corporation | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | */ | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/firmware.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/soc.h> | ||
30 | #include <sound/compress_driver.h> | ||
31 | #include <asm/intel-mid.h> | ||
32 | #include <asm/platform_sst_audio.h> | ||
33 | #include "../sst-mfld-platform.h" | ||
34 | #include "sst.h" | ||
35 | #include "../../common/sst-dsp.h" | ||
36 | |||
37 | struct sst_block *sst_create_block(struct intel_sst_drv *ctx, | ||
38 | u32 msg_id, u32 drv_id) | ||
39 | { | ||
40 | struct sst_block *msg = NULL; | ||
41 | |||
42 | dev_dbg(ctx->dev, "Enter\n"); | ||
43 | msg = kzalloc(sizeof(*msg), GFP_KERNEL); | ||
44 | if (!msg) | ||
45 | return NULL; | ||
46 | msg->condition = false; | ||
47 | msg->on = true; | ||
48 | msg->msg_id = msg_id; | ||
49 | msg->drv_id = drv_id; | ||
50 | spin_lock_bh(&ctx->block_lock); | ||
51 | list_add_tail(&msg->node, &ctx->block_list); | ||
52 | spin_unlock_bh(&ctx->block_lock); | ||
53 | |||
54 | return msg; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * while handling the interrupts, we need to check for message status and | ||
59 | * then if we are blocking for a message | ||
60 | * | ||
61 | * here we are unblocking the blocked ones, this is based on id we have | ||
62 | * passed and search that for block threads. | ||
63 | * We will not find block in two cases | ||
64 | * a) when its small message and block in not there, so silently ignore | ||
65 | * them | ||
66 | * b) when we are actually not able to find the block (bug perhaps) | ||
67 | * | ||
68 | * Since we have bit of small messages we can spam kernel log with err | ||
69 | * print on above so need to keep as debug prints which should be enabled | ||
70 | * via dynamic debug while debugging IPC issues | ||
71 | */ | ||
72 | int sst_wake_up_block(struct intel_sst_drv *ctx, int result, | ||
73 | u32 drv_id, u32 ipc, void *data, u32 size) | ||
74 | { | ||
75 | struct sst_block *block = NULL; | ||
76 | |||
77 | dev_dbg(ctx->dev, "Enter\n"); | ||
78 | |||
79 | spin_lock_bh(&ctx->block_lock); | ||
80 | list_for_each_entry(block, &ctx->block_list, node) { | ||
81 | dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id, | ||
82 | block->drv_id); | ||
83 | if (block->msg_id == ipc && block->drv_id == drv_id) { | ||
84 | dev_dbg(ctx->dev, "free up the block\n"); | ||
85 | block->ret_code = result; | ||
86 | block->data = data; | ||
87 | block->size = size; | ||
88 | block->condition = true; | ||
89 | spin_unlock_bh(&ctx->block_lock); | ||
90 | wake_up(&ctx->wait_queue); | ||
91 | return 0; | ||
92 | } | ||
93 | } | ||
94 | spin_unlock_bh(&ctx->block_lock); | ||
95 | dev_dbg(ctx->dev, | ||
96 | "Block not found or a response received for a short msg for ipc %d, drv_id %d\n", | ||
97 | ipc, drv_id); | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | |||
101 | int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed) | ||
102 | { | ||
103 | struct sst_block *block = NULL, *__block; | ||
104 | |||
105 | dev_dbg(ctx->dev, "Enter\n"); | ||
106 | spin_lock_bh(&ctx->block_lock); | ||
107 | list_for_each_entry_safe(block, __block, &ctx->block_list, node) { | ||
108 | if (block == freed) { | ||
109 | pr_debug("pvt_id freed --> %d\n", freed->drv_id); | ||
110 | /* toggle the index position of pvt_id */ | ||
111 | list_del(&freed->node); | ||
112 | spin_unlock_bh(&ctx->block_lock); | ||
113 | kfree(freed->data); | ||
114 | freed->data = NULL; | ||
115 | kfree(freed); | ||
116 | return 0; | ||
117 | } | ||
118 | } | ||
119 | spin_unlock_bh(&ctx->block_lock); | ||
120 | dev_err(ctx->dev, "block is already freed!!!\n"); | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
124 | int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx, | ||
125 | struct ipc_post *ipc_msg, bool sync) | ||
126 | { | ||
127 | struct ipc_post *msg = ipc_msg; | ||
128 | union ipc_header_mrfld header; | ||
129 | unsigned int loop_count = 0; | ||
130 | int retval = 0; | ||
131 | unsigned long irq_flags; | ||
132 | |||
133 | dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync); | ||
134 | spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); | ||
135 | header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); | ||
136 | if (sync) { | ||
137 | while (header.p.header_high.part.busy) { | ||
138 | if (loop_count > 25) { | ||
139 | dev_err(sst_drv_ctx->dev, | ||
140 | "sst: Busy wait failed, cant send this msg\n"); | ||
141 | retval = -EBUSY; | ||
142 | goto out; | ||
143 | } | ||
144 | cpu_relax(); | ||
145 | loop_count++; | ||
146 | header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); | ||
147 | } | ||
148 | } else { | ||
149 | if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { | ||
150 | /* queue is empty, nothing to send */ | ||
151 | spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); | ||
152 | dev_dbg(sst_drv_ctx->dev, | ||
153 | "Empty msg queue... NO Action\n"); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | if (header.p.header_high.part.busy) { | ||
158 | spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); | ||
159 | dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n"); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* copy msg from list */ | ||
164 | msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, | ||
165 | struct ipc_post, node); | ||
166 | list_del(&msg->node); | ||
167 | } | ||
168 | dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n", | ||
169 | msg->mrfld_header.p.header_high.full); | ||
170 | dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n", | ||
171 | msg->mrfld_header.p.header_low_payload); | ||
172 | |||
173 | if (msg->mrfld_header.p.header_high.part.large) | ||
174 | memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, | ||
175 | msg->mailbox_data, | ||
176 | msg->mrfld_header.p.header_low_payload); | ||
177 | |||
178 | sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full); | ||
179 | |||
180 | out: | ||
181 | spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); | ||
182 | kfree(msg->mailbox_data); | ||
183 | kfree(msg); | ||
184 | return retval; | ||
185 | } | ||
186 | |||
187 | void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) | ||
188 | { | ||
189 | union interrupt_reg_mrfld isr; | ||
190 | union interrupt_reg_mrfld imr; | ||
191 | union ipc_header_mrfld clear_ipc; | ||
192 | unsigned long irq_flags; | ||
193 | |||
194 | spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); | ||
195 | imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); | ||
196 | isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); | ||
197 | |||
198 | /* write 1 to clear*/ | ||
199 | isr.part.busy_interrupt = 1; | ||
200 | sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full); | ||
201 | |||
202 | /* Set IA done bit */ | ||
203 | clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD); | ||
204 | |||
205 | clear_ipc.p.header_high.part.busy = 0; | ||
206 | clear_ipc.p.header_high.part.done = 1; | ||
207 | clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS; | ||
208 | sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full); | ||
209 | /* un mask busy interrupt */ | ||
210 | imr.part.busy_interrupt = 0; | ||
211 | sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); | ||
212 | spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); | ||
213 | } | ||
214 | |||
215 | |||
216 | /* | ||
217 | * process_fw_init - process the FW init msg | ||
218 | * | ||
219 | * @msg: IPC message mailbox data from FW | ||
220 | * | ||
221 | * This function processes the FW init msg from FW | ||
222 | * marks FW state and prints debug info of loaded FW | ||
223 | */ | ||
224 | static void process_fw_init(struct intel_sst_drv *sst_drv_ctx, | ||
225 | void *msg) | ||
226 | { | ||
227 | struct ipc_header_fw_init *init = | ||
228 | (struct ipc_header_fw_init *)msg; | ||
229 | int retval = 0; | ||
230 | |||
231 | dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n"); | ||
232 | if (init->result) { | ||
233 | sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); | ||
234 | dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n", | ||
235 | init->result); | ||
236 | retval = init->result; | ||
237 | goto ret; | ||
238 | } | ||
239 | |||
240 | ret: | ||
241 | sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0); | ||
242 | } | ||
243 | |||
244 | static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx, | ||
245 | struct ipc_post *msg) | ||
246 | { | ||
247 | u32 msg_id; | ||
248 | int str_id; | ||
249 | u32 data_size, i; | ||
250 | void *data_offset; | ||
251 | struct stream_info *stream; | ||
252 | union ipc_header_high msg_high; | ||
253 | u32 msg_low, pipe_id; | ||
254 | |||
255 | msg_high = msg->mrfld_header.p.header_high; | ||
256 | msg_low = msg->mrfld_header.p.header_low_payload; | ||
257 | msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id; | ||
258 | data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr)); | ||
259 | data_size = msg_low - (sizeof(struct ipc_dsp_hdr)); | ||
260 | |||
261 | switch (msg_id) { | ||
262 | case IPC_SST_PERIOD_ELAPSED_MRFLD: | ||
263 | pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; | ||
264 | str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id); | ||
265 | if (str_id > 0) { | ||
266 | dev_dbg(sst_drv_ctx->dev, | ||
267 | "Period elapsed rcvd for pipe id 0x%x\n", | ||
268 | pipe_id); | ||
269 | stream = &sst_drv_ctx->streams[str_id]; | ||
270 | if (stream->period_elapsed) | ||
271 | stream->period_elapsed(stream->pcm_substream); | ||
272 | if (stream->compr_cb) | ||
273 | stream->compr_cb(stream->compr_cb_param); | ||
274 | } | ||
275 | break; | ||
276 | |||
277 | case IPC_IA_DRAIN_STREAM_MRFLD: | ||
278 | pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; | ||
279 | str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id); | ||
280 | if (str_id > 0) { | ||
281 | stream = &sst_drv_ctx->streams[str_id]; | ||
282 | if (stream->drain_notify) | ||
283 | stream->drain_notify(stream->drain_cb_param); | ||
284 | } | ||
285 | break; | ||
286 | |||
287 | case IPC_IA_FW_ASYNC_ERR_MRFLD: | ||
288 | dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n"); | ||
289 | for (i = 0; i < (data_size/4); i++) | ||
290 | print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE, | ||
291 | 16, 4, data_offset, data_size, false); | ||
292 | break; | ||
293 | |||
294 | case IPC_IA_FW_INIT_CMPLT_MRFLD: | ||
295 | process_fw_init(sst_drv_ctx, data_offset); | ||
296 | break; | ||
297 | |||
298 | case IPC_IA_BUF_UNDER_RUN_MRFLD: | ||
299 | pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; | ||
300 | str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id); | ||
301 | if (str_id > 0) | ||
302 | dev_err(sst_drv_ctx->dev, | ||
303 | "Buffer under-run for pipe:%#x str_id:%d\n", | ||
304 | pipe_id, str_id); | ||
305 | break; | ||
306 | |||
307 | default: | ||
308 | dev_err(sst_drv_ctx->dev, | ||
309 | "Unrecognized async msg from FW msg_id %#x\n", msg_id); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, | ||
314 | struct ipc_post *msg) | ||
315 | { | ||
316 | unsigned int drv_id; | ||
317 | void *data; | ||
318 | union ipc_header_high msg_high; | ||
319 | u32 msg_low; | ||
320 | struct ipc_dsp_hdr *dsp_hdr; | ||
321 | unsigned int cmd_id; | ||
322 | |||
323 | msg_high = msg->mrfld_header.p.header_high; | ||
324 | msg_low = msg->mrfld_header.p.header_low_payload; | ||
325 | |||
326 | dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n", | ||
327 | msg->mrfld_header.p.header_high.full, | ||
328 | msg->mrfld_header.p.header_low_payload); | ||
329 | |||
330 | drv_id = msg_high.part.drv_id; | ||
331 | |||
332 | /* Check for async messages first */ | ||
333 | if (drv_id == SST_ASYNC_DRV_ID) { | ||
334 | /*FW sent async large message*/ | ||
335 | process_fw_async_msg(sst_drv_ctx, msg); | ||
336 | return; | ||
337 | } | ||
338 | |||
339 | /* FW sent short error response for an IPC */ | ||
340 | if (msg_high.part.result && drv_id && !msg_high.part.large) { | ||
341 | /* 32-bit FW error code in msg_low */ | ||
342 | dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low); | ||
343 | sst_wake_up_block(sst_drv_ctx, msg_high.part.result, | ||
344 | msg_high.part.drv_id, | ||
345 | msg_high.part.msg_id, NULL, 0); | ||
346 | return; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * Process all valid responses | ||
351 | * if it is a large message, the payload contains the size to | ||
352 | * copy from mailbox | ||
353 | **/ | ||
354 | if (msg_high.part.large) { | ||
355 | data = kzalloc(msg_low, GFP_KERNEL); | ||
356 | if (!data) | ||
357 | return; | ||
358 | memcpy(data, (void *) msg->mailbox_data, msg_low); | ||
359 | /* Copy command id so that we can use to put sst to reset */ | ||
360 | dsp_hdr = (struct ipc_dsp_hdr *)data; | ||
361 | cmd_id = dsp_hdr->cmd_id; | ||
362 | dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id); | ||
363 | if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result, | ||
364 | msg_high.part.drv_id, | ||
365 | msg_high.part.msg_id, data, msg_low)) | ||
366 | kfree(data); | ||
367 | } else { | ||
368 | sst_wake_up_block(sst_drv_ctx, msg_high.part.result, | ||
369 | msg_high.part.drv_id, | ||
370 | msg_high.part.msg_id, NULL, 0); | ||
371 | } | ||
372 | |||
373 | } | ||
diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c new file mode 100644 index 000000000000..33917146d9c4 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_loader.c | |||
@@ -0,0 +1,463 @@ | |||
1 | /* | ||
2 | * sst_dsp.c - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corp | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | * | ||
22 | * This file contains all dsp controlling functions like firmware download, | ||
23 | * setting/resetting dsp cores, etc | ||
24 | */ | ||
25 | #include <linux/pci.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/fs.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/firmware.h> | ||
30 | #include <linux/dmaengine.h> | ||
31 | #include <linux/pm_runtime.h> | ||
32 | #include <linux/pm_qos.h> | ||
33 | #include <sound/core.h> | ||
34 | #include <sound/pcm.h> | ||
35 | #include <sound/soc.h> | ||
36 | #include <sound/compress_driver.h> | ||
37 | #include <asm/platform_sst_audio.h> | ||
38 | #include "../sst-mfld-platform.h" | ||
39 | #include "sst.h" | ||
40 | #include "../../common/sst-dsp.h" | ||
41 | |||
42 | void memcpy32_toio(void __iomem *dst, const void *src, int count) | ||
43 | { | ||
44 | /* __iowrite32_copy uses 32-bit count values so divide by 4 for | ||
45 | * right count in words | ||
46 | */ | ||
47 | __iowrite32_copy(dst, src, count/4); | ||
48 | } | ||
49 | |||
50 | void memcpy32_fromio(void *dst, const void __iomem *src, int count) | ||
51 | { | ||
52 | /* __iowrite32_copy uses 32-bit count values so divide by 4 for | ||
53 | * right count in words | ||
54 | */ | ||
55 | __iowrite32_copy(dst, src, count/4); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * intel_sst_reset_dsp_mrfld - Resetting SST DSP | ||
60 | * | ||
61 | * This resets DSP in case of MRFLD platfroms | ||
62 | */ | ||
63 | int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx) | ||
64 | { | ||
65 | union config_status_reg_mrfld csr; | ||
66 | |||
67 | dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n"); | ||
68 | csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); | ||
69 | |||
70 | dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); | ||
71 | |||
72 | csr.full |= 0x7; | ||
73 | sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
74 | csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); | ||
75 | |||
76 | dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); | ||
77 | |||
78 | csr.full &= ~(0x1); | ||
79 | sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
80 | |||
81 | csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); | ||
82 | dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * sst_start_merrifield - Start the SST DSP processor | ||
88 | * | ||
89 | * This starts the DSP in MERRIFIELD platfroms | ||
90 | */ | ||
91 | int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx) | ||
92 | { | ||
93 | union config_status_reg_mrfld csr; | ||
94 | |||
95 | dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n"); | ||
96 | csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); | ||
97 | dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); | ||
98 | |||
99 | csr.full |= 0x7; | ||
100 | sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
101 | |||
102 | csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); | ||
103 | dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); | ||
104 | |||
105 | csr.part.xt_snoop = 1; | ||
106 | csr.full &= ~(0x5); | ||
107 | sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
108 | |||
109 | csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); | ||
110 | dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n", | ||
111 | csr.full); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size, | ||
116 | struct fw_module_header **module, u32 *num_modules) | ||
117 | { | ||
118 | struct sst_fw_header *header; | ||
119 | const void *sst_fw_in_mem = ctx->fw_in_mem; | ||
120 | |||
121 | dev_dbg(ctx->dev, "Enter\n"); | ||
122 | |||
123 | /* Read the header information from the data pointer */ | ||
124 | header = (struct sst_fw_header *)sst_fw_in_mem; | ||
125 | dev_dbg(ctx->dev, | ||
126 | "header sign=%s size=%x modules=%x fmt=%x size=%zx\n", | ||
127 | header->signature, header->file_size, header->modules, | ||
128 | header->file_format, sizeof(*header)); | ||
129 | |||
130 | /* verify FW */ | ||
131 | if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) || | ||
132 | (size != header->file_size + sizeof(*header))) { | ||
133 | /* Invalid FW signature */ | ||
134 | dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n"); | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | *num_modules = header->modules; | ||
138 | *module = (void *)sst_fw_in_mem + sizeof(*header); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * sst_fill_memcpy_list - Fill the memcpy list | ||
145 | * | ||
146 | * @memcpy_list: List to be filled | ||
147 | * @destn: Destination addr to be filled in the list | ||
148 | * @src: Source addr to be filled in the list | ||
149 | * @size: Size to be filled in the list | ||
150 | * | ||
151 | * Adds the node to the list after required fields | ||
152 | * are populated in the node | ||
153 | */ | ||
154 | static int sst_fill_memcpy_list(struct list_head *memcpy_list, | ||
155 | void *destn, const void *src, u32 size, bool is_io) | ||
156 | { | ||
157 | struct sst_memcpy_list *listnode; | ||
158 | |||
159 | listnode = kzalloc(sizeof(*listnode), GFP_KERNEL); | ||
160 | if (listnode == NULL) | ||
161 | return -ENOMEM; | ||
162 | listnode->dstn = destn; | ||
163 | listnode->src = src; | ||
164 | listnode->size = size; | ||
165 | listnode->is_io = is_io; | ||
166 | list_add_tail(&listnode->memcpylist, memcpy_list); | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list | ||
173 | * | ||
174 | * @sst_drv_ctx : driver context | ||
175 | * @module : FW module header | ||
176 | * @memcpy_list : Pointer to the list to be populated | ||
177 | * Create the memcpy list as the number of block to be copied | ||
178 | * returns error or 0 if module sizes are proper | ||
179 | */ | ||
180 | static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx, | ||
181 | struct fw_module_header *module, struct list_head *memcpy_list) | ||
182 | { | ||
183 | struct fw_block_info *block; | ||
184 | u32 count; | ||
185 | int ret_val = 0; | ||
186 | void __iomem *ram_iomem; | ||
187 | |||
188 | dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n", | ||
189 | module->signature, module->mod_size, | ||
190 | module->blocks, module->type); | ||
191 | dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point); | ||
192 | |||
193 | block = (void *)module + sizeof(*module); | ||
194 | |||
195 | for (count = 0; count < module->blocks; count++) { | ||
196 | if (block->size <= 0) { | ||
197 | dev_err(sst_drv_ctx->dev, "block size invalid\n"); | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | switch (block->type) { | ||
201 | case SST_IRAM: | ||
202 | ram_iomem = sst_drv_ctx->iram; | ||
203 | break; | ||
204 | case SST_DRAM: | ||
205 | ram_iomem = sst_drv_ctx->dram; | ||
206 | break; | ||
207 | case SST_DDR: | ||
208 | ram_iomem = sst_drv_ctx->ddr; | ||
209 | break; | ||
210 | case SST_CUSTOM_INFO: | ||
211 | block = (void *)block + sizeof(*block) + block->size; | ||
212 | continue; | ||
213 | default: | ||
214 | dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n", | ||
215 | block->type, count); | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | ret_val = sst_fill_memcpy_list(memcpy_list, | ||
220 | ram_iomem + block->ram_offset, | ||
221 | (void *)block + sizeof(*block), block->size, 1); | ||
222 | if (ret_val) | ||
223 | return ret_val; | ||
224 | |||
225 | block = (void *)block + sizeof(*block) + block->size; | ||
226 | } | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy | ||
232 | * | ||
233 | * @ctx : pointer to drv context | ||
234 | * @size : size of the firmware | ||
235 | * @fw_list : pointer to list_head to be populated | ||
236 | * This function parses the FW image and saves the parsed image in the list | ||
237 | * for memcpy | ||
238 | */ | ||
239 | static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size, | ||
240 | struct list_head *fw_list) | ||
241 | { | ||
242 | struct fw_module_header *module; | ||
243 | u32 count, num_modules; | ||
244 | int ret_val; | ||
245 | |||
246 | ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules); | ||
247 | if (ret_val) | ||
248 | return ret_val; | ||
249 | |||
250 | for (count = 0; count < num_modules; count++) { | ||
251 | ret_val = sst_parse_module_memcpy(ctx, module, fw_list); | ||
252 | if (ret_val) | ||
253 | return ret_val; | ||
254 | module = (void *)module + sizeof(*module) + module->mod_size; | ||
255 | } | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * sst_do_memcpy - function initiates the memcpy | ||
262 | * | ||
263 | * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated | ||
264 | * | ||
265 | * Triggers the memcpy | ||
266 | */ | ||
267 | static void sst_do_memcpy(struct list_head *memcpy_list) | ||
268 | { | ||
269 | struct sst_memcpy_list *listnode; | ||
270 | |||
271 | list_for_each_entry(listnode, memcpy_list, memcpylist) { | ||
272 | if (listnode->is_io == true) | ||
273 | memcpy32_toio((void __iomem *)listnode->dstn, | ||
274 | listnode->src, listnode->size); | ||
275 | else | ||
276 | memcpy(listnode->dstn, listnode->src, listnode->size); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx) | ||
281 | { | ||
282 | struct sst_memcpy_list *listnode, *tmplistnode; | ||
283 | |||
284 | /* Free the list */ | ||
285 | if (!list_empty(&sst_drv_ctx->memcpy_list)) { | ||
286 | list_for_each_entry_safe(listnode, tmplistnode, | ||
287 | &sst_drv_ctx->memcpy_list, memcpylist) { | ||
288 | list_del(&listnode->memcpylist); | ||
289 | kfree(listnode); | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | static int sst_cache_and_parse_fw(struct intel_sst_drv *sst, | ||
295 | const struct firmware *fw) | ||
296 | { | ||
297 | int retval = 0; | ||
298 | |||
299 | sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL); | ||
300 | if (!sst->fw_in_mem) { | ||
301 | retval = -ENOMEM; | ||
302 | goto end_release; | ||
303 | } | ||
304 | dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem); | ||
305 | dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem)); | ||
306 | memcpy(sst->fw_in_mem, fw->data, fw->size); | ||
307 | retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list); | ||
308 | if (retval) { | ||
309 | dev_err(sst->dev, "Failed to parse fw\n"); | ||
310 | kfree(sst->fw_in_mem); | ||
311 | sst->fw_in_mem = NULL; | ||
312 | } | ||
313 | |||
314 | end_release: | ||
315 | release_firmware(fw); | ||
316 | return retval; | ||
317 | |||
318 | } | ||
319 | |||
320 | void sst_firmware_load_cb(const struct firmware *fw, void *context) | ||
321 | { | ||
322 | struct intel_sst_drv *ctx = context; | ||
323 | |||
324 | dev_dbg(ctx->dev, "Enter\n"); | ||
325 | |||
326 | if (fw == NULL) { | ||
327 | dev_err(ctx->dev, "request fw failed\n"); | ||
328 | return; | ||
329 | } | ||
330 | |||
331 | mutex_lock(&ctx->sst_lock); | ||
332 | |||
333 | if (ctx->sst_state != SST_RESET || | ||
334 | ctx->fw_in_mem != NULL) { | ||
335 | release_firmware(fw); | ||
336 | mutex_unlock(&ctx->sst_lock); | ||
337 | return; | ||
338 | } | ||
339 | |||
340 | dev_dbg(ctx->dev, "Request Fw completed\n"); | ||
341 | sst_cache_and_parse_fw(ctx, fw); | ||
342 | mutex_unlock(&ctx->sst_lock); | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * sst_request_fw - requests audio fw from kernel and saves a copy | ||
347 | * | ||
348 | * This function requests the SST FW from the kernel, parses it and | ||
349 | * saves a copy in the driver context | ||
350 | */ | ||
351 | static int sst_request_fw(struct intel_sst_drv *sst) | ||
352 | { | ||
353 | int retval = 0; | ||
354 | const struct firmware *fw; | ||
355 | |||
356 | retval = request_firmware(&fw, sst->firmware_name, sst->dev); | ||
357 | if (fw == NULL) { | ||
358 | dev_err(sst->dev, "fw is returning as null\n"); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | if (retval) { | ||
362 | dev_err(sst->dev, "request fw failed %d\n", retval); | ||
363 | return retval; | ||
364 | } | ||
365 | mutex_lock(&sst->sst_lock); | ||
366 | retval = sst_cache_and_parse_fw(sst, fw); | ||
367 | mutex_unlock(&sst->sst_lock); | ||
368 | |||
369 | return retval; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * Writing the DDR physical base to DCCM offset | ||
374 | * so that FW can use it to setup TLB | ||
375 | */ | ||
376 | static void sst_dccm_config_write(void __iomem *dram_base, | ||
377 | unsigned int ddr_base) | ||
378 | { | ||
379 | void __iomem *addr; | ||
380 | u32 bss_reset = 0; | ||
381 | |||
382 | addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET); | ||
383 | memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32)); | ||
384 | bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT); | ||
385 | addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET); | ||
386 | memcpy32_toio(addr, &bss_reset, sizeof(u32)); | ||
387 | |||
388 | } | ||
389 | |||
390 | void sst_post_download_mrfld(struct intel_sst_drv *ctx) | ||
391 | { | ||
392 | sst_dccm_config_write(ctx->dram, ctx->ddr_base); | ||
393 | dev_dbg(ctx->dev, "config written to DCCM\n"); | ||
394 | } | ||
395 | |||
396 | /** | ||
397 | * sst_load_fw - function to load FW into DSP | ||
398 | * Transfers the FW to DSP using dma/memcpy | ||
399 | */ | ||
400 | int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) | ||
401 | { | ||
402 | int ret_val = 0; | ||
403 | struct sst_block *block; | ||
404 | |||
405 | dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n"); | ||
406 | |||
407 | if (sst_drv_ctx->sst_state != SST_RESET || | ||
408 | sst_drv_ctx->sst_state == SST_SHUTDOWN) | ||
409 | return -EAGAIN; | ||
410 | |||
411 | if (!sst_drv_ctx->fw_in_mem) { | ||
412 | dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n"); | ||
413 | ret_val = sst_request_fw(sst_drv_ctx); | ||
414 | if (ret_val) | ||
415 | return ret_val; | ||
416 | } | ||
417 | |||
418 | BUG_ON(!sst_drv_ctx->fw_in_mem); | ||
419 | block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); | ||
420 | if (block == NULL) | ||
421 | return -ENOMEM; | ||
422 | |||
423 | /* Prevent C-states beyond C6 */ | ||
424 | pm_qos_update_request(sst_drv_ctx->qos, 0); | ||
425 | |||
426 | sst_drv_ctx->sst_state = SST_FW_LOADING; | ||
427 | |||
428 | ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx); | ||
429 | if (ret_val) | ||
430 | goto restore; | ||
431 | |||
432 | sst_do_memcpy(&sst_drv_ctx->memcpy_list); | ||
433 | |||
434 | /* Write the DRAM/DCCM config before enabling FW */ | ||
435 | if (sst_drv_ctx->ops->post_download) | ||
436 | sst_drv_ctx->ops->post_download(sst_drv_ctx); | ||
437 | |||
438 | /* bring sst out of reset */ | ||
439 | ret_val = sst_drv_ctx->ops->start(sst_drv_ctx); | ||
440 | if (ret_val) | ||
441 | goto restore; | ||
442 | |||
443 | ret_val = sst_wait_timeout(sst_drv_ctx, block); | ||
444 | if (ret_val) { | ||
445 | dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val); | ||
446 | /* FW download failed due to timeout */ | ||
447 | ret_val = -EBUSY; | ||
448 | |||
449 | } | ||
450 | |||
451 | |||
452 | restore: | ||
453 | /* Re-enable Deeper C-states beyond C6 */ | ||
454 | pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); | ||
455 | sst_free_block(sst_drv_ctx, block); | ||
456 | dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n"); | ||
457 | |||
458 | if (sst_drv_ctx->ops->restore_dsp_context) | ||
459 | sst_drv_ctx->ops->restore_dsp_context(); | ||
460 | sst_drv_ctx->sst_state = SST_FW_RUNNING; | ||
461 | return ret_val; | ||
462 | } | ||
463 | |||
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c new file mode 100644 index 000000000000..3a0b3bf0af97 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_pci.c | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * sst_pci.c - SST (LPE) driver init file for pci enumeration. | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corp | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | */ | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <asm/platform_sst_audio.h> | ||
30 | #include "../sst-mfld-platform.h" | ||
31 | #include "sst.h" | ||
32 | |||
33 | static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||
34 | { | ||
35 | int ddr_base, ret = 0; | ||
36 | struct pci_dev *pci = ctx->pci; | ||
37 | |||
38 | ret = pci_request_regions(pci, SST_DRV_NAME); | ||
39 | if (ret) | ||
40 | return ret; | ||
41 | |||
42 | /* map registers */ | ||
43 | /* DDR base */ | ||
44 | if (ctx->dev_id == SST_MRFLD_PCI_ID) { | ||
45 | ctx->ddr_base = pci_resource_start(pci, 0); | ||
46 | /* check that the relocated IMR base matches with FW Binary */ | ||
47 | ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); | ||
48 | if (!ctx->pdata->lib_info) { | ||
49 | dev_err(ctx->dev, "lib_info pointer NULL\n"); | ||
50 | ret = -EINVAL; | ||
51 | goto do_release_regions; | ||
52 | } | ||
53 | if (ddr_base != ctx->pdata->lib_info->mod_base) { | ||
54 | dev_err(ctx->dev, | ||
55 | "FW LSP DDR BASE does not match with IFWI\n"); | ||
56 | ret = -EINVAL; | ||
57 | goto do_release_regions; | ||
58 | } | ||
59 | ctx->ddr_end = pci_resource_end(pci, 0); | ||
60 | |||
61 | ctx->ddr = pcim_iomap(pci, 0, | ||
62 | pci_resource_len(pci, 0)); | ||
63 | if (!ctx->ddr) { | ||
64 | ret = -EINVAL; | ||
65 | goto do_release_regions; | ||
66 | } | ||
67 | dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr); | ||
68 | } else { | ||
69 | ctx->ddr = NULL; | ||
70 | } | ||
71 | /* SHIM */ | ||
72 | ctx->shim_phy_add = pci_resource_start(pci, 1); | ||
73 | ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); | ||
74 | if (!ctx->shim) { | ||
75 | ret = -EINVAL; | ||
76 | goto do_release_regions; | ||
77 | } | ||
78 | dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim); | ||
79 | |||
80 | /* Shared SRAM */ | ||
81 | ctx->mailbox_add = pci_resource_start(pci, 2); | ||
82 | ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); | ||
83 | if (!ctx->mailbox) { | ||
84 | ret = -EINVAL; | ||
85 | goto do_release_regions; | ||
86 | } | ||
87 | dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox); | ||
88 | |||
89 | /* IRAM */ | ||
90 | ctx->iram_end = pci_resource_end(pci, 3); | ||
91 | ctx->iram_base = pci_resource_start(pci, 3); | ||
92 | ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); | ||
93 | if (!ctx->iram) { | ||
94 | ret = -EINVAL; | ||
95 | goto do_release_regions; | ||
96 | } | ||
97 | dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram); | ||
98 | |||
99 | /* DRAM */ | ||
100 | ctx->dram_end = pci_resource_end(pci, 4); | ||
101 | ctx->dram_base = pci_resource_start(pci, 4); | ||
102 | ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); | ||
103 | if (!ctx->dram) { | ||
104 | ret = -EINVAL; | ||
105 | goto do_release_regions; | ||
106 | } | ||
107 | dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); | ||
108 | do_release_regions: | ||
109 | pci_release_regions(pci); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * intel_sst_probe - PCI probe function | ||
115 | * | ||
116 | * @pci: PCI device structure | ||
117 | * @pci_id: PCI device ID structure | ||
118 | * | ||
119 | */ | ||
120 | static int intel_sst_probe(struct pci_dev *pci, | ||
121 | const struct pci_device_id *pci_id) | ||
122 | { | ||
123 | int ret = 0; | ||
124 | struct intel_sst_drv *sst_drv_ctx; | ||
125 | struct sst_platform_info *sst_pdata = pci->dev.platform_data; | ||
126 | |||
127 | dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); | ||
128 | ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); | ||
129 | if (ret < 0) | ||
130 | return ret; | ||
131 | |||
132 | sst_drv_ctx->pdata = sst_pdata; | ||
133 | sst_drv_ctx->irq_num = pci->irq; | ||
134 | snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), | ||
135 | "%s%04x%s", "fw_sst_", | ||
136 | sst_drv_ctx->dev_id, ".bin"); | ||
137 | |||
138 | ret = sst_context_init(sst_drv_ctx); | ||
139 | if (ret < 0) | ||
140 | return ret; | ||
141 | |||
142 | /* Init the device */ | ||
143 | ret = pcim_enable_device(pci); | ||
144 | if (ret) { | ||
145 | dev_err(sst_drv_ctx->dev, | ||
146 | "device can't be enabled. Returned err: %d\n", ret); | ||
147 | goto do_free_drv_ctx; | ||
148 | } | ||
149 | sst_drv_ctx->pci = pci_dev_get(pci); | ||
150 | ret = sst_platform_get_resources(sst_drv_ctx); | ||
151 | if (ret < 0) | ||
152 | goto do_free_drv_ctx; | ||
153 | |||
154 | pci_set_drvdata(pci, sst_drv_ctx); | ||
155 | sst_configure_runtime_pm(sst_drv_ctx); | ||
156 | |||
157 | return ret; | ||
158 | |||
159 | do_free_drv_ctx: | ||
160 | sst_context_cleanup(sst_drv_ctx); | ||
161 | dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); | ||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * intel_sst_remove - PCI remove function | ||
167 | * | ||
168 | * @pci: PCI device structure | ||
169 | * | ||
170 | * This function is called by OS when a device is unloaded | ||
171 | * This frees the interrupt etc | ||
172 | */ | ||
173 | static void intel_sst_remove(struct pci_dev *pci) | ||
174 | { | ||
175 | struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); | ||
176 | |||
177 | sst_context_cleanup(sst_drv_ctx); | ||
178 | pci_dev_put(sst_drv_ctx->pci); | ||
179 | pci_release_regions(pci); | ||
180 | pci_set_drvdata(pci, NULL); | ||
181 | } | ||
182 | |||
183 | /* PCI Routines */ | ||
184 | static struct pci_device_id intel_sst_ids[] = { | ||
185 | { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, | ||
186 | { 0, } | ||
187 | }; | ||
188 | |||
189 | static struct pci_driver sst_driver = { | ||
190 | .name = SST_DRV_NAME, | ||
191 | .id_table = intel_sst_ids, | ||
192 | .probe = intel_sst_probe, | ||
193 | .remove = intel_sst_remove, | ||
194 | #ifdef CONFIG_PM | ||
195 | .driver = { | ||
196 | .pm = &intel_sst_pm, | ||
197 | }, | ||
198 | #endif | ||
199 | }; | ||
200 | |||
201 | module_pci_driver(sst_driver); | ||
202 | |||
203 | MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver"); | ||
204 | MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); | ||
205 | MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); | ||
206 | MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>"); | ||
207 | MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>"); | ||
208 | MODULE_LICENSE("GPL v2"); | ||
209 | MODULE_ALIAS("sst"); | ||
diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c new file mode 100644 index 000000000000..adb32fefd693 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_pvt.c | |||
@@ -0,0 +1,425 @@ | |||
1 | /* | ||
2 | * sst_pvt.c - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corp | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | */ | ||
22 | #include <linux/kobject.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <sound/asound.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/pcm.h> | ||
32 | #include <sound/soc.h> | ||
33 | #include <sound/compress_driver.h> | ||
34 | #include <asm/platform_sst_audio.h> | ||
35 | #include "../sst-mfld-platform.h" | ||
36 | #include "sst.h" | ||
37 | #include "../../common/sst-dsp.h" | ||
38 | |||
39 | int sst_shim_write(void __iomem *addr, int offset, int value) | ||
40 | { | ||
41 | writel(value, addr + offset); | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | u32 sst_shim_read(void __iomem *addr, int offset) | ||
46 | { | ||
47 | return readl(addr + offset); | ||
48 | } | ||
49 | |||
50 | u64 sst_reg_read64(void __iomem *addr, int offset) | ||
51 | { | ||
52 | u64 val = 0; | ||
53 | |||
54 | memcpy_fromio(&val, addr + offset, sizeof(val)); | ||
55 | |||
56 | return val; | ||
57 | } | ||
58 | |||
59 | int sst_shim_write64(void __iomem *addr, int offset, u64 value) | ||
60 | { | ||
61 | memcpy_toio(addr + offset, &value, sizeof(value)); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | u64 sst_shim_read64(void __iomem *addr, int offset) | ||
66 | { | ||
67 | u64 val = 0; | ||
68 | |||
69 | memcpy_fromio(&val, addr + offset, sizeof(val)); | ||
70 | return val; | ||
71 | } | ||
72 | |||
73 | void sst_set_fw_state_locked( | ||
74 | struct intel_sst_drv *sst_drv_ctx, int sst_state) | ||
75 | { | ||
76 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
77 | sst_drv_ctx->sst_state = sst_state; | ||
78 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * sst_wait_interruptible - wait on event | ||
83 | * | ||
84 | * @sst_drv_ctx: Driver context | ||
85 | * @block: Driver block to wait on | ||
86 | * | ||
87 | * This function waits without a timeout (and is interruptable) for a | ||
88 | * given block event | ||
89 | */ | ||
90 | int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, | ||
91 | struct sst_block *block) | ||
92 | { | ||
93 | int retval = 0; | ||
94 | |||
95 | if (!wait_event_interruptible(sst_drv_ctx->wait_queue, | ||
96 | block->condition)) { | ||
97 | /* event wake */ | ||
98 | if (block->ret_code < 0) { | ||
99 | dev_err(sst_drv_ctx->dev, | ||
100 | "stream failed %d\n", block->ret_code); | ||
101 | retval = -EBUSY; | ||
102 | } else { | ||
103 | dev_dbg(sst_drv_ctx->dev, "event up\n"); | ||
104 | retval = 0; | ||
105 | } | ||
106 | } else { | ||
107 | dev_err(sst_drv_ctx->dev, "signal interrupted\n"); | ||
108 | retval = -EINTR; | ||
109 | } | ||
110 | return retval; | ||
111 | |||
112 | } | ||
113 | |||
114 | /* | ||
115 | * sst_wait_timeout - wait on event for timeout | ||
116 | * | ||
117 | * @sst_drv_ctx: Driver context | ||
118 | * @block: Driver block to wait on | ||
119 | * | ||
120 | * This function waits with a timeout value (and is not interruptible) on a | ||
121 | * given block event | ||
122 | */ | ||
123 | int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block) | ||
124 | { | ||
125 | int retval = 0; | ||
126 | |||
127 | /* | ||
128 | * NOTE: | ||
129 | * Observed that FW processes the alloc msg and replies even | ||
130 | * before the alloc thread has finished execution | ||
131 | */ | ||
132 | dev_dbg(sst_drv_ctx->dev, | ||
133 | "waiting for condition %x ipc %d drv_id %d\n", | ||
134 | block->condition, block->msg_id, block->drv_id); | ||
135 | if (wait_event_timeout(sst_drv_ctx->wait_queue, | ||
136 | block->condition, | ||
137 | msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { | ||
138 | /* event wake */ | ||
139 | dev_dbg(sst_drv_ctx->dev, "Event wake %x\n", | ||
140 | block->condition); | ||
141 | dev_dbg(sst_drv_ctx->dev, "message ret: %d\n", | ||
142 | block->ret_code); | ||
143 | retval = -block->ret_code; | ||
144 | } else { | ||
145 | block->on = false; | ||
146 | dev_err(sst_drv_ctx->dev, | ||
147 | "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n", | ||
148 | block->condition, block->msg_id, sst_drv_ctx->sst_state); | ||
149 | sst_drv_ctx->sst_state = SST_RESET; | ||
150 | |||
151 | retval = -EBUSY; | ||
152 | } | ||
153 | return retval; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * sst_create_ipc_msg - create a IPC message | ||
158 | * | ||
159 | * @arg: ipc message | ||
160 | * @large: large or short message | ||
161 | * | ||
162 | * this function allocates structures to send a large or short | ||
163 | * message to the firmware | ||
164 | */ | ||
165 | int sst_create_ipc_msg(struct ipc_post **arg, bool large) | ||
166 | { | ||
167 | struct ipc_post *msg; | ||
168 | |||
169 | msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); | ||
170 | if (!msg) | ||
171 | return -ENOMEM; | ||
172 | if (large) { | ||
173 | msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); | ||
174 | if (!msg->mailbox_data) { | ||
175 | kfree(msg); | ||
176 | return -ENOMEM; | ||
177 | } | ||
178 | } else { | ||
179 | msg->mailbox_data = NULL; | ||
180 | } | ||
181 | msg->is_large = large; | ||
182 | *arg = msg; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * sst_create_block_and_ipc_msg - Creates IPC message and sst block | ||
188 | * @arg: passed to sst_create_ipc_message API | ||
189 | * @large: large or short message | ||
190 | * @sst_drv_ctx: sst driver context | ||
191 | * @block: return block allocated | ||
192 | * @msg_id: IPC | ||
193 | * @drv_id: stream id or private id | ||
194 | */ | ||
195 | int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, | ||
196 | struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, | ||
197 | u32 msg_id, u32 drv_id) | ||
198 | { | ||
199 | int retval = 0; | ||
200 | |||
201 | retval = sst_create_ipc_msg(arg, large); | ||
202 | if (retval) | ||
203 | return retval; | ||
204 | *block = sst_create_block(sst_drv_ctx, msg_id, drv_id); | ||
205 | if (*block == NULL) { | ||
206 | kfree(*arg); | ||
207 | return -ENOMEM; | ||
208 | } | ||
209 | return retval; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * sst_clean_stream - clean the stream context | ||
214 | * | ||
215 | * @stream: stream structure | ||
216 | * | ||
217 | * this function resets the stream contexts | ||
218 | * should be called in free | ||
219 | */ | ||
220 | void sst_clean_stream(struct stream_info *stream) | ||
221 | { | ||
222 | stream->status = STREAM_UN_INIT; | ||
223 | stream->prev = STREAM_UN_INIT; | ||
224 | mutex_lock(&stream->lock); | ||
225 | stream->cumm_bytes = 0; | ||
226 | mutex_unlock(&stream->lock); | ||
227 | } | ||
228 | |||
229 | int sst_prepare_and_post_msg(struct intel_sst_drv *sst, | ||
230 | int task_id, int ipc_msg, int cmd_id, int pipe_id, | ||
231 | size_t mbox_data_len, const void *mbox_data, void **data, | ||
232 | bool large, bool fill_dsp, bool sync, bool response) | ||
233 | { | ||
234 | struct ipc_post *msg = NULL; | ||
235 | struct ipc_dsp_hdr dsp_hdr; | ||
236 | struct sst_block *block; | ||
237 | int ret = 0, pvt_id; | ||
238 | |||
239 | pvt_id = sst_assign_pvt_id(sst); | ||
240 | if (pvt_id < 0) | ||
241 | return pvt_id; | ||
242 | |||
243 | if (response) | ||
244 | ret = sst_create_block_and_ipc_msg( | ||
245 | &msg, large, sst, &block, ipc_msg, pvt_id); | ||
246 | else | ||
247 | ret = sst_create_ipc_msg(&msg, large); | ||
248 | |||
249 | if (ret < 0) { | ||
250 | test_and_clear_bit(pvt_id, &sst->pvt_id); | ||
251 | return -ENOMEM; | ||
252 | } | ||
253 | |||
254 | dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n", | ||
255 | pvt_id, pipe_id, task_id, ipc_msg); | ||
256 | sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg, | ||
257 | task_id, large, pvt_id); | ||
258 | msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len; | ||
259 | msg->mrfld_header.p.header_high.part.res_rqd = !sync; | ||
260 | dev_dbg(sst->dev, "header:%x\n", | ||
261 | msg->mrfld_header.p.header_high.full); | ||
262 | dev_dbg(sst->dev, "response rqd: %x", | ||
263 | msg->mrfld_header.p.header_high.part.res_rqd); | ||
264 | dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d", | ||
265 | msg->mrfld_header.p.header_low_payload); | ||
266 | if (fill_dsp) { | ||
267 | sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len); | ||
268 | memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); | ||
269 | if (mbox_data_len) { | ||
270 | memcpy(msg->mailbox_data + sizeof(dsp_hdr), | ||
271 | mbox_data, mbox_data_len); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | if (sync) | ||
276 | sst->ops->post_message(sst, msg, true); | ||
277 | else | ||
278 | sst_add_to_dispatch_list_and_post(sst, msg); | ||
279 | |||
280 | if (response) { | ||
281 | ret = sst_wait_timeout(sst, block); | ||
282 | if (ret < 0) { | ||
283 | goto out; | ||
284 | } else if(block->data) { | ||
285 | if (!data) | ||
286 | goto out; | ||
287 | *data = kzalloc(block->size, GFP_KERNEL); | ||
288 | if (!(*data)) { | ||
289 | ret = -ENOMEM; | ||
290 | goto out; | ||
291 | } else | ||
292 | memcpy(data, (void *) block->data, block->size); | ||
293 | } | ||
294 | } | ||
295 | out: | ||
296 | if (response) | ||
297 | sst_free_block(sst, block); | ||
298 | test_and_clear_bit(pvt_id, &sst->pvt_id); | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | int sst_pm_runtime_put(struct intel_sst_drv *sst_drv) | ||
303 | { | ||
304 | int ret; | ||
305 | |||
306 | pm_runtime_mark_last_busy(sst_drv->dev); | ||
307 | ret = pm_runtime_put_autosuspend(sst_drv->dev); | ||
308 | if (ret < 0) | ||
309 | return ret; | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | void sst_fill_header_mrfld(union ipc_header_mrfld *header, | ||
314 | int msg, int task_id, int large, int drv_id) | ||
315 | { | ||
316 | header->full = 0; | ||
317 | header->p.header_high.part.msg_id = msg; | ||
318 | header->p.header_high.part.task_id = task_id; | ||
319 | header->p.header_high.part.large = large; | ||
320 | header->p.header_high.part.drv_id = drv_id; | ||
321 | header->p.header_high.part.done = 0; | ||
322 | header->p.header_high.part.busy = 1; | ||
323 | header->p.header_high.part.res_rqd = 1; | ||
324 | } | ||
325 | |||
326 | void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, | ||
327 | int pipe_id, int len) | ||
328 | { | ||
329 | dsp->cmd_id = msg; | ||
330 | dsp->mod_index_id = 0xff; | ||
331 | dsp->pipe_id = pipe_id; | ||
332 | dsp->length = len; | ||
333 | dsp->mod_id = 0; | ||
334 | } | ||
335 | |||
336 | #define SST_MAX_BLOCKS 15 | ||
337 | /* | ||
338 | * sst_assign_pvt_id - assign a pvt id for stream | ||
339 | * | ||
340 | * @sst_drv_ctx : driver context | ||
341 | * | ||
342 | * this function assigns a private id for calls that dont have stream | ||
343 | * context yet, should be called with lock held | ||
344 | * uses bits for the id, and finds first free bits and assigns that | ||
345 | */ | ||
346 | int sst_assign_pvt_id(struct intel_sst_drv *drv) | ||
347 | { | ||
348 | int local; | ||
349 | |||
350 | spin_lock(&drv->block_lock); | ||
351 | /* find first zero index from lsb */ | ||
352 | local = ffz(drv->pvt_id); | ||
353 | dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); | ||
354 | if (local >= SST_MAX_BLOCKS){ | ||
355 | spin_unlock(&drv->block_lock); | ||
356 | dev_err(drv->dev, "PVT _ID error: no free id blocks "); | ||
357 | return -EINVAL; | ||
358 | } | ||
359 | /* toggle the index */ | ||
360 | change_bit(local, &drv->pvt_id); | ||
361 | spin_unlock(&drv->block_lock); | ||
362 | return local; | ||
363 | } | ||
364 | |||
365 | void sst_init_stream(struct stream_info *stream, | ||
366 | int codec, int sst_id, int ops, u8 slot) | ||
367 | { | ||
368 | stream->status = STREAM_INIT; | ||
369 | stream->prev = STREAM_UN_INIT; | ||
370 | stream->ops = ops; | ||
371 | } | ||
372 | |||
373 | int sst_validate_strid( | ||
374 | struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
375 | { | ||
376 | if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) { | ||
377 | dev_err(sst_drv_ctx->dev, | ||
378 | "SST ERR: invalid stream id : %d, max %d\n", | ||
379 | str_id, sst_drv_ctx->info.max_streams); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | struct stream_info *get_stream_info( | ||
387 | struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
388 | { | ||
389 | if (sst_validate_strid(sst_drv_ctx, str_id)) | ||
390 | return NULL; | ||
391 | return &sst_drv_ctx->streams[str_id]; | ||
392 | } | ||
393 | |||
394 | int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx, | ||
395 | u32 pipe_id) | ||
396 | { | ||
397 | int i; | ||
398 | |||
399 | for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) | ||
400 | if (pipe_id == sst_drv_ctx->streams[i].pipe_id) | ||
401 | return i; | ||
402 | |||
403 | dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id); | ||
404 | return -1; | ||
405 | } | ||
406 | |||
407 | u32 relocate_imr_addr_mrfld(u32 base_addr) | ||
408 | { | ||
409 | /* Get the difference from 512MB aligned base addr */ | ||
410 | /* relocate the base */ | ||
411 | base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024)); | ||
412 | return base_addr; | ||
413 | } | ||
414 | EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld); | ||
415 | |||
416 | void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, | ||
417 | struct ipc_post *msg) | ||
418 | { | ||
419 | unsigned long irq_flags; | ||
420 | |||
421 | spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); | ||
422 | list_add_tail(&msg->node, &sst->ipc_dispatch_list); | ||
423 | spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); | ||
424 | sst->ops->post_message(sst, NULL, false); | ||
425 | } | ||
diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c new file mode 100644 index 000000000000..a74c64c7053c --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_stream.c | |||
@@ -0,0 +1,437 @@ | |||
1 | /* | ||
2 | * sst_stream.c - Intel SST Driver for audio engine | ||
3 | * | ||
4 | * Copyright (C) 2008-14 Intel Corp | ||
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
6 | * Harsha Priya <priya.harsha@intel.com> | ||
7 | * Dharageswari R <dharageswari.r@intel.com> | ||
8 | * KP Jeeja <jeeja.kp@intel.com> | ||
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
21 | */ | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/firmware.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/soc.h> | ||
30 | #include <sound/compress_driver.h> | ||
31 | #include <asm/platform_sst_audio.h> | ||
32 | #include "../sst-mfld-platform.h" | ||
33 | #include "sst.h" | ||
34 | #include "../../common/sst-dsp.h" | ||
35 | |||
36 | int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) | ||
37 | { | ||
38 | struct snd_sst_alloc_mrfld alloc_param; | ||
39 | struct snd_sst_params *str_params; | ||
40 | struct snd_sst_tstamp fw_tstamp; | ||
41 | struct stream_info *str_info; | ||
42 | struct snd_sst_alloc_response *response; | ||
43 | unsigned int str_id, pipe_id, task_id; | ||
44 | int i, num_ch, ret = 0; | ||
45 | void *data = NULL; | ||
46 | |||
47 | dev_dbg(sst_drv_ctx->dev, "Enter\n"); | ||
48 | BUG_ON(!params); | ||
49 | |||
50 | str_params = (struct snd_sst_params *)params; | ||
51 | memset(&alloc_param, 0, sizeof(alloc_param)); | ||
52 | alloc_param.operation = str_params->ops; | ||
53 | alloc_param.codec_type = str_params->codec; | ||
54 | alloc_param.sg_count = str_params->aparams.sg_count; | ||
55 | alloc_param.ring_buf_info[0].addr = | ||
56 | str_params->aparams.ring_buf_info[0].addr; | ||
57 | alloc_param.ring_buf_info[0].size = | ||
58 | str_params->aparams.ring_buf_info[0].size; | ||
59 | alloc_param.frag_size = str_params->aparams.frag_size; | ||
60 | |||
61 | memcpy(&alloc_param.codec_params, &str_params->sparams, | ||
62 | sizeof(struct snd_sst_stream_params)); | ||
63 | |||
64 | /* | ||
65 | * fill channel map params for multichannel support. | ||
66 | * Ideally channel map should be received from upper layers | ||
67 | * for multichannel support. | ||
68 | * Currently hardcoding as per FW reqm. | ||
69 | */ | ||
70 | num_ch = sst_get_num_channel(str_params); | ||
71 | for (i = 0; i < 8; i++) { | ||
72 | if (i < num_ch) | ||
73 | alloc_param.codec_params.uc.pcm_params.channel_map[i] = i; | ||
74 | else | ||
75 | alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF; | ||
76 | } | ||
77 | |||
78 | str_id = str_params->stream_id; | ||
79 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
80 | if (str_info == NULL) { | ||
81 | dev_err(sst_drv_ctx->dev, "get stream info returned null\n"); | ||
82 | return -EINVAL; | ||
83 | } | ||
84 | |||
85 | pipe_id = str_params->device_type; | ||
86 | task_id = str_params->task; | ||
87 | sst_drv_ctx->streams[str_id].pipe_id = pipe_id; | ||
88 | sst_drv_ctx->streams[str_id].task_id = task_id; | ||
89 | sst_drv_ctx->streams[str_id].num_ch = num_ch; | ||
90 | |||
91 | if (sst_drv_ctx->info.lpe_viewpt_rqd) | ||
92 | alloc_param.ts = sst_drv_ctx->info.mailbox_start + | ||
93 | sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); | ||
94 | else | ||
95 | alloc_param.ts = sst_drv_ctx->mailbox_add + | ||
96 | sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); | ||
97 | |||
98 | dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n", | ||
99 | alloc_param.ts); | ||
100 | dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n", | ||
101 | pipe_id, task_id); | ||
102 | |||
103 | /* allocate device type context */ | ||
104 | sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, | ||
105 | str_id, alloc_param.operation, 0); | ||
106 | |||
107 | dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n", | ||
108 | str_id, pipe_id); | ||
109 | ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD, | ||
110 | IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param), | ||
111 | &alloc_param, data, true, true, false, true); | ||
112 | |||
113 | if (ret < 0) { | ||
114 | dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret); | ||
115 | /* alloc failed, so reset the state to uninit */ | ||
116 | str_info->status = STREAM_UN_INIT; | ||
117 | str_id = ret; | ||
118 | } else if (data) { | ||
119 | response = (struct snd_sst_alloc_response *)data; | ||
120 | ret = response->str_type.result; | ||
121 | if (!ret) | ||
122 | goto out; | ||
123 | dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret); | ||
124 | if (ret == SST_ERR_STREAM_IN_USE) { | ||
125 | dev_err(sst_drv_ctx->dev, | ||
126 | "FW not in clean state, send free for:%d\n", str_id); | ||
127 | sst_free_stream(sst_drv_ctx, str_id); | ||
128 | } | ||
129 | str_id = -ret; | ||
130 | } | ||
131 | out: | ||
132 | kfree(data); | ||
133 | return str_id; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * sst_start_stream - Send msg for a starting stream | ||
138 | * @str_id: stream ID | ||
139 | * | ||
140 | * This function is called by any function which wants to start | ||
141 | * a stream. | ||
142 | */ | ||
143 | int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
144 | { | ||
145 | int retval = 0; | ||
146 | struct stream_info *str_info; | ||
147 | u16 data = 0; | ||
148 | |||
149 | dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id); | ||
150 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
151 | if (!str_info) | ||
152 | return -EINVAL; | ||
153 | if (str_info->status != STREAM_RUNNING) | ||
154 | return -EBADRQC; | ||
155 | |||
156 | retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, | ||
157 | IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id, | ||
158 | sizeof(u16), &data, NULL, true, true, true, false); | ||
159 | |||
160 | return retval; | ||
161 | } | ||
162 | |||
163 | int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, | ||
164 | struct snd_sst_bytes_v2 *bytes) | ||
165 | { struct ipc_post *msg = NULL; | ||
166 | u32 length; | ||
167 | int pvt_id, ret = 0; | ||
168 | struct sst_block *block = NULL; | ||
169 | |||
170 | dev_dbg(sst_drv_ctx->dev, | ||
171 | "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n", | ||
172 | bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id, | ||
173 | bytes->pipe_id, bytes->len); | ||
174 | |||
175 | if (sst_create_ipc_msg(&msg, true)) | ||
176 | return -ENOMEM; | ||
177 | |||
178 | pvt_id = sst_assign_pvt_id(sst_drv_ctx); | ||
179 | sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg, | ||
180 | bytes->task_id, 1, pvt_id); | ||
181 | msg->mrfld_header.p.header_high.part.res_rqd = bytes->block; | ||
182 | length = bytes->len; | ||
183 | msg->mrfld_header.p.header_low_payload = length; | ||
184 | dev_dbg(sst_drv_ctx->dev, "length is %d\n", length); | ||
185 | memcpy(msg->mailbox_data, &bytes->bytes, bytes->len); | ||
186 | if (bytes->block) { | ||
187 | block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id); | ||
188 | if (block == NULL) { | ||
189 | kfree(msg); | ||
190 | ret = -ENOMEM; | ||
191 | goto out; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); | ||
196 | dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d", | ||
197 | msg->mrfld_header.p.header_low_payload); | ||
198 | |||
199 | if (bytes->block) { | ||
200 | ret = sst_wait_timeout(sst_drv_ctx, block); | ||
201 | if (ret) { | ||
202 | dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret); | ||
203 | sst_free_block(sst_drv_ctx, block); | ||
204 | goto out; | ||
205 | } | ||
206 | } | ||
207 | if (bytes->type == SND_SST_BYTES_GET) { | ||
208 | /* | ||
209 | * copy the reply and send back | ||
210 | * we need to update only sz and payload | ||
211 | */ | ||
212 | if (bytes->block) { | ||
213 | unsigned char *r = block->data; | ||
214 | |||
215 | dev_dbg(sst_drv_ctx->dev, "read back %d bytes", | ||
216 | bytes->len); | ||
217 | memcpy(bytes->bytes, r, bytes->len); | ||
218 | } | ||
219 | } | ||
220 | if (bytes->block) | ||
221 | sst_free_block(sst_drv_ctx, block); | ||
222 | out: | ||
223 | test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * sst_pause_stream - Send msg for a pausing stream | ||
229 | * @str_id: stream ID | ||
230 | * | ||
231 | * This function is called by any function which wants to pause | ||
232 | * an already running stream. | ||
233 | */ | ||
234 | int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
235 | { | ||
236 | int retval = 0; | ||
237 | struct stream_info *str_info; | ||
238 | |||
239 | dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id); | ||
240 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
241 | if (!str_info) | ||
242 | return -EINVAL; | ||
243 | if (str_info->status == STREAM_PAUSED) | ||
244 | return 0; | ||
245 | if (str_info->status == STREAM_RUNNING || | ||
246 | str_info->status == STREAM_INIT) { | ||
247 | if (str_info->prev == STREAM_UN_INIT) | ||
248 | return -EBADRQC; | ||
249 | |||
250 | retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, | ||
251 | IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id, | ||
252 | 0, NULL, NULL, true, true, false, true); | ||
253 | |||
254 | if (retval == 0) { | ||
255 | str_info->prev = str_info->status; | ||
256 | str_info->status = STREAM_PAUSED; | ||
257 | } else if (retval == SST_ERR_INVALID_STREAM_ID) { | ||
258 | retval = -EINVAL; | ||
259 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
260 | sst_clean_stream(str_info); | ||
261 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
262 | } | ||
263 | } else { | ||
264 | retval = -EBADRQC; | ||
265 | dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n "); | ||
266 | } | ||
267 | |||
268 | return retval; | ||
269 | } | ||
270 | |||
271 | /** | ||
272 | * sst_resume_stream - Send msg for resuming stream | ||
273 | * @str_id: stream ID | ||
274 | * | ||
275 | * This function is called by any function which wants to resume | ||
276 | * an already paused stream. | ||
277 | */ | ||
278 | int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
279 | { | ||
280 | int retval = 0; | ||
281 | struct stream_info *str_info; | ||
282 | |||
283 | dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id); | ||
284 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
285 | if (!str_info) | ||
286 | return -EINVAL; | ||
287 | if (str_info->status == STREAM_RUNNING) | ||
288 | return 0; | ||
289 | if (str_info->status == STREAM_PAUSED) { | ||
290 | retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, | ||
291 | IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD, | ||
292 | str_info->pipe_id, 0, NULL, NULL, | ||
293 | true, true, false, true); | ||
294 | |||
295 | if (!retval) { | ||
296 | if (str_info->prev == STREAM_RUNNING) | ||
297 | str_info->status = STREAM_RUNNING; | ||
298 | else | ||
299 | str_info->status = STREAM_INIT; | ||
300 | str_info->prev = STREAM_PAUSED; | ||
301 | } else if (retval == -SST_ERR_INVALID_STREAM_ID) { | ||
302 | retval = -EINVAL; | ||
303 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
304 | sst_clean_stream(str_info); | ||
305 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
306 | } | ||
307 | } else { | ||
308 | retval = -EBADRQC; | ||
309 | dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n"); | ||
310 | } | ||
311 | |||
312 | return retval; | ||
313 | } | ||
314 | |||
315 | |||
316 | /** | ||
317 | * sst_drop_stream - Send msg for stopping stream | ||
318 | * @str_id: stream ID | ||
319 | * | ||
320 | * This function is called by any function which wants to stop | ||
321 | * a stream. | ||
322 | */ | ||
323 | int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
324 | { | ||
325 | int retval = 0; | ||
326 | struct stream_info *str_info; | ||
327 | |||
328 | dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id); | ||
329 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
330 | if (!str_info) | ||
331 | return -EINVAL; | ||
332 | |||
333 | if (str_info->status != STREAM_UN_INIT) { | ||
334 | str_info->prev = STREAM_UN_INIT; | ||
335 | str_info->status = STREAM_INIT; | ||
336 | str_info->cumm_bytes = 0; | ||
337 | retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, | ||
338 | IPC_CMD, IPC_IA_DROP_STREAM_MRFLD, | ||
339 | str_info->pipe_id, 0, NULL, NULL, | ||
340 | true, true, true, false); | ||
341 | } else { | ||
342 | retval = -EBADRQC; | ||
343 | dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n", | ||
344 | str_info->status); | ||
345 | } | ||
346 | return retval; | ||
347 | } | ||
348 | |||
349 | /** | ||
350 | * sst_drain_stream - Send msg for draining stream | ||
351 | * @str_id: stream ID | ||
352 | * | ||
353 | * This function is called by any function which wants to drain | ||
354 | * a stream. | ||
355 | */ | ||
356 | int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx, | ||
357 | int str_id, bool partial_drain) | ||
358 | { | ||
359 | int retval = 0; | ||
360 | struct stream_info *str_info; | ||
361 | |||
362 | dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id); | ||
363 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
364 | if (!str_info) | ||
365 | return -EINVAL; | ||
366 | if (str_info->status != STREAM_RUNNING && | ||
367 | str_info->status != STREAM_INIT && | ||
368 | str_info->status != STREAM_PAUSED) { | ||
369 | dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n", | ||
370 | str_info->status); | ||
371 | return -EBADRQC; | ||
372 | } | ||
373 | |||
374 | retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, | ||
375 | IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id, | ||
376 | sizeof(u8), &partial_drain, NULL, true, true, false, false); | ||
377 | /* | ||
378 | * with new non blocked drain implementation in core we dont need to | ||
379 | * wait for respsonse, and need to only invoke callback for drain | ||
380 | * complete | ||
381 | */ | ||
382 | |||
383 | return retval; | ||
384 | } | ||
385 | |||
386 | /** | ||
387 | * sst_free_stream - Frees a stream | ||
388 | * @str_id: stream ID | ||
389 | * | ||
390 | * This function is called by any function which wants to free | ||
391 | * a stream. | ||
392 | */ | ||
393 | int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) | ||
394 | { | ||
395 | int retval = 0; | ||
396 | struct stream_info *str_info; | ||
397 | struct intel_sst_ops *ops; | ||
398 | |||
399 | dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id); | ||
400 | |||
401 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
402 | if (sst_drv_ctx->sst_state == SST_RESET) { | ||
403 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
404 | return -ENODEV; | ||
405 | } | ||
406 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
407 | str_info = get_stream_info(sst_drv_ctx, str_id); | ||
408 | if (!str_info) | ||
409 | return -EINVAL; | ||
410 | ops = sst_drv_ctx->ops; | ||
411 | |||
412 | mutex_lock(&str_info->lock); | ||
413 | if (str_info->status != STREAM_UN_INIT) { | ||
414 | str_info->prev = str_info->status; | ||
415 | str_info->status = STREAM_UN_INIT; | ||
416 | mutex_unlock(&str_info->lock); | ||
417 | |||
418 | dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n", | ||
419 | str_id, str_info->pipe_id); | ||
420 | retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, | ||
421 | IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0, | ||
422 | NULL, NULL, true, true, false, true); | ||
423 | |||
424 | dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n", | ||
425 | retval); | ||
426 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
427 | sst_clean_stream(str_info); | ||
428 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
429 | dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n"); | ||
430 | } else { | ||
431 | mutex_unlock(&str_info->lock); | ||
432 | retval = -EBADRQC; | ||
433 | dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n"); | ||
434 | } | ||
435 | |||
436 | return retval; | ||
437 | } | ||