diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
commit | e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7 (patch) | |
tree | ea51b391f7d74ca695dcb9f5e46eb02688a92ed9 /drivers/staging/intel_sst/intel_sst_pvt.c | |
parent | 81280572ca6f54009edfa4deee563e8678784218 (diff) | |
parent | a4ac0d847af9dd34d5953a5e264400326144b6b2 (diff) | |
download | op-kernel-dev-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.zip op-kernel-dev-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.tar.gz |
Merge 'staging-next' to Linus's tree
This merges the staging-next tree to Linus's tree and resolves
some conflicts that were present due to changes in other trees that were
affected by files here.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/intel_sst/intel_sst_pvt.c')
-rw-r--r-- | drivers/staging/intel_sst/intel_sst_pvt.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/drivers/staging/intel_sst/intel_sst_pvt.c b/drivers/staging/intel_sst/intel_sst_pvt.c new file mode 100644 index 0000000..6487e19 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_pvt.c @@ -0,0 +1,311 @@ +/* + * intel_sst_pvt.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> + * KP Jeeja <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. + * + * This file contains all private functions + */ + +#include <linux/pci.h> +#include <linux/fs.h> +#include <linux/firmware.h> +#include <linux/sched.h> +#include "intel_sst.h" +#include "intel_sst_ioctl.h" +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" + +/* + * sst_get_block_stream - get a new block stream + * + * @sst_drv_ctx: Driver context structure + * + * This function assigns a block for the calls that dont have stream context yet + * the blocks are used for waiting on Firmware's response for any operation + * Should be called with stream lock held + */ +int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx) +{ + int i; + + for (i = 0; i < MAX_ACTIVE_STREAM; i++) { + if (sst_drv_ctx->alloc_block[i].sst_id == BLOCK_UNINIT) { + sst_drv_ctx->alloc_block[i].ops_block.condition = false; + sst_drv_ctx->alloc_block[i].ops_block.ret_code = 0; + sst_drv_ctx->alloc_block[i].sst_id = 0; + break; + } + } + if (i == MAX_ACTIVE_STREAM) { + pr_err("sst: max alloc_stream reached"); + i = -EBUSY; /* active stream limit reached */ + } + return i; +} + +/* + * sst_wait_interruptible - wait on event + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * + * This function waits without a timeout (and is interruptable) for a + * given block event + */ +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block) +{ + int retval = 0; + + if (!wait_event_interruptible(sst_drv_ctx->wait_queue, + block->condition)) { + /* event wake */ + if (block->ret_code < 0) { + pr_err("sst: stream failed %d\n", block->ret_code); + retval = -EBUSY; + } else { + pr_debug("sst: event up\n"); + retval = 0; + } + } else { + pr_err("sst: signal interrupted\n"); + retval = -EINTR; + } + return retval; + +} + + +/* + * sst_wait_interruptible_timeout - wait on event interruptable + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * @timeout: time for wait on + * + * This function waits with a timeout value (and is interruptible) on a + * given block event + */ +int sst_wait_interruptible_timeout( + struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block, int timeout) +{ + int retval = 0; + + pr_debug("sst: sst_wait_interruptible_timeout - waiting....\n"); + if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, + block->condition, + msecs_to_jiffies(timeout))) { + if (block->ret_code < 0) + pr_err("sst: stream failed %d\n", block->ret_code); + else + pr_debug("sst: event up\n"); + retval = block->ret_code; + } else { + block->on = false; + pr_err("sst: timeout occured...\n"); + /*setting firmware state as uninit so that the + firmware will get re-downloaded on next request + this is because firmare not responding for 5 sec + is equalant to some unrecoverable error of FW + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + retval = -EBUSY; + } + return retval; + +} + + +/* + * sst_wait_timeout - wait on event for timeout + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * + * This function waits with a timeout value (and is not interruptible) on a + * given block event + */ +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, + struct stream_alloc_block *block) +{ + int retval = 0; + + /* NOTE: + Observed that FW processes the alloc msg and replies even + before the alloc thread has finished execution */ + pr_debug("sst: waiting for %x, condition %x\n", + block->sst_id, block->ops_block.condition); + if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, + block->ops_block.condition, + msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { + /* event wake */ + pr_debug("sst: Event wake %x\n", block->ops_block.condition); + pr_debug("sst: message ret: %d\n", block->ops_block.ret_code); + retval = block->ops_block.ret_code; + } else { + block->ops_block.on = false; + pr_err("sst: Wait timed-out %x\n", block->ops_block.condition); + /* settign firmware state as uninit so that the + firmware will get redownloaded on next request + this is because firmare not responding for 5 sec + is equalant to some unrecoverable error of FW + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + retval = -EBUSY; + } + return retval; + +} + +/* + * sst_create_large_msg - create a large IPC message + * + * @arg: ipc message + * + * this function allocates structures to send a large message to the firmware + */ +int sst_create_large_msg(struct ipc_post **arg) +{ + struct ipc_post *msg; + + msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); + if (!msg) { + pr_err("sst: kzalloc msg failed\n"); + return -ENOMEM; + } + + msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); + if (!msg->mailbox_data) { + kfree(msg); + pr_err("sst: kzalloc mailbox_data failed"); + return -ENOMEM; + }; + *arg = msg; + return 0; +} + +/* + * sst_create_short_msg - create a short IPC message + * + * @arg: ipc message + * + * this function allocates structures to send a short message to the firmware + */ +int sst_create_short_msg(struct ipc_post **arg) +{ + struct ipc_post *msg; + + msg = kzalloc(sizeof(*msg), GFP_ATOMIC); + if (!msg) { + pr_err("sst: kzalloc msg failed\n"); + return -ENOMEM; + } + msg->mailbox_data = NULL; + *arg = msg; + return 0; +} + +/* + * sst_clean_stream - clean the stream context + * + * @stream: stream structure + * + * this function resets the stream contexts + * should be called in free + */ +void sst_clean_stream(struct stream_info *stream) +{ + struct sst_stream_bufs *bufs = NULL, *_bufs; + stream->status = STREAM_UN_INIT; + stream->prev = STREAM_UN_INIT; + mutex_lock(&stream->lock); + list_for_each_entry_safe(bufs, _bufs, &stream->bufs, node) { + list_del(&bufs->node); + kfree(bufs); + } + mutex_unlock(&stream->lock); + + if (stream->ops != STREAM_OPS_PLAYBACK_DRM) + kfree(stream->decode_ibuf); +} + +/* + * sst_wake_up_alloc_block - wake up waiting block + * + * @sst_drv_ctx: Driver context + * @sst_id: stream id + * @status: status of wakeup + * @data: data pointer of wakeup + * + * This function wakes up a sleeping block event based on the response + */ +void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx, + u8 sst_id, int status, void *data) +{ + int i; + + /* Unblock with retval code */ + for (i = 0; i < MAX_ACTIVE_STREAM; i++) { + if (sst_id == sst_drv_ctx->alloc_block[i].sst_id) { + sst_drv_ctx->alloc_block[i].ops_block.condition = true; + sst_drv_ctx->alloc_block[i].ops_block.ret_code = status; + sst_drv_ctx->alloc_block[i].ops_block.data = data; + wake_up(&sst_drv_ctx->wait_queue); + break; + } + } +} + +/* + * sst_enable_rx_timeslot - Send msg to query for stream parameters + * @status: rx timeslot to be enabled + * + * This function is called when the RX timeslot is required to be enabled + */ +int sst_enable_rx_timeslot(int status) +{ + int retval = 0; + struct ipc_post *msg = NULL; + + if (sst_create_short_msg(&msg)) { + pr_err("sst: mem allocation failed\n"); + return -ENOMEM; + } + pr_debug("sst: ipc message sending: ENABLE_RX_TIME_SLOT\n"); + sst_fill_header(&msg->header, IPC_IA_ENABLE_RX_TIME_SLOT, 0, 0); + msg->header.part.data = status; + sst_drv_ctx->hs_info_blk.condition = false; + sst_drv_ctx->hs_info_blk.ret_code = 0; + sst_drv_ctx->hs_info_blk.on = true; + 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); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &sst_drv_ctx->hs_info_blk, SST_BLOCK_TIMEOUT); + return retval; +} + |