d0f40c5041
Remove leading "sst: " from format strings. Add #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt Prefix is changed from "sst: " to "snd_intel_sst: " Add missing newlines Trim trailing spaces after newlines Fix several different misspellings Signed-off-by: Joe Perches <joe@perches.com> Cc: Vinod Koul <vinod.koul@intel.com> Cc: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
314 lines
8.3 KiB
C
314 lines
8.3 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#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("max alloc_stream reached\n");
|
|
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("stream failed %d\n", block->ret_code);
|
|
retval = -EBUSY;
|
|
} else {
|
|
pr_debug("event up\n");
|
|
retval = 0;
|
|
}
|
|
} else {
|
|
pr_err("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_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("stream failed %d\n", block->ret_code);
|
|
else
|
|
pr_debug("event up\n");
|
|
retval = block->ret_code;
|
|
} else {
|
|
block->on = false;
|
|
pr_err("timeout occurred...\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("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("Event wake %x\n", block->ops_block.condition);
|
|
pr_debug("message ret: %d\n", block->ops_block.ret_code);
|
|
retval = block->ops_block.ret_code;
|
|
} else {
|
|
block->ops_block.on = false;
|
|
pr_err("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("kzalloc msg failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
|
|
if (!msg->mailbox_data) {
|
|
kfree(msg);
|
|
pr_err("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("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("mem allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
pr_debug("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;
|
|
}
|
|
|