Commit | Line | Data |
---|---|---|
fffa1cca VK |
1 | /* |
2 | * intel_sst_interface.c - Intel SST Driver for audio engine | |
3 | * | |
4 | * Copyright (C) 2008-10 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 | * Jeeja KP <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 | * You should have received a copy of the GNU General Public License along | |
21 | * with this program; if not, write to the Free Software Foundation, Inc., | |
22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
23 | * | |
24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
25 | * This driver exposes the audio engine functionalities to the ALSA | |
26 | * and middleware. | |
27 | * Upper layer interfaces (MAD driver, MMF) to SST driver | |
28 | */ | |
29 | ||
d0f40c50 JP |
30 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
31 | ||
fffa1cca VK |
32 | #include <linux/pci.h> |
33 | #include <linux/fs.h> | |
34 | #include <linux/uio.h> | |
35 | #include <linux/aio.h> | |
36 | #include <linux/uaccess.h> | |
37 | #include <linux/firmware.h> | |
964c6975 | 38 | #include <linux/pm_runtime.h> |
fffa1cca | 39 | #include <linux/ioctl.h> |
fffa1cca VK |
40 | #ifdef CONFIG_MRST_RAR_HANDLER |
41 | #include <linux/rar_register.h> | |
42 | #include "../../../drivers/staging/memrar/memrar.h" | |
43 | #endif | |
44 | #include "intel_sst.h" | |
45 | #include "intel_sst_ioctl.h" | |
46 | #include "intel_sst_fw_ipc.h" | |
47 | #include "intel_sst_common.h" | |
48 | ||
49 | #define AM_MODULE 1 | |
50 | #define STREAM_MODULE 0 | |
51 | ||
52 | ||
53 | /** | |
54 | * intel_sst_check_device - checks SST device | |
55 | * | |
56 | * This utility function checks the state of SST device and downlaods FW if | |
57 | * not done, or resumes the device if suspended | |
58 | */ | |
59 | ||
60 | static int intel_sst_check_device(void) | |
61 | { | |
62 | int retval = 0; | |
63 | if (sst_drv_ctx->pmic_state != SND_MAD_INIT_DONE) { | |
d0f40c50 | 64 | pr_warn("Sound card not available\n"); |
fffa1cca VK |
65 | return -EIO; |
66 | } | |
67 | if (sst_drv_ctx->sst_state == SST_SUSPENDED) { | |
d0f40c50 | 68 | pr_debug("Resuming from Suspended state\n"); |
fffa1cca VK |
69 | retval = intel_sst_resume(sst_drv_ctx->pci); |
70 | if (retval) { | |
d0f40c50 | 71 | pr_debug("Resume Failed= %#x,abort\n", retval); |
fffa1cca VK |
72 | return retval; |
73 | } | |
74 | } | |
75 | ||
76 | if (sst_drv_ctx->sst_state == SST_UN_INIT) { | |
77 | /* FW is not downloaded */ | |
78 | retval = sst_download_fw(); | |
79 | if (retval) | |
80 | return -ENODEV; | |
81 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | |
82 | retval = sst_drv_ctx->rx_time_slot_status; | |
83 | if (retval != RX_TIMESLOT_UNINIT | |
84 | && sst_drv_ctx->pmic_vendor != SND_NC) | |
85 | sst_enable_rx_timeslot(retval); | |
86 | } | |
87 | } | |
88 | return 0; | |
89 | } | |
90 | ||
91 | /** | |
92 | * intel_sst_open - opens a handle to driver | |
93 | * | |
94 | * @i_node: inode structure | |
95 | * @file_ptr:pointer to file | |
96 | * | |
97 | * This function is called by OS when a user space component | |
98 | * tries to get a driver handle. Only one handle at a time | |
99 | * will be allowed | |
100 | */ | |
101 | int intel_sst_open(struct inode *i_node, struct file *file_ptr) | |
102 | { | |
6f6ffec1 | 103 | unsigned int retval; |
fffa1cca VK |
104 | |
105 | mutex_lock(&sst_drv_ctx->stream_lock); | |
964c6975 | 106 | pm_runtime_get_sync(&sst_drv_ctx->pci->dev); |
6f6ffec1 VK |
107 | retval = intel_sst_check_device(); |
108 | if (retval) { | |
964c6975 | 109 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
6f6ffec1 VK |
110 | mutex_unlock(&sst_drv_ctx->stream_lock); |
111 | return retval; | |
112 | } | |
113 | ||
fffa1cca VK |
114 | if (sst_drv_ctx->encoded_cnt < MAX_ENC_STREAM) { |
115 | struct ioctl_pvt_data *data = | |
116 | kzalloc(sizeof(struct ioctl_pvt_data), GFP_KERNEL); | |
117 | if (!data) { | |
964c6975 | 118 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
fffa1cca VK |
119 | mutex_unlock(&sst_drv_ctx->stream_lock); |
120 | return -ENOMEM; | |
121 | } | |
122 | ||
123 | sst_drv_ctx->encoded_cnt++; | |
124 | mutex_unlock(&sst_drv_ctx->stream_lock); | |
125 | data->pvt_id = sst_assign_pvt_id(sst_drv_ctx); | |
126 | data->str_id = 0; | |
127 | file_ptr->private_data = (void *)data; | |
d0f40c50 | 128 | pr_debug("pvt_id handle = %d!\n", data->pvt_id); |
fffa1cca VK |
129 | } else { |
130 | retval = -EUSERS; | |
964c6975 | 131 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
fffa1cca VK |
132 | mutex_unlock(&sst_drv_ctx->stream_lock); |
133 | } | |
134 | return retval; | |
135 | } | |
136 | ||
137 | /** | |
138 | * intel_sst_open_cntrl - opens a handle to driver | |
139 | * | |
140 | * @i_node: inode structure | |
141 | * @file_ptr:pointer to file | |
142 | * | |
143 | * This function is called by OS when a user space component | |
144 | * tries to get a driver handle to /dev/intel_sst_control. | |
145 | * Only one handle at a time will be allowed | |
146 | * This is for control operations only | |
147 | */ | |
148 | int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr) | |
149 | { | |
6f6ffec1 | 150 | unsigned int retval; |
fffa1cca VK |
151 | |
152 | /* audio manager open */ | |
153 | mutex_lock(&sst_drv_ctx->stream_lock); | |
964c6975 | 154 | pm_runtime_get_sync(&sst_drv_ctx->pci->dev); |
6f6ffec1 VK |
155 | retval = intel_sst_check_device(); |
156 | if (retval) { | |
964c6975 | 157 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
6f6ffec1 VK |
158 | mutex_unlock(&sst_drv_ctx->stream_lock); |
159 | return retval; | |
160 | } | |
161 | ||
fffa1cca VK |
162 | if (sst_drv_ctx->am_cnt < MAX_AM_HANDLES) { |
163 | sst_drv_ctx->am_cnt++; | |
d0f40c50 | 164 | pr_debug("AM handle opened...\n"); |
fffa1cca | 165 | file_ptr->private_data = NULL; |
964c6975 | 166 | } else { |
fffa1cca | 167 | retval = -EACCES; |
964c6975 VK |
168 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
169 | } | |
fffa1cca VK |
170 | |
171 | mutex_unlock(&sst_drv_ctx->stream_lock); | |
172 | return retval; | |
173 | } | |
174 | ||
175 | /** | |
176 | * intel_sst_release - releases a handle to driver | |
177 | * | |
178 | * @i_node: inode structure | |
179 | * @file_ptr: pointer to file | |
180 | * | |
181 | * This function is called by OS when a user space component | |
182 | * tries to release a driver handle. | |
183 | */ | |
184 | int intel_sst_release(struct inode *i_node, struct file *file_ptr) | |
185 | { | |
186 | struct ioctl_pvt_data *data = file_ptr->private_data; | |
187 | ||
d0f40c50 | 188 | pr_debug("Release called, closing app handle\n"); |
fffa1cca VK |
189 | mutex_lock(&sst_drv_ctx->stream_lock); |
190 | sst_drv_ctx->encoded_cnt--; | |
191 | sst_drv_ctx->stream_cnt--; | |
964c6975 | 192 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
fffa1cca VK |
193 | mutex_unlock(&sst_drv_ctx->stream_lock); |
194 | free_stream_context(data->str_id); | |
195 | kfree(data); | |
196 | return 0; | |
197 | } | |
198 | ||
199 | int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr) | |
200 | { | |
201 | /* audio manager close */ | |
202 | mutex_lock(&sst_drv_ctx->stream_lock); | |
203 | sst_drv_ctx->am_cnt--; | |
964c6975 | 204 | pm_runtime_put(&sst_drv_ctx->pci->dev); |
fffa1cca | 205 | mutex_unlock(&sst_drv_ctx->stream_lock); |
d0f40c50 | 206 | pr_debug("AM handle closed\n"); |
fffa1cca VK |
207 | return 0; |
208 | } | |
209 | ||
210 | /** | |
211 | * intel_sst_mmap - mmaps a kernel buffer to user space for copying data | |
212 | * | |
213 | * @vma: vm area structure instance | |
214 | * @file_ptr: pointer to file | |
215 | * | |
216 | * This function is called by OS when a user space component | |
217 | * tries to get mmap memory from driver | |
218 | */ | |
219 | int intel_sst_mmap(struct file *file_ptr, struct vm_area_struct *vma) | |
220 | { | |
221 | int retval, length; | |
222 | struct ioctl_pvt_data *data = | |
223 | (struct ioctl_pvt_data *)file_ptr->private_data; | |
224 | int str_id = data->str_id; | |
225 | void *mem_area; | |
226 | ||
227 | retval = sst_validate_strid(str_id); | |
228 | if (retval) | |
229 | return -EINVAL; | |
230 | ||
231 | length = vma->vm_end - vma->vm_start; | |
d0f40c50 | 232 | pr_debug("called for stream %d length 0x%x\n", str_id, length); |
fffa1cca VK |
233 | |
234 | if (length > sst_drv_ctx->mmap_len) | |
235 | return -ENOMEM; | |
236 | if (!sst_drv_ctx->mmap_mem) | |
237 | return -EIO; | |
238 | ||
25985edc | 239 | /* round it up to the page boundary */ |
fffa1cca VK |
240 | /*mem_area = (void *)((((unsigned long)sst_drv_ctx->mmap_mem) |
241 | + PAGE_SIZE - 1) & PAGE_MASK);*/ | |
242 | mem_area = (void *) PAGE_ALIGN((unsigned int) sst_drv_ctx->mmap_mem); | |
243 | ||
244 | /* map the whole physically contiguous area in one piece */ | |
245 | retval = remap_pfn_range(vma, | |
246 | vma->vm_start, | |
247 | virt_to_phys((void *)mem_area) >> PAGE_SHIFT, | |
248 | length, | |
249 | vma->vm_page_prot); | |
250 | if (retval) | |
251 | sst_drv_ctx->streams[str_id].mmapped = false; | |
252 | else | |
253 | sst_drv_ctx->streams[str_id].mmapped = true; | |
254 | ||
d0f40c50 | 255 | pr_debug("mmap ret 0x%x\n", retval); |
fffa1cca VK |
256 | return retval; |
257 | } | |
258 | ||
259 | /* sets mmap data buffers to play/capture*/ | |
260 | static int intel_sst_mmap_play_capture(u32 str_id, | |
261 | struct snd_sst_mmap_buffs *mmap_buf) | |
262 | { | |
263 | struct sst_stream_bufs *bufs; | |
264 | int retval, i; | |
265 | struct stream_info *stream; | |
266 | struct snd_sst_mmap_buff_entry *buf_entry; | |
08da782b | 267 | struct snd_sst_mmap_buff_entry *tmp_buf; |
fffa1cca | 268 | |
d0f40c50 | 269 | pr_debug("called for str_id %d\n", str_id); |
fffa1cca VK |
270 | retval = sst_validate_strid(str_id); |
271 | if (retval) | |
272 | return -EINVAL; | |
fffa1cca VK |
273 | |
274 | stream = &sst_drv_ctx->streams[str_id]; | |
275 | if (stream->mmapped != true) | |
276 | return -EIO; | |
277 | ||
278 | if (stream->status == STREAM_UN_INIT || | |
279 | stream->status == STREAM_DECODE) { | |
280 | return -EBADRQC; | |
281 | } | |
282 | stream->curr_bytes = 0; | |
283 | stream->cumm_bytes = 0; | |
284 | ||
08da782b DC |
285 | tmp_buf = kcalloc(mmap_buf->entries, sizeof(*tmp_buf), GFP_KERNEL); |
286 | if (!tmp_buf) | |
287 | return -ENOMEM; | |
288 | if (copy_from_user(tmp_buf, (void __user *)mmap_buf->buff, | |
289 | mmap_buf->entries * sizeof(*tmp_buf))) { | |
290 | retval = -EFAULT; | |
291 | goto out_free; | |
292 | } | |
293 | ||
d0f40c50 | 294 | pr_debug("new buffers count %d status %d\n", |
fffa1cca | 295 | mmap_buf->entries, stream->status); |
08da782b | 296 | buf_entry = tmp_buf; |
fffa1cca | 297 | for (i = 0; i < mmap_buf->entries; i++) { |
fffa1cca | 298 | bufs = kzalloc(sizeof(*bufs), GFP_KERNEL); |
08da782b DC |
299 | if (!bufs) { |
300 | retval = -ENOMEM; | |
301 | goto out_free; | |
302 | } | |
fffa1cca VK |
303 | bufs->size = buf_entry->size; |
304 | bufs->offset = buf_entry->offset; | |
305 | bufs->addr = sst_drv_ctx->mmap_mem; | |
306 | bufs->in_use = false; | |
307 | buf_entry++; | |
308 | /* locking here */ | |
309 | mutex_lock(&stream->lock); | |
310 | list_add_tail(&bufs->node, &stream->bufs); | |
311 | mutex_unlock(&stream->lock); | |
312 | } | |
313 | ||
314 | mutex_lock(&stream->lock); | |
315 | stream->data_blk.condition = false; | |
316 | stream->data_blk.ret_code = 0; | |
317 | if (stream->status == STREAM_INIT && | |
318 | stream->prev != STREAM_UN_INIT && | |
319 | stream->need_draining != true) { | |
320 | stream->prev = stream->status; | |
321 | stream->status = STREAM_RUNNING; | |
322 | if (stream->ops == STREAM_OPS_PLAYBACK) { | |
323 | if (sst_play_frame(str_id) < 0) { | |
d0f40c50 | 324 | pr_warn("play frames fail\n"); |
fffa1cca | 325 | mutex_unlock(&stream->lock); |
08da782b DC |
326 | retval = -EIO; |
327 | goto out_free; | |
fffa1cca VK |
328 | } |
329 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | |
330 | if (sst_capture_frame(str_id) < 0) { | |
d0f40c50 | 331 | pr_warn("capture frame fail\n"); |
fffa1cca | 332 | mutex_unlock(&stream->lock); |
08da782b DC |
333 | retval = -EIO; |
334 | goto out_free; | |
fffa1cca VK |
335 | } |
336 | } | |
337 | } | |
338 | mutex_unlock(&stream->lock); | |
339 | /* Block the call for reply */ | |
340 | if (!list_empty(&stream->bufs)) { | |
341 | stream->data_blk.on = true; | |
342 | retval = sst_wait_interruptible(sst_drv_ctx, | |
343 | &stream->data_blk); | |
344 | } | |
345 | ||
346 | if (retval >= 0) | |
347 | retval = stream->cumm_bytes; | |
d0f40c50 | 348 | pr_debug("end of play/rec ioctl bytes = %d!!\n", retval); |
08da782b DC |
349 | |
350 | out_free: | |
351 | kfree(tmp_buf); | |
fffa1cca VK |
352 | return retval; |
353 | } | |
354 | ||
355 | /*sets user data buffers to play/capture*/ | |
356 | static int intel_sst_play_capture(struct stream_info *stream, int str_id) | |
357 | { | |
358 | int retval; | |
359 | ||
360 | stream->data_blk.ret_code = 0; | |
361 | stream->data_blk.on = true; | |
362 | stream->data_blk.condition = false; | |
363 | ||
364 | mutex_lock(&stream->lock); | |
365 | if (stream->status == STREAM_INIT && stream->prev != STREAM_UN_INIT) { | |
366 | /* stream is started */ | |
367 | stream->prev = stream->status; | |
368 | stream->status = STREAM_RUNNING; | |
369 | } | |
370 | ||
371 | if (stream->status == STREAM_INIT && stream->prev == STREAM_UN_INIT) { | |
372 | /* stream is not started yet */ | |
d0f40c50 | 373 | pr_debug("Stream isn't in started state %d, prev %d\n", |
fffa1cca VK |
374 | stream->status, stream->prev); |
375 | } else if ((stream->status == STREAM_RUNNING || | |
376 | stream->status == STREAM_PAUSED) && | |
377 | stream->need_draining != true) { | |
378 | /* stream is started */ | |
379 | if (stream->ops == STREAM_OPS_PLAYBACK || | |
380 | stream->ops == STREAM_OPS_PLAYBACK_DRM) { | |
381 | if (sst_play_frame(str_id) < 0) { | |
d0f40c50 | 382 | pr_warn("play frames failed\n"); |
fffa1cca VK |
383 | mutex_unlock(&stream->lock); |
384 | return -EIO; | |
385 | } | |
386 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | |
387 | if (sst_capture_frame(str_id) < 0) { | |
d0f40c50 | 388 | pr_warn("capture frames failed\n"); |
fffa1cca VK |
389 | mutex_unlock(&stream->lock); |
390 | return -EIO; | |
391 | } | |
392 | } | |
393 | } else { | |
394 | mutex_unlock(&stream->lock); | |
395 | return -EIO; | |
396 | } | |
397 | mutex_unlock(&stream->lock); | |
398 | /* Block the call for reply */ | |
399 | ||
400 | retval = sst_wait_interruptible(sst_drv_ctx, &stream->data_blk); | |
401 | if (retval) { | |
402 | stream->status = STREAM_INIT; | |
d0f40c50 | 403 | pr_debug("wait returned error...\n"); |
fffa1cca VK |
404 | } |
405 | return retval; | |
406 | } | |
407 | ||
408 | /* fills kernel list with buffer addresses for SST DSP driver to process*/ | |
409 | static int snd_sst_fill_kernel_list(struct stream_info *stream, | |
410 | const struct iovec *iovec, unsigned long nr_segs, | |
411 | struct list_head *copy_to_list) | |
412 | { | |
413 | struct sst_stream_bufs *stream_bufs; | |
388b2b97 | 414 | unsigned long index, mmap_len; |
4fc718a4 | 415 | unsigned char __user *bufp; |
fffa1cca VK |
416 | unsigned long size, copied_size; |
417 | int retval = 0, add_to_list = 0; | |
418 | static int sent_offset; | |
419 | static unsigned long sent_index; | |
420 | ||
421 | stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); | |
422 | if (!stream_bufs) | |
423 | return -ENOMEM; | |
424 | stream_bufs->addr = sst_drv_ctx->mmap_mem; | |
425 | #ifdef CONFIG_MRST_RAR_HANDLER | |
426 | if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { | |
427 | for (index = stream->sg_index; index < nr_segs; index++) { | |
428 | __u32 rar_handle; | |
429 | struct sst_stream_bufs *stream_bufs = | |
430 | kzalloc(sizeof(*stream_bufs), GFP_KERNEL); | |
431 | ||
432 | stream->sg_index = index; | |
433 | if (!stream_bufs) | |
434 | return -ENOMEM; | |
388b2b97 DC |
435 | if (copy_from_user((void *) &rar_handle, |
436 | iovec[index].iov_base, | |
437 | sizeof(__u32))) | |
fffa1cca VK |
438 | return -EFAULT; |
439 | stream_bufs->addr = (char *)rar_handle; | |
440 | stream_bufs->in_use = false; | |
441 | stream_bufs->size = iovec[0].iov_len; | |
442 | /* locking here */ | |
443 | mutex_lock(&stream->lock); | |
444 | list_add_tail(&stream_bufs->node, &stream->bufs); | |
445 | mutex_unlock(&stream->lock); | |
446 | } | |
447 | stream->sg_index = index; | |
448 | return retval; | |
449 | } | |
450 | #endif | |
451 | mmap_len = sst_drv_ctx->mmap_len; | |
452 | stream_bufs->addr = sst_drv_ctx->mmap_mem; | |
453 | bufp = stream->cur_ptr; | |
454 | ||
455 | copied_size = 0; | |
456 | ||
457 | if (!stream->sg_index) | |
458 | sent_index = sent_offset = 0; | |
459 | ||
460 | for (index = stream->sg_index; index < nr_segs; index++) { | |
461 | stream->sg_index = index; | |
462 | if (!stream->cur_ptr) | |
463 | bufp = iovec[index].iov_base; | |
464 | ||
465 | size = ((unsigned long)iovec[index].iov_base | |
466 | + iovec[index].iov_len) - (unsigned long) bufp; | |
467 | ||
468 | if ((copied_size + size) > mmap_len) | |
469 | size = mmap_len - copied_size; | |
470 | ||
471 | ||
472 | if (stream->ops == STREAM_OPS_PLAYBACK) { | |
388b2b97 DC |
473 | if (copy_from_user((void *) |
474 | (stream_bufs->addr + copied_size), | |
475 | bufp, size)) { | |
fffa1cca VK |
476 | /* Clean up the list and return error code */ |
477 | retval = -EFAULT; | |
478 | break; | |
479 | } | |
480 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | |
481 | struct snd_sst_user_cap_list *entry = | |
482 | kzalloc(sizeof(*entry), GFP_KERNEL); | |
483 | ||
484 | if (!entry) { | |
485 | kfree(stream_bufs); | |
486 | return -ENOMEM; | |
487 | } | |
488 | entry->iov_index = index; | |
489 | entry->iov_offset = (unsigned long) bufp - | |
490 | (unsigned long)iovec[index].iov_base; | |
491 | entry->offset = copied_size; | |
492 | entry->size = size; | |
493 | list_add_tail(&entry->node, copy_to_list); | |
494 | } | |
495 | ||
496 | stream->cur_ptr = bufp + size; | |
497 | ||
498 | if (((unsigned long)iovec[index].iov_base | |
499 | + iovec[index].iov_len) < | |
500 | ((unsigned long)iovec[index].iov_base)) { | |
d0f40c50 | 501 | pr_debug("Buffer overflows\n"); |
fffa1cca VK |
502 | kfree(stream_bufs); |
503 | return -EINVAL; | |
504 | } | |
505 | ||
506 | if (((unsigned long)iovec[index].iov_base | |
507 | + iovec[index].iov_len) == | |
508 | (unsigned long)stream->cur_ptr) { | |
509 | stream->cur_ptr = NULL; | |
510 | stream->sg_index++; | |
511 | } | |
512 | ||
513 | copied_size += size; | |
d0f40c50 | 514 | pr_debug("copied_size - %lx\n", copied_size); |
fffa1cca VK |
515 | if ((copied_size >= mmap_len) || |
516 | (stream->sg_index == nr_segs)) { | |
517 | add_to_list = 1; | |
518 | } | |
519 | ||
520 | if (add_to_list) { | |
521 | stream_bufs->in_use = false; | |
522 | stream_bufs->size = copied_size; | |
523 | /* locking here */ | |
524 | mutex_lock(&stream->lock); | |
525 | list_add_tail(&stream_bufs->node, &stream->bufs); | |
526 | mutex_unlock(&stream->lock); | |
527 | break; | |
528 | } | |
529 | } | |
530 | return retval; | |
531 | } | |
532 | ||
533 | /* This function copies the captured data returned from SST DSP engine | |
534 | * to the user buffers*/ | |
535 | static int snd_sst_copy_userbuf_capture(struct stream_info *stream, | |
536 | const struct iovec *iovec, | |
537 | struct list_head *copy_to_list) | |
538 | { | |
539 | struct snd_sst_user_cap_list *entry, *_entry; | |
540 | struct sst_stream_bufs *kbufs = NULL, *_kbufs; | |
541 | int retval = 0; | |
fffa1cca VK |
542 | |
543 | /* copy sent buffers */ | |
d0f40c50 | 544 | pr_debug("capture stream copying to user now...\n"); |
fffa1cca VK |
545 | list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { |
546 | if (kbufs->in_use == true) { | |
547 | /* copy to user */ | |
548 | list_for_each_entry_safe(entry, _entry, | |
549 | copy_to_list, node) { | |
4fc718a4 | 550 | if (copy_to_user(iovec[entry->iov_index].iov_base + entry->iov_offset, |
388b2b97 DC |
551 | kbufs->addr + entry->offset, |
552 | entry->size)) { | |
fffa1cca VK |
553 | /* Clean up the list and return error */ |
554 | retval = -EFAULT; | |
555 | break; | |
556 | } | |
557 | list_del(&entry->node); | |
558 | kfree(entry); | |
559 | } | |
560 | } | |
561 | } | |
d0f40c50 | 562 | pr_debug("end of cap copy\n"); |
fffa1cca VK |
563 | return retval; |
564 | } | |
565 | ||
566 | /* | |
567 | * snd_sst_userbufs_play_cap - constructs the list from user buffers | |
568 | * | |
569 | * @iovec:pointer to iovec structure | |
570 | * @nr_segs:number entries in the iovec structure | |
571 | * @str_id:stream id | |
572 | * @stream:pointer to stream_info structure | |
573 | * | |
574 | * This function will traverse the user list and copy the data to the kernel | |
575 | * space buffers. | |
576 | */ | |
577 | static int snd_sst_userbufs_play_cap(const struct iovec *iovec, | |
578 | unsigned long nr_segs, unsigned int str_id, | |
579 | struct stream_info *stream) | |
580 | { | |
581 | int retval; | |
582 | LIST_HEAD(copy_to_list); | |
583 | ||
584 | ||
585 | retval = snd_sst_fill_kernel_list(stream, iovec, nr_segs, | |
586 | ©_to_list); | |
587 | ||
588 | retval = intel_sst_play_capture(stream, str_id); | |
589 | if (retval < 0) | |
590 | return retval; | |
591 | ||
592 | if (stream->ops == STREAM_OPS_CAPTURE) { | |
593 | retval = snd_sst_copy_userbuf_capture(stream, iovec, | |
594 | ©_to_list); | |
595 | } | |
596 | return retval; | |
597 | } | |
598 | ||
599 | /* This function is common function across read/write | |
600 | for user buffers called from system calls*/ | |
601 | static int intel_sst_read_write(unsigned int str_id, char __user *buf, | |
602 | size_t count) | |
603 | { | |
604 | int retval; | |
605 | struct stream_info *stream; | |
606 | struct iovec iovec; | |
607 | unsigned long nr_segs; | |
608 | ||
609 | retval = sst_validate_strid(str_id); | |
610 | if (retval) | |
611 | return -EINVAL; | |
612 | stream = &sst_drv_ctx->streams[str_id]; | |
613 | if (stream->mmapped == true) { | |
d0f40c50 | 614 | pr_warn("user write and stream is mapped\n"); |
fffa1cca VK |
615 | return -EIO; |
616 | } | |
617 | if (!count) | |
618 | return -EINVAL; | |
619 | stream->curr_bytes = 0; | |
620 | stream->cumm_bytes = 0; | |
621 | /* copy user buf details */ | |
d0f40c50 | 622 | pr_debug("new buffers %p, copy size %d, status %d\n" , |
fffa1cca VK |
623 | buf, (int) count, (int) stream->status); |
624 | ||
625 | stream->buf_type = SST_BUF_USER_STATIC; | |
4fc718a4 | 626 | iovec.iov_base = buf; |
fffa1cca VK |
627 | iovec.iov_len = count; |
628 | nr_segs = 1; | |
629 | ||
630 | do { | |
631 | retval = snd_sst_userbufs_play_cap( | |
632 | &iovec, nr_segs, str_id, stream); | |
633 | if (retval < 0) | |
634 | break; | |
635 | ||
636 | } while (stream->sg_index < nr_segs); | |
637 | ||
638 | stream->sg_index = 0; | |
639 | stream->cur_ptr = NULL; | |
640 | if (retval >= 0) | |
641 | retval = stream->cumm_bytes; | |
d0f40c50 | 642 | pr_debug("end of play/rec bytes = %d!!\n", retval); |
fffa1cca VK |
643 | return retval; |
644 | } | |
645 | ||
646 | /*** | |
647 | * intel_sst_write - This function is called when user tries to play out data | |
648 | * | |
649 | * @file_ptr:pointer to file | |
650 | * @buf:user buffer to be played out | |
651 | * @count:size of tthe buffer | |
652 | * @offset:offset to start from | |
653 | * | |
654 | * writes the encoded data into DSP | |
655 | */ | |
656 | int intel_sst_write(struct file *file_ptr, const char __user *buf, | |
657 | size_t count, loff_t *offset) | |
658 | { | |
659 | struct ioctl_pvt_data *data = file_ptr->private_data; | |
660 | int str_id = data->str_id; | |
661 | struct stream_info *stream = &sst_drv_ctx->streams[str_id]; | |
662 | ||
d0f40c50 | 663 | pr_debug("called for %d\n", str_id); |
fffa1cca VK |
664 | if (stream->status == STREAM_UN_INIT || |
665 | stream->status == STREAM_DECODE) { | |
666 | return -EBADRQC; | |
667 | } | |
668 | return intel_sst_read_write(str_id, (char __user *)buf, count); | |
669 | } | |
670 | ||
671 | /* | |
672 | * intel_sst_aio_write - write buffers | |
673 | * | |
674 | * @kiocb:pointer to a structure containing file pointer | |
675 | * @iov:list of user buffer to be played out | |
676 | * @nr_segs:number of entries | |
677 | * @offset:offset to start from | |
678 | * | |
679 | * This function is called when user tries to play out multiple data buffers | |
680 | */ | |
681 | ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov, | |
682 | unsigned long nr_segs, loff_t offset) | |
683 | { | |
684 | int retval; | |
685 | struct ioctl_pvt_data *data = kiocb->ki_filp->private_data; | |
686 | int str_id = data->str_id; | |
687 | struct stream_info *stream; | |
688 | ||
d0f40c50 | 689 | pr_debug("entry - %ld\n", nr_segs); |
fffa1cca VK |
690 | |
691 | if (is_sync_kiocb(kiocb) == false) | |
692 | return -EINVAL; | |
693 | ||
d0f40c50 | 694 | pr_debug("called for str_id %d\n", str_id); |
fffa1cca VK |
695 | retval = sst_validate_strid(str_id); |
696 | if (retval) | |
697 | return -EINVAL; | |
698 | stream = &sst_drv_ctx->streams[str_id]; | |
699 | if (stream->mmapped == true) | |
700 | return -EIO; | |
701 | if (stream->status == STREAM_UN_INIT || | |
702 | stream->status == STREAM_DECODE) { | |
703 | return -EBADRQC; | |
704 | } | |
705 | stream->curr_bytes = 0; | |
706 | stream->cumm_bytes = 0; | |
d0f40c50 | 707 | pr_debug("new segs %ld, offset %d, status %d\n" , |
fffa1cca VK |
708 | nr_segs, (int) offset, (int) stream->status); |
709 | stream->buf_type = SST_BUF_USER_STATIC; | |
710 | do { | |
711 | retval = snd_sst_userbufs_play_cap(iov, nr_segs, | |
712 | str_id, stream); | |
713 | if (retval < 0) | |
714 | break; | |
715 | ||
716 | } while (stream->sg_index < nr_segs); | |
717 | ||
718 | stream->sg_index = 0; | |
719 | stream->cur_ptr = NULL; | |
720 | if (retval >= 0) | |
721 | retval = stream->cumm_bytes; | |
d0f40c50 | 722 | pr_debug("end of play/rec bytes = %d!!\n", retval); |
fffa1cca VK |
723 | return retval; |
724 | } | |
725 | ||
726 | /* | |
727 | * intel_sst_read - read the encoded data | |
728 | * | |
729 | * @file_ptr: pointer to file | |
730 | * @buf: user buffer to be filled with captured data | |
731 | * @count: size of tthe buffer | |
732 | * @offset: offset to start from | |
733 | * | |
734 | * This function is called when user tries to capture data | |
735 | */ | |
736 | int intel_sst_read(struct file *file_ptr, char __user *buf, | |
737 | size_t count, loff_t *offset) | |
738 | { | |
739 | struct ioctl_pvt_data *data = file_ptr->private_data; | |
740 | int str_id = data->str_id; | |
741 | struct stream_info *stream = &sst_drv_ctx->streams[str_id]; | |
742 | ||
d0f40c50 | 743 | pr_debug("called for %d\n", str_id); |
fffa1cca VK |
744 | if (stream->status == STREAM_UN_INIT || |
745 | stream->status == STREAM_DECODE) | |
746 | return -EBADRQC; | |
747 | return intel_sst_read_write(str_id, buf, count); | |
748 | } | |
749 | ||
750 | /* | |
751 | * intel_sst_aio_read - aio read | |
752 | * | |
753 | * @kiocb: pointer to a structure containing file pointer | |
754 | * @iov: list of user buffer to be filled with captured | |
755 | * @nr_segs: number of entries | |
756 | * @offset: offset to start from | |
757 | * | |
758 | * This function is called when user tries to capture out multiple data buffers | |
759 | */ | |
760 | ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov, | |
761 | unsigned long nr_segs, loff_t offset) | |
762 | { | |
763 | int retval; | |
764 | struct ioctl_pvt_data *data = kiocb->ki_filp->private_data; | |
765 | int str_id = data->str_id; | |
766 | struct stream_info *stream; | |
767 | ||
d0f40c50 | 768 | pr_debug("entry - %ld\n", nr_segs); |
fffa1cca VK |
769 | |
770 | if (is_sync_kiocb(kiocb) == false) { | |
d0f40c50 | 771 | pr_debug("aio_read from user space is not allowed\n"); |
fffa1cca VK |
772 | return -EINVAL; |
773 | } | |
774 | ||
d0f40c50 | 775 | pr_debug("called for str_id %d\n", str_id); |
fffa1cca VK |
776 | retval = sst_validate_strid(str_id); |
777 | if (retval) | |
778 | return -EINVAL; | |
779 | stream = &sst_drv_ctx->streams[str_id]; | |
780 | if (stream->mmapped == true) | |
781 | return -EIO; | |
782 | if (stream->status == STREAM_UN_INIT || | |
783 | stream->status == STREAM_DECODE) | |
784 | return -EBADRQC; | |
785 | stream->curr_bytes = 0; | |
786 | stream->cumm_bytes = 0; | |
787 | ||
d0f40c50 | 788 | pr_debug("new segs %ld, offset %d, status %d\n" , |
fffa1cca VK |
789 | nr_segs, (int) offset, (int) stream->status); |
790 | stream->buf_type = SST_BUF_USER_STATIC; | |
791 | do { | |
792 | retval = snd_sst_userbufs_play_cap(iov, nr_segs, | |
793 | str_id, stream); | |
794 | if (retval < 0) | |
795 | break; | |
796 | ||
797 | } while (stream->sg_index < nr_segs); | |
798 | ||
799 | stream->sg_index = 0; | |
800 | stream->cur_ptr = NULL; | |
801 | if (retval >= 0) | |
802 | retval = stream->cumm_bytes; | |
d0f40c50 | 803 | pr_debug("end of play/rec bytes = %d!!\n", retval); |
fffa1cca VK |
804 | return retval; |
805 | } | |
806 | ||
807 | /* sst_print_stream_params - prints the stream parameters (debug fn)*/ | |
808 | static void sst_print_stream_params(struct snd_sst_get_stream_params *get_prm) | |
809 | { | |
d0f40c50 | 810 | pr_debug("codec params:result = %d\n", |
fffa1cca | 811 | get_prm->codec_params.result); |
d0f40c50 | 812 | pr_debug("codec params:stream = %d\n", |
fffa1cca | 813 | get_prm->codec_params.stream_id); |
d0f40c50 | 814 | pr_debug("codec params:codec = %d\n", |
fffa1cca | 815 | get_prm->codec_params.codec); |
d0f40c50 | 816 | pr_debug("codec params:ops = %d\n", |
fffa1cca | 817 | get_prm->codec_params.ops); |
d0f40c50 | 818 | pr_debug("codec params:stream_type = %d\n", |
fffa1cca | 819 | get_prm->codec_params.stream_type); |
d0f40c50 | 820 | pr_debug("pcmparams:sfreq = %d\n", |
fffa1cca | 821 | get_prm->pcm_params.sfreq); |
d0f40c50 | 822 | pr_debug("pcmparams:num_chan = %d\n", |
fffa1cca | 823 | get_prm->pcm_params.num_chan); |
d0f40c50 | 824 | pr_debug("pcmparams:pcm_wd_sz = %d\n", |
fffa1cca VK |
825 | get_prm->pcm_params.pcm_wd_sz); |
826 | return; | |
827 | } | |
828 | ||
62877913 VK |
829 | /** |
830 | * sst_create_algo_ipc - create ipc msg for algorithm parameters | |
831 | * | |
832 | * @algo_params: Algorithm parameters | |
833 | * @msg: post msg pointer | |
834 | * | |
835 | * This function is called to create ipc msg | |
836 | */ | |
837 | int sst_create_algo_ipc(struct snd_ppp_params *algo_params, | |
838 | struct ipc_post **msg) | |
839 | { | |
840 | if (sst_create_large_msg(msg)) | |
841 | return -ENOMEM; | |
842 | sst_fill_header(&(*msg)->header, | |
843 | IPC_IA_ALG_PARAMS, 1, algo_params->str_id); | |
844 | (*msg)->header.part.data = sizeof(u32) + | |
845 | sizeof(*algo_params) + algo_params->size; | |
846 | memcpy((*msg)->mailbox_data, &(*msg)->header, sizeof(u32)); | |
847 | memcpy((*msg)->mailbox_data + sizeof(u32), | |
848 | algo_params, sizeof(*algo_params)); | |
849 | return 0; | |
850 | } | |
851 | ||
852 | /** | |
853 | * sst_send_algo_ipc - send ipc msg for algorithm parameters | |
854 | * | |
855 | * @msg: post msg pointer | |
856 | * | |
857 | * This function is called to send ipc msg | |
858 | */ | |
859 | int sst_send_algo_ipc(struct ipc_post **msg) | |
860 | { | |
861 | sst_drv_ctx->ppp_params_blk.condition = false; | |
862 | sst_drv_ctx->ppp_params_blk.ret_code = 0; | |
863 | sst_drv_ctx->ppp_params_blk.on = true; | |
864 | sst_drv_ctx->ppp_params_blk.data = NULL; | |
865 | spin_lock(&sst_drv_ctx->list_spin_lock); | |
866 | list_add_tail(&(*msg)->node, &sst_drv_ctx->ipc_dispatch_list); | |
867 | spin_unlock(&sst_drv_ctx->list_spin_lock); | |
868 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | |
869 | return sst_wait_interruptible_timeout(sst_drv_ctx, | |
870 | &sst_drv_ctx->ppp_params_blk, SST_BLOCK_TIMEOUT); | |
871 | } | |
872 | ||
873 | /** | |
25985edc | 874 | * intel_sst_ioctl_dsp - receives the device ioctl's |
62877913 VK |
875 | * |
876 | * @cmd:Ioctl cmd | |
877 | * @arg:data | |
878 | * | |
879 | * This function is called when a user space component | |
880 | * sends a DSP Ioctl to SST driver | |
881 | */ | |
882 | long intel_sst_ioctl_dsp(unsigned int cmd, unsigned long arg) | |
883 | { | |
884 | int retval = 0; | |
885 | struct snd_ppp_params algo_params; | |
886 | struct snd_ppp_params *algo_params_copied; | |
887 | struct ipc_post *msg; | |
888 | ||
889 | switch (_IOC_NR(cmd)) { | |
890 | case _IOC_NR(SNDRV_SST_SET_ALGO): | |
891 | if (copy_from_user(&algo_params, (void __user *)arg, | |
892 | sizeof(algo_params))) | |
893 | return -EFAULT; | |
894 | if (algo_params.size > SST_MAILBOX_SIZE) | |
895 | return -EMSGSIZE; | |
896 | ||
897 | pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", | |
898 | algo_params.algo_id, algo_params.str_id, | |
899 | algo_params.enable, algo_params.size); | |
900 | retval = sst_create_algo_ipc(&algo_params, &msg); | |
901 | if (retval) | |
902 | break; | |
903 | algo_params.reserved = 0; | |
904 | if (copy_from_user(msg->mailbox_data + sizeof(algo_params), | |
905 | algo_params.params, algo_params.size)) | |
906 | return -EFAULT; | |
907 | ||
908 | retval = sst_send_algo_ipc(&msg); | |
909 | if (retval) { | |
910 | pr_debug("Error in sst_set_algo = %d\n", retval); | |
911 | retval = -EIO; | |
912 | } | |
913 | break; | |
914 | ||
915 | case _IOC_NR(SNDRV_SST_GET_ALGO): | |
916 | if (copy_from_user(&algo_params, (void __user *)arg, | |
917 | sizeof(algo_params))) | |
918 | return -EFAULT; | |
919 | pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", | |
920 | algo_params.algo_id, algo_params.str_id, | |
921 | algo_params.enable, algo_params.size); | |
922 | retval = sst_create_algo_ipc(&algo_params, &msg); | |
923 | if (retval) | |
924 | break; | |
925 | algo_params.reserved = 1; | |
926 | retval = sst_send_algo_ipc(&msg); | |
927 | if (retval) { | |
928 | pr_debug("Error in sst_get_algo = %d\n", retval); | |
929 | retval = -EIO; | |
930 | break; | |
931 | } | |
932 | algo_params_copied = (struct snd_ppp_params *) | |
933 | sst_drv_ctx->ppp_params_blk.data; | |
934 | if (algo_params_copied->size > algo_params.size) { | |
935 | pr_debug("mem insufficient to copy\n"); | |
936 | retval = -EMSGSIZE; | |
937 | goto free_mem; | |
938 | } else { | |
939 | char __user *tmp; | |
940 | ||
941 | if (copy_to_user(algo_params.params, | |
942 | algo_params_copied->params, | |
943 | algo_params_copied->size)) { | |
944 | retval = -EFAULT; | |
945 | goto free_mem; | |
946 | } | |
947 | tmp = (char __user *)arg + offsetof( | |
948 | struct snd_ppp_params, size); | |
949 | if (copy_to_user(tmp, &algo_params_copied->size, | |
950 | sizeof(__u32))) { | |
951 | retval = -EFAULT; | |
952 | goto free_mem; | |
953 | } | |
954 | ||
955 | } | |
956 | free_mem: | |
957 | kfree(algo_params_copied->params); | |
958 | kfree(algo_params_copied); | |
959 | break; | |
960 | } | |
961 | return retval; | |
962 | } | |
963 | ||
fffa1cca | 964 | /** |
d0f40c50 | 965 | * intel_sst_ioctl - receives the device ioctl's |
fffa1cca VK |
966 | * @file_ptr:pointer to file |
967 | * @cmd:Ioctl cmd | |
968 | * @arg:data | |
969 | * | |
970 | * This function is called by OS when a user space component | |
971 | * sends an Ioctl to SST driver | |
972 | */ | |
973 | long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg) | |
974 | { | |
975 | int retval = 0; | |
976 | struct ioctl_pvt_data *data = NULL; | |
977 | int str_id = 0, minor = 0; | |
978 | ||
fffa1cca VK |
979 | data = file_ptr->private_data; |
980 | if (data) { | |
981 | minor = 0; | |
982 | str_id = data->str_id; | |
983 | } else | |
984 | minor = 1; | |
985 | ||
88eea8de | 986 | if (sst_drv_ctx->sst_state != SST_FW_RUNNING) |
fffa1cca | 987 | return -EBUSY; |
fffa1cca VK |
988 | |
989 | switch (_IOC_NR(cmd)) { | |
990 | case _IOC_NR(SNDRV_SST_STREAM_PAUSE): | |
d0f40c50 | 991 | pr_debug("IOCTL_PAUSE received for %d!\n", str_id); |
fffa1cca VK |
992 | if (minor != STREAM_MODULE) { |
993 | retval = -EBADRQC; | |
994 | break; | |
995 | } | |
996 | retval = sst_pause_stream(str_id); | |
997 | break; | |
998 | ||
999 | case _IOC_NR(SNDRV_SST_STREAM_RESUME): | |
d0f40c50 | 1000 | pr_debug("SNDRV_SST_IOCTL_RESUME received!\n"); |
fffa1cca VK |
1001 | if (minor != STREAM_MODULE) { |
1002 | retval = -EBADRQC; | |
1003 | break; | |
1004 | } | |
1005 | retval = sst_resume_stream(str_id); | |
1006 | break; | |
1007 | ||
1008 | case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): { | |
bc704e31 | 1009 | struct snd_sst_params str_param; |
fffa1cca | 1010 | |
d0f40c50 | 1011 | pr_debug("IOCTL_SET_PARAMS received!\n"); |
fffa1cca VK |
1012 | if (minor != STREAM_MODULE) { |
1013 | retval = -EBADRQC; | |
1014 | break; | |
1015 | } | |
1016 | ||
bc704e31 DC |
1017 | if (copy_from_user(&str_param, (void __user *)arg, |
1018 | sizeof(str_param))) { | |
1019 | retval = -EFAULT; | |
1020 | break; | |
1021 | } | |
1022 | ||
fffa1cca VK |
1023 | if (!str_id) { |
1024 | ||
bc704e31 | 1025 | retval = sst_get_stream(&str_param); |
fffa1cca VK |
1026 | if (retval > 0) { |
1027 | struct stream_info *str_info; | |
bc704e31 DC |
1028 | char __user *dest; |
1029 | ||
fffa1cca VK |
1030 | sst_drv_ctx->stream_cnt++; |
1031 | data->str_id = retval; | |
1032 | str_info = &sst_drv_ctx->streams[retval]; | |
1033 | str_info->src = SST_DRV; | |
4fc718a4 | 1034 | dest = (char __user *)arg + offsetof(struct snd_sst_params, stream_id); |
bc704e31 | 1035 | retval = copy_to_user(dest, &retval, sizeof(__u32)); |
87b554a0 DC |
1036 | if (retval) |
1037 | retval = -EFAULT; | |
fffa1cca VK |
1038 | } else { |
1039 | if (retval == -SST_ERR_INVALID_PARAMS) | |
1040 | retval = -EINVAL; | |
1041 | } | |
1042 | } else { | |
d0f40c50 | 1043 | pr_debug("SET_STREAM_PARAMS received!\n"); |
fffa1cca | 1044 | /* allocated set params only */ |
bc704e31 | 1045 | retval = sst_set_stream_param(str_id, &str_param); |
fffa1cca VK |
1046 | /* Block the call for reply */ |
1047 | if (!retval) { | |
1048 | int sfreq = 0, word_size = 0, num_channel = 0; | |
bc704e31 DC |
1049 | sfreq = str_param.sparams.uc.pcm_params.sfreq; |
1050 | word_size = str_param.sparams.uc.pcm_params.pcm_wd_sz; | |
1051 | num_channel = str_param.sparams.uc.pcm_params.num_chan; | |
1052 | if (str_param.ops == STREAM_OPS_CAPTURE) { | |
fffa1cca VK |
1053 | sst_drv_ctx->scard_ops->\ |
1054 | set_pcm_audio_params(sfreq, | |
1055 | word_size, num_channel); | |
1056 | } | |
1057 | } | |
1058 | } | |
1059 | break; | |
1060 | } | |
1061 | case _IOC_NR(SNDRV_SST_SET_VOL): { | |
3b97eed2 DC |
1062 | struct snd_sst_vol set_vol; |
1063 | ||
1064 | if (copy_from_user(&set_vol, (void __user *)arg, | |
1065 | sizeof(set_vol))) { | |
d0f40c50 | 1066 | pr_debug("copy failed\n"); |
388b2b97 | 1067 | retval = -EFAULT; |
fffa1cca VK |
1068 | break; |
1069 | } | |
25985edc | 1070 | pr_debug("SET_VOLUME received for %d!\n", |
3b97eed2 DC |
1071 | set_vol.stream_id); |
1072 | if (minor == STREAM_MODULE && set_vol.stream_id == 0) { | |
491acf00 | 1073 | pr_debug("invalid operation!\n"); |
fffa1cca VK |
1074 | retval = -EPERM; |
1075 | break; | |
1076 | } | |
3b97eed2 | 1077 | retval = sst_set_vol(&set_vol); |
fffa1cca VK |
1078 | break; |
1079 | } | |
1080 | case _IOC_NR(SNDRV_SST_GET_VOL): { | |
fffa1cca | 1081 | struct snd_sst_vol get_vol; |
3b97eed2 DC |
1082 | |
1083 | if (copy_from_user(&get_vol, (void __user *)arg, | |
1084 | sizeof(get_vol))) { | |
1085 | retval = -EFAULT; | |
1086 | break; | |
1087 | } | |
25985edc | 1088 | pr_debug("IOCTL_GET_VOLUME received for stream = %d!\n", |
3b97eed2 DC |
1089 | get_vol.stream_id); |
1090 | if (minor == STREAM_MODULE && get_vol.stream_id == 0) { | |
d0f40c50 | 1091 | pr_debug("invalid operation!\n"); |
fffa1cca VK |
1092 | retval = -EPERM; |
1093 | break; | |
1094 | } | |
fffa1cca VK |
1095 | retval = sst_get_vol(&get_vol); |
1096 | if (retval) { | |
1097 | retval = -EIO; | |
1098 | break; | |
1099 | } | |
d0f40c50 | 1100 | pr_debug("id:%d\n, vol:%d, ramp_dur:%d, ramp_type:%d\n", |
fffa1cca VK |
1101 | get_vol.stream_id, get_vol.volume, |
1102 | get_vol.ramp_duration, get_vol.ramp_type); | |
4fc718a4 | 1103 | if (copy_to_user((struct snd_sst_vol __user *)arg, |
388b2b97 | 1104 | &get_vol, sizeof(get_vol))) { |
87b554a0 | 1105 | retval = -EFAULT; |
fffa1cca VK |
1106 | break; |
1107 | } | |
1108 | /*sst_print_get_vol_info(str_id, &get_vol);*/ | |
1109 | break; | |
1110 | } | |
1111 | ||
1112 | case _IOC_NR(SNDRV_SST_MUTE): { | |
3b97eed2 DC |
1113 | struct snd_sst_mute set_mute; |
1114 | ||
1115 | if (copy_from_user(&set_mute, (void __user *)arg, | |
1116 | sizeof(set_mute))) { | |
1117 | retval = -EFAULT; | |
fffa1cca VK |
1118 | break; |
1119 | } | |
25985edc | 1120 | pr_debug("SNDRV_SST_SET_VOLUME received for %d!\n", |
3b97eed2 DC |
1121 | set_mute.stream_id); |
1122 | if (minor == STREAM_MODULE && set_mute.stream_id == 0) { | |
1123 | retval = -EPERM; | |
fffa1cca VK |
1124 | break; |
1125 | } | |
3b97eed2 | 1126 | retval = sst_set_mute(&set_mute); |
fffa1cca VK |
1127 | break; |
1128 | } | |
1129 | case _IOC_NR(SNDRV_SST_STREAM_GET_PARAMS): { | |
1130 | struct snd_sst_get_stream_params get_params; | |
1131 | ||
d0f40c50 | 1132 | pr_debug("IOCTL_GET_PARAMS received!\n"); |
fffa1cca VK |
1133 | if (minor != 0) { |
1134 | retval = -EBADRQC; | |
1135 | break; | |
1136 | } | |
1137 | ||
1138 | retval = sst_get_stream_params(str_id, &get_params); | |
1139 | if (retval) { | |
1140 | retval = -EIO; | |
1141 | break; | |
1142 | } | |
4fc718a4 | 1143 | if (copy_to_user((struct snd_sst_get_stream_params __user *)arg, |
388b2b97 DC |
1144 | &get_params, sizeof(get_params))) { |
1145 | retval = -EFAULT; | |
fffa1cca VK |
1146 | break; |
1147 | } | |
1148 | sst_print_stream_params(&get_params); | |
1149 | break; | |
1150 | } | |
1151 | ||
1152 | case _IOC_NR(SNDRV_SST_MMAP_PLAY): | |
bc704e31 DC |
1153 | case _IOC_NR(SNDRV_SST_MMAP_CAPTURE): { |
1154 | struct snd_sst_mmap_buffs mmap_buf; | |
1155 | ||
25985edc | 1156 | pr_debug("SNDRV_SST_MMAP_PLAY/CAPTURE received!\n"); |
fffa1cca VK |
1157 | if (minor != STREAM_MODULE) { |
1158 | retval = -EBADRQC; | |
1159 | break; | |
1160 | } | |
bc704e31 DC |
1161 | if (copy_from_user(&mmap_buf, (void __user *)arg, |
1162 | sizeof(mmap_buf))) { | |
1163 | retval = -EFAULT; | |
1164 | break; | |
1165 | } | |
1166 | retval = intel_sst_mmap_play_capture(str_id, &mmap_buf); | |
fffa1cca | 1167 | break; |
bc704e31 | 1168 | } |
fffa1cca | 1169 | case _IOC_NR(SNDRV_SST_STREAM_DROP): |
d0f40c50 | 1170 | pr_debug("SNDRV_SST_IOCTL_DROP received!\n"); |
fffa1cca VK |
1171 | if (minor != STREAM_MODULE) { |
1172 | retval = -EINVAL; | |
1173 | break; | |
1174 | } | |
1175 | retval = sst_drop_stream(str_id); | |
1176 | break; | |
1177 | ||
1178 | case _IOC_NR(SNDRV_SST_STREAM_GET_TSTAMP): { | |
fffa1cca VK |
1179 | struct snd_sst_tstamp tstamp = {0}; |
1180 | unsigned long long time, freq, mod; | |
1181 | ||
d0f40c50 | 1182 | pr_debug("SNDRV_SST_STREAM_GET_TSTAMP received!\n"); |
fffa1cca VK |
1183 | if (minor != STREAM_MODULE) { |
1184 | retval = -EBADRQC; | |
1185 | break; | |
1186 | } | |
1187 | memcpy_fromio(&tstamp, | |
4fc718a4 | 1188 | sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp), |
fffa1cca VK |
1189 | sizeof(tstamp)); |
1190 | time = tstamp.samples_rendered; | |
1191 | freq = (unsigned long long) tstamp.sampling_frequency; | |
1192 | time = time * 1000; /* converting it to ms */ | |
1193 | mod = do_div(time, freq); | |
bc704e31 DC |
1194 | if (copy_to_user((void __user *)arg, &time, |
1195 | sizeof(unsigned long long))) | |
fffa1cca VK |
1196 | retval = -EFAULT; |
1197 | break; | |
1198 | } | |
1199 | ||
1200 | case _IOC_NR(SNDRV_SST_STREAM_START):{ | |
1201 | struct stream_info *stream; | |
1202 | ||
d0f40c50 | 1203 | pr_debug("SNDRV_SST_STREAM_START received!\n"); |
fffa1cca VK |
1204 | if (minor != STREAM_MODULE) { |
1205 | retval = -EINVAL; | |
1206 | break; | |
1207 | } | |
1208 | retval = sst_validate_strid(str_id); | |
1209 | if (retval) | |
1210 | break; | |
1211 | stream = &sst_drv_ctx->streams[str_id]; | |
1212 | mutex_lock(&stream->lock); | |
1213 | if (stream->status == STREAM_INIT && | |
1214 | stream->need_draining != true) { | |
1215 | stream->prev = stream->status; | |
1216 | stream->status = STREAM_RUNNING; | |
1217 | if (stream->ops == STREAM_OPS_PLAYBACK || | |
1218 | stream->ops == STREAM_OPS_PLAYBACK_DRM) { | |
1219 | retval = sst_play_frame(str_id); | |
1220 | } else if (stream->ops == STREAM_OPS_CAPTURE) | |
1221 | retval = sst_capture_frame(str_id); | |
1222 | else { | |
1223 | retval = -EINVAL; | |
81a2fff6 | 1224 | mutex_unlock(&stream->lock); |
fffa1cca VK |
1225 | break; |
1226 | } | |
1227 | if (retval < 0) { | |
1228 | stream->status = STREAM_INIT; | |
81a2fff6 | 1229 | mutex_unlock(&stream->lock); |
fffa1cca VK |
1230 | break; |
1231 | } | |
1232 | } else { | |
1233 | retval = -EINVAL; | |
1234 | } | |
81a2fff6 | 1235 | mutex_unlock(&stream->lock); |
fffa1cca VK |
1236 | break; |
1237 | } | |
1238 | ||
1239 | case _IOC_NR(SNDRV_SST_SET_TARGET_DEVICE): { | |
bc704e31 | 1240 | struct snd_sst_target_device target_device; |
fffa1cca | 1241 | |
25985edc | 1242 | pr_debug("SET_TARGET_DEVICE received!\n"); |
bc704e31 DC |
1243 | if (copy_from_user(&target_device, (void __user *)arg, |
1244 | sizeof(target_device))) { | |
1245 | retval = -EFAULT; | |
1246 | break; | |
1247 | } | |
fffa1cca VK |
1248 | if (minor != AM_MODULE) { |
1249 | retval = -EBADRQC; | |
1250 | break; | |
1251 | } | |
bc704e31 | 1252 | retval = sst_target_device_select(&target_device); |
fffa1cca VK |
1253 | break; |
1254 | } | |
1255 | ||
1256 | case _IOC_NR(SNDRV_SST_DRIVER_INFO): { | |
bc704e31 | 1257 | struct snd_sst_driver_info info; |
fffa1cca | 1258 | |
25985edc | 1259 | pr_debug("SNDRV_SST_DRIVER_INFO received\n"); |
bc704e31 | 1260 | info.version = SST_VERSION_NUM; |
fffa1cca | 1261 | /* hard coding, shud get sumhow later */ |
bc704e31 | 1262 | info.active_pcm_streams = sst_drv_ctx->stream_cnt - |
fffa1cca | 1263 | sst_drv_ctx->encoded_cnt; |
bc704e31 DC |
1264 | info.active_enc_streams = sst_drv_ctx->encoded_cnt; |
1265 | info.max_pcm_streams = MAX_ACTIVE_STREAM - MAX_ENC_STREAM; | |
1266 | info.max_enc_streams = MAX_ENC_STREAM; | |
1267 | info.buf_per_stream = sst_drv_ctx->mmap_len; | |
1268 | if (copy_to_user((void __user *)arg, &info, | |
1269 | sizeof(info))) | |
1270 | retval = -EFAULT; | |
fffa1cca VK |
1271 | break; |
1272 | } | |
1273 | ||
1274 | case _IOC_NR(SNDRV_SST_STREAM_DECODE): { | |
e9f25689 DC |
1275 | struct snd_sst_dbufs param; |
1276 | struct snd_sst_dbufs dbufs_local; | |
fffa1cca | 1277 | struct snd_sst_buffs ibufs, obufs; |
e9f25689 DC |
1278 | struct snd_sst_buff_entry *ibuf_tmp, *obuf_tmp; |
1279 | char __user *dest; | |
fffa1cca | 1280 | |
d0f40c50 | 1281 | pr_debug("SNDRV_SST_STREAM_DECODE received\n"); |
fffa1cca VK |
1282 | if (minor != STREAM_MODULE) { |
1283 | retval = -EBADRQC; | |
1284 | break; | |
1285 | } | |
e9f25689 DC |
1286 | if (copy_from_user(¶m, (void __user *)arg, |
1287 | sizeof(param))) { | |
1288 | retval = -EFAULT; | |
fffa1cca VK |
1289 | break; |
1290 | } | |
1291 | ||
e9f25689 | 1292 | dbufs_local.input_bytes_consumed = param.input_bytes_consumed; |
fffa1cca | 1293 | dbufs_local.output_bytes_produced = |
e9f25689 DC |
1294 | param.output_bytes_produced; |
1295 | ||
4fc718a4 | 1296 | if (copy_from_user(&ibufs, (void __user *)param.ibufs, sizeof(ibufs))) { |
e9f25689 DC |
1297 | retval = -EFAULT; |
1298 | break; | |
fffa1cca | 1299 | } |
4fc718a4 | 1300 | if (copy_from_user(&obufs, (void __user *)param.obufs, sizeof(obufs))) { |
e9f25689 DC |
1301 | retval = -EFAULT; |
1302 | break; | |
fffa1cca | 1303 | } |
e9f25689 DC |
1304 | |
1305 | ibuf_tmp = kcalloc(ibufs.entries, sizeof(*ibuf_tmp), GFP_KERNEL); | |
1306 | obuf_tmp = kcalloc(obufs.entries, sizeof(*obuf_tmp), GFP_KERNEL); | |
1307 | if (!ibuf_tmp || !obuf_tmp) { | |
1308 | retval = -ENOMEM; | |
1309 | goto free_iobufs; | |
fffa1cca | 1310 | } |
e9f25689 | 1311 | |
4fc718a4 | 1312 | if (copy_from_user(ibuf_tmp, (void __user *)ibufs.buff_entry, |
e9f25689 DC |
1313 | ibufs.entries * sizeof(*ibuf_tmp))) { |
1314 | retval = -EFAULT; | |
1315 | goto free_iobufs; | |
1316 | } | |
1317 | ibufs.buff_entry = ibuf_tmp; | |
1318 | dbufs_local.ibufs = &ibufs; | |
1319 | ||
4fc718a4 | 1320 | if (copy_from_user(obuf_tmp, (void __user *)obufs.buff_entry, |
e9f25689 DC |
1321 | obufs.entries * sizeof(*obuf_tmp))) { |
1322 | retval = -EFAULT; | |
1323 | goto free_iobufs; | |
1324 | } | |
1325 | obufs.buff_entry = obuf_tmp; | |
1326 | dbufs_local.obufs = &obufs; | |
1327 | ||
fffa1cca | 1328 | retval = sst_decode(str_id, &dbufs_local); |
e9f25689 DC |
1329 | if (retval) { |
1330 | retval = -EAGAIN; | |
1331 | goto free_iobufs; | |
1332 | } | |
1333 | ||
4fc718a4 | 1334 | dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed); |
e9f25689 | 1335 | if (copy_to_user(dest, |
388b2b97 DC |
1336 | &dbufs_local.input_bytes_consumed, |
1337 | sizeof(unsigned long long))) { | |
e9f25689 DC |
1338 | retval = -EFAULT; |
1339 | goto free_iobufs; | |
fffa1cca | 1340 | } |
e9f25689 | 1341 | |
4fc718a4 | 1342 | dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed); |
e9f25689 | 1343 | if (copy_to_user(dest, |
fffa1cca | 1344 | &dbufs_local.output_bytes_produced, |
388b2b97 | 1345 | sizeof(unsigned long long))) { |
e9f25689 DC |
1346 | retval = -EFAULT; |
1347 | goto free_iobufs; | |
fffa1cca | 1348 | } |
e9f25689 DC |
1349 | free_iobufs: |
1350 | kfree(ibuf_tmp); | |
1351 | kfree(obuf_tmp); | |
fffa1cca VK |
1352 | break; |
1353 | } | |
1354 | ||
1355 | case _IOC_NR(SNDRV_SST_STREAM_DRAIN): | |
d0f40c50 | 1356 | pr_debug("SNDRV_SST_STREAM_DRAIN received\n"); |
fffa1cca VK |
1357 | if (minor != STREAM_MODULE) { |
1358 | retval = -EINVAL; | |
1359 | break; | |
1360 | } | |
1361 | retval = sst_drain_stream(str_id); | |
1362 | break; | |
1363 | ||
1364 | case _IOC_NR(SNDRV_SST_STREAM_BYTES_DECODED): { | |
4fc718a4 | 1365 | unsigned long long __user *bytes = (unsigned long long __user *)arg; |
fffa1cca VK |
1366 | struct snd_sst_tstamp tstamp = {0}; |
1367 | ||
d0f40c50 | 1368 | pr_debug("STREAM_BYTES_DECODED received!\n"); |
fffa1cca VK |
1369 | if (minor != STREAM_MODULE) { |
1370 | retval = -EINVAL; | |
1371 | break; | |
1372 | } | |
1373 | memcpy_fromio(&tstamp, | |
4fc718a4 | 1374 | sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp), |
fffa1cca | 1375 | sizeof(tstamp)); |
388b2b97 DC |
1376 | if (copy_to_user(bytes, &tstamp.bytes_processed, |
1377 | sizeof(*bytes))) | |
fffa1cca VK |
1378 | retval = -EFAULT; |
1379 | break; | |
1380 | } | |
1381 | case _IOC_NR(SNDRV_SST_FW_INFO): { | |
1382 | struct snd_sst_fw_info *fw_info; | |
1383 | ||
d0f40c50 | 1384 | pr_debug("SNDRV_SST_FW_INFO received\n"); |
fffa1cca VK |
1385 | |
1386 | fw_info = kzalloc(sizeof(*fw_info), GFP_ATOMIC); | |
1387 | if (!fw_info) { | |
1388 | retval = -ENOMEM; | |
1389 | break; | |
1390 | } | |
1391 | retval = sst_get_fw_info(fw_info); | |
1392 | if (retval) { | |
1393 | retval = -EIO; | |
1394 | kfree(fw_info); | |
1395 | break; | |
1396 | } | |
4fc718a4 | 1397 | if (copy_to_user((struct snd_sst_dbufs __user *)arg, |
388b2b97 | 1398 | fw_info, sizeof(*fw_info))) { |
fffa1cca VK |
1399 | kfree(fw_info); |
1400 | retval = -EFAULT; | |
1401 | break; | |
1402 | } | |
1403 | /*sst_print_fw_info(fw_info);*/ | |
1404 | kfree(fw_info); | |
1405 | break; | |
1406 | } | |
62877913 VK |
1407 | case _IOC_NR(SNDRV_SST_GET_ALGO): |
1408 | case _IOC_NR(SNDRV_SST_SET_ALGO): | |
1409 | if (minor != AM_MODULE) { | |
1410 | retval = -EBADRQC; | |
1411 | break; | |
1412 | } | |
1413 | retval = intel_sst_ioctl_dsp(cmd, arg); | |
1414 | break; | |
fffa1cca VK |
1415 | default: |
1416 | retval = -EINVAL; | |
1417 | } | |
d0f40c50 | 1418 | pr_debug("intel_sst_ioctl:complete ret code = %d\n", retval); |
fffa1cca VK |
1419 | return retval; |
1420 | } | |
1421 |