1459 lines
38 KiB
C
1459 lines
38 KiB
C
/*
|
|
* intel_sst_interface.c - Intel SST Driver for audio engine
|
|
*
|
|
* Copyright (C) 2008-10 Intel Corp
|
|
* Authors: Vinod Koul <vinod.koul@intel.com>
|
|
* Harsha Priya <priya.harsha@intel.com>
|
|
* Dharageswari R <dharageswari.r@intel.com>
|
|
* Jeeja KP <jeeja.kp@intel.com>
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* This driver exposes the audio engine functionalities to the ALSA
|
|
* and middleware.
|
|
* Upper layer interfaces (MAD driver, MMF) to SST driver
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/aio.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/ioctl.h>
|
|
#ifdef CONFIG_MRST_RAR_HANDLER
|
|
#include <linux/rar_register.h>
|
|
#include "../../../drivers/staging/memrar/memrar.h"
|
|
#endif
|
|
#include "intel_sst.h"
|
|
#include "intel_sst_ioctl.h"
|
|
#include "intel_sst_fw_ipc.h"
|
|
#include "intel_sst_common.h"
|
|
|
|
#define AM_MODULE 1
|
|
#define STREAM_MODULE 0
|
|
|
|
|
|
/**
|
|
* intel_sst_check_device - checks SST device
|
|
*
|
|
* This utility function checks the state of SST device and downlaods FW if
|
|
* not done, or resumes the device if suspended
|
|
*/
|
|
|
|
static int intel_sst_check_device(void)
|
|
{
|
|
int retval = 0;
|
|
if (sst_drv_ctx->pmic_state != SND_MAD_INIT_DONE) {
|
|
pr_warn("Sound card not available\n");
|
|
return -EIO;
|
|
}
|
|
if (sst_drv_ctx->sst_state == SST_SUSPENDED) {
|
|
pr_debug("Resuming from Suspended state\n");
|
|
retval = intel_sst_resume(sst_drv_ctx->pci);
|
|
if (retval) {
|
|
pr_debug("Resume Failed= %#x,abort\n", retval);
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
if (sst_drv_ctx->sst_state == SST_UN_INIT) {
|
|
/* FW is not downloaded */
|
|
retval = sst_download_fw();
|
|
if (retval)
|
|
return -ENODEV;
|
|
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
|
|
retval = sst_drv_ctx->rx_time_slot_status;
|
|
if (retval != RX_TIMESLOT_UNINIT
|
|
&& sst_drv_ctx->pmic_vendor != SND_NC)
|
|
sst_enable_rx_timeslot(retval);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* intel_sst_open - opens a handle to driver
|
|
*
|
|
* @i_node: inode structure
|
|
* @file_ptr:pointer to file
|
|
*
|
|
* This function is called by OS when a user space component
|
|
* tries to get a driver handle. Only one handle at a time
|
|
* will be allowed
|
|
*/
|
|
int intel_sst_open(struct inode *i_node, struct file *file_ptr)
|
|
{
|
|
unsigned int retval;
|
|
|
|
mutex_lock(&sst_drv_ctx->stream_lock);
|
|
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
|
|
retval = intel_sst_check_device();
|
|
if (retval) {
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
return retval;
|
|
}
|
|
|
|
if (sst_drv_ctx->encoded_cnt < MAX_ENC_STREAM) {
|
|
struct ioctl_pvt_data *data =
|
|
kzalloc(sizeof(struct ioctl_pvt_data), GFP_KERNEL);
|
|
if (!data) {
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
sst_drv_ctx->encoded_cnt++;
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
data->pvt_id = sst_assign_pvt_id(sst_drv_ctx);
|
|
data->str_id = 0;
|
|
file_ptr->private_data = (void *)data;
|
|
pr_debug("pvt_id handle = %d!\n", data->pvt_id);
|
|
} else {
|
|
retval = -EUSERS;
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* intel_sst_open_cntrl - opens a handle to driver
|
|
*
|
|
* @i_node: inode structure
|
|
* @file_ptr:pointer to file
|
|
*
|
|
* This function is called by OS when a user space component
|
|
* tries to get a driver handle to /dev/intel_sst_control.
|
|
* Only one handle at a time will be allowed
|
|
* This is for control operations only
|
|
*/
|
|
int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr)
|
|
{
|
|
unsigned int retval;
|
|
|
|
/* audio manager open */
|
|
mutex_lock(&sst_drv_ctx->stream_lock);
|
|
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
|
|
retval = intel_sst_check_device();
|
|
if (retval) {
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
return retval;
|
|
}
|
|
|
|
if (sst_drv_ctx->am_cnt < MAX_AM_HANDLES) {
|
|
sst_drv_ctx->am_cnt++;
|
|
pr_debug("AM handle opened...\n");
|
|
file_ptr->private_data = NULL;
|
|
} else {
|
|
retval = -EACCES;
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
}
|
|
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* intel_sst_release - releases a handle to driver
|
|
*
|
|
* @i_node: inode structure
|
|
* @file_ptr: pointer to file
|
|
*
|
|
* This function is called by OS when a user space component
|
|
* tries to release a driver handle.
|
|
*/
|
|
int intel_sst_release(struct inode *i_node, struct file *file_ptr)
|
|
{
|
|
struct ioctl_pvt_data *data = file_ptr->private_data;
|
|
|
|
pr_debug("Release called, closing app handle\n");
|
|
mutex_lock(&sst_drv_ctx->stream_lock);
|
|
sst_drv_ctx->encoded_cnt--;
|
|
sst_drv_ctx->stream_cnt--;
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
free_stream_context(data->str_id);
|
|
kfree(data);
|
|
return 0;
|
|
}
|
|
|
|
int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr)
|
|
{
|
|
/* audio manager close */
|
|
mutex_lock(&sst_drv_ctx->stream_lock);
|
|
sst_drv_ctx->am_cnt--;
|
|
pm_runtime_put(&sst_drv_ctx->pci->dev);
|
|
mutex_unlock(&sst_drv_ctx->stream_lock);
|
|
pr_debug("AM handle closed\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* intel_sst_mmap - mmaps a kernel buffer to user space for copying data
|
|
*
|
|
* @vma: vm area structure instance
|
|
* @file_ptr: pointer to file
|
|
*
|
|
* This function is called by OS when a user space component
|
|
* tries to get mmap memory from driver
|
|
*/
|
|
int intel_sst_mmap(struct file *file_ptr, struct vm_area_struct *vma)
|
|
{
|
|
int retval, length;
|
|
struct ioctl_pvt_data *data =
|
|
(struct ioctl_pvt_data *)file_ptr->private_data;
|
|
int str_id = data->str_id;
|
|
void *mem_area;
|
|
|
|
retval = sst_validate_strid(str_id);
|
|
if (retval)
|
|
return -EINVAL;
|
|
|
|
length = vma->vm_end - vma->vm_start;
|
|
pr_debug("called for stream %d length 0x%x\n", str_id, length);
|
|
|
|
if (length > sst_drv_ctx->mmap_len)
|
|
return -ENOMEM;
|
|
if (!sst_drv_ctx->mmap_mem)
|
|
return -EIO;
|
|
|
|
/* round it up to the page boundary */
|
|
/*mem_area = (void *)((((unsigned long)sst_drv_ctx->mmap_mem)
|
|
+ PAGE_SIZE - 1) & PAGE_MASK);*/
|
|
mem_area = (void *) PAGE_ALIGN((unsigned int) sst_drv_ctx->mmap_mem);
|
|
|
|
/* map the whole physically contiguous area in one piece */
|
|
retval = remap_pfn_range(vma,
|
|
vma->vm_start,
|
|
virt_to_phys((void *)mem_area) >> PAGE_SHIFT,
|
|
length,
|
|
vma->vm_page_prot);
|
|
if (retval)
|
|
sst_drv_ctx->streams[str_id].mmapped = false;
|
|
else
|
|
sst_drv_ctx->streams[str_id].mmapped = true;
|
|
|
|
pr_debug("mmap ret 0x%x\n", retval);
|
|
return retval;
|
|
}
|
|
|
|
/* sets mmap data buffers to play/capture*/
|
|
static int intel_sst_mmap_play_capture(u32 str_id,
|
|
struct snd_sst_mmap_buffs *mmap_buf)
|
|
{
|
|
struct sst_stream_bufs *bufs;
|
|
int retval, i;
|
|
struct stream_info *stream;
|
|
struct snd_sst_mmap_buff_entry *buf_entry;
|
|
struct snd_sst_mmap_buff_entry *tmp_buf;
|
|
|
|
pr_debug("called for str_id %d\n", str_id);
|
|
retval = sst_validate_strid(str_id);
|
|
if (retval)
|
|
return -EINVAL;
|
|
|
|
stream = &sst_drv_ctx->streams[str_id];
|
|
if (stream->mmapped != true)
|
|
return -EIO;
|
|
|
|
if (stream->status == STREAM_UN_INIT ||
|
|
stream->status == STREAM_DECODE) {
|
|
return -EBADRQC;
|
|
}
|
|
stream->curr_bytes = 0;
|
|
stream->cumm_bytes = 0;
|
|
|
|
tmp_buf = kcalloc(mmap_buf->entries, sizeof(*tmp_buf), GFP_KERNEL);
|
|
if (!tmp_buf)
|
|
return -ENOMEM;
|
|
if (copy_from_user(tmp_buf, (void __user *)mmap_buf->buff,
|
|
mmap_buf->entries * sizeof(*tmp_buf))) {
|
|
retval = -EFAULT;
|
|
goto out_free;
|
|
}
|
|
|
|
pr_debug("new buffers count %d status %d\n",
|
|
mmap_buf->entries, stream->status);
|
|
buf_entry = tmp_buf;
|
|
for (i = 0; i < mmap_buf->entries; i++) {
|
|
bufs = kzalloc(sizeof(*bufs), GFP_KERNEL);
|
|
if (!bufs) {
|
|
retval = -ENOMEM;
|
|
goto out_free;
|
|
}
|
|
bufs->size = buf_entry->size;
|
|
bufs->offset = buf_entry->offset;
|
|
bufs->addr = sst_drv_ctx->mmap_mem;
|
|
bufs->in_use = false;
|
|
buf_entry++;
|
|
/* locking here */
|
|
mutex_lock(&stream->lock);
|
|
list_add_tail(&bufs->node, &stream->bufs);
|
|
mutex_unlock(&stream->lock);
|
|
}
|
|
|
|
mutex_lock(&stream->lock);
|
|
stream->data_blk.condition = false;
|
|
stream->data_blk.ret_code = 0;
|
|
if (stream->status == STREAM_INIT &&
|
|
stream->prev != STREAM_UN_INIT &&
|
|
stream->need_draining != true) {
|
|
stream->prev = stream->status;
|
|
stream->status = STREAM_RUNNING;
|
|
if (stream->ops == STREAM_OPS_PLAYBACK) {
|
|
if (sst_play_frame(str_id) < 0) {
|
|
pr_warn("play frames fail\n");
|
|
mutex_unlock(&stream->lock);
|
|
retval = -EIO;
|
|
goto out_free;
|
|
}
|
|
} else if (stream->ops == STREAM_OPS_CAPTURE) {
|
|
if (sst_capture_frame(str_id) < 0) {
|
|
pr_warn("capture frame fail\n");
|
|
mutex_unlock(&stream->lock);
|
|
retval = -EIO;
|
|
goto out_free;
|
|
}
|
|
}
|
|
}
|
|
mutex_unlock(&stream->lock);
|
|
/* Block the call for reply */
|
|
if (!list_empty(&stream->bufs)) {
|
|
stream->data_blk.on = true;
|
|
retval = sst_wait_interruptible(sst_drv_ctx,
|
|
&stream->data_blk);
|
|
}
|
|
|
|
if (retval >= 0)
|
|
retval = stream->cumm_bytes;
|
|
pr_debug("end of play/rec ioctl bytes = %d!!\n", retval);
|
|
|
|
out_free:
|
|
kfree(tmp_buf);
|
|
return retval;
|
|
}
|
|
|
|
/*sets user data buffers to play/capture*/
|
|
static int intel_sst_play_capture(struct stream_info *stream, int str_id)
|
|
{
|
|
int retval;
|
|
|
|
stream->data_blk.ret_code = 0;
|
|
stream->data_blk.on = true;
|
|
stream->data_blk.condition = false;
|
|
|
|
mutex_lock(&stream->lock);
|
|
if (stream->status == STREAM_INIT && stream->prev != STREAM_UN_INIT) {
|
|
/* stream is started */
|
|
stream->prev = stream->status;
|
|
stream->status = STREAM_RUNNING;
|
|
}
|
|
|
|
if (stream->status == STREAM_INIT && stream->prev == STREAM_UN_INIT) {
|
|
/* stream is not started yet */
|
|
pr_debug("Stream isn't in started state %d, prev %d\n",
|
|
stream->status, stream->prev);
|
|
} else if ((stream->status == STREAM_RUNNING ||
|
|
stream->status == STREAM_PAUSED) &&
|
|
stream->need_draining != true) {
|
|
/* stream is started */
|
|
if (stream->ops == STREAM_OPS_PLAYBACK ||
|
|
stream->ops == STREAM_OPS_PLAYBACK_DRM) {
|
|
if (sst_play_frame(str_id) < 0) {
|
|
pr_warn("play frames failed\n");
|
|
mutex_unlock(&stream->lock);
|
|
return -EIO;
|
|
}
|
|
} else if (stream->ops == STREAM_OPS_CAPTURE) {
|
|
if (sst_capture_frame(str_id) < 0) {
|
|
pr_warn("capture frames failed\n");
|
|
mutex_unlock(&stream->lock);
|
|
return -EIO;
|
|
}
|
|
}
|
|
} else {
|
|
mutex_unlock(&stream->lock);
|
|
return -EIO;
|
|
}
|
|
mutex_unlock(&stream->lock);
|
|
/* Block the call for reply */
|
|
|
|
retval = sst_wait_interruptible(sst_drv_ctx, &stream->data_blk);
|
|
if (retval) {
|
|
stream->status = STREAM_INIT;
|
|
pr_debug("wait returned error...\n");
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/* fills kernel list with buffer addresses for SST DSP driver to process*/
|
|
static int snd_sst_fill_kernel_list(struct stream_info *stream,
|
|
const struct iovec *iovec, unsigned long nr_segs,
|
|
struct list_head *copy_to_list)
|
|
{
|
|
struct sst_stream_bufs *stream_bufs;
|
|
unsigned long index, mmap_len;
|
|
unsigned char __user *bufp;
|
|
unsigned long size, copied_size;
|
|
int retval = 0, add_to_list = 0;
|
|
static int sent_offset;
|
|
static unsigned long sent_index;
|
|
|
|
#ifdef CONFIG_MRST_RAR_HANDLER
|
|
if (stream->ops == STREAM_OPS_PLAYBACK_DRM) {
|
|
for (index = stream->sg_index; index < nr_segs; index++) {
|
|
__u32 rar_handle;
|
|
struct sst_stream_bufs *stream_bufs =
|
|
kzalloc(sizeof(*stream_bufs), GFP_KERNEL);
|
|
|
|
stream->sg_index = index;
|
|
if (!stream_bufs)
|
|
return -ENOMEM;
|
|
if (copy_from_user((void *) &rar_handle,
|
|
iovec[index].iov_base,
|
|
sizeof(__u32)))
|
|
return -EFAULT;
|
|
stream_bufs->addr = (char *)rar_handle;
|
|
stream_bufs->in_use = false;
|
|
stream_bufs->size = iovec[0].iov_len;
|
|
/* locking here */
|
|
mutex_lock(&stream->lock);
|
|
list_add_tail(&stream_bufs->node, &stream->bufs);
|
|
mutex_unlock(&stream->lock);
|
|
}
|
|
stream->sg_index = index;
|
|
return retval;
|
|
}
|
|
#endif
|
|
stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL);
|
|
if (!stream_bufs)
|
|
return -ENOMEM;
|
|
stream_bufs->addr = sst_drv_ctx->mmap_mem;
|
|
mmap_len = sst_drv_ctx->mmap_len;
|
|
stream_bufs->addr = sst_drv_ctx->mmap_mem;
|
|
bufp = stream->cur_ptr;
|
|
|
|
copied_size = 0;
|
|
|
|
if (!stream->sg_index)
|
|
sent_index = sent_offset = 0;
|
|
|
|
for (index = stream->sg_index; index < nr_segs; index++) {
|
|
stream->sg_index = index;
|
|
if (!stream->cur_ptr)
|
|
bufp = iovec[index].iov_base;
|
|
|
|
size = ((unsigned long)iovec[index].iov_base
|
|
+ iovec[index].iov_len) - (unsigned long) bufp;
|
|
|
|
if ((copied_size + size) > mmap_len)
|
|
size = mmap_len - copied_size;
|
|
|
|
|
|
if (stream->ops == STREAM_OPS_PLAYBACK) {
|
|
if (copy_from_user((void *)
|
|
(stream_bufs->addr + copied_size),
|
|
bufp, size)) {
|
|
/* Clean up the list and return error code */
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
} else if (stream->ops == STREAM_OPS_CAPTURE) {
|
|
struct snd_sst_user_cap_list *entry =
|
|
kzalloc(sizeof(*entry), GFP_KERNEL);
|
|
|
|
if (!entry) {
|
|
kfree(stream_bufs);
|
|
return -ENOMEM;
|
|
}
|
|
entry->iov_index = index;
|
|
entry->iov_offset = (unsigned long) bufp -
|
|
(unsigned long)iovec[index].iov_base;
|
|
entry->offset = copied_size;
|
|
entry->size = size;
|
|
list_add_tail(&entry->node, copy_to_list);
|
|
}
|
|
|
|
stream->cur_ptr = bufp + size;
|
|
|
|
if (((unsigned long)iovec[index].iov_base
|
|
+ iovec[index].iov_len) <
|
|
((unsigned long)iovec[index].iov_base)) {
|
|
pr_debug("Buffer overflows\n");
|
|
kfree(stream_bufs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (((unsigned long)iovec[index].iov_base
|
|
+ iovec[index].iov_len) ==
|
|
(unsigned long)stream->cur_ptr) {
|
|
stream->cur_ptr = NULL;
|
|
stream->sg_index++;
|
|
}
|
|
|
|
copied_size += size;
|
|
pr_debug("copied_size - %lx\n", copied_size);
|
|
if ((copied_size >= mmap_len) ||
|
|
(stream->sg_index == nr_segs)) {
|
|
add_to_list = 1;
|
|
}
|
|
|
|
if (add_to_list) {
|
|
stream_bufs->in_use = false;
|
|
stream_bufs->size = copied_size;
|
|
/* locking here */
|
|
mutex_lock(&stream->lock);
|
|
list_add_tail(&stream_bufs->node, &stream->bufs);
|
|
mutex_unlock(&stream->lock);
|
|
break;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/* This function copies the captured data returned from SST DSP engine
|
|
* to the user buffers*/
|
|
static int snd_sst_copy_userbuf_capture(struct stream_info *stream,
|
|
const struct iovec *iovec,
|
|
struct list_head *copy_to_list)
|
|
{
|
|
struct snd_sst_user_cap_list *entry, *_entry;
|
|
struct sst_stream_bufs *kbufs = NULL, *_kbufs;
|
|
int retval = 0;
|
|
|
|
/* copy sent buffers */
|
|
pr_debug("capture stream copying to user now...\n");
|
|
list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) {
|
|
if (kbufs->in_use == true) {
|
|
/* copy to user */
|
|
list_for_each_entry_safe(entry, _entry,
|
|
copy_to_list, node) {
|
|
if (copy_to_user(iovec[entry->iov_index].iov_base + entry->iov_offset,
|
|
kbufs->addr + entry->offset,
|
|
entry->size)) {
|
|
/* Clean up the list and return error */
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
list_del(&entry->node);
|
|
kfree(entry);
|
|
}
|
|
}
|
|
}
|
|
pr_debug("end of cap copy\n");
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* snd_sst_userbufs_play_cap - constructs the list from user buffers
|
|
*
|
|
* @iovec:pointer to iovec structure
|
|
* @nr_segs:number entries in the iovec structure
|
|
* @str_id:stream id
|
|
* @stream:pointer to stream_info structure
|
|
*
|
|
* This function will traverse the user list and copy the data to the kernel
|
|
* space buffers.
|
|
*/
|
|
static int snd_sst_userbufs_play_cap(const struct iovec *iovec,
|
|
unsigned long nr_segs, unsigned int str_id,
|
|
struct stream_info *stream)
|
|
{
|
|
int retval;
|
|
LIST_HEAD(copy_to_list);
|
|
|
|
|
|
retval = snd_sst_fill_kernel_list(stream, iovec, nr_segs,
|
|
©_to_list);
|
|
|
|
retval = intel_sst_play_capture(stream, str_id);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
if (stream->ops == STREAM_OPS_CAPTURE) {
|
|
retval = snd_sst_copy_userbuf_capture(stream, iovec,
|
|
©_to_list);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/* This function is common function across read/write
|
|
for user buffers called from system calls*/
|
|
static int intel_sst_read_write(unsigned int str_id, char __user *buf,
|
|
size_t count)
|
|
{
|
|
int retval;
|
|
struct stream_info *stream;
|
|
struct iovec iovec;
|
|
unsigned long nr_segs;
|
|
|
|
retval = sst_validate_strid(str_id);
|
|
if (retval)
|
|
return -EINVAL;
|
|
stream = &sst_drv_ctx->streams[str_id];
|
|
if (stream->mmapped == true) {
|
|
pr_warn("user write and stream is mapped\n");
|
|
return -EIO;
|
|
}
|
|
if (!count)
|
|
return -EINVAL;
|
|
stream->curr_bytes = 0;
|
|
stream->cumm_bytes = 0;
|
|
/* copy user buf details */
|
|
pr_debug("new buffers %p, copy size %d, status %d\n" ,
|
|
buf, (int) count, (int) stream->status);
|
|
|
|
stream->buf_type = SST_BUF_USER_STATIC;
|
|
iovec.iov_base = buf;
|
|
iovec.iov_len = count;
|
|
nr_segs = 1;
|
|
|
|
do {
|
|
retval = snd_sst_userbufs_play_cap(
|
|
&iovec, nr_segs, str_id, stream);
|
|
if (retval < 0)
|
|
break;
|
|
|
|
} while (stream->sg_index < nr_segs);
|
|
|
|
stream->sg_index = 0;
|
|
stream->cur_ptr = NULL;
|
|
if (retval >= 0)
|
|
retval = stream->cumm_bytes;
|
|
pr_debug("end of play/rec bytes = %d!!\n", retval);
|
|
return retval;
|
|
}
|
|
|
|
/***
|
|
* intel_sst_write - This function is called when user tries to play out data
|
|
*
|
|
* @file_ptr:pointer to file
|
|
* @buf:user buffer to be played out
|
|
* @count:size of tthe buffer
|
|
* @offset:offset to start from
|
|
*
|
|
* writes the encoded data into DSP
|
|
*/
|
|
int intel_sst_write(struct file *file_ptr, const char __user *buf,
|
|
size_t count, loff_t *offset)
|
|
{
|
|
struct ioctl_pvt_data *data = file_ptr->private_data;
|
|
int str_id = data->str_id;
|
|
struct stream_info *stream = &sst_drv_ctx->streams[str_id];
|
|
|
|
pr_debug("called for %d\n", str_id);
|
|
if (stream->status == STREAM_UN_INIT ||
|
|
stream->status == STREAM_DECODE) {
|
|
return -EBADRQC;
|
|
}
|
|
return intel_sst_read_write(str_id, (char __user *)buf, count);
|
|
}
|
|
|
|
/*
|
|
* intel_sst_aio_write - write buffers
|
|
*
|
|
* @kiocb:pointer to a structure containing file pointer
|
|
* @iov:list of user buffer to be played out
|
|
* @nr_segs:number of entries
|
|
* @offset:offset to start from
|
|
*
|
|
* This function is called when user tries to play out multiple data buffers
|
|
*/
|
|
ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov,
|
|
unsigned long nr_segs, loff_t offset)
|
|
{
|
|
int retval;
|
|
struct ioctl_pvt_data *data = kiocb->ki_filp->private_data;
|
|
int str_id = data->str_id;
|
|
struct stream_info *stream;
|
|
|
|
pr_debug("entry - %ld\n", nr_segs);
|
|
|
|
if (is_sync_kiocb(kiocb) == false)
|
|
return -EINVAL;
|
|
|
|
pr_debug("called for str_id %d\n", str_id);
|
|
retval = sst_validate_strid(str_id);
|
|
if (retval)
|
|
return -EINVAL;
|
|
stream = &sst_drv_ctx->streams[str_id];
|
|
if (stream->mmapped == true)
|
|
return -EIO;
|
|
if (stream->status == STREAM_UN_INIT ||
|
|
stream->status == STREAM_DECODE) {
|
|
return -EBADRQC;
|
|
}
|
|
stream->curr_bytes = 0;
|
|
stream->cumm_bytes = 0;
|
|
pr_debug("new segs %ld, offset %d, status %d\n" ,
|
|
nr_segs, (int) offset, (int) stream->status);
|
|
stream->buf_type = SST_BUF_USER_STATIC;
|
|
do {
|
|
retval = snd_sst_userbufs_play_cap(iov, nr_segs,
|
|
str_id, stream);
|
|
if (retval < 0)
|
|
break;
|
|
|
|
} while (stream->sg_index < nr_segs);
|
|
|
|
stream->sg_index = 0;
|
|
stream->cur_ptr = NULL;
|
|
if (retval >= 0)
|
|
retval = stream->cumm_bytes;
|
|
pr_debug("end of play/rec bytes = %d!!\n", retval);
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* intel_sst_read - read the encoded data
|
|
*
|
|
* @file_ptr: pointer to file
|
|
* @buf: user buffer to be filled with captured data
|
|
* @count: size of tthe buffer
|
|
* @offset: offset to start from
|
|
*
|
|
* This function is called when user tries to capture data
|
|
*/
|
|
int intel_sst_read(struct file *file_ptr, char __user *buf,
|
|
size_t count, loff_t *offset)
|
|
{
|
|
struct ioctl_pvt_data *data = file_ptr->private_data;
|
|
int str_id = data->str_id;
|
|
struct stream_info *stream = &sst_drv_ctx->streams[str_id];
|
|
|
|
pr_debug("called for %d\n", str_id);
|
|
if (stream->status == STREAM_UN_INIT ||
|
|
stream->status == STREAM_DECODE)
|
|
return -EBADRQC;
|
|
return intel_sst_read_write(str_id, buf, count);
|
|
}
|
|
|
|
/*
|
|
* intel_sst_aio_read - aio read
|
|
*
|
|
* @kiocb: pointer to a structure containing file pointer
|
|
* @iov: list of user buffer to be filled with captured
|
|
* @nr_segs: number of entries
|
|
* @offset: offset to start from
|
|
*
|
|
* This function is called when user tries to capture out multiple data buffers
|
|
*/
|
|
ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov,
|
|
unsigned long nr_segs, loff_t offset)
|
|
{
|
|
int retval;
|
|
struct ioctl_pvt_data *data = kiocb->ki_filp->private_data;
|
|
int str_id = data->str_id;
|
|
struct stream_info *stream;
|
|
|
|
pr_debug("entry - %ld\n", nr_segs);
|
|
|
|
if (is_sync_kiocb(kiocb) == false) {
|
|
pr_debug("aio_read from user space is not allowed\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_debug("called for str_id %d\n", str_id);
|
|
retval = sst_validate_strid(str_id);
|
|
if (retval)
|
|
return -EINVAL;
|
|
stream = &sst_drv_ctx->streams[str_id];
|
|
if (stream->mmapped == true)
|
|
return -EIO;
|
|
if (stream->status == STREAM_UN_INIT ||
|
|
stream->status == STREAM_DECODE)
|
|
return -EBADRQC;
|
|
stream->curr_bytes = 0;
|
|
stream->cumm_bytes = 0;
|
|
|
|
pr_debug("new segs %ld, offset %d, status %d\n" ,
|
|
nr_segs, (int) offset, (int) stream->status);
|
|
stream->buf_type = SST_BUF_USER_STATIC;
|
|
do {
|
|
retval = snd_sst_userbufs_play_cap(iov, nr_segs,
|
|
str_id, stream);
|
|
if (retval < 0)
|
|
break;
|
|
|
|
} while (stream->sg_index < nr_segs);
|
|
|
|
stream->sg_index = 0;
|
|
stream->cur_ptr = NULL;
|
|
if (retval >= 0)
|
|
retval = stream->cumm_bytes;
|
|
pr_debug("end of play/rec bytes = %d!!\n", retval);
|
|
return retval;
|
|
}
|
|
|
|
/* sst_print_stream_params - prints the stream parameters (debug fn)*/
|
|
static void sst_print_stream_params(struct snd_sst_get_stream_params *get_prm)
|
|
{
|
|
pr_debug("codec params:result = %d\n",
|
|
get_prm->codec_params.result);
|
|
pr_debug("codec params:stream = %d\n",
|
|
get_prm->codec_params.stream_id);
|
|
pr_debug("codec params:codec = %d\n",
|
|
get_prm->codec_params.codec);
|
|
pr_debug("codec params:ops = %d\n",
|
|
get_prm->codec_params.ops);
|
|
pr_debug("codec params:stream_type = %d\n",
|
|
get_prm->codec_params.stream_type);
|
|
pr_debug("pcmparams:sfreq = %d\n",
|
|
get_prm->pcm_params.sfreq);
|
|
pr_debug("pcmparams:num_chan = %d\n",
|
|
get_prm->pcm_params.num_chan);
|
|
pr_debug("pcmparams:pcm_wd_sz = %d\n",
|
|
get_prm->pcm_params.pcm_wd_sz);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* sst_create_algo_ipc - create ipc msg for algorithm parameters
|
|
*
|
|
* @algo_params: Algorithm parameters
|
|
* @msg: post msg pointer
|
|
*
|
|
* This function is called to create ipc msg
|
|
*/
|
|
int sst_create_algo_ipc(struct snd_ppp_params *algo_params,
|
|
struct ipc_post **msg)
|
|
{
|
|
if (sst_create_large_msg(msg))
|
|
return -ENOMEM;
|
|
sst_fill_header(&(*msg)->header,
|
|
IPC_IA_ALG_PARAMS, 1, algo_params->str_id);
|
|
(*msg)->header.part.data = sizeof(u32) +
|
|
sizeof(*algo_params) + algo_params->size;
|
|
memcpy((*msg)->mailbox_data, &(*msg)->header, sizeof(u32));
|
|
memcpy((*msg)->mailbox_data + sizeof(u32),
|
|
algo_params, sizeof(*algo_params));
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sst_send_algo_ipc - send ipc msg for algorithm parameters
|
|
*
|
|
* @msg: post msg pointer
|
|
*
|
|
* This function is called to send ipc msg
|
|
*/
|
|
int sst_send_algo_ipc(struct ipc_post **msg)
|
|
{
|
|
sst_drv_ctx->ppp_params_blk.condition = false;
|
|
sst_drv_ctx->ppp_params_blk.ret_code = 0;
|
|
sst_drv_ctx->ppp_params_blk.on = true;
|
|
sst_drv_ctx->ppp_params_blk.data = NULL;
|
|
spin_lock(&sst_drv_ctx->list_spin_lock);
|
|
list_add_tail(&(*msg)->node, &sst_drv_ctx->ipc_dispatch_list);
|
|
spin_unlock(&sst_drv_ctx->list_spin_lock);
|
|
sst_post_message(&sst_drv_ctx->ipc_post_msg_wq);
|
|
return sst_wait_interruptible_timeout(sst_drv_ctx,
|
|
&sst_drv_ctx->ppp_params_blk, SST_BLOCK_TIMEOUT);
|
|
}
|
|
|
|
/**
|
|
* intel_sst_ioctl_dsp - receives the device ioctl's
|
|
*
|
|
* @cmd:Ioctl cmd
|
|
* @arg:data
|
|
*
|
|
* This function is called when a user space component
|
|
* sends a DSP Ioctl to SST driver
|
|
*/
|
|
long intel_sst_ioctl_dsp(unsigned int cmd, unsigned long arg)
|
|
{
|
|
int retval = 0;
|
|
struct snd_ppp_params algo_params;
|
|
struct snd_ppp_params *algo_params_copied;
|
|
struct ipc_post *msg;
|
|
|
|
switch (_IOC_NR(cmd)) {
|
|
case _IOC_NR(SNDRV_SST_SET_ALGO):
|
|
if (copy_from_user(&algo_params, (void __user *)arg,
|
|
sizeof(algo_params)))
|
|
return -EFAULT;
|
|
if (algo_params.size > SST_MAILBOX_SIZE)
|
|
return -EMSGSIZE;
|
|
|
|
pr_debug("Algo ID %d Str id %d Enable %d Size %d\n",
|
|
algo_params.algo_id, algo_params.str_id,
|
|
algo_params.enable, algo_params.size);
|
|
retval = sst_create_algo_ipc(&algo_params, &msg);
|
|
if (retval)
|
|
break;
|
|
algo_params.reserved = 0;
|
|
if (copy_from_user(msg->mailbox_data + sizeof(algo_params),
|
|
algo_params.params, algo_params.size))
|
|
return -EFAULT;
|
|
|
|
retval = sst_send_algo_ipc(&msg);
|
|
if (retval) {
|
|
pr_debug("Error in sst_set_algo = %d\n", retval);
|
|
retval = -EIO;
|
|
}
|
|
break;
|
|
|
|
case _IOC_NR(SNDRV_SST_GET_ALGO):
|
|
if (copy_from_user(&algo_params, (void __user *)arg,
|
|
sizeof(algo_params)))
|
|
return -EFAULT;
|
|
pr_debug("Algo ID %d Str id %d Enable %d Size %d\n",
|
|
algo_params.algo_id, algo_params.str_id,
|
|
algo_params.enable, algo_params.size);
|
|
retval = sst_create_algo_ipc(&algo_params, &msg);
|
|
if (retval)
|
|
break;
|
|
algo_params.reserved = 1;
|
|
retval = sst_send_algo_ipc(&msg);
|
|
if (retval) {
|
|
pr_debug("Error in sst_get_algo = %d\n", retval);
|
|
retval = -EIO;
|
|
break;
|
|
}
|
|
algo_params_copied = (struct snd_ppp_params *)
|
|
sst_drv_ctx->ppp_params_blk.data;
|
|
if (algo_params_copied->size > algo_params.size) {
|
|
pr_debug("mem insufficient to copy\n");
|
|
retval = -EMSGSIZE;
|
|
goto free_mem;
|
|
} else {
|
|
char __user *tmp;
|
|
|
|
if (copy_to_user(algo_params.params,
|
|
algo_params_copied->params,
|
|
algo_params_copied->size)) {
|
|
retval = -EFAULT;
|
|
goto free_mem;
|
|
}
|
|
tmp = (char __user *)arg + offsetof(
|
|
struct snd_ppp_params, size);
|
|
if (copy_to_user(tmp, &algo_params_copied->size,
|
|
sizeof(__u32))) {
|
|
retval = -EFAULT;
|
|
goto free_mem;
|
|
}
|
|
|
|
}
|
|
free_mem:
|
|
kfree(algo_params_copied->params);
|
|
kfree(algo_params_copied);
|
|
break;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
int sst_ioctl_tuning_params(unsigned long arg)
|
|
{
|
|
struct snd_sst_tuning_params params;
|
|
struct ipc_post *msg;
|
|
|
|
if (copy_from_user(¶ms, (void __user *)arg, sizeof(params)))
|
|
return -EFAULT;
|
|
if (params.size > SST_MAILBOX_SIZE)
|
|
return -ENOMEM;
|
|
pr_debug("Parameter %d, Stream %d, Size %d\n", params.type,
|
|
params.str_id, params.size);
|
|
if (sst_create_large_msg(&msg))
|
|
return -ENOMEM;
|
|
|
|
sst_fill_header(&msg->header, IPC_IA_TUNING_PARAMS, 1, params.str_id);
|
|
msg->header.part.data = sizeof(u32) + sizeof(params) + params.size;
|
|
memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32));
|
|
memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params));
|
|
if (copy_from_user(msg->mailbox_data + sizeof(params),
|
|
(void __user *)(unsigned long)params.addr,
|
|
params.size)) {
|
|
kfree(msg->mailbox_data);
|
|
kfree(msg);
|
|
return -EFAULT;
|
|
}
|
|
return sst_send_algo_ipc(&msg);
|
|
}
|
|
/**
|
|
* intel_sst_ioctl - receives the device ioctl's
|
|
* @file_ptr:pointer to file
|
|
* @cmd:Ioctl cmd
|
|
* @arg:data
|
|
*
|
|
* This function is called by OS when a user space component
|
|
* sends an Ioctl to SST driver
|
|
*/
|
|
long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int retval = 0;
|
|
struct ioctl_pvt_data *data = NULL;
|
|
int str_id = 0, minor = 0;
|
|
|
|
data = file_ptr->private_data;
|
|
if (data) {
|
|
minor = 0;
|
|
str_id = data->str_id;
|
|
} else
|
|
minor = 1;
|
|
|
|
if (sst_drv_ctx->sst_state != SST_FW_RUNNING)
|
|
return -EBUSY;
|
|
|
|
switch (_IOC_NR(cmd)) {
|
|
case _IOC_NR(SNDRV_SST_STREAM_PAUSE):
|
|
pr_debug("IOCTL_PAUSE received for %d!\n", str_id);
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
retval = sst_pause_stream(str_id);
|
|
break;
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_RESUME):
|
|
pr_debug("SNDRV_SST_IOCTL_RESUME received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
retval = sst_resume_stream(str_id);
|
|
break;
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): {
|
|
struct snd_sst_params str_param;
|
|
|
|
pr_debug("IOCTL_SET_PARAMS received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
|
|
if (copy_from_user(&str_param, (void __user *)arg,
|
|
sizeof(str_param))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
if (!str_id) {
|
|
|
|
retval = sst_get_stream(&str_param);
|
|
if (retval > 0) {
|
|
struct stream_info *str_info;
|
|
char __user *dest;
|
|
|
|
sst_drv_ctx->stream_cnt++;
|
|
data->str_id = retval;
|
|
str_info = &sst_drv_ctx->streams[retval];
|
|
str_info->src = SST_DRV;
|
|
dest = (char __user *)arg + offsetof(struct snd_sst_params, stream_id);
|
|
retval = copy_to_user(dest, &retval, sizeof(__u32));
|
|
if (retval)
|
|
retval = -EFAULT;
|
|
} else {
|
|
if (retval == -SST_ERR_INVALID_PARAMS)
|
|
retval = -EINVAL;
|
|
}
|
|
} else {
|
|
pr_debug("SET_STREAM_PARAMS received!\n");
|
|
/* allocated set params only */
|
|
retval = sst_set_stream_param(str_id, &str_param);
|
|
/* Block the call for reply */
|
|
if (!retval) {
|
|
int sfreq = 0, word_size = 0, num_channel = 0;
|
|
sfreq = str_param.sparams.uc.pcm_params.sfreq;
|
|
word_size = str_param.sparams.uc.pcm_params.pcm_wd_sz;
|
|
num_channel = str_param.sparams.uc.pcm_params.num_chan;
|
|
if (str_param.ops == STREAM_OPS_CAPTURE) {
|
|
sst_drv_ctx->scard_ops->\
|
|
set_pcm_audio_params(sfreq,
|
|
word_size, num_channel);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case _IOC_NR(SNDRV_SST_SET_VOL): {
|
|
struct snd_sst_vol set_vol;
|
|
|
|
if (copy_from_user(&set_vol, (void __user *)arg,
|
|
sizeof(set_vol))) {
|
|
pr_debug("copy failed\n");
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
pr_debug("SET_VOLUME received for %d!\n",
|
|
set_vol.stream_id);
|
|
if (minor == STREAM_MODULE && set_vol.stream_id == 0) {
|
|
pr_debug("invalid operation!\n");
|
|
retval = -EPERM;
|
|
break;
|
|
}
|
|
retval = sst_set_vol(&set_vol);
|
|
break;
|
|
}
|
|
case _IOC_NR(SNDRV_SST_GET_VOL): {
|
|
struct snd_sst_vol get_vol;
|
|
|
|
if (copy_from_user(&get_vol, (void __user *)arg,
|
|
sizeof(get_vol))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
pr_debug("IOCTL_GET_VOLUME received for stream = %d!\n",
|
|
get_vol.stream_id);
|
|
if (minor == STREAM_MODULE && get_vol.stream_id == 0) {
|
|
pr_debug("invalid operation!\n");
|
|
retval = -EPERM;
|
|
break;
|
|
}
|
|
retval = sst_get_vol(&get_vol);
|
|
if (retval) {
|
|
retval = -EIO;
|
|
break;
|
|
}
|
|
pr_debug("id:%d\n, vol:%d, ramp_dur:%d, ramp_type:%d\n",
|
|
get_vol.stream_id, get_vol.volume,
|
|
get_vol.ramp_duration, get_vol.ramp_type);
|
|
if (copy_to_user((struct snd_sst_vol __user *)arg,
|
|
&get_vol, sizeof(get_vol))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
/*sst_print_get_vol_info(str_id, &get_vol);*/
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_MUTE): {
|
|
struct snd_sst_mute set_mute;
|
|
|
|
if (copy_from_user(&set_mute, (void __user *)arg,
|
|
sizeof(set_mute))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
pr_debug("SNDRV_SST_SET_VOLUME received for %d!\n",
|
|
set_mute.stream_id);
|
|
if (minor == STREAM_MODULE && set_mute.stream_id == 0) {
|
|
retval = -EPERM;
|
|
break;
|
|
}
|
|
retval = sst_set_mute(&set_mute);
|
|
break;
|
|
}
|
|
case _IOC_NR(SNDRV_SST_STREAM_GET_PARAMS): {
|
|
struct snd_sst_get_stream_params get_params;
|
|
|
|
pr_debug("IOCTL_GET_PARAMS received!\n");
|
|
if (minor != 0) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
|
|
retval = sst_get_stream_params(str_id, &get_params);
|
|
if (retval) {
|
|
retval = -EIO;
|
|
break;
|
|
}
|
|
if (copy_to_user((struct snd_sst_get_stream_params __user *)arg,
|
|
&get_params, sizeof(get_params))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
sst_print_stream_params(&get_params);
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_MMAP_PLAY):
|
|
case _IOC_NR(SNDRV_SST_MMAP_CAPTURE): {
|
|
struct snd_sst_mmap_buffs mmap_buf;
|
|
|
|
pr_debug("SNDRV_SST_MMAP_PLAY/CAPTURE received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
if (copy_from_user(&mmap_buf, (void __user *)arg,
|
|
sizeof(mmap_buf))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
retval = intel_sst_mmap_play_capture(str_id, &mmap_buf);
|
|
break;
|
|
}
|
|
case _IOC_NR(SNDRV_SST_STREAM_DROP):
|
|
pr_debug("SNDRV_SST_IOCTL_DROP received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
retval = sst_drop_stream(str_id);
|
|
break;
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_GET_TSTAMP): {
|
|
struct snd_sst_tstamp tstamp = {0};
|
|
unsigned long long time, freq, mod;
|
|
|
|
pr_debug("SNDRV_SST_STREAM_GET_TSTAMP received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
memcpy_fromio(&tstamp,
|
|
sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp),
|
|
sizeof(tstamp));
|
|
time = tstamp.samples_rendered;
|
|
freq = (unsigned long long) tstamp.sampling_frequency;
|
|
time = time * 1000; /* converting it to ms */
|
|
mod = do_div(time, freq);
|
|
if (copy_to_user((void __user *)arg, &time,
|
|
sizeof(unsigned long long)))
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_START):{
|
|
struct stream_info *stream;
|
|
|
|
pr_debug("SNDRV_SST_STREAM_START received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
retval = sst_validate_strid(str_id);
|
|
if (retval)
|
|
break;
|
|
stream = &sst_drv_ctx->streams[str_id];
|
|
mutex_lock(&stream->lock);
|
|
if (stream->status == STREAM_INIT &&
|
|
stream->need_draining != true) {
|
|
stream->prev = stream->status;
|
|
stream->status = STREAM_RUNNING;
|
|
if (stream->ops == STREAM_OPS_PLAYBACK ||
|
|
stream->ops == STREAM_OPS_PLAYBACK_DRM) {
|
|
retval = sst_play_frame(str_id);
|
|
} else if (stream->ops == STREAM_OPS_CAPTURE)
|
|
retval = sst_capture_frame(str_id);
|
|
else {
|
|
retval = -EINVAL;
|
|
mutex_unlock(&stream->lock);
|
|
break;
|
|
}
|
|
if (retval < 0) {
|
|
stream->status = STREAM_INIT;
|
|
mutex_unlock(&stream->lock);
|
|
break;
|
|
}
|
|
} else {
|
|
retval = -EINVAL;
|
|
}
|
|
mutex_unlock(&stream->lock);
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_SET_TARGET_DEVICE): {
|
|
struct snd_sst_target_device target_device;
|
|
|
|
pr_debug("SET_TARGET_DEVICE received!\n");
|
|
if (copy_from_user(&target_device, (void __user *)arg,
|
|
sizeof(target_device))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
if (minor != AM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
retval = sst_target_device_select(&target_device);
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_DRIVER_INFO): {
|
|
struct snd_sst_driver_info info;
|
|
|
|
pr_debug("SNDRV_SST_DRIVER_INFO received\n");
|
|
info.version = SST_VERSION_NUM;
|
|
/* hard coding, shud get sumhow later */
|
|
info.active_pcm_streams = sst_drv_ctx->stream_cnt -
|
|
sst_drv_ctx->encoded_cnt;
|
|
info.active_enc_streams = sst_drv_ctx->encoded_cnt;
|
|
info.max_pcm_streams = MAX_ACTIVE_STREAM - MAX_ENC_STREAM;
|
|
info.max_enc_streams = MAX_ENC_STREAM;
|
|
info.buf_per_stream = sst_drv_ctx->mmap_len;
|
|
if (copy_to_user((void __user *)arg, &info,
|
|
sizeof(info)))
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_DECODE): {
|
|
struct snd_sst_dbufs param;
|
|
struct snd_sst_dbufs dbufs_local;
|
|
struct snd_sst_buffs ibufs, obufs;
|
|
struct snd_sst_buff_entry *ibuf_tmp, *obuf_tmp;
|
|
char __user *dest;
|
|
|
|
pr_debug("SNDRV_SST_STREAM_DECODE received\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
if (copy_from_user(¶m, (void __user *)arg,
|
|
sizeof(param))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
dbufs_local.input_bytes_consumed = param.input_bytes_consumed;
|
|
dbufs_local.output_bytes_produced =
|
|
param.output_bytes_produced;
|
|
|
|
if (copy_from_user(&ibufs, (void __user *)param.ibufs, sizeof(ibufs))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
if (copy_from_user(&obufs, (void __user *)param.obufs, sizeof(obufs))) {
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
ibuf_tmp = kcalloc(ibufs.entries, sizeof(*ibuf_tmp), GFP_KERNEL);
|
|
obuf_tmp = kcalloc(obufs.entries, sizeof(*obuf_tmp), GFP_KERNEL);
|
|
if (!ibuf_tmp || !obuf_tmp) {
|
|
retval = -ENOMEM;
|
|
goto free_iobufs;
|
|
}
|
|
|
|
if (copy_from_user(ibuf_tmp, (void __user *)ibufs.buff_entry,
|
|
ibufs.entries * sizeof(*ibuf_tmp))) {
|
|
retval = -EFAULT;
|
|
goto free_iobufs;
|
|
}
|
|
ibufs.buff_entry = ibuf_tmp;
|
|
dbufs_local.ibufs = &ibufs;
|
|
|
|
if (copy_from_user(obuf_tmp, (void __user *)obufs.buff_entry,
|
|
obufs.entries * sizeof(*obuf_tmp))) {
|
|
retval = -EFAULT;
|
|
goto free_iobufs;
|
|
}
|
|
obufs.buff_entry = obuf_tmp;
|
|
dbufs_local.obufs = &obufs;
|
|
|
|
retval = sst_decode(str_id, &dbufs_local);
|
|
if (retval) {
|
|
retval = -EAGAIN;
|
|
goto free_iobufs;
|
|
}
|
|
|
|
dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed);
|
|
if (copy_to_user(dest,
|
|
&dbufs_local.input_bytes_consumed,
|
|
sizeof(unsigned long long))) {
|
|
retval = -EFAULT;
|
|
goto free_iobufs;
|
|
}
|
|
|
|
dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed);
|
|
if (copy_to_user(dest,
|
|
&dbufs_local.output_bytes_produced,
|
|
sizeof(unsigned long long))) {
|
|
retval = -EFAULT;
|
|
goto free_iobufs;
|
|
}
|
|
free_iobufs:
|
|
kfree(ibuf_tmp);
|
|
kfree(obuf_tmp);
|
|
break;
|
|
}
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_DRAIN):
|
|
pr_debug("SNDRV_SST_STREAM_DRAIN received\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
retval = sst_drain_stream(str_id);
|
|
break;
|
|
|
|
case _IOC_NR(SNDRV_SST_STREAM_BYTES_DECODED): {
|
|
unsigned long long __user *bytes = (unsigned long long __user *)arg;
|
|
struct snd_sst_tstamp tstamp = {0};
|
|
|
|
pr_debug("STREAM_BYTES_DECODED received!\n");
|
|
if (minor != STREAM_MODULE) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
memcpy_fromio(&tstamp,
|
|
sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp),
|
|
sizeof(tstamp));
|
|
if (copy_to_user(bytes, &tstamp.bytes_processed,
|
|
sizeof(*bytes)))
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
case _IOC_NR(SNDRV_SST_FW_INFO): {
|
|
struct snd_sst_fw_info *fw_info;
|
|
|
|
pr_debug("SNDRV_SST_FW_INFO received\n");
|
|
|
|
fw_info = kzalloc(sizeof(*fw_info), GFP_ATOMIC);
|
|
if (!fw_info) {
|
|
retval = -ENOMEM;
|
|
break;
|
|
}
|
|
retval = sst_get_fw_info(fw_info);
|
|
if (retval) {
|
|
retval = -EIO;
|
|
kfree(fw_info);
|
|
break;
|
|
}
|
|
if (copy_to_user((struct snd_sst_dbufs __user *)arg,
|
|
fw_info, sizeof(*fw_info))) {
|
|
kfree(fw_info);
|
|
retval = -EFAULT;
|
|
break;
|
|
}
|
|
/*sst_print_fw_info(fw_info);*/
|
|
kfree(fw_info);
|
|
break;
|
|
}
|
|
case _IOC_NR(SNDRV_SST_GET_ALGO):
|
|
case _IOC_NR(SNDRV_SST_SET_ALGO):
|
|
if (minor != AM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
retval = intel_sst_ioctl_dsp(cmd, arg);
|
|
break;
|
|
|
|
case _IOC_NR(SNDRV_SST_TUNING_PARAMS):
|
|
if (minor != AM_MODULE) {
|
|
retval = -EBADRQC;
|
|
break;
|
|
}
|
|
retval = sst_ioctl_tuning_params(arg);
|
|
break;
|
|
|
|
default:
|
|
retval = -EINVAL;
|
|
}
|
|
pr_debug("intel_sst_ioctl:complete ret code = %d\n", retval);
|
|
return retval;
|
|
}
|
|
|