[alsa-devel] [PATCH] [RFC 5/13] Intel SST driver interface module
Vinod Koul
vinod.koul at intel.com
Fri Jul 3 09:03:58 CEST 2009
This patch adds the SST driver interface modules. Interface
module is the one which talks to other layers of SST drivers.
intel_sst_interface.c - This file implements the MAD driver
registration and de registration functions. SST driver is also
a character driver that allows players/middleware to communicate
with SST driver. All char driver routines are implemented here.
The ioctls used by middleware to open/close, control and
configure stream and transfer the data are implemented here
Signed-off-by: Vinod Koul <vinod.koul at intel.com>
Signed-off-by: Harsha Priya <priya.harsha at intel.com>
Signed-off-by: R Dharageswari <dharageswari.r at intel.com>
new file: sound/pci/sst/intel_sst_interface.c
---
sound/pci/sst/intel_sst_interface.c | 1359 +++++++++++++++++++++++++++++++++++
1 files changed, 1359 insertions(+), 0 deletions(-)
create mode 100644 sound/pci/sst/intel_sst_interface.c
diff --git a/sound/pci/sst/intel_sst_interface.c b/sound/pci/sst/intel_sst_interface.c
new file mode 100644
index 0000000..f8cefa5
--- /dev/null
+++ b/sound/pci/sst/intel_sst_interface.c
@@ -0,0 +1,1359 @@
+/*
+ * intel_sst_interface.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-09 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul at intel.com>
+ * Harsha Priya <priya.harsha at intel.com>
+ * R Dharageswari <dharageswari.r at 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
+ */
+
+#include <linux/cdev.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <asm/div64.h>
+#include <linux/ioctl.h>
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intel_sst_fw_ipc.h"
+#include "intel_sst_common.h"
+#include "intel_sst_pvt.h"
+#ifdef CONFIG_SST_OSPM_SUPPORT
+#include <linux/intel_mid.h>
+#endif
+
+
+int sst_download_fw(void)
+{
+ int retval = 0;
+ const struct firmware *fw_sst;
+
+ sst_dbg("SST Downloading FW now...\n");
+ retval = request_firmware(&fw_sst,
+ SST_FW_STD_FILENAME,
+ &sst_ops->pci->dev);
+ if (0 != retval) {
+ sst_err("fw load failed %d \n", retval);
+ return retval;
+ }
+ sst_ops->alloc_block[0].sst_id = 0xFF;
+ sst_load_fw(fw_sst, NULL);
+ retval = sst_wait_timeout(sst_ops, &sst_ops->alloc_block[0]);
+ release_firmware(fw_sst);
+ sst_ops->alloc_block[0].sst_id = BLOCK_UNINIT;
+ return retval;
+}
+
+/**
+* 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)
+{
+ dev_t device = i_node->i_rdev;
+ unsigned int retval = 0;
+ struct ioctl_pvt_data *data = NULL;
+
+ if (sst_ops->pmic_state != PMIC_SND_INIT_DONE) {
+ sst_err("Sound card not availble \n");
+ return -EIO;
+ }
+
+ if (SST_UN_INIT == sst_ops->sst_state) {
+ /*FW is not downloaded*/
+ retval = sst_download_fw();
+ if (retval != 0) {
+ sst_err("FW download failed...abort\n");
+ return -ENODEV;
+ }
+ }
+ if (device == MKDEV(INTEL_SST_MAJOR, 0)) {
+ /*app open*/
+ if (sst_ops->active_cnt < MAX_ENC_STREAM) {
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (NULL == data)
+ return -ENOMEM;
+ sst_ops->active_cnt++;
+ sst_ops->stream_cnt++;
+ data->pvt_id = sst_assign_pvt_id(sst_ops);
+ data->str_id = 0;
+ file_ptr->private_data = (void *)data;
+ sst_dbg("sst id allocated = %d!\n", data->pvt_id);
+ } else
+ retval = -EACCES;
+ } else if (device == MKDEV(INTEL_SST_MAJOR, 1)) {
+ /*audio manager open*/
+ if (sst_ops->am_cnt < MAX_AM_HANDLES) {
+ sst_ops->am_cnt++;
+ sst_dbg("AM handle opened...\n");
+ } else
+ retval = -EACCES;
+ } else
+ retval = -EINVAL;
+ 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)
+{
+ dev_t device = i_node->i_rdev;
+ struct ioctl_pvt_data *data =
+ (struct ioctl_pvt_data *)file_ptr->private_data;
+
+ sst_dbg("Release called \n");
+ if (device == MKDEV(INTEL_SST_MAJOR, 0)) {
+ /*app close*/
+ sst_dbg("Closing app handle \n");
+ sst_ops->active_cnt--;
+ sst_ops->stream_cnt--;
+ if (0 != sst_free_stream(data->str_id)) {
+ if (sst_validate_strid(data->str_id) == 0)
+ sst_clean_stream(
+ &sst_ops->streams[data->str_id]);
+ }
+ kfree(data);
+ } else if (device == MKDEV(INTEL_SST_MAJOR, 1))
+ /*audio manager close*/
+ sst_dbg("AM handle closed \n");
+ sst_ops->am_cnt--;
+ return 0;
+}
+
+int intel_sst_mmap(struct file *file_ptr, struct vm_area_struct *vma)
+{
+ int retval = 0, length = 0;
+ struct ioctl_pvt_data *data =
+ (struct ioctl_pvt_data *)file_ptr->private_data;
+ int str_id = data->str_id;
+ void *mem_area = NULL;
+
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return -EINVAL;
+
+ length = vma->vm_end - vma->vm_start;
+ sst_dbg("called for stream %d length 0x%x\n", str_id, length);
+
+ if (length > sst_ops->mmap_len)
+ return -ENOMEM;
+ if (sst_ops->mmap_mem == NULL)
+ return -EIO;
+
+ /* round it up to the page bondary */
+ mem_area = (void *)((((unsigned long)sst_ops->mmap_mem) + PAGE_SIZE - 1)
+ & PAGE_MASK);
+
+ /* 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 != 0) {
+ sst_ops->streams[str_id].mmapped = false;
+ sst_err("mapping failed %d", retval);
+ } else
+ sst_ops->streams[str_id].mmapped = true;
+
+ sst_dbg("mmap ret 0x%x \n", retval);
+ return retval;
+}
+/**
+* intel_sst_write- send data to play out
+*/
+int intel_sst_mmap_play_capture(u32 str_id, struct snd_sst_buffs *mmap_buf)
+{
+ struct sst_stream_bufs *bufs = NULL;
+ int retval = 0, i;
+ struct stream_info *stream = NULL;
+ struct snd_sst_buff_entry *buf_entry = NULL;
+
+ sst_dbg("called for str_id %d \n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0) {
+ sst_err("val failed %d", retval);
+ return -EINVAL;
+ }
+ BUG_ON(!mmap_buf);
+
+ stream = &sst_ops->streams[str_id];
+ if (stream->mmapped != true) {
+ sst_err("stream not mapped!!!");
+ return -EIO;
+ }
+
+ if (stream->status != STREAM_RUNNING &&
+ stream->status != STREAM_INIT) {
+ sst_err("BAD REQUEST!, stream state is %d\n", stream->status);
+ return -EBADRQC;
+ }
+
+ sst_dbg("new buffers count %d status %d\n",
+ mmap_buf->entries, stream->status);
+ buf_entry = mmap_buf->buff;
+ for (i = 0; i < mmap_buf->entries; i++) {
+ BUG_ON(!buf_entry);
+ bufs = kzalloc(sizeof(*bufs), GFP_KERNEL);
+ if (bufs == NULL)
+ return -ENOMEM;
+ bufs->size = buf_entry->size;
+ bufs->offset = buf_entry->buffer.offset;
+ bufs->addr = sst_ops->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->prev = stream->status;
+ stream->status = STREAM_RUNNING;
+ if (stream->ops == STREAM_OPS_PLAYBACK) {
+ sst_dbg("play frames...\n");
+ if (sst_play_frame(str_id) < 0) {
+ sst_err("play frames failed \n");
+ mutex_unlock(&stream->lock);
+ return -EIO;
+ }
+ } else if (stream->ops == STREAM_OPS_CAPTURE) {
+ sst_dbg("capture frames...\n");
+ if (sst_capture_frame(str_id) < 0) {
+ sst_err("capture frames failed \n");
+ mutex_unlock(&stream->lock);
+ return -EIO;
+ }
+ }
+ }
+ mutex_unlock(&stream->lock);
+ /*Block the call for reply*/
+ if (0 == list_empty(&stream->bufs)) {
+ sst_dbg("ioctl waiting...\n");
+ stream->data_blk.on = true;
+ retval = sst_wait_interruptible(sst_ops, &stream->data_blk);
+ }
+
+ sst_dbg("end of play/rec ioctl!!\n");
+ return retval;
+}
+
+int sst_play_capture(struct stream_info *stream, int str_id)
+{
+ int retval;
+
+ stream->data_blk.condition = false;
+ stream->data_blk.ret_code = 0;
+ 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*/
+ sst_err("Stream isnt started yet state %d, prev %d \n",
+ stream->status, stream->prev);
+ } else if (stream->status == STREAM_RUNNING) {
+ /*stream is started*/
+ if (stream->ops == STREAM_OPS_PLAYBACK) {
+ if (sst_play_frame(str_id) < 0) {
+ sst_err("play frames failed \n");
+ mutex_unlock(&stream->lock);
+ return -EIO;
+ }
+ } else if (stream->ops == STREAM_OPS_CAPTURE) {
+ if (sst_capture_frame(str_id) < 0) {
+ sst_err("capture frames failed \n");
+ mutex_unlock(&stream->lock);
+ return -EIO;
+ }
+ }
+ } else
+ sst_err("Stream state %d invalid will still block, prev %d\n",
+ stream->status, stream->prev);
+ mutex_unlock(&stream->lock);
+ /*Block the call for reply*/
+ sst_dbg("write waiting...\n");
+ stream->data_blk.on = true;
+ retval = sst_wait_interruptible(sst_ops, &stream->data_blk);
+ if (retval != 0) {
+ stream->status = STREAM_INIT;
+ sst_dbg("wait returned error...\n");
+ }
+ return retval;
+}
+
+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 = NULL;
+ unsigned long index, data_not_copied, mmap_len;
+ unsigned char *bufp;
+ unsigned long size, copied_size;
+ int retval = 0, add_to_list = 0;
+ static int sent_offset;
+ static unsigned long sent_index;
+
+ stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL);
+ if (stream_bufs == NULL) {
+ sst_err("memory allocation failed \n");
+ return -ENOMEM;
+ }
+
+ mmap_len = sst_ops->mmap_len;
+ stream_bufs->addr = sst_ops->mmap_mem;
+ bufp = stream->cur_ptr;
+
+ sst_dbg("mmap_len - %lx\n", mmap_len);
+ copied_size = 0;
+
+ if (stream->sg_index == 0)
+ sent_index = sent_offset = 0;
+
+ for (index = stream->sg_index; index < nr_segs; index++) {
+ stream->sg_index = index;
+ sst_dbg("index - %lx, cur_ptr - %p\n", index, stream->cur_ptr);
+ sst_dbg("base - %p, size - 0x%x\n", iovec[index].iov_base,
+ iovec[index].iov_len);
+ sst_dbg("bufp - %p\n", bufp);
+ if (stream->cur_ptr == NULL)
+ bufp = iovec[index].iov_base;
+
+ size = ((unsigned long)iovec[index].iov_base
+ + iovec[index].iov_len) - (unsigned long) bufp;
+
+ sst_dbg("size - %lx\n", size);
+ if ((copied_size + size) > mmap_len)
+ size = mmap_len - copied_size;
+
+ sst_dbg("size - %lx\n", size);
+
+ if (stream->ops == STREAM_OPS_PLAYBACK) {
+ sst_dbg("Playback stream copying now....\n");
+ data_not_copied = copy_from_user(
+ (void *)(stream_bufs->addr + copied_size),
+ bufp, size);
+ if (data_not_copied > 0) {
+ /*Clean up the list and return error code */
+ sst_err("copy from user not copied -%ld\n",
+ data_not_copied);
+ retval = -EIO;
+ break;
+ }
+ } else {
+ struct snd_sst_user_cap_list *entry =
+ kzalloc(sizeof(*entry), GFP_KERNEL);
+
+ if (entry == NULL) {
+ sst_err("mem alloacation failed \n");
+ 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;
+ sst_dbg("ENTRY:ioindx %d,iooff %ld,koff %ld,ksz %ld \n",
+ entry->iov_index, entry->iov_offset,
+ entry->offset, entry->size);
+ list_add_tail(&entry->node, copy_to_list);
+ }
+
+ sst_dbg("cur_ptr - %lx\n", (unsigned long) stream->cur_ptr);
+ stream->cur_ptr = bufp + size;
+
+ 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;
+ sst_dbg("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;
+}
+
+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;
+ unsigned long data_not_copied;
+
+ /*copy sent buffers*/
+ sst_dbg("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) {
+ sst_dbg("filling now... \n");
+ sst_dbg("iindx %d,ioff %ld,koff %ld,ksz %ld \n",
+ entry->iov_index, entry->iov_offset,
+ entry->offset, entry->size);
+ sst_dbg("Copying at %p size %lx\n",
+ iovec[entry->iov_index].iov_base +
+ entry->iov_offset,
+ entry->size);
+ data_not_copied = copy_to_user((void *)
+ iovec[entry->iov_index].iov_base +
+ entry->iov_offset,
+ kbufs->addr + entry->offset,
+ entry->size);
+ if (data_not_copied > 0) {
+ /*Clean up the list and return error*/
+ sst_err("copy to user err -%ld\n",
+ data_not_copied);
+ retval = -EIO;
+ break;
+ }
+ list_del(&entry->node);
+ kfree(entry);
+ }
+ sst_dbg("coming out of loop\n");
+ }
+ }
+ sst_dbg("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.
+ */
+int snd_sst_userbufs_play_cap(const struct iovec *iovec,
+ unsigned long nr_segs, unsigned int str_id,
+ struct stream_info *stream)
+{
+ int retval = 0;
+ LIST_HEAD(copy_to_list);
+
+
+ retval = snd_sst_fill_kernel_list(stream, iovec, nr_segs,
+ ©_to_list);
+
+ retval = 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;
+}
+
+int intel_sst_read_write(unsigned int str_id, char __user *buf, size_t count)
+{
+ int retval = 0;
+ struct stream_info *stream = NULL;
+ struct iovec iovec;
+ unsigned long nr_segs;
+
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return -EINVAL;
+ stream = &sst_ops->streams[str_id];
+ if (stream->mmapped == true) {
+ sst_err("user write & stream is mapped!!!");
+ return -EIO;
+ }
+ if (count == 0) {
+ sst_err("args invalid %d", retval);
+ return -EINVAL;
+ }
+ /*copy user buf details*/
+ if (stream->status != STREAM_RUNNING && stream->status != STREAM_INIT) {
+ sst_err("BAD REQUEST!\n");
+ return -EBADRQC;
+ }
+ sst_dbg("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 = (void *)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;
+ return retval;
+}
+
+int intel_sst_write(struct file *file_ptr, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct ioctl_pvt_data *data =
+ (struct ioctl_pvt_data *)file_ptr->private_data;
+ int str_id = data->str_id;
+
+ sst_dbg("called for %d\n", str_id);
+ return intel_sst_read_write(str_id, (char __user *)buf, count);
+}
+
+/*
+ *
+ * intel_sst_aio_write- send data to play out
+ *
+ */
+ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t offset)
+{
+ int retval = 0;
+ struct ioctl_pvt_data *data =
+ (struct ioctl_pvt_data *)kiocb->ki_filp->private_data;
+ int str_id = data->str_id;
+ struct stream_info *stream = NULL;
+
+ sst_dbg("entry - %ld\n", nr_segs);
+
+ if (is_sync_kiocb(kiocb) == false) {
+ sst_dbg("aio_read from user space is not allowed\n");
+ return -EINVAL;
+ }
+
+ sst_dbg("called for str_id %d \n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return -EINVAL;
+ stream = &sst_ops->streams[str_id];
+ if (stream->mmapped == true) {
+ sst_err("user write & stream is mapped!!!");
+ return -EIO;
+ }
+ if (stream->status != STREAM_RUNNING && stream->status != STREAM_INIT) {
+ sst_err("BAD REQUEST!\n");
+ return -EBADRQC;
+ }
+ sst_dbg("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;
+
+ return retval;
+}
+
+/*
+* intel_sst_read- get captured data
+*/
+int intel_sst_read(struct file *file_ptr, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct ioctl_pvt_data *data =
+ (struct ioctl_pvt_data *)file_ptr->private_data;
+ int str_id = data->str_id;
+
+ sst_dbg("called for %d\n", str_id);
+ return intel_sst_read_write(str_id, buf, count);
+}
+
+ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t offset)
+{
+ int retval = 0;
+ struct ioctl_pvt_data *data =
+ (struct ioctl_pvt_data *)kiocb->ki_filp->private_data;
+ int str_id = data->str_id;
+ struct stream_info *stream = NULL;
+
+ sst_dbg("entry - %ld\n", nr_segs);
+
+ if (is_sync_kiocb(kiocb) == false) {
+ sst_dbg("aio_read from user space is not allowed\n");
+ return -EINVAL;
+ }
+
+ sst_dbg("called for str_id %d \n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return -EINVAL;
+ stream = &sst_ops->streams[str_id];
+ if (stream->mmapped == true) {
+ sst_err("user write & stream is mapped!!!");
+ return -EIO;
+ }
+ if (stream->status != STREAM_RUNNING && stream->status != STREAM_INIT) {
+ sst_err("BAD REQUEST!\n");
+ return -EBADRQC;
+ }
+ sst_dbg("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;
+
+ return retval;
+}
+
+int sst_get_stream_allocated(struct snd_sst_params *str_param,
+ u32 block, u32 pvt_id)
+{
+ int retval = 0;
+
+ retval = sst_alloc_stream((char *) &str_param->sparams, str_param->ops,
+ str_param->codec, pvt_id);
+ if (0 != retval) {
+ sst_err("sst_alloc_stream failed %d", retval);
+ goto err;
+ }
+ /*Block the call for reply*/
+ retval = sst_wait_timeout(sst_ops, &sst_ops->alloc_block[block]);
+err: return retval;
+}
+
+void set_port_params(struct snd_sst_params *str_param,
+ enum snd_sst_stream_ops ops)
+{
+ int sfreq = str_param->sparams.uc.pcm_params.sfreq;
+ int word_size = str_param->sparams.uc.pcm_params.pcm_wd_sz;
+
+ sst_dbg("sampling frequency = %d wd_size = %d \n", sfreq, word_size);
+
+ if (ops == STREAM_OPS_PLAYBACK) {
+ sst_dbg("Setting playback path and port settings...\n");
+ sst_ops->scard_ops.init_card();
+ sst_ops->scard_ops.set_pcm_params(sfreq, word_size);
+ } else if (ops == STREAM_OPS_CAPTURE) {
+ sst_dbg("Setting capture path...\n");
+ if (sst_ops->scard_ops.init_card_capture != NULL)
+ sst_ops->scard_ops.init_card_capture();
+ }
+ return;
+}
+
+int sst_get_stream(struct snd_sst_params *str_param, u32 pvt_id)
+{
+ int i = 0, retval = 0;
+
+ /*stream is not allocated, we are allocating*/
+ i = sst_get_block_stream(sst_ops);
+ sst_dbg("alloc block allocated = %d\n", i);
+ if (i < 0)
+ return -ENOMEM;
+ sst_ops->alloc_block[i].sst_id = pvt_id;
+
+ retval = sst_get_stream_allocated(str_param, i, pvt_id);
+ if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) {
+ /*codec download is required*/
+ struct snd_sst_alloc_response *response =
+ sst_ops->alloc_block[i].ops_block.data;
+ sst_dbg("Codec is required.... trying that\n");
+ retval = sst_load_library(&response->lib_dnld,
+ str_param->ops, pvt_id);
+ kfree(response);
+
+ if (retval == 0) {
+ sst_dbg("codec was downloaded sucesfully \n");
+ sst_dbg("try alloac again\n");
+ sst_ops->alloc_block[i].ops_block.condition = false;
+
+ retval = sst_get_stream_allocated(str_param, i, pvt_id);
+
+ if (retval <= 0)
+ goto err;
+ set_port_params(str_param, str_param->ops);
+
+ sst_dbg("Allocation done stream id %d \n", retval);
+ } else {
+ sst_dbg("codec download failed \n");
+ retval = -EIO;
+ goto err;
+ }
+ } else if (retval <= 0)
+ goto err;
+ else
+ set_port_params(str_param, str_param->ops);
+
+err:
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ return retval;
+}
+
+/**
+* intel_sst_ioctl - recieves the device ioctl's
+* @i_node: inode structure
+* @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
+*/
+int intel_sst_ioctl(struct inode *i_node, struct file *file_ptr,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0, i = 0;
+ struct ioctl_pvt_data *data = (struct ioctl_pvt_data *)
+ file_ptr->private_data;
+ int str_id = data->str_id, minor;
+
+ dev_t device = i_node->i_rdev;
+
+ if (device == MKDEV(INTEL_SST_MAJOR, 0))
+ minor = 0;
+ else if (device == MKDEV(INTEL_SST_MAJOR, 1))
+ minor = 1;
+ else
+ return -EINVAL;
+
+ if (SST_FW_RUNNING != sst_ops->sst_state) {
+ sst_err("SST Not runng %d\n", sst_ops->sst_state);
+ return -EBUSY;
+ }
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(SNDRV_SST_STREAM_PAUSE):
+ sst_dbg("SNDRV_SST_IOCTL_PAUSE recieved for %d!\n", str_id);
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ retval = sst_pause_stream(str_id);
+ break;
+
+ case _IOC_NR(SNDRV_SST_STREAM_RESUME):
+ sst_dbg("SNDRV_SST_IOCTL_RESUME recieved!\n");
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ retval = sst_resume_stream(str_id);
+ break;
+
+ case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): {
+ struct snd_sst_params *str_param = (struct snd_sst_params *)arg;
+
+ sst_dbg("IOCTL_SET_PARAMS recieved!\n");
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ sst_print_params(&str_param->sparams);
+
+ if (str_id == 0) {
+ retval = sst_get_stream(str_param, data->pvt_id);
+ if (retval > 0) {
+ data->str_id = retval;
+ retval = 0;
+ } else {
+ if (retval == -SST_ERR_INVALID_PARAMS)
+ retval = -EINVAL;
+ }
+ } else {
+ sst_dbg("SET_STREAM_PARAMS recieved!\n");
+ /*allocated set params only*/
+ retval = sst_set_stream_param(str_id, str_param);
+ /*Block the call for reply*/
+ if (retval == 0) {
+ int sfreq = 0, word_size = 0;
+ sfreq = str_param->sparams.uc.pcm_params.sfreq;
+ word_size = str_param->sparams.
+ uc.pcm_params.pcm_wd_sz;
+ retval = sst_wait_timeout(sst_ops,
+ &sst_ops->alloc_block[i]);
+ sst_dbg("SST sampling frequency= %d\n" , sfreq);
+ sst_ops->scard_ops.set_pcm_params(sfreq,
+ word_size);
+ }
+ }
+ break;
+ }
+
+ case _IOC_NR(SNDRV_SST_MMAP_PLAY):
+ case _IOC_NR(SNDRV_SST_MMAP_CAPTURE):
+ sst_dbg("SNDRV_SST_MMAP_PLAY/CAPTURE recieved!\n");
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ retval = intel_sst_mmap_play_capture(str_id,
+ (struct snd_sst_buffs *)arg);
+ break;
+
+ case _IOC_NR(SNDRV_SST_STREAM_DROP):
+ sst_dbg("SNDRV_SST_IOCTL_DROP recieved!\n");
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ retval = sst_drop_stream(str_id);
+ break;
+
+ case _IOC_NR(SNDRV_SST_STREAM_GET_TSTAMP): {
+ unsigned long long *ms = (unsigned long long *)arg;
+ struct snd_sst_tstamp tstamp = {0};
+ unsigned long long time = 0, freq = 0;
+ unsigned long mod = 0;
+
+ sst_dbg("SNDRV_SST_STREAM_GET_TSTAMP recieved!\n");
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ memcpy_fromio(&tstamp,
+ ((void *)(sst_ops->mailbox + SST_TIME_STAMP)
+ +(str_id * sizeof(tstamp))),
+ sizeof(tstamp));
+ time = tstamp.samples_rendered;
+ sst_dbg("samples rendered! = 0x%llx\n", time);
+ freq = (unsigned long long) tstamp.sampling_frequency;
+ sst_dbg("freq = %llx\n", freq);
+ mod = do_div(time, freq);
+ sst_dbg("mod = 0x%lx\n", mod);
+ sst_dbg("sec = 0x%llx\n", time);
+ time = time * 1000;
+ sst_dbg("msec = 0x%llx\n", time);
+ retval = copy_to_user(ms, &time, sizeof(*ms));
+ if (retval != 0)
+ sst_err("copy failed = %d\n", retval);
+ break;
+ }
+
+ case _IOC_NR(SNDRV_SST_STREAM_START):{
+ struct stream_info *stream = NULL;
+
+ sst_dbg("SNDRV_SST_STREAM_START recieved!\n");
+ if (minor != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ break;
+ stream = &sst_ops->streams[str_id];
+ mutex_lock(&stream->lock);
+ if (stream->status == STREAM_INIT) {
+ sst_dbg("calling play frames...\n");
+ stream->prev = stream->status;
+ stream->status = STREAM_RUNNING;
+ if (stream->ops == STREAM_OPS_PLAYBACK) {
+ retval = sst_play_frame(str_id);
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(
+ OSPM_EVENT_SUBSYS_START_PLAY);
+#endif
+ } else if (stream->ops == STREAM_OPS_CAPTURE)
+ retval = sst_capture_frame(str_id);
+ else {
+ sst_err("Invalid ops 0x%x\n", stream->ops);
+ retval = -EINVAL;
+ mutex_unlock(&sst_ops->streams[str_id].lock);
+ break;
+ }
+ if (retval < 0) {
+ sst_err("play/capture frames failed \n");
+ stream->status = STREAM_INIT;
+ mutex_unlock(&sst_ops->streams[str_id].lock);
+ break;
+ }
+ } else {
+ sst_err("Invalid start for stream %d state 0x%x\n",
+ str_id, stream->status);
+ retval = -EINVAL;
+ }
+ mutex_unlock(&sst_ops->streams[str_id].lock);
+ break;
+ }
+ case _IOC_NR(SNDRV_SST_SET_TARGET_DEVICE): {
+ struct snd_sst_target_device *target_device = NULL;
+
+ sst_info("SNDRV_SST_SET_TARGET_PLAYBACK DEVICE recieved!\n");
+ target_device = (struct snd_sst_target_device *)arg;
+ BUG_ON(!target_device);
+ if (minor != 1) {
+ sst_err("called for non AM handle minor %d\n", minor);
+ retval = -EINVAL;
+ break;
+ }
+ retval = sst_target_device_select(target_device);
+ if (0 != retval)
+ return retval;
+ break;
+
+ }
+
+ case _IOC_NR(SNDRV_SST_DRIVER_INFO): {
+ struct snd_sst_driver_info *info =
+ (struct snd_sst_driver_info *)arg;
+ sst_dbg("SNDRV_SST_DRIVER_INFO recived \n");
+ info->version = SST_VERSION_NUM;
+ /*hard coding, shud get sumhow later*/
+ info->active_pcm_streams = sst_ops->stream_cnt -
+ sst_ops->active_cnt;
+ info->active_enc_streams = sst_ops->active_cnt;
+ info->max_pcm_streams = MAX_ACTIVE_STREAM - MAX_ENC_STREAM;
+ info->max_enc_streams = MAX_ENC_STREAM;
+ info->buf_per_stream = sst_ops->mmap_len;
+ break;
+ }
+
+ default:
+ sst_dbg("IOCTL not supported yet !\n");
+ retval = -ENOTTY;
+ }
+ sst_dbg("...complete ret code = %d\n", retval);
+
+ return retval;
+}
+
+/*
+ MAD Routines
+*/
+
+/**
+* sst_control_set - Set Control params
+* @control_list: list of controls to be set
+*
+* This function is called by MID sound card driver to set
+* SST/Sound card controls. This is registered with MID driver
+*/
+int sst_control_set(int control_element, void *value)
+{
+ int retval = 0, str_id = 0;
+
+ if (SST_UN_INIT == sst_ops->sst_state) {
+ /*FW is not downloaded*/
+ sst_dbg("DSP Downloading FW now...\n");
+ retval = sst_download_fw();
+ if (retval != 0) {
+ sst_err("FW download failed = 0x%x, abort\n", retval);
+ return retval;
+ }
+ }
+
+ switch (control_element) {
+ case SST_SND_ALLOC: {
+ struct snd_sst_params *str_param = NULL;
+ int pcm_id = sst_assign_pvt_id(sst_ops);
+
+ str_param = (struct snd_sst_params *)value;
+ BUG_ON(!str_param);
+ sst_print_params(&str_param->sparams);
+ retval = sst_get_stream(str_param, pcm_id);
+ if (retval >= 0)
+ sst_ops->stream_cnt++;
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ if (str_param->ops == STREAM_OPS_PLAYBACK)
+ sst_ospm_send_event(OSPM_EVENT_SUBSYS_START_PLAY);
+#endif
+
+ break;
+ }
+
+ case SST_SND_PAUSE:
+ retval = sst_pause_stream(*(int *)value);
+ break;
+
+ case SST_SND_RESUME:
+ retval = sst_resume_stream(*(int *)value);
+ break;
+
+ case SST_SND_DROP:
+ retval = sst_drop_stream(*(int *)value);
+ break;
+
+ case SST_SND_FREE: {
+ struct stream_info *stream = NULL;
+
+ str_id = *(int *)value;
+ stream = &sst_ops->streams[str_id];
+ retval = sst_free_stream(str_id);
+ if (retval == 0) {
+ stream->pcm_substream = NULL;
+ stream->period_elapsed = NULL;
+ sst_ops->stream_cnt--;
+ }
+ break;
+ }
+
+ case SST_SND_STREAM_INIT: {
+ struct pcm_stream_info *str_info = NULL;
+ struct stream_info *stream = NULL;
+
+ sst_dbg("stream init called\n");
+ str_info = (struct pcm_stream_info *)value;
+ str_id = str_info->str_id;
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ break;
+
+ stream = &sst_ops->streams[str_id];
+ sst_dbg("setting the period ptrs\n");
+ stream->pcm_substream = str_info->mad_substream;
+ stream->period_elapsed = str_info->period_elapsed;
+ break;
+ }
+
+ case SST_SND_BUFFER_POINTER: {
+ struct pcm_stream_info *buf_ptr;
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ struct stream_info *stream = NULL;
+
+ sst_dbg("buffer pointer query\n");
+
+ buf_ptr = (struct pcm_stream_info *)value;
+ str_id = buf_ptr->str_id;
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ break;
+ stream = &sst_ops->streams[str_id];
+
+ if (stream->pcm_substream == NULL)
+ break;
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(sst_ops->mailbox + SST_TIME_STAMP)
+ +(str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+
+ sst_dbg("strid = %d\n", str_id);
+
+ if (stream->ops == STREAM_OPS_PLAYBACK)
+ buf_ptr->buffer_ptr = fw_tstamp.samples_rendered;
+ else
+ buf_ptr->buffer_ptr = fw_tstamp.samples_processed;
+ sst_dbg("samples played = %ld\n",
+ buf_ptr->buffer_ptr);
+ break;
+ }
+
+ default:
+ /*Illegal case*/
+ sst_err("illegal req\n");
+ return -EINVAL;
+ }
+ sst_dbg("...complete ret code = %d\n", retval);
+
+ return retval;
+}
+
+
+/**
+* sst_send_data_to_HW - send data buffers
+* @buffer_data: user buffer
+*
+* This function is called by MID sound card driver to send buffer
+* to HW. This is registered with MID driver
+*/
+int sst_send_buffer_to_HW(int str_id, struct stream_buffer *mad_buf)
+{
+ /*recvd a buffer map it to stream*/
+ /*this is a PCM stream and playback*/
+ int retval = 0;
+ bool flag_add = false;
+ struct sst_stream_bufs *sst_buf = NULL;
+ struct stream_info *stream = NULL;
+
+ if (NULL == mad_buf ||
+ 0 == mad_buf->addr ||
+ 0 == mad_buf->length) {
+ sst_err("Null Ptr or buf size = 0\n");
+ return -EINVAL;
+ }
+
+ if (SST_FW_RUNNING != sst_ops->sst_state) {
+ sst_err("SST Not runng: %d\n", sst_ops->sst_state);
+ return -EBUSY;
+ }
+
+ retval = sst_validate_strid(str_id);
+ if (retval < 0)
+ return -EINVAL;
+
+ stream = &sst_ops->streams[str_id];
+ sst_dbg("stream status = %d strid=%d\n", stream->status, str_id);
+ sst_dbg("stream codec = %d, prevstate=%d\n",
+ stream->codec, stream->prev);
+ if (stream->status == STREAM_UN_INIT) {
+ sst_err("BAD REQUEST!\n");
+ return -EBADRQC;
+ }
+ sst_dbg("received addr=0x%x size = 0x%x\n",
+ (unsigned int)mad_buf->addr, mad_buf->length);
+ /*list is not empty*/
+ list_for_each_entry(sst_buf, &stream->bufs, node) {
+ if (sst_buf->in_use == true)
+ continue;
+ else if ((int) mad_buf->addr !=
+ (int)sst_buf->addr + sst_buf->size)
+ continue;
+ else {
+ sst_buf->size += mad_buf->length;
+ flag_add = true;
+ sst_dbg("inc addr = 0x%p, base = 0x%x inc_val = 0x%x\n",
+ sst_buf->addr, sst_buf->size, mad_buf->length);
+ break;
+ }
+ }
+
+ if (flag_add == false) {
+ sst_buf = kzalloc(sizeof(*sst_buf), GFP_ATOMIC);
+ if (sst_buf == NULL)
+ return -ENOMEM;
+ sst_buf->size = mad_buf->length;
+ sst_buf->addr = (void *)mad_buf->addr;
+ sst_buf->offset = 0;
+ sst_buf->in_use = false;
+ list_add_tail(&sst_buf->node, &stream->bufs);
+ flag_add = true;
+ sst_dbg("entry added addr = 0x%x size = 0x%x\n",
+ (unsigned int)mad_buf->addr, mad_buf->length);
+ }
+
+ if (stream->status == STREAM_INIT) {
+ sst_dbg("play/capt frames...\n");
+ stream->prev = stream->status;
+ stream->status = STREAM_RUNNING;
+ stream->data_blk.on = false;
+ if (stream->ops == STREAM_OPS_PLAYBACK)
+ retval = sst_play_frame(str_id);
+ else if (stream->ops == STREAM_OPS_CAPTURE)
+ retval = sst_capture_frame(str_id);
+ else
+ return -EINVAL;
+ if (retval < 0) {
+ sst_err("play/capture frames failed \n");
+ return -EIO;
+ }
+ }
+
+ return retval;
+}
+
+struct intel_sst_card_ops sst_pmic_ops = {
+ .control_set = sst_control_set,
+ .send_buffer = sst_send_buffer_to_HW,
+};
+
+/**
+* register_sst_card- function for sound card to register
+*@card: pointer to structure of operations
+* This function is called card driver loads and is ready for registration
+*/
+int register_sst_card(struct intel_sst_card_ops *card)
+{
+
+ if (NULL == card ||
+ NULL == card->module_name) {
+ sst_err(" Null Pointer Passed\n");
+ return -EINVAL;
+ }
+
+ if (PMIC_SND_UN_INIT == sst_ops->pmic_state) {
+ /*register this driver*/
+ if (0 == (strncmp(SST_CARD_NAMES, card->module_name,
+ strlen(SST_CARD_NAMES)))) {
+ sst_ops->pmic_vendor = card->vendor_id;
+ sst_ops->scard_ops = *card->scard_ops;
+ sst_pmic_ops.module_name = card->module_name;
+ sst_ops->pmic_state = PMIC_SND_INIT_DONE;
+ card->control_set = sst_pmic_ops.control_set;
+ card->send_buffer = sst_pmic_ops.send_buffer;
+ return 0;
+ } else {
+ sst_err("strcmp failed %s \n", card->module_name);
+ return -EINVAL;
+ }
+
+ } else {
+ /*already registered a driver*/
+ sst_err("Repeat for register..denied\n");
+ return -EBADRQC;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_sst_card);
+
+/**
+* unregister_sst_card- function for sound card to un-register
+*@card: pointer to structure of operations
+* This function is called when card driver unloads
+*/
+void unregister_sst_card(struct intel_sst_card_ops *card)
+{
+ if (sst_pmic_ops.module_name == card->module_name) {
+ /*unreg*/
+ sst_pmic_ops.module_name = "";
+ sst_ops->pmic_state = PMIC_SND_UN_INIT;
+ sst_dbg("Unregistered %s\n", card->module_name);
+ }
+ return;
+}
+EXPORT_SYMBOL_GPL(unregister_sst_card);
+
+int lpe_mask_periphral_intr(enum lpe_periphral device)
+{
+ union sst_pimr_reg pimr = {{0},};
+ if (sst_ops == NULL)
+ return -EIO;
+
+ pimr.full = readl(sst_ops->shim + SST_PIMR);
+
+ switch (device) {
+ case LPE_DMA:
+ pimr.part.dmac_sc = 1;
+ writel(pimr.full, sst_ops->shim + SST_PIMR);
+ break;
+
+ case LPE_SSP0:
+ break;
+
+ case LPE_SSP1:
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lpe_mask_periphral_intr);
+
+int lpe_unmask_periphral_intr(enum lpe_periphral device)
+{
+ union sst_pimr_reg pimr = {{0},};
+ if (sst_ops == NULL)
+ return -EIO;
+
+ pimr.full = readl(sst_ops->shim + SST_PIMR);
+
+ switch (device) {
+ case LPE_DMA:
+ pimr.part.dmac_sc = 0;
+ writel(pimr.full, sst_ops->shim + SST_PIMR);
+ break;
+
+ case LPE_SSP0:
+ break;
+
+ case LPE_SSP1:
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(lpe_unmask_periphral_intr);
+
+int lpe_periphral_intr_status(enum lpe_periphral device, int *status)
+{
+ union sst_pisr_reg pisr = {{0},};
+ if (sst_ops == NULL)
+ return -EIO;
+
+ pisr.full = readl(sst_ops->shim + SST_PISR);
+
+ switch (device) {
+ case LPE_DMA:
+ *status = pisr.part.dmac;
+ break;
+
+ case LPE_SSP0:
+ break;
+
+ case LPE_SSP1:
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lpe_periphral_intr_status);
--
1.5.4.5
More information about the Alsa-devel
mailing list