[alsa-devel] [PATCH] [RFC 7/13] Intel SST driver stream module
Vinod Koul
vinod.koul at intel.com
Fri Jul 3 09:05:07 CEST 2009
This adds the stream module which contains the function for
stream operations & control. For a stream the control
and data are two major parts. This module implements the
control (play/pause/resume/stop/free/alloc) for a stream.
It also implements data play/capture frames where buffers
are sent/received to Firmware.
The objective of SST driver is to achieve Low power
playback by utilizing DSP as much as possible. So SST gets
large music buffers from player/middleware and sends
them to firmware in a scatter gather list.
The firmware decodes and renders them, while IA can goto
low power states
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_stream.c
---
sound/pci/sst/intel_sst_stream.c | 711 ++++++++++++++++++++++++++++++++++++++
1 files changed, 711 insertions(+), 0 deletions(-)
create mode 100644 sound/pci/sst/intel_sst_stream.c
diff --git a/sound/pci/sst/intel_sst_stream.c b/sound/pci/sst/intel_sst_stream.c
new file mode 100644
index 0000000..aabf057
--- /dev/null
+++ b/sound/pci/sst/intel_sst_stream.c
@@ -0,0 +1,711 @@
+/*
+ * intel_sst_stream.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-09 Intel Corp
+ * 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 file contains the stream operations of SST driver
+ */
+
+#include <linux/cdev.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/firmware.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intel_sst_fw_ipc.h"
+#include <sound/intel_sst.h>
+#include "intel_sst_common.h"
+#include "intel_sst_pvt.h"
+#ifdef CONFIG_SST_OSPM_SUPPORT
+#include <linux/intel_mid.h>
+#endif
+
+/**
+* sst_alloc_stream - Send msg for a new stream ID
+* @params: stream params
+* @stream_ops: operation of stream PB/capture
+* @codec: codec for stream
+* @session_id: pvt_id passed by MMF to distinguish stream
+*
+* This function is called by any function which wants to start
+* a new stream. This also check if a stream exists which is idle
+* it initializes idle stream id to this request
+*/
+int sst_alloc_stream(char *params, int stream_ops, int codec, int session_id)
+{
+ struct ipc_post *msg = NULL;
+ struct snd_sst_alloc_params alloc_param = {{0,},};
+
+ sst_dbg("entering sst_alloc_stream \n");
+ sst_dbg("%d %d %d\n", stream_ops, codec, session_id);
+
+ BUG_ON(!params);
+
+ /*send msg to FW to allocate a stream*/
+ if (sst_create_large_msg(&msg) != 0)
+ return -ENOMEM;
+
+ sst_fill_header(&msg->header, IPC_IA_ALLOC_STREAM, 1, 0);
+ msg->header.part.data = sizeof(alloc_param) + sizeof(u32);
+ alloc_param.str_type.codec_type = codec;
+ alloc_param.str_type.str_type = 2; /*music*/
+ alloc_param.str_type.operation = stream_ops;
+ alloc_param.str_type.protected_str = 0; /*non drm*/
+ alloc_param.str_type.pvt_id = session_id;
+ memcpy(&alloc_param.stream_params, params,
+ sizeof(struct snd_sst_stream_params));
+
+ memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
+ memcpy(msg->mailbox_data + sizeof(u32), &alloc_param,
+ sizeof(alloc_param));
+
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ sst_dbg("alloc stream done\n");
+ return 0;
+}
+
+/**
+* sst_alloc_stream_response - process alloc reply
+* @alloc_header: respose header
+*
+* This function is called by process_reply when reply from FW comes
+* based on response has to process further
+*/
+int sst_alloc_stream_response(unsigned int str_id,
+ struct snd_sst_str_type *type)
+{
+ int retval = 0, i, valid_str = 0;
+ struct ipc_post *msg = NULL;
+
+ /*allocation succesfull*/
+ sst_dbg("stream number given = %d \n", str_id);
+
+ for (i = 0; i < MAX_STREAM_FIELD; i++) {
+ if (type->pvt_id == sst_ops->alloc_block[i].sst_id) {
+ valid_str = 1;
+ break;
+ }
+ }
+ if (valid_str == 0) {
+ /*this is not valid stream*/
+ sst_err("Invalid stream allocation detetcted... freeing\n");
+ if (sst_create_short_msg(&msg) != 0)
+ return -ENOMEM;
+ sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, 0, str_id);
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ return 0;
+ }
+
+ sst_init_stream(&sst_ops->streams[str_id], type->codec_type,
+ type->pvt_id, type->operation);
+
+ sst_dbg("stream pvt id = %d \n", type->pvt_id);
+
+ /*Unblock with retval code*/
+ sst_wake_up_alloc_block(sst_ops, type->pvt_id, str_id, NULL);
+ return retval;
+}
+
+/**
+* sst_pause_stream - Send msg for a pausing stream
+* @id: stream ID
+*
+* This function is called by any function which wants to pause
+* an already running stream.
+*/
+int sst_pause_stream(int str_id)
+{
+ int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct stream_info *str_info = NULL;
+
+ sst_dbg("sst_pause_stream for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return retval;
+ str_info = &sst_ops->streams[str_id];
+ if (STREAM_PAUSED == str_info->status)
+ return 0;
+
+ if (STREAM_RUNNING == str_info->status ||
+ STREAM_INIT == str_info->status) {
+
+ if (str_info->prev == STREAM_UN_INIT)
+ return -EBADRQC;
+
+ mutex_lock(&str_info->lock);
+ if (sst_create_short_msg(&msg) != 0) {
+ mutex_unlock(&str_info->lock);
+ return -ENOMEM;
+ }
+ sst_fill_header(&msg->header, IPC_IA_PAUSE_STREAM, 0, str_id);
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ mutex_unlock(&str_info->lock);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ str_info->ctrl_blk.condition = false;
+ str_info->ctrl_blk.ret_code = 0;
+ str_info->ctrl_blk.on = true;
+ retval = sst_wait_interruptible_timeout(sst_ops,
+ &str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
+ if (retval == 0) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_PAUSED;
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(OSPM_EVENT_SUBSYS_STOP_PLAY);
+#endif
+ } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = EINVAL;
+ sst_clean_stream(str_info);
+ }
+ } else {
+ retval = -EBADRQC;
+ sst_err("BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+/**
+* sst_resume_stream - Send msg for resuming stream
+* @id: stream ID
+*
+* This function is called by any function which wants to resume
+* an already paused stream.
+*/
+int sst_resume_stream(int str_id)
+{
+ int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct stream_info *str_info = NULL;
+
+ sst_dbg("sst_resume_stream for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return retval;
+ str_info = &sst_ops->streams[str_id];
+ if (STREAM_RUNNING == str_info->status)
+ return 0;
+ if (STREAM_PAUSED == str_info->status) {
+ mutex_lock(&str_info->lock);
+ if (sst_create_short_msg(&msg) != 0) {
+ mutex_unlock(&str_info->lock);
+ return -ENOMEM;
+ }
+ sst_fill_header(&msg->header, IPC_IA_RESUME_STREAM, 0, str_id);
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ mutex_unlock(&str_info->lock);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ str_info->ctrl_blk.condition = false;
+ str_info->ctrl_blk.ret_code = 0;
+ str_info->ctrl_blk.on = true;
+ retval = sst_wait_interruptible_timeout(sst_ops,
+ &str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
+ if (retval == 0) {
+ if (STREAM_RUNNING == str_info->prev)
+ str_info->status = STREAM_RUNNING;
+ else
+ str_info->status = STREAM_INIT;
+ str_info->prev = STREAM_PAUSED;
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(OSPM_EVENT_SUBSYS_START_PLAY);
+#endif
+ } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = EINVAL;
+ sst_clean_stream(str_info);
+ }
+ } else {
+ retval = -EBADRQC;
+ sst_err("BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+
+/**
+* sst_drop_stream - Send msg for stopping stream
+* @id: stream ID
+*
+* This function is called by any function which wants to stop
+* a stream.
+*/
+int sst_drop_stream(int str_id)
+{
+ int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct sst_stream_bufs *bufs = NULL, *_bufs = NULL;
+ struct stream_info *str_info = NULL;
+
+ sst_dbg("sst_drop_stream for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return retval;
+ str_info = &sst_ops->streams[str_id];
+ if (STREAM_UN_INIT != str_info->status) {
+
+ mutex_lock(&str_info->lock);
+
+ if (sst_create_short_msg(&msg) != 0) {
+ mutex_unlock(&str_info->lock);
+ return -ENOMEM;
+ }
+ sst_fill_header(&msg->header, IPC_IA_DROP_STREAM, 0, str_id);
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ mutex_unlock(&sst_ops->streams[str_id].lock);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ str_info->ctrl_blk.condition = false;
+ str_info->ctrl_blk.ret_code = 0;
+ str_info->ctrl_blk.on = true;
+ retval = sst_wait_interruptible_timeout(sst_ops,
+ &str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
+ if (retval == 0) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_INIT;
+ if (str_info->data_blk.on == true) {
+ /*moved codec specfic check to generic check*/
+ str_info->data_blk.condition = true;
+ str_info->data_blk.ret_code = 0;
+ wake_up(&sst_ops->wait_queue);
+ }
+ list_for_each_entry_safe(bufs, _bufs,
+ &str_info->bufs, node) {
+ list_del(&bufs->node);
+ kfree(bufs);
+ }
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(OSPM_EVENT_SUBSYS_STOP_PLAY);
+#endif
+ } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = EINVAL;
+ sst_clean_stream(str_info);
+ }
+ } else {
+ retval = -EBADRQC;
+ sst_err("BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+/**
+* sst_free_stream - Frees a stream
+* @id: stream ID
+*
+* This function is called by any function which wants to free
+* a stream.
+*/
+int sst_free_stream(int str_id)
+{
+ int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct stream_info *str_info = NULL;
+
+ sst_dbg("sst_free_stream for %d\n", str_id);
+
+ retval = sst_validate_strid(str_id);
+ if (0 != retval)
+ return retval;
+ str_info = &sst_ops->streams[str_id];
+
+ if (STREAM_UN_INIT != str_info->status) {
+ mutex_lock(&str_info->lock);
+ if (sst_create_short_msg(&msg) != 0) {
+ mutex_unlock(&str_info->lock);
+ return -ENOMEM;
+ }
+ sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, 0, str_id);
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ mutex_unlock(&str_info->lock);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_UN_INIT;
+ if (str_info->data_blk.on == true) {
+ str_info->data_blk.condition = true;
+ str_info->data_blk.ret_code = 0;
+ wake_up(&sst_ops->wait_queue);
+ }
+ sst_clean_stream(str_info);
+ sst_dbg("Stream freed\n");
+
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(OSPM_EVENT_SUBSYS_STOP_PLAY);
+#endif
+ } else {
+ retval = -EBADRQC;
+ sst_dbg("BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+
+/**
+* sst_set_stream_param - Send msg for setting stream
+* @id: stream id
+* @params: stream params
+*
+* This function sets stream params during runtime
+*/
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param)
+{
+ int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct stream_info *str_info = NULL;
+
+ BUG_ON(!str_param);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return retval;
+ sst_dbg("set_stream for %d\n", str_id);
+ if (STREAM_INIT == sst_ops->streams[str_id].status) {
+ mutex_lock(&sst_ops->streams[str_id].lock);
+
+ if (sst_create_large_msg(&msg) != 0) {
+ mutex_unlock(&sst_ops->streams[str_id].lock);
+ return -ENOMEM;
+ }
+
+ sst_fill_header(&msg->header,
+ IPC_IA_SET_STREAM_PARAMS, 1, str_id);
+ msg->header.part.data = sizeof(u32) +
+ sizeof(str_param->sparams);
+ memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
+ memcpy(msg->mailbox_data + sizeof(u32), &str_param->sparams,
+ sizeof(str_param->sparams));
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ mutex_unlock(&sst_ops->streams[str_id].lock);
+ str_info->ctrl_blk.condition = false;
+ str_info->ctrl_blk.ret_code = 0;
+ str_info->ctrl_blk.on = true;
+ retval = sst_wait_interruptible_timeout(sst_ops,
+ &str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
+ if (retval != 0) {
+ retval = EINVAL;
+ sst_free_stream(str_id);
+ } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = EINVAL;
+ sst_clean_stream(str_info);
+ }
+ } else {
+ retval = -EBADRQC;
+ sst_err("BADQRC for stream\n");
+ }
+ return retval;
+}
+
+/**
+* sst_route_control - To start or drop the routing from once device to another.
+* @id: stream id
+*
+* This function gets stream params during runtime
+*/
+/*
+int sst_route_control(int str_id)
+{
+ int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct snd_sst_control_routing *route = {0,};
+ struct stream_info *str_info = NULL;
+ sst_dbg("sst_route_control for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ route->control = 0;
+ if (0 != retval)
+ return retval;
+ str_info = &sst_ops->streams[str_id];
+ mutex_lock(&str_info->lock);
+ if (sst_create_large_msg(&msg) != 0) {
+ mutex_unlock(&str_info->lock);
+ return -ENOMEM;
+ }
+ sst_fill_header(&msg->header, IPC_IA_CONTROL_ROUTING, 1, str_id);
+ msg->header.part.data = sizeof(u32) + sizeof(*route);
+ memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
+ memcpy(msg->mailbox_data + sizeof(u32), route,
+ sizeof(*route));
+
+
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ mutex_unlock(&str_info->lock);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+
+ str_info->ctrl_blk.condition = false;
+ str_info->ctrl_blk.ret_code = 0;
+ str_info->ctrl_blk.on = true;
+ retval = sst_wait_interruptible_timeout(sst_ops,
+ &str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
+ if (retval == 0)
+ return retval;
+ else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = EINVAL;
+ sst_clean_stream(str_info);
+ }
+ return retval;
+
+}*/
+
+/**
+* sst_target_device_select - This fuction allows to select the target device
+* for the playback or capture
+* @id: stream id
+*
+* This function gets stream params and the target device during runtime
+*/
+
+int sst_target_device_select(struct snd_sst_target_device *target_device)
+{
+/* int retval = 0;
+ struct ipc_post *msg = NULL;
+ struct snd_sst_slot_info *slot_info = NULL;
+ struct stream_info *str_info = NULL;
+*/ sst_dbg("stream_control ");
+ return 0;
+#if 0
+ if (sst_create_large_msg(&msg) != 0) {
+ mutex_unlock(&str_info->lock);
+ return -ENOMEM;
+ }
+ sst_fill_header(&msg->header, IPC_IA_TARGET_DEV_SELECT,
+ 1, str_id);
+ msg->header.part.data = sizeof(u32) + sizeof(*target_device);
+ memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
+ memcpy(msg->mailbox_data + sizeof(u32), target_device,
+ sizeof(*target_device));
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ mutex_unlock(&str_info->lock);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+
+ str_info->ctrl_blk.condition = false;
+ str_info->ctrl_blk.ret_code = 0;
+ str_info->ctrl_blk.on = true;
+ retval = sst_wait_interruptible_timeout(sst_ops,
+ &str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
+ if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ sst_clean_stream(str_info);
+ }
+ if (target_device->device_route < 0 ||
+ target_device->device_route > 2)
+ return -EINVAL;
+ slot_info = &target_device->devices[0];
+
+ if (slot_info->type == SND_SST_TARGET_PMIC)
+ retval = sst_set_pmic_config(str_id, &slot_info->pcm_params);
+ if (target_device->device_route == 2) {
+ slot_info = &target_device->devices[1];
+ if (target_device->devices[1].type == SND_SST_TARGET_PMIC)
+ retval = sst_set_pmic_config(str_id,
+ &slot_info->pcm_params);
+ }
+ if (retval >= 0)
+ retval = sst_route_control(str_id);
+ return retval;
+#endif
+}
+
+
+int sst_set_pmic_config(int str_id, struct snd_sst_pmic_config *pc)
+{
+ int retval = 0;
+ sst_dbg("stream_control for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (0 != retval)
+ return retval;
+ sst_ops->scard_ops.set_pcm_params(pc->sfreq, pc->pcm_wd_sz);
+ return retval;
+}
+
+
+int sst_create_sg_list(struct stream_info *stream,
+ struct sst_frame_info *sg_list)
+{
+ struct sst_stream_bufs *kbufs = NULL;
+ int i = 0;
+
+ list_for_each_entry(kbufs, &stream->bufs, node) {
+ if (kbufs->in_use == false) {
+ sg_list->addr[i].addr =
+ virt_to_phys((void *)
+ kbufs->addr + kbufs->offset);
+ sst_dbg("phy addr[%d] 0x%x Size 0x%x\n", i,
+ sg_list->addr[i].addr, kbufs->size);
+ sg_list->addr[i].size = kbufs->size;
+ kbufs->in_use = true;
+ i++;
+ }
+ if (i >= MAX_NUM_SCATTER_BUFFERS)
+ break;
+ }
+
+ sg_list->num_entries = i;
+ sst_dbg("sg list entries = %d \n", sg_list->num_entries);
+ return i;
+}
+
+/**
+* sst_play_frame - Send msg for sending stream frames
+* @streamID: ID of stream
+*
+* This function is called when codec set is sucessfull
+* it send the initial frames to SST
+*/
+int sst_play_frame(int str_id)
+{
+ int i = 0, retval = 0;
+ struct ipc_post *msg = NULL;
+ struct sst_frame_info sg_list = {0};
+ struct sst_stream_bufs *kbufs = NULL, *_kbufs;
+ struct stream_info *stream = NULL;
+
+ sst_dbg("play frame for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return retval;
+
+ stream = &sst_ops->streams[str_id];
+ if (stream->status == STREAM_UN_INIT ||
+ stream->status == STREAM_PAUSED) {
+ sst_err("stream in UNIT state\n");
+ return -EINVAL;
+ }
+ /*clear prev sent buffers*/
+ list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) {
+ if (kbufs->in_use == true) {
+ list_del(&kbufs->node);
+ kfree(kbufs);
+ sst_dbg("del node \n");
+ }
+ }
+ if (list_empty(&stream->bufs)) {
+ /*no user buffer available*/
+ sst_dbg("Null buffer!!!!stream status = %d \n", stream->status);
+ stream->prev = stream->status;
+ stream->status = STREAM_INIT;
+ sst_dbg("new stream status = %d \n", stream->status);
+ if (stream->data_blk.on == true) {
+ sst_dbg("user list is empty.. wake \n");
+ /*unblock*/
+ stream->data_blk.ret_code = 0;
+ stream->data_blk.condition = true;
+ stream->data_blk.on = false;
+ wake_up(&sst_ops->wait_queue);
+ }
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(OSPM_EVENT_AUDIO_BUF_EMPTY);
+#endif
+ return -EINVAL;
+ }
+
+ /*create list*/
+ i = sst_create_sg_list(stream, &sg_list);
+
+ /*post msg*/
+ if (0 != sst_create_large_msg(&msg))
+ return -ENOMEM;
+
+ sst_fill_header(&msg->header, IPC_IA_PLAY_FRAMES, 1, str_id);
+ msg->header.part.data = sizeof(u32) + sizeof(sg_list);
+ memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
+ memcpy(msg->mailbox_data + sizeof(u32), &sg_list, sizeof(sg_list));
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ return 0;
+
+}
+
+/**
+* sst_capture_frame - switches based on ecoded/pcm capture for sending stream frames
+* @streamID: ID of stream
+*
+* This function is called when frames to SST are sent for capture
+*/
+int sst_capture_frame(int str_id)
+{
+ int i = 0, retval = 0;
+ struct ipc_post *msg = NULL;
+ struct sst_frame_info sg_list = {0};
+ struct sst_stream_bufs *kbufs = NULL, *_kbufs;
+ struct stream_info *stream = NULL;
+
+
+ sst_dbg("capture frame for %d\n", str_id);
+ retval = sst_validate_strid(str_id);
+ if (retval != 0)
+ return retval;
+
+ stream = &sst_ops->streams[str_id];
+ if (stream->status == STREAM_UN_INIT ||
+ stream->status == STREAM_PAUSED) {
+ sst_err("stream in UNIT state\n");
+ return -EINVAL;
+ }
+
+ /*clear prev sent buffers*/
+ list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) {
+ if (kbufs->in_use == true) {
+ list_del(&kbufs->node);
+ kfree(kbufs);
+ sst_dbg("del node \n");
+ }
+ }
+
+ if (list_empty(&stream->bufs)) {
+ /*no user buffer available*/
+ sst_dbg("Null buffer!!!!stream status = %d \n", stream->status);
+ stream->prev = stream->status;
+ stream->status = STREAM_INIT;
+ sst_dbg("new stream status = %d \n", stream->status);
+ if (stream->data_blk.on == true) {
+ sst_dbg("user list is empty.. wake \n");
+ /*unblock*/
+ stream->data_blk.ret_code = 0;
+ stream->data_blk.condition = true;
+ stream->data_blk.on = false;
+ wake_up(&sst_ops->wait_queue);
+ }
+#ifdef CONFIG_SST_OSPM_SUPPORT
+ sst_ospm_send_event(OSPM_EVENT_AUDIO_BUF_FULL);
+#endif
+ return -EINVAL;
+ }
+
+ /*create new sg list*/
+ i = sst_create_sg_list(stream, &sg_list);
+
+ /*post msg*/
+ if (0 != sst_create_large_msg(&msg))
+ return -ENOMEM;
+
+ sst_fill_header(&msg->header, IPC_IA_CAPT_FRAMES, 1, str_id);
+ msg->header.part.data = sizeof(u32) + sizeof(sg_list);
+ memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
+ memcpy(msg->mailbox_data + sizeof(u32), &sg_list, sizeof(sg_list));
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ return 0;
+}
--
1.5.4.5
More information about the Alsa-devel
mailing list