staging: remove intel_sst driver

Intel has asked that this driver now be removed from the tree, and I am
happy to oblige.

Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Greg Kroah-Hartman 2011-12-08 15:17:40 -08:00
parent 4588d6d408
commit 03cf152646
27 changed files with 0 additions and 14483 deletions

View File

@ -116,8 +116,6 @@ source "drivers/staging/bcm/Kconfig"
source "drivers/staging/ft1000/Kconfig"
source "drivers/staging/intel_sst/Kconfig"
source "drivers/staging/speakup/Kconfig"
source "drivers/staging/cptm1217/Kconfig"

View File

@ -50,7 +50,6 @@ obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/
obj-$(CONFIG_USB_ENESTORAGE) += keucr/
obj-$(CONFIG_BCM_WIMAX) += bcm/
obj-$(CONFIG_FT1000) += ft1000/
obj-$(CONFIG_SND_INTEL_SST) += intel_sst/
obj-$(CONFIG_SPEAKUP) += speakup/
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/

View File

@ -1,19 +0,0 @@
config SND_INTEL_SST
tristate "Intel SST (LPE) Driver"
depends on X86 && INTEL_SCU_IPC
default n
help
Say Y here to include support for the Intel(R) MID SST DSP driver
On other PC platforms if you are unsure answer 'N'
config SND_INTELMID
tristate "Intel MID sound card driver"
depends on SOUND && SND
select SND_PCM
select SND_SEQUENCER
select SND_JACK
depends on SND_INTEL_SST
default n
help
Say Y here to include support for the Intel(R) MID sound card driver
On other PC platforms if you are unsure answer 'N'

View File

@ -1,7 +0,0 @@
#
# Makefile for Intel MID Audio drivers
#
snd-intel-sst-y := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_drv_interface.o intel_sst_dsp.o intel_sst_pvt.o intel_sst_stream_encoded.o intel_sst_app_interface.o
snd-intelmid-y := intelmid.o intelmid_msic_control.o intelmid_ctrl.o intelmid_pvt.o intelmid_v0_control.o intelmid_v1_control.o intelmid_v2_control.o
obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o
obj-$(CONFIG_SND_INTELMID) += snd-intelmid.o

View File

@ -1,13 +0,0 @@
TODO
----
Get the memrar driver cleaned up and upstream (dependency blocking SST)
Replace long/short press with two virtual buttons
Review the printks and kill off any left over ST_ERR: messages
Review the misc device ioctls for 32/64bit safety and sanity
Review the misc device ioctls for size safety depending on config and decide
if space/unused areas should be left
What the sound folks turn up on full review
Using the ALSA frameworks properly

View File

@ -1,649 +0,0 @@
/*
* intel_sst.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 init functions
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
#include <asm/mrst.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(SST_DRIVER_VERSION);
struct intel_sst_drv *sst_drv_ctx;
static struct mutex drv_ctx_lock;
struct class *sst_class;
/* fops Routines */
static const struct file_operations intel_sst_fops = {
.owner = THIS_MODULE,
.open = intel_sst_open,
.release = intel_sst_release,
.read = intel_sst_read,
.write = intel_sst_write,
.unlocked_ioctl = intel_sst_ioctl,
.mmap = intel_sst_mmap,
.aio_read = intel_sst_aio_read,
.aio_write = intel_sst_aio_write,
};
static const struct file_operations intel_sst_fops_cntrl = {
.owner = THIS_MODULE,
.open = intel_sst_open_cntrl,
.release = intel_sst_release_cntrl,
.unlocked_ioctl = intel_sst_ioctl,
};
static struct miscdevice lpe_dev = {
.minor = MISC_DYNAMIC_MINOR,/* dynamic allocation */
.name = "intel_sst",/* /dev/intel_sst */
.fops = &intel_sst_fops
};
static struct miscdevice lpe_ctrl = {
.minor = MISC_DYNAMIC_MINOR,/* dynamic allocation */
.name = "intel_sst_ctrl",/* /dev/intel_sst_ctrl */
.fops = &intel_sst_fops_cntrl
};
/**
* intel_sst_interrupt - Interrupt service routine for SST
*
* @irq: irq number of interrupt
* @context: pointer to device structre
*
* This function is called by OS when SST device raises
* an interrupt. This will be result of write in IPC register
* Source can be busy or done interrupt
*/
static irqreturn_t intel_sst_interrupt(int irq, void *context)
{
union interrupt_reg isr;
union ipc_header header;
union interrupt_reg imr;
struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
unsigned int size = 0, str_id;
struct stream_info *stream ;
/* Do not handle interrupt in suspended state */
if (drv->sst_state == SST_SUSPENDED)
return IRQ_NONE;
/* Interrupt arrived, check src */
isr.full = sst_shim_read(drv->shim, SST_ISRX);
if (isr.part.busy_interrupt) {
header.full = sst_shim_read(drv->shim, SST_IPCD);
if (header.part.msg_id == IPC_SST_PERIOD_ELAPSED) {
sst_clear_interrupt();
str_id = header.part.str_id;
stream = &sst_drv_ctx->streams[str_id];
if (stream->period_elapsed)
stream->period_elapsed(stream->pcm_substream);
return IRQ_HANDLED;
}
if (header.part.large)
size = header.part.data;
if (header.part.msg_id & REPLY_MSG) {
sst_drv_ctx->ipc_process_msg.header = header;
memcpy_fromio(sst_drv_ctx->ipc_process_msg.mailbox,
drv->mailbox + SST_MAILBOX_RCV, size);
queue_work(sst_drv_ctx->process_msg_wq,
&sst_drv_ctx->ipc_process_msg.wq);
} else {
sst_drv_ctx->ipc_process_reply.header = header;
memcpy_fromio(sst_drv_ctx->ipc_process_reply.mailbox,
drv->mailbox + SST_MAILBOX_RCV, size);
queue_work(sst_drv_ctx->process_reply_wq,
&sst_drv_ctx->ipc_process_reply.wq);
}
/* mask busy inetrrupt */
imr.full = sst_shim_read(drv->shim, SST_IMRX);
imr.part.busy_interrupt = 1;
sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
return IRQ_HANDLED;
} else if (isr.part.done_interrupt) {
/* Clear done bit */
header.full = sst_shim_read(drv->shim, SST_IPCX);
header.part.done = 0;
sst_shim_write(sst_drv_ctx->shim, SST_IPCX, header.full);
/* write 1 to clear status register */;
isr.part.done_interrupt = 1;
/* dummy register for shim workaround */
sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
queue_work(sst_drv_ctx->post_msg_wq,
&sst_drv_ctx->ipc_post_msg.wq);
return IRQ_HANDLED;
} else
return IRQ_NONE;
}
/*
* intel_sst_probe - PCI probe function
*
* @pci: PCI device structure
* @pci_id: PCI device ID structure
*
* This function is called by OS when a device is found
* This enables the device, interrupt etc
*/
static int __devinit intel_sst_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
int i, ret = 0;
pr_debug("Probe for DID %x\n", pci->device);
mutex_lock(&drv_ctx_lock);
if (sst_drv_ctx) {
pr_err("Only one sst handle is supported\n");
mutex_unlock(&drv_ctx_lock);
return -EBUSY;
}
sst_drv_ctx = kzalloc(sizeof(*sst_drv_ctx), GFP_KERNEL);
if (!sst_drv_ctx) {
pr_err("malloc fail\n");
mutex_unlock(&drv_ctx_lock);
return -ENOMEM;
}
mutex_unlock(&drv_ctx_lock);
sst_drv_ctx->pci_id = pci->device;
mutex_init(&sst_drv_ctx->stream_lock);
mutex_init(&sst_drv_ctx->sst_lock);
sst_drv_ctx->pmic_state = SND_MAD_UN_INIT;
sst_drv_ctx->stream_cnt = 0;
sst_drv_ctx->encoded_cnt = 0;
sst_drv_ctx->am_cnt = 0;
sst_drv_ctx->pb_streams = 0;
sst_drv_ctx->cp_streams = 0;
sst_drv_ctx->unique_id = 0;
sst_drv_ctx->pmic_port_instance = SST_DEFAULT_PMIC_PORT;
INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list);
INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, sst_post_message);
INIT_WORK(&sst_drv_ctx->ipc_process_msg.wq, sst_process_message);
INIT_WORK(&sst_drv_ctx->ipc_process_reply.wq, sst_process_reply);
INIT_WORK(&sst_drv_ctx->mad_ops.wq, sst_process_mad_ops);
init_waitqueue_head(&sst_drv_ctx->wait_queue);
sst_drv_ctx->mad_wq = create_workqueue("sst_mad_wq");
if (!sst_drv_ctx->mad_wq)
goto do_free_drv_ctx;
sst_drv_ctx->post_msg_wq = create_workqueue("sst_post_msg_wq");
if (!sst_drv_ctx->post_msg_wq)
goto free_mad_wq;
sst_drv_ctx->process_msg_wq = create_workqueue("sst_process_msg_wqq");
if (!sst_drv_ctx->process_msg_wq)
goto free_post_msg_wq;
sst_drv_ctx->process_reply_wq = create_workqueue("sst_proces_reply_wq");
if (!sst_drv_ctx->process_reply_wq)
goto free_process_msg_wq;
for (i = 0; i < MAX_ACTIVE_STREAM; i++) {
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
sst_drv_ctx->alloc_block[i].ops_block.condition = false;
}
spin_lock_init(&sst_drv_ctx->list_spin_lock);
sst_drv_ctx->max_streams = pci_id->driver_data;
pr_debug("Got drv data max stream %d\n",
sst_drv_ctx->max_streams);
for (i = 1; i <= sst_drv_ctx->max_streams; i++) {
struct stream_info *stream = &sst_drv_ctx->streams[i];
INIT_LIST_HEAD(&stream->bufs);
mutex_init(&stream->lock);
spin_lock_init(&stream->pcm_lock);
}
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
sst_drv_ctx->mmap_mem = NULL;
sst_drv_ctx->mmap_len = SST_MMAP_PAGES * PAGE_SIZE;
while (sst_drv_ctx->mmap_len > 0) {
sst_drv_ctx->mmap_mem =
kzalloc(sst_drv_ctx->mmap_len, GFP_KERNEL);
if (sst_drv_ctx->mmap_mem) {
pr_debug("Got memory %p size 0x%x\n",
sst_drv_ctx->mmap_mem,
sst_drv_ctx->mmap_len);
break;
}
if (sst_drv_ctx->mmap_len < (SST_MMAP_STEP*PAGE_SIZE)) {
pr_err("mem alloc fail...abort!!\n");
ret = -ENOMEM;
goto free_process_reply_wq;
}
sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE);
pr_debug("mem alloc failed...trying %d\n",
sst_drv_ctx->mmap_len);
}
}
/* Init the device */
ret = pci_enable_device(pci);
if (ret) {
pr_err("device can't be enabled\n");
goto do_free_mem;
}
sst_drv_ctx->pci = pci_dev_get(pci);
ret = pci_request_regions(pci, SST_DRV_NAME);
if (ret)
goto do_disable_device;
/* map registers */
/* SST Shim */
sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1);
sst_drv_ctx->shim = pci_ioremap_bar(pci, 1);
if (!sst_drv_ctx->shim)
goto do_release_regions;
pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim);
/* Shared SRAM */
sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2);
if (!sst_drv_ctx->mailbox)
goto do_unmap_shim;
pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox);
/* IRAM */
sst_drv_ctx->iram = pci_ioremap_bar(pci, 3);
if (!sst_drv_ctx->iram)
goto do_unmap_sram;
pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram);
/* DRAM */
sst_drv_ctx->dram = pci_ioremap_bar(pci, 4);
if (!sst_drv_ctx->dram)
goto do_unmap_iram;
pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram);
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->sst_lock);
/* Register the ISR */
ret = request_irq(pci->irq, intel_sst_interrupt,
IRQF_SHARED, SST_DRV_NAME, sst_drv_ctx);
if (ret)
goto do_unmap_dram;
pr_debug("Registered IRQ 0x%x\n", pci->irq);
/*Register LPE Control as misc driver*/
ret = misc_register(&lpe_ctrl);
if (ret) {
pr_err("couldn't register control device\n");
goto do_free_irq;
}
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
ret = misc_register(&lpe_dev);
if (ret) {
pr_err("couldn't register LPE device\n");
goto do_free_misc;
}
} else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) {
u32 csr;
/*allocate mem for fw context save during suspend*/
sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL);
if (!sst_drv_ctx->fw_cntx) {
ret = -ENOMEM;
goto do_free_misc;
}
/*setting zero as that is valid mem to restore*/
sst_drv_ctx->fw_cntx_size = 0;
/*set lpe start clock and ram size*/
csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr |= 0x30060; /*remove the clock ratio after fw fix*/
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr);
}
sst_drv_ctx->lpe_stalled = 0;
pci_set_drvdata(pci, sst_drv_ctx);
pm_runtime_allow(&pci->dev);
pm_runtime_put_noidle(&pci->dev);
pr_debug("...successfully done!!!\n");
return ret;
do_free_misc:
misc_deregister(&lpe_ctrl);
do_free_irq:
free_irq(pci->irq, sst_drv_ctx);
do_unmap_dram:
iounmap(sst_drv_ctx->dram);
do_unmap_iram:
iounmap(sst_drv_ctx->iram);
do_unmap_sram:
iounmap(sst_drv_ctx->mailbox);
do_unmap_shim:
iounmap(sst_drv_ctx->shim);
do_release_regions:
pci_release_regions(pci);
do_disable_device:
pci_disable_device(pci);
do_free_mem:
kfree(sst_drv_ctx->mmap_mem);
free_process_reply_wq:
destroy_workqueue(sst_drv_ctx->process_reply_wq);
free_process_msg_wq:
destroy_workqueue(sst_drv_ctx->process_msg_wq);
free_post_msg_wq:
destroy_workqueue(sst_drv_ctx->post_msg_wq);
free_mad_wq:
destroy_workqueue(sst_drv_ctx->mad_wq);
do_free_drv_ctx:
kfree(sst_drv_ctx);
sst_drv_ctx = NULL;
pr_err("Probe failed with %d\n", ret);
return ret;
}
/**
* intel_sst_remove - PCI remove function
*
* @pci: PCI device structure
*
* This function is called by OS when a device is unloaded
* This frees the interrupt etc
*/
static void __devexit intel_sst_remove(struct pci_dev *pci)
{
pm_runtime_get_noresume(&pci->dev);
pm_runtime_forbid(&pci->dev);
pci_dev_put(sst_drv_ctx->pci);
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->sst_lock);
misc_deregister(&lpe_ctrl);
free_irq(pci->irq, sst_drv_ctx);
iounmap(sst_drv_ctx->dram);
iounmap(sst_drv_ctx->iram);
iounmap(sst_drv_ctx->mailbox);
iounmap(sst_drv_ctx->shim);
sst_drv_ctx->pmic_state = SND_MAD_UN_INIT;
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
misc_deregister(&lpe_dev);
kfree(sst_drv_ctx->mmap_mem);
} else
kfree(sst_drv_ctx->fw_cntx);
flush_scheduled_work();
destroy_workqueue(sst_drv_ctx->process_reply_wq);
destroy_workqueue(sst_drv_ctx->process_msg_wq);
destroy_workqueue(sst_drv_ctx->post_msg_wq);
destroy_workqueue(sst_drv_ctx->mad_wq);
kfree(pci_get_drvdata(pci));
sst_drv_ctx = NULL;
pci_release_regions(pci);
pci_disable_device(pci);
pci_set_drvdata(pci, NULL);
}
void sst_save_dsp_context(void)
{
struct snd_sst_ctxt_params fw_context;
unsigned int pvt_id, i;
struct ipc_post *msg = NULL;
/*check cpu type*/
if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID)
return;
/*not supported for rest*/
if (sst_drv_ctx->sst_state != SST_FW_RUNNING) {
pr_debug("fw not running no context save ...\n");
return;
}
/*send msg to fw*/
if (sst_create_large_msg(&msg))
return;
pvt_id = sst_assign_pvt_id(sst_drv_ctx);
i = sst_get_block_stream(sst_drv_ctx);
sst_drv_ctx->alloc_block[i].sst_id = pvt_id;
sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id);
msg->header.part.data = sizeof(fw_context) + sizeof(u32);
fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx);
fw_context.size = FW_CONTEXT_MEM;
memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
memcpy(msg->mailbox_data + sizeof(u32),
&fw_context, sizeof(fw_context));
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);
/*wait for reply*/
if (sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]))
pr_debug("err fw context save timeout ...\n");
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
pr_debug("fw context saved ...\n");
return;
}
/* Power Management */
/*
* intel_sst_suspend - PCI suspend function
*
* @pci: PCI device structure
* @state: PM message
*
* This function is called by OS when a power event occurs
*/
int intel_sst_suspend(struct pci_dev *pci, pm_message_t state)
{
union config_status_reg csr;
pr_debug("intel_sst_suspend called\n");
if (sst_drv_ctx->stream_cnt) {
pr_err("active streams,not able to suspend\n");
return -EBUSY;
}
/*save fw context*/
sst_save_dsp_context();
/*Assert RESET on LPE Processor*/
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.full = csr.full | 0x2;
/* Move the SST state to Suspended */
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_SUSPENDED;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
mutex_unlock(&sst_drv_ctx->sst_lock);
pci_set_drvdata(pci, sst_drv_ctx);
pci_save_state(pci);
pci_disable_device(pci);
pci_set_power_state(pci, PCI_D3hot);
return 0;
}
/**
* intel_sst_resume - PCI resume function
*
* @pci: PCI device structure
*
* This function is called by OS when a power event occurs
*/
int intel_sst_resume(struct pci_dev *pci)
{
int ret = 0;
pr_debug("intel_sst_resume called\n");
if (sst_drv_ctx->sst_state != SST_SUSPENDED) {
pr_err("SST is not in suspended state\n");
return 0;
}
sst_drv_ctx = pci_get_drvdata(pci);
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
ret = pci_enable_device(pci);
if (ret)
pr_err("device can't be enabled\n");
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->sst_lock);
return 0;
}
/* The runtime_suspend/resume is pretty much similar to the legacy
* suspend/resume with the noted exception below:
* The PCI core takes care of taking the system through D3hot and
* restoring it back to D0 and so there is no need to duplicate
* that here.
*/
static int intel_sst_runtime_suspend(struct device *dev)
{
union config_status_reg csr;
pr_debug("intel_sst_runtime_suspend called\n");
if (sst_drv_ctx->stream_cnt) {
pr_err("active streams,not able to suspend\n");
return -EBUSY;
}
/*save fw context*/
sst_save_dsp_context();
/*Assert RESET on LPE Processor*/
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.full = csr.full | 0x2;
/* Move the SST state to Suspended */
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_SUSPENDED;
/* Only needed by Medfield */
if (sst_drv_ctx->pci_id != SST_MRST_PCI_ID)
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
mutex_unlock(&sst_drv_ctx->sst_lock);
return 0;
}
static int intel_sst_runtime_resume(struct device *dev)
{
pr_debug("intel_sst_runtime_resume called\n");
if (sst_drv_ctx->sst_state != SST_SUSPENDED) {
pr_err("SST is not in suspended state\n");
return 0;
}
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->sst_lock);
return 0;
}
static int intel_sst_runtime_idle(struct device *dev)
{
pr_debug("runtime_idle called\n");
if (sst_drv_ctx->stream_cnt == 0 && sst_drv_ctx->am_cnt == 0)
pm_schedule_suspend(dev, SST_SUSPEND_DELAY);
return -EBUSY;
}
static const struct dev_pm_ops intel_sst_pm = {
.runtime_suspend = intel_sst_runtime_suspend,
.runtime_resume = intel_sst_runtime_resume,
.runtime_idle = intel_sst_runtime_idle,
};
/* PCI Routines */
static struct pci_device_id intel_sst_ids[] = {
{ PCI_VDEVICE(INTEL, SST_MRST_PCI_ID), 3},
{ PCI_VDEVICE(INTEL, SST_MFLD_PCI_ID), 6},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, intel_sst_ids);
static struct pci_driver driver = {
.name = SST_DRV_NAME,
.id_table = intel_sst_ids,
.probe = intel_sst_probe,
.remove = __devexit_p(intel_sst_remove),
#ifdef CONFIG_PM
.suspend = intel_sst_suspend,
.resume = intel_sst_resume,
.driver = {
.pm = &intel_sst_pm,
},
#endif
};
/**
* intel_sst_init - Module init function
*
* Registers with PCI
* Registers with /dev
* Init all data strutures
*/
static int __init intel_sst_init(void)
{
/* Init all variables, data structure etc....*/
int ret = 0;
pr_debug("INFO: ******** SST DRIVER loading.. Ver: %s\n",
SST_DRIVER_VERSION);
mutex_init(&drv_ctx_lock);
/* Register with PCI */
ret = pci_register_driver(&driver);
if (ret)
pr_err("PCI register failed\n");
return ret;
}
/**
* intel_sst_exit - Module exit function
*
* Unregisters with PCI
* Unregisters with /dev
* Frees all data strutures
*/
static void __exit intel_sst_exit(void)
{
pci_unregister_driver(&driver);
pr_debug("driver unloaded\n");
sst_drv_ctx = NULL;
return;
}
module_init(intel_sst_init);
module_exit(intel_sst_exit);

View File

@ -1,162 +0,0 @@
#ifndef __INTEL_SST_H__
#define __INTEL_SST_H__
/*
* intel_sst.h - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* 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 is shared between the SST and MAD drivers
*/
#include "intel_sst_ioctl.h"
#include <sound/jack.h>
#define SST_CARD_NAMES "intel_mid_card"
#define MFLD_MAX_HW_CH 4
/* control list Pmic & Lpe */
/* Input controls */
enum port_status {
ACTIVATE = 1,
DEACTIVATE,
};
/* Card states */
enum sst_card_states {
SND_CARD_UN_INIT = 0,
SND_CARD_INIT_DONE,
};
enum sst_controls {
SST_SND_ALLOC = 0x1000,
SST_SND_PAUSE = 0x1001,
SST_SND_RESUME = 0x1002,
SST_SND_DROP = 0x1003,
SST_SND_FREE = 0x1004,
SST_SND_BUFFER_POINTER = 0x1005,
SST_SND_STREAM_INIT = 0x1006,
SST_SND_START = 0x1007,
SST_SND_STREAM_PROCESS = 0x1008,
SST_MAX_CONTROLS = 0x1008,
SST_CONTROL_BASE = 0x1000,
SST_ENABLE_RX_TIME_SLOT = 0x1009,
};
enum SND_CARDS {
SND_FS = 0,
SND_MX,
SND_NC,
SND_MSIC
};
struct pcm_stream_info {
int str_id;
void *mad_substream;
void (*period_elapsed) (void *mad_substream);
unsigned long long buffer_ptr;
int sfreq;
};
struct snd_pmic_ops {
int card_status;
int master_mute;
int num_channel;
int input_dev_id;
int mute_status;
struct mutex lock;
int pb_on, pbhs_on;
int cap_on;
int output_dev_id;
int lineout_dev_id, line_out_names_cnt;
int prev_lineout_dev_id;
bool jack_interrupt_status;
int (*set_input_dev) (u8 value);
int (*set_output_dev) (u8 value);
int (*set_lineout_dev) (u8 value);
int (*set_mute) (int dev_id, u8 value);
int (*get_mute) (int dev_id, u8 *value);
int (*set_vol) (int dev_id, int value);
int (*get_vol) (int dev_id, int *value);
int (*init_card) (void);
int (*set_pcm_audio_params)
(int sfreq, int word_size , int num_channel);
int (*set_pcm_voice_params) (void);
int (*set_voice_port) (int status);
int (*set_audio_port) (int status);
int (*power_up_pmic_pb) (unsigned int port);
int (*power_up_pmic_cp) (unsigned int port);
int (*power_down_pmic_pb) (unsigned int device);
int (*power_down_pmic_cp) (unsigned int device);
int (*power_down_pmic) (void);
void (*pmic_irq_cb) (void *cb_data, u8 value);
void (*pmic_irq_enable)(void *data);
int (*pmic_jack_enable) (void);
int (*pmic_get_mic_bias)(void *intelmaddata);
int (*pmic_set_headset_state)(int state);
unsigned int hw_dmic_map[MFLD_MAX_HW_CH];
unsigned int available_dmics;
int (*set_hw_dmic_route) (u8 index);
int gpio_amp;
};
extern void sst_mad_send_jack_report(struct snd_jack *jack,
int buttonpressevent,
int status);
int intemad_set_headset_state(int state);
int intelmad_get_mic_bias(void);
struct intel_sst_pcm_control {
int (*open) (struct snd_sst_params *str_param);
int (*device_control) (int cmd, void *arg);
int (*close) (unsigned int str_id);
};
struct intel_sst_card_ops {
char *module_name;
unsigned int vendor_id;
struct intel_sst_pcm_control *pcm_control;
struct snd_pmic_ops *scard_ops;
};
/* modified for generic access */
struct sc_reg_access {
u16 reg_addr;
u8 value;
u8 mask;
};
enum sc_reg_access_type {
PMIC_READ = 0,
PMIC_WRITE,
PMIC_READ_MODIFY,
};
int register_sst_card(struct intel_sst_card_ops *card);
void unregister_sst_card(struct intel_sst_card_ops *card);
#endif /* __INTEL_SST_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -1,623 +0,0 @@
#ifndef __INTEL_SST_COMMON_H__
#define __INTEL_SST_COMMON_H__
/*
* intel_sst_common.h - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Common private declarations for SST
*/
#define SST_DRIVER_VERSION "1.2.17"
#define SST_VERSION_NUM 0x1217
/* driver names */
#define SST_DRV_NAME "intel_sst_driver"
#define SST_MRST_PCI_ID 0x080A
#define SST_MFLD_PCI_ID 0x082F
#define PCI_ID_LENGTH 4
#define SST_SUSPEND_DELAY 2000
#define FW_CONTEXT_MEM (64*1024)
enum sst_states {
SST_FW_LOADED = 1,
SST_FW_RUNNING,
SST_UN_INIT,
SST_ERROR,
SST_SUSPENDED
};
#define MAX_ACTIVE_STREAM 3
#define MAX_ENC_STREAM 1
#define MAX_AM_HANDLES 1
#define ALLOC_TIMEOUT 5000
/* SST numbers */
#define SST_BLOCK_TIMEOUT 5000
#define TARGET_DEV_BLOCK_TIMEOUT 5000
#define BLOCK_UNINIT -1
#define RX_TIMESLOT_UNINIT -1
/* SST register map */
#define SST_CSR 0x00
#define SST_PISR 0x08
#define SST_PIMR 0x10
#define SST_ISRX 0x18
#define SST_IMRX 0x28
#define SST_IPCX 0x38 /* IPC IA-SST */
#define SST_IPCD 0x40 /* IPC SST-IA */
#define SST_ISRD 0x20 /* dummy register for shim workaround */
#define SST_SHIM_SIZE 0X44
#define SPI_MODE_ENABLE_BASE_ADDR 0xffae4000
#define FW_SIGNATURE_SIZE 4
/* PMIC and SST hardware states */
enum sst_mad_states {
SND_MAD_UN_INIT = 0,
SND_MAD_INIT_DONE,
};
/* stream states */
enum sst_stream_states {
STREAM_UN_INIT = 0, /* Freed/Not used stream */
STREAM_RUNNING = 1, /* Running */
STREAM_PAUSED = 2, /* Paused stream */
STREAM_DECODE = 3, /* stream is in decoding only state */
STREAM_INIT = 4, /* stream init, waiting for data */
};
enum sst_ram_type {
SST_IRAM = 1,
SST_DRAM = 2,
};
/* SST shim registers to structure mapping */
union config_status_reg {
struct {
u32 mfld_strb:1;
u32 sst_reset:1;
u32 hw_rsvd:3;
u32 sst_clk:2;
u32 bypass:3;
u32 run_stall:1;
u32 rsvd1:2;
u32 strb_cntr_rst:1;
u32 rsvd:18;
} part;
u32 full;
};
union interrupt_reg {
struct {
u32 done_interrupt:1;
u32 busy_interrupt:1;
u32 rsvd:30;
} part;
u32 full;
};
union sst_pisr_reg {
struct {
u32 pssp0:1;
u32 pssp1:1;
u32 rsvd0:3;
u32 dmac:1;
u32 rsvd1:26;
} part;
u32 full;
};
union sst_pimr_reg {
struct {
u32 ssp0:1;
u32 ssp1:1;
u32 rsvd0:3;
u32 dmac:1;
u32 rsvd1:10;
u32 ssp0_sc:1;
u32 ssp1_sc:1;
u32 rsvd2:3;
u32 dmac_sc:1;
u32 rsvd3:10;
} part;
u32 full;
};
struct sst_stream_bufs {
struct list_head node;
u32 size;
const char *addr;
u32 data_copied;
bool in_use;
u32 offset;
};
struct snd_sst_user_cap_list {
unsigned int iov_index; /* index of iov */
unsigned long iov_offset; /* offset in iov */
unsigned long offset; /* offset in kmem */
unsigned long size; /* size copied */
struct list_head node;
};
/*
This structure is used to block a user/fw data call to another
fw/user call
*/
struct sst_block {
bool condition; /* condition for blocking check */
int ret_code; /* ret code when block is released */
void *data; /* data to be appsed for block if any */
bool on;
};
enum snd_sst_buf_type {
SST_BUF_USER_STATIC = 1,
SST_BUF_USER_DYNAMIC,
SST_BUF_MMAP_STATIC,
SST_BUF_MMAP_DYNAMIC,
};
enum snd_src {
SST_DRV = 1,
MAD_DRV = 2
};
/**
* struct stream_info - structure that holds the stream information
*
* @status : stream current state
* @prev : stream prev state
* @codec : stream codec
* @sst_id : stream id
* @ops : stream operation pb/cp/drm...
* @bufs: stream buffer list
* @lock : stream mutex for protecting state
* @pcm_lock : spinlock for pcm path only
* @mmapped : is stream mmapped
* @sg_index : current stream user buffer index
* @cur_ptr : stream user buffer pointer
* @buf_entry : current user buffer
* @data_blk : stream block for data operations
* @ctrl_blk : stream block for ctrl operations
* @buf_type : stream user buffer type
* @pcm_substream : PCM substream
* @period_elapsed : PCM period elapsed callback
* @sfreq : stream sampling freq
* @decode_ibuf : Decoded i/p buffers pointer
* @decode_obuf : Decoded o/p buffers pointer
* @decode_isize : Decoded i/p buffers size
* @decode_osize : Decoded o/p buffers size
* @decode_ibuf_type : Decoded i/p buffer type
* @decode_obuf_type : Decoded o/p buffer type
* @idecode_alloc : Decode alloc index
* @need_draining : stream set for drain
* @str_type : stream type
* @curr_bytes : current bytes decoded
* @cumm_bytes : cummulative bytes decoded
* @str_type : stream type
* @src : stream source
* @device : output device type (medfield only)
* @pcm_slot : pcm slot value
*/
struct stream_info {
unsigned int status;
unsigned int prev;
u8 codec;
unsigned int sst_id;
unsigned int ops;
struct list_head bufs;
struct mutex lock; /* mutex */
spinlock_t pcm_lock;
bool mmapped;
unsigned int sg_index; /* current buf Index */
unsigned char __user *cur_ptr; /* Current static bufs */
struct snd_sst_buf_entry __user *buf_entry;
struct sst_block data_blk; /* stream ops block */
struct sst_block ctrl_blk; /* stream control cmd block */
enum snd_sst_buf_type buf_type;
void *pcm_substream;
void (*period_elapsed) (void *pcm_substream);
unsigned int sfreq;
void *decode_ibuf, *decode_obuf;
unsigned int decode_isize, decode_osize;
u8 decode_ibuf_type, decode_obuf_type;
unsigned int idecode_alloc;
unsigned int need_draining;
unsigned int str_type;
u32 curr_bytes;
u32 cumm_bytes;
u32 src;
enum snd_sst_audio_device_type device;
u8 pcm_slot;
};
/*
* struct stream_alloc_bloc - this structure is used for blocking the user's
* alloc calls to fw's response to alloc calls
*
* @sst_id : session id of blocked stream
* @ops_block : ops block struture
*/
struct stream_alloc_block {
int sst_id; /* session id of blocked stream */
struct sst_block ops_block; /* ops block struture */
};
#define SST_FW_SIGN "$SST"
#define SST_FW_LIB_SIGN "$LIB"
/*
* struct fw_header - FW file headers
*
* @signature : FW signature
* @modules : # of modules
* @file_format : version of header format
* @reserved : reserved fields
*/
struct fw_header {
unsigned char signature[FW_SIGNATURE_SIZE]; /* FW signature */
u32 file_size; /* size of fw minus this header */
u32 modules; /* # of modules */
u32 file_format; /* version of header format */
u32 reserved[4];
};
struct fw_module_header {
unsigned char signature[FW_SIGNATURE_SIZE]; /* module signature */
u32 mod_size; /* size of module */
u32 blocks; /* # of blocks */
u32 type; /* codec type, pp lib */
u32 entry_point;
};
struct dma_block_info {
enum sst_ram_type type; /* IRAM/DRAM */
u32 size; /* Bytes */
u32 ram_offset; /* Offset in I/DRAM */
u32 rsvd; /* Reserved field */
};
struct ioctl_pvt_data {
int str_id;
int pvt_id;
};
struct sst_ipc_msg_wq {
union ipc_header header;
char mailbox[SST_MAILBOX_SIZE];
struct work_struct wq;
};
struct mad_ops_wq {
int stream_id;
enum sst_controls control_op;
struct work_struct wq;
};
#define SST_MMAP_PAGES (640*1024 / PAGE_SIZE)
#define SST_MMAP_STEP (40*1024 / PAGE_SIZE)
/***
* struct intel_sst_drv - driver ops
*
* @pmic_state : pmic state
* @pmic_vendor : pmic vendor detected
* @sst_state : current sst device state
* @pci_id : PCI device id loaded
* @shim : SST shim pointer
* @mailbox : SST mailbox pointer
* @iram : SST IRAM pointer
* @dram : SST DRAM pointer
* @shim_phy_add : SST shim phy addr
* @ipc_dispatch_list : ipc messages dispatched
* @ipc_post_msg_wq : wq to post IPC messages context
* @ipc_process_msg : wq to process msgs from FW context
* @ipc_process_reply : wq to process reply from FW context
* @ipc_post_msg : wq to post reply from FW context
* @mad_ops : MAD driver operations registered
* @mad_wq : MAD driver wq
* @post_msg_wq : wq to post IPC messages
* @process_msg_wq : wq to process msgs from FW
* @process_reply_wq : wq to process reply from FW
* @streams : sst stream contexts
* @alloc_block : block structure for alloc
* @tgt_dev_blk : block structure for target device
* @fw_info_blk : block structure for fw info block
* @vol_info_blk : block structure for vol info block
* @mute_info_blk : block structure for mute info block
* @hs_info_blk : block structure for hs info block
* @list_lock : sst driver list lock (deprecated)
* @list_spin_lock : sst driver spin lock block
* @scard_ops : sst card ops
* @pci : sst pci device struture
* @active_streams : sst active streams
* @sst_lock : sst device lock
* @stream_lock : sst stream lock
* @unique_id : sst unique id
* @stream_cnt : total sst active stream count
* @pb_streams : total active pb streams
* @cp_streams : total active cp streams
* @lpe_stalled : lpe stall status
* @pmic_port_instance : active pmic port instance
* @rx_time_slot_status : active rx slot
* @lpaudio_start : lpaudio status
* @audio_start : audio status
* @devt_d : pointer to /dev/lpe node
* @devt_c : pointer to /dev/lpe_ctrl node
* @max_streams : max streams allowed
*/
struct intel_sst_drv {
bool pmic_state;
int pmic_vendor;
int sst_state;
unsigned int pci_id;
void __iomem *shim;
void __iomem *mailbox;
void __iomem *iram;
void __iomem *dram;
unsigned int shim_phy_add;
struct list_head ipc_dispatch_list;
struct work_struct ipc_post_msg_wq;
struct sst_ipc_msg_wq ipc_process_msg;
struct sst_ipc_msg_wq ipc_process_reply;
struct sst_ipc_msg_wq ipc_post_msg;
struct mad_ops_wq mad_ops;
wait_queue_head_t wait_queue;
struct workqueue_struct *mad_wq;
struct workqueue_struct *post_msg_wq;
struct workqueue_struct *process_msg_wq;
struct workqueue_struct *process_reply_wq;
struct stream_info streams[MAX_NUM_STREAMS];
struct stream_alloc_block alloc_block[MAX_ACTIVE_STREAM];
struct sst_block tgt_dev_blk, fw_info_blk, ppp_params_blk,
vol_info_blk, mute_info_blk, hs_info_blk;
struct mutex list_lock;/* mutex for IPC list locking */
spinlock_t list_spin_lock; /* mutex for IPC list locking */
struct snd_pmic_ops *scard_ops;
struct pci_dev *pci;
int active_streams[MAX_NUM_STREAMS];
void *mmap_mem;
struct mutex sst_lock;
struct mutex stream_lock;
unsigned int mmap_len;
unsigned int unique_id;
unsigned int stream_cnt; /* total streams */
unsigned int encoded_cnt; /* enocded streams only */
unsigned int am_cnt;
unsigned int pb_streams; /* pb streams active */
unsigned int cp_streams; /* cp streams active */
unsigned int lpe_stalled; /* LPE is stalled or not */
unsigned int pmic_port_instance; /*pmic port instance*/
int rx_time_slot_status;
unsigned int lpaudio_start;
/* 1 - LPA stream(MP3 pb) in progress*/
unsigned int audio_start;
dev_t devt_d, devt_c;
unsigned int max_streams;
unsigned int *fw_cntx;
unsigned int fw_cntx_size;
unsigned int fw_downloaded;
};
extern struct intel_sst_drv *sst_drv_ctx;
#define CHIP_REV_REG 0xff108000
#define CHIP_REV_ADDR 0x78
/* misc definitions */
#define FW_DWNL_ID 0xFF
#define LOOP1 0x11111111
#define LOOP2 0x22222222
#define LOOP3 0x33333333
#define LOOP4 0x44444444
#define SST_DEFAULT_PMIC_PORT 1 /*audio port*/
/* NOTE: status will have +ve for good cases and -ve for error ones */
#define MAX_STREAM_FIELD 255
int sst_alloc_stream(char *params, unsigned int stream_ops, u8 codec,
unsigned int session_id);
int sst_alloc_stream_response(unsigned int str_id,
struct snd_sst_alloc_response *response);
int sst_stalled(void);
int sst_pause_stream(int id);
int sst_resume_stream(int id);
int sst_enable_rx_timeslot(int status);
int sst_drop_stream(int id);
int sst_free_stream(int id);
int sst_start_stream(int streamID);
int sst_play_frame(int streamID);
int sst_pcm_play_frame(int str_id, struct sst_stream_bufs *sst_buf);
int sst_capture_frame(int streamID);
int sst_set_stream_param(int streamID, struct snd_sst_params *str_param);
int sst_target_device_select(struct snd_sst_target_device *target_device);
int sst_decode(int str_id, struct snd_sst_dbufs *dbufs);
int sst_get_decoded_bytes(int str_id, unsigned long long *bytes);
int sst_get_fw_info(struct snd_sst_fw_info *info);
int sst_get_stream_params(int str_id,
struct snd_sst_get_stream_params *get_params);
int sst_get_stream(struct snd_sst_params *str_param);
int sst_get_stream_allocated(struct snd_sst_params *str_param,
struct snd_sst_lib_download **lib_dnld);
int sst_drain_stream(int str_id);
int sst_get_vol(struct snd_sst_vol *set_vol);
int sst_set_vol(struct snd_sst_vol *set_vol);
int sst_set_mute(struct snd_sst_mute *set_mute);
void sst_post_message(struct work_struct *work);
void sst_process_message(struct work_struct *work);
void sst_process_reply(struct work_struct *work);
void sst_process_mad_ops(struct work_struct *work);
void sst_process_mad_jack_detection(struct work_struct *work);
long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd,
unsigned long arg);
int intel_sst_open(struct inode *i_node, struct file *file_ptr);
int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr);
int intel_sst_release(struct inode *i_node, struct file *file_ptr);
int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr);
int intel_sst_read(struct file *file_ptr, char __user *buf,
size_t count, loff_t *ppos);
int intel_sst_write(struct file *file_ptr, const char __user *buf,
size_t count, loff_t *ppos);
int intel_sst_mmap(struct file *fp, struct vm_area_struct *vma);
ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov,
unsigned long nr_segs, loff_t offset);
ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov,
unsigned long nr_segs, loff_t offset);
int sst_load_fw(const struct firmware *fw, void *context);
int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
int sst_spi_mode_enable(void);
int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
struct sst_block *block);
int sst_wait_interruptible_timeout(struct intel_sst_drv *sst_drv_ctx,
struct sst_block *block, int timeout);
int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
struct stream_alloc_block *block);
int sst_create_large_msg(struct ipc_post **arg);
int sst_create_short_msg(struct ipc_post **arg);
void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx,
u8 sst_id, int status, void *data);
void sst_clear_interrupt(void);
int intel_sst_resume(struct pci_dev *pci);
int sst_download_fw(void);
void free_stream_context(unsigned int str_id);
void sst_clean_stream(struct stream_info *stream);
/*
* sst_fill_header - inline to fill sst header
*
* @header : ipc header
* @msg : IPC message to be sent
* @large : is ipc large msg
* @str_id : stream id
*
* this function is an inline function that sets the headers before
* sending a message
*/
static inline void sst_fill_header(union ipc_header *header,
int msg, int large, int str_id)
{
header->part.msg_id = msg;
header->part.str_id = str_id;
header->part.large = large;
header->part.done = 0;
header->part.busy = 1;
header->part.data = 0;
}
/*
* sst_assign_pvt_id - assign a pvt id for stream
*
* @sst_drv_ctx : driver context
*
* this inline function assigns a private id for calls that dont have stream
* context yet, should be called with lock held
*/
static inline unsigned int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx)
{
sst_drv_ctx->unique_id++;
if (sst_drv_ctx->unique_id >= MAX_NUM_STREAMS)
sst_drv_ctx->unique_id = 1;
return sst_drv_ctx->unique_id;
}
/*
* sst_init_stream - this function initialzes stream context
*
* @stream : stream struture
* @codec : codec for stream
* @sst_id : stream id
* @ops : stream operation
* @slot : stream pcm slot
* @device : device type
*
* this inline function initialzes stream context for allocated stream
*/
static inline void sst_init_stream(struct stream_info *stream,
int codec, int sst_id, int ops, u8 slot,
enum snd_sst_audio_device_type device)
{
stream->status = STREAM_INIT;
stream->prev = STREAM_UN_INIT;
stream->codec = codec;
stream->sst_id = sst_id;
stream->str_type = 0;
stream->ops = ops;
stream->data_blk.on = false;
stream->data_blk.condition = false;
stream->data_blk.ret_code = 0;
stream->data_blk.data = NULL;
stream->ctrl_blk.on = false;
stream->ctrl_blk.condition = false;
stream->ctrl_blk.ret_code = 0;
stream->ctrl_blk.data = NULL;
stream->need_draining = false;
stream->decode_ibuf = NULL;
stream->decode_isize = 0;
stream->mmapped = false;
stream->pcm_slot = slot;
stream->device = device;
}
/*
* sst_validate_strid - this function validates the stream id
*
* @str_id : stream id to be validated
*
* returns 0 if valid stream
*/
static inline int sst_validate_strid(int str_id)
{
if (str_id <= 0 || str_id > sst_drv_ctx->max_streams) {
pr_err("SST ERR: invalid stream id : %d MAX_STREAMS:%d\n",
str_id, sst_drv_ctx->max_streams);
return -EINVAL;
} else
return 0;
}
static inline int sst_shim_write(void __iomem *addr, int offset, int value)
{
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
writel(value, addr + SST_ISRD); /*dummy*/
writel(value, addr + offset);
return 0;
}
static inline int sst_shim_read(void __iomem *addr, int offset)
{
return readl(addr + offset);
}
#endif /* __INTEL_SST_COMMON_H__ */

View File

@ -1,564 +0,0 @@
/*
* 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)
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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/delay.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
#include <linux/export.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
/*
* sst_download_fw - download the audio firmware to DSP
*
* This function is called when the FW needs to be downloaded to SST DSP engine
*/
int sst_download_fw(void)
{
int retval;
const struct firmware *fw_sst;
char name[20];
if (sst_drv_ctx->sst_state != SST_UN_INIT)
return -EPERM;
/* Reload firmware is not needed for MRST */
if ( (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) && sst_drv_ctx->fw_downloaded) {
pr_debug("FW already downloaded, skip for MRST platform\n");
sst_drv_ctx->sst_state = SST_FW_RUNNING;
return 0;
}
snprintf(name, sizeof(name), "%s%04x%s", "fw_sst_",
sst_drv_ctx->pci_id, ".bin");
pr_debug("Downloading %s FW now...\n", name);
retval = request_firmware(&fw_sst, name, &sst_drv_ctx->pci->dev);
if (retval) {
pr_err("request fw failed %d\n", retval);
return retval;
}
sst_drv_ctx->alloc_block[0].sst_id = FW_DWNL_ID;
sst_drv_ctx->alloc_block[0].ops_block.condition = false;
retval = sst_load_fw(fw_sst, NULL);
if (retval)
goto end_restore;
retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[0]);
if (retval)
pr_err("fw download failed %d\n" , retval);
else
sst_drv_ctx->fw_downloaded = 1;
end_restore:
release_firmware(fw_sst);
sst_drv_ctx->alloc_block[0].sst_id = BLOCK_UNINIT;
return retval;
}
/*
* sst_stalled - this function checks if the lpe is in stalled state
*/
int sst_stalled(void)
{
int retry = 1000;
int retval = -1;
while (retry) {
if (!sst_drv_ctx->lpe_stalled)
return 0;
/*wait for time and re-check*/
msleep(1);
retry--;
}
pr_debug("in Stalled State\n");
return retval;
}
void free_stream_context(unsigned int str_id)
{
struct stream_info *stream;
if (!sst_validate_strid(str_id)) {
/* str_id is valid, so stream is alloacted */
stream = &sst_drv_ctx->streams[str_id];
if (sst_free_stream(str_id))
sst_clean_stream(&sst_drv_ctx->streams[str_id]);
if (stream->ops == STREAM_OPS_PLAYBACK ||
stream->ops == STREAM_OPS_PLAYBACK_DRM) {
sst_drv_ctx->pb_streams--;
if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID)
sst_drv_ctx->scard_ops->power_down_pmic_pb(
stream->device);
else {
if (sst_drv_ctx->pb_streams == 0)
sst_drv_ctx->scard_ops->
power_down_pmic_pb(stream->device);
}
} else if (stream->ops == STREAM_OPS_CAPTURE) {
sst_drv_ctx->cp_streams--;
if (sst_drv_ctx->cp_streams == 0)
sst_drv_ctx->scard_ops->power_down_pmic_cp(
stream->device);
}
if (sst_drv_ctx->pb_streams == 0
&& sst_drv_ctx->cp_streams == 0)
sst_drv_ctx->scard_ops->power_down_pmic();
}
}
/*
* sst_get_stream_allocated - this function gets a stream allocated with
* the given params
*
* @str_param : stream params
* @lib_dnld : pointer to pointer of lib downlaod struct
*
* This creates new stream id for a stream, in case lib is to be downloaded to
* DSP, it downloads that
*/
int sst_get_stream_allocated(struct snd_sst_params *str_param,
struct snd_sst_lib_download **lib_dnld)
{
int retval, str_id;
struct stream_info *str_info;
retval = sst_alloc_stream((char *) &str_param->sparams, str_param->ops,
str_param->codec, str_param->device_type);
if (retval < 0) {
pr_err("sst_alloc_stream failed %d\n", retval);
return retval;
}
pr_debug("Stream allocated %d\n", retval);
str_id = retval;
str_info = &sst_drv_ctx->streams[str_id];
/* Block the call for reply */
retval = sst_wait_interruptible_timeout(sst_drv_ctx,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
if ((retval != 0) || (str_info->ctrl_blk.ret_code != 0)) {
pr_debug("FW alloc failed retval %d, ret_code %d\n",
retval, str_info->ctrl_blk.ret_code);
str_id = -str_info->ctrl_blk.ret_code; /*return error*/
*lib_dnld = str_info->ctrl_blk.data;
sst_clean_stream(str_info);
} else
pr_debug("FW Stream allocated success\n");
return str_id; /*will ret either error (in above if) or correct str id*/
}
/*
* sst_get_sfreq - this function returns the frequency of the stream
*
* @str_param : stream params
*/
static int sst_get_sfreq(struct snd_sst_params *str_param)
{
switch (str_param->codec) {
case SST_CODEC_TYPE_PCM:
return 48000; /*str_param->sparams.uc.pcm_params.sfreq;*/
case SST_CODEC_TYPE_MP3:
return str_param->sparams.uc.mp3_params.sfreq;
case SST_CODEC_TYPE_AAC:
return str_param->sparams.uc.aac_params.sfreq;
case SST_CODEC_TYPE_WMA9:
return str_param->sparams.uc.wma_params.sfreq;
default:
return 0;
}
}
/*
* sst_get_stream - this function prepares for stream allocation
*
* @str_param : stream param
*/
int sst_get_stream(struct snd_sst_params *str_param)
{
int i, retval;
struct stream_info *str_info;
struct snd_sst_lib_download *lib_dnld;
/* stream is not allocated, we are allocating */
retval = sst_get_stream_allocated(str_param, &lib_dnld);
if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) {
/* codec download is required */
struct snd_sst_alloc_response *response;
pr_debug("Codec is required.... trying that\n");
if (lib_dnld == NULL) {
pr_err("lib download null!!! abort\n");
return -EIO;
}
i = sst_get_block_stream(sst_drv_ctx);
response = sst_drv_ctx->alloc_block[i].ops_block.data;
pr_debug("alloc block allocated = %d\n", i);
if (i < 0) {
kfree(lib_dnld);
return -ENOMEM;
}
retval = sst_load_library(lib_dnld, str_param->ops);
kfree(lib_dnld);
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
if (!retval) {
pr_debug("codec was downloaded successfully\n");
retval = sst_get_stream_allocated(str_param, &lib_dnld);
if (retval <= 0)
goto err;
pr_debug("Alloc done stream id %d\n", retval);
} else {
pr_debug("codec download failed\n");
retval = -EIO;
goto err;
}
} else if (retval <= 0)
goto err;
/*else
set_port_params(str_param, str_param->ops);*/
/* store sampling freq */
str_info = &sst_drv_ctx->streams[retval];
str_info->sfreq = sst_get_sfreq(str_param);
/* power on the analog, if reqd */
if (str_param->ops == STREAM_OPS_PLAYBACK ||
str_param->ops == STREAM_OPS_PLAYBACK_DRM) {
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
sst_drv_ctx->scard_ops->power_up_pmic_pb(
sst_drv_ctx->pmic_port_instance);
else
sst_drv_ctx->scard_ops->power_up_pmic_pb(
str_info->device);
/*Only if the playback is MP3 - Send a message*/
sst_drv_ctx->pb_streams++;
} else if (str_param->ops == STREAM_OPS_CAPTURE) {
sst_drv_ctx->scard_ops->power_up_pmic_cp(
sst_drv_ctx->pmic_port_instance);
/*Send a messageif not sent already*/
sst_drv_ctx->cp_streams++;
}
err:
return retval;
}
void sst_process_mad_ops(struct work_struct *work)
{
struct mad_ops_wq *mad_ops =
container_of(work, struct mad_ops_wq, wq);
int retval = 0;
switch (mad_ops->control_op) {
case SST_SND_PAUSE:
retval = sst_pause_stream(mad_ops->stream_id);
break;
case SST_SND_RESUME:
retval = sst_resume_stream(mad_ops->stream_id);
break;
case SST_SND_DROP:
retval = sst_drop_stream(mad_ops->stream_id);
break;
case SST_SND_START:
pr_debug("SST Debug: start stream\n");
retval = sst_start_stream(mad_ops->stream_id);
break;
case SST_SND_STREAM_PROCESS:
pr_debug("play/capt frames...\n");
break;
default:
pr_err(" wrong control_ops reported\n");
}
return;
}
void send_intial_rx_timeslot(void)
{
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID &&
sst_drv_ctx->rx_time_slot_status != RX_TIMESLOT_UNINIT
&& sst_drv_ctx->pmic_vendor != SND_NC)
sst_enable_rx_timeslot(sst_drv_ctx->rx_time_slot_status);
}
/*
* sst_open_pcm_stream - Open PCM interface
*
* @str_param: parameters of pcm stream
*
* This function is called by MID sound card driver to open
* a new pcm interface
*/
int sst_open_pcm_stream(struct snd_sst_params *str_param)
{
struct stream_info *str_info;
int retval;
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
if (sst_drv_ctx->sst_state == SST_SUSPENDED) {
/* LPE is suspended, resume it before proceeding*/
pr_debug("Resuming from Suspended state\n");
retval = intel_sst_resume(sst_drv_ctx->pci);
if (retval) {
pr_err("Resume Failed = %#x, abort\n", retval);
pm_runtime_put(&sst_drv_ctx->pci->dev);
return retval;
}
}
if (sst_drv_ctx->sst_state == SST_UN_INIT) {
/* FW is not downloaded */
pr_debug("DSP Downloading FW now...\n");
retval = sst_download_fw();
if (retval) {
pr_err("FW download fail %x, abort\n", retval);
pm_runtime_put(&sst_drv_ctx->pci->dev);
return retval;
}
send_intial_rx_timeslot();
}
if (!str_param) {
pm_runtime_put(&sst_drv_ctx->pci->dev);
return -EINVAL;
}
retval = sst_get_stream(str_param);
if (retval > 0) {
sst_drv_ctx->stream_cnt++;
str_info = &sst_drv_ctx->streams[retval];
str_info->src = MAD_DRV;
} else
pm_runtime_put(&sst_drv_ctx->pci->dev);
return retval;
}
/*
* sst_close_pcm_stream - Close PCM interface
*
* @str_id: stream id to be closed
*
* This function is called by MID sound card driver to close
* an existing pcm interface
*/
int sst_close_pcm_stream(unsigned int str_id)
{
struct stream_info *stream;
pr_debug("sst: stream free called\n");
if (sst_validate_strid(str_id))
return -EINVAL;
stream = &sst_drv_ctx->streams[str_id];
free_stream_context(str_id);
stream->pcm_substream = NULL;
stream->status = STREAM_UN_INIT;
stream->period_elapsed = NULL;
sst_drv_ctx->stream_cnt--;
pr_debug("sst: will call runtime put now\n");
pm_runtime_put(&sst_drv_ctx->pci->dev);
return 0;
}
/*
* sst_device_control - Set Control params
*
* @cmd: control cmd to be set
* @arg: command argument
*
* This function is called by MID sound card driver to set
* SST/Sound card controls for an opened stream.
* This is registered with MID driver
*/
int sst_device_control(int cmd, void *arg)
{
int retval = 0, str_id = 0;
switch (cmd) {
case SST_SND_PAUSE:
case SST_SND_RESUME:
case SST_SND_DROP:
case SST_SND_START:
sst_drv_ctx->mad_ops.control_op = cmd;
sst_drv_ctx->mad_ops.stream_id = *(int *)arg;
queue_work(sst_drv_ctx->mad_wq, &sst_drv_ctx->mad_ops.wq);
break;
case SST_SND_STREAM_INIT: {
struct pcm_stream_info *str_info;
struct stream_info *stream;
pr_debug("stream init called\n");
str_info = (struct pcm_stream_info *)arg;
str_id = str_info->str_id;
retval = sst_validate_strid(str_id);
if (retval)
break;
stream = &sst_drv_ctx->streams[str_id];
pr_debug("setting the period ptrs\n");
stream->pcm_substream = str_info->mad_substream;
stream->period_elapsed = str_info->period_elapsed;
stream->sfreq = str_info->sfreq;
stream->prev = stream->status;
stream->status = STREAM_INIT;
break;
}
case SST_SND_BUFFER_POINTER: {
struct pcm_stream_info *stream_info;
struct snd_sst_tstamp fw_tstamp = {0,};
struct stream_info *stream;
stream_info = (struct pcm_stream_info *)arg;
str_id = stream_info->str_id;
retval = sst_validate_strid(str_id);
if (retval)
break;
stream = &sst_drv_ctx->streams[str_id];
if (!stream->pcm_substream)
break;
memcpy_fromio(&fw_tstamp,
((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP)
+(str_id * sizeof(fw_tstamp))),
sizeof(fw_tstamp));
pr_debug("Pointer Query on strid = %d ops %d\n",
str_id, stream->ops);
if (stream->ops == STREAM_OPS_PLAYBACK)
stream_info->buffer_ptr = fw_tstamp.samples_rendered;
else
stream_info->buffer_ptr = fw_tstamp.samples_processed;
pr_debug("Samples rendered = %llu, buffer ptr %llu\n",
fw_tstamp.samples_rendered, stream_info->buffer_ptr);
break;
}
case SST_ENABLE_RX_TIME_SLOT: {
int status = *(int *)arg;
sst_drv_ctx->rx_time_slot_status = status ;
sst_enable_rx_timeslot(status);
break;
}
default:
/* Illegal case */
pr_warn("illegal req\n");
return -EINVAL;
}
return retval;
}
struct intel_sst_pcm_control pcm_ops = {
.open = sst_open_pcm_stream,
.device_control = sst_device_control,
.close = sst_close_pcm_stream,
};
struct intel_sst_card_ops sst_pmic_ops = {
.pcm_control = &pcm_ops,
};
/*
* 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 (!sst_drv_ctx) {
pr_err("No SST driver register card reject\n");
return -ENODEV;
}
if (!card || !card->module_name) {
pr_err("Null Pointer Passed\n");
return -EINVAL;
}
if (sst_drv_ctx->pmic_state == SND_MAD_UN_INIT) {
/* register this driver */
if ((strncmp(SST_CARD_NAMES, card->module_name,
strlen(SST_CARD_NAMES))) == 0) {
sst_drv_ctx->pmic_vendor = card->vendor_id;
sst_drv_ctx->scard_ops = card->scard_ops;
sst_pmic_ops.module_name = card->module_name;
sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE;
sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/
card->pcm_control = sst_pmic_ops.pcm_control;
return 0;
} else {
pr_err("strcmp fail %s\n", card->module_name);
return -EINVAL;
}
} else {
/* already registered a driver */
pr_err("Repeat for registration..denied\n");
return -EBADRQC;
}
/* The ASoC code doesn't set scard_ops */
if (sst_drv_ctx->scard_ops)
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
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.pcm_control == card->pcm_control) {
/* unreg */
sst_pmic_ops.module_name = "";
sst_drv_ctx->pmic_state = SND_MAD_UN_INIT;
pr_debug("Unregistered %s\n", card->module_name);
}
return;
}
EXPORT_SYMBOL_GPL(unregister_sst_card);

View File

@ -1,496 +0,0 @@
/*
* intel_sst_dsp.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 dsp controlling functions like firmware download,
* setting/resetting dsp cores, etc
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/firmware.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
/**
* intel_sst_reset_dsp_mrst - Resetting SST DSP
*
* This resets DSP in case of MRST platfroms
*/
static int intel_sst_reset_dsp_mrst(void)
{
union config_status_reg csr;
pr_debug("Resetting the DSP in mrst\n");
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.full |= 0x382;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.strb_cntr_rst = 0;
csr.part.run_stall = 0x1;
csr.part.bypass = 0x7;
csr.part.sst_reset = 0x1;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
return 0;
}
/**
* intel_sst_reset_dsp_medfield - Resetting SST DSP
*
* This resets DSP in case of Medfield platfroms
*/
static int intel_sst_reset_dsp_medfield(void)
{
union config_status_reg csr;
pr_debug("Resetting the DSP in medfield\n");
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.full |= 0x382;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
return 0;
}
/**
* sst_start_mrst - Start the SST DSP processor
*
* This starts the DSP in MRST platfroms
*/
static int sst_start_mrst(void)
{
union config_status_reg csr;
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.bypass = 0;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
csr.part.run_stall = 0;
csr.part.sst_reset = 0;
csr.part.strb_cntr_rst = 1;
pr_debug("Setting SST to execute_mrst 0x%x\n", csr.full);
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
return 0;
}
/**
* sst_start_medfield - Start the SST DSP processor
*
* This starts the DSP in Medfield platfroms
*/
static int sst_start_medfield(void)
{
union config_status_reg csr;
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.bypass = 0;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.mfld_strb = 1;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.run_stall = 0;
csr.part.sst_reset = 0;
pr_debug("Starting the DSP_medfld %x\n", csr.full);
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
pr_debug("Starting the DSP_medfld\n");
return 0;
}
/**
* sst_parse_module - Parse audio FW modules
*
* @module: FW module header
*
* Parses modules that need to be placed in SST IRAM and DRAM
* returns error or 0 if module sizes are proper
*/
static int sst_parse_module(struct fw_module_header *module)
{
struct dma_block_info *block;
u32 count;
void __iomem *ram;
pr_debug("module sign %s size %x blocks %x type %x\n",
module->signature, module->mod_size,
module->blocks, module->type);
pr_debug("module entrypoint 0x%x\n", module->entry_point);
block = (void *)module + sizeof(*module);
for (count = 0; count < module->blocks; count++) {
if (block->size <= 0) {
pr_err("block size invalid\n");
return -EINVAL;
}
switch (block->type) {
case SST_IRAM:
ram = sst_drv_ctx->iram;
break;
case SST_DRAM:
ram = sst_drv_ctx->dram;
break;
default:
pr_err("wrong ram type0x%x in block0x%x\n",
block->type, count);
return -EINVAL;
}
memcpy_toio(ram + block->ram_offset,
(void *)block + sizeof(*block), block->size);
block = (void *)block + sizeof(*block) + block->size;
}
return 0;
}
/**
* sst_parse_fw_image - parse and load FW
*
* @sst_fw: pointer to audio fw
*
* This function is called to parse and download the FW image
*/
static int sst_parse_fw_image(const struct firmware *sst_fw)
{
struct fw_header *header;
u32 count;
int ret_val;
struct fw_module_header *module;
BUG_ON(!sst_fw);
/* Read the header information from the data pointer */
header = (struct fw_header *)sst_fw->data;
/* verify FW */
if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
(sst_fw->size != header->file_size + sizeof(*header))) {
/* Invalid FW signature */
pr_err("Invalid FW sign/filesize mismatch\n");
return -EINVAL;
}
pr_debug("header sign=%s size=%x modules=%x fmt=%x size=%x\n",
header->signature, header->file_size, header->modules,
header->file_format, sizeof(*header));
module = (void *)sst_fw->data + sizeof(*header);
for (count = 0; count < header->modules; count++) {
/* module */
ret_val = sst_parse_module(module);
if (ret_val)
return ret_val;
module = (void *)module + sizeof(*module) + module->mod_size ;
}
return 0;
}
/**
* sst_load_fw - function to load FW into DSP
*
* @fw: Pointer to driver loaded FW
* @context: driver context
*
* This function is called by OS when the FW is loaded into kernel
*/
int sst_load_fw(const struct firmware *fw, void *context)
{
int ret_val;
pr_debug("load_fw called\n");
BUG_ON(!fw);
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
ret_val = intel_sst_reset_dsp_mrst();
else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID)
ret_val = intel_sst_reset_dsp_medfield();
if (ret_val)
return ret_val;
ret_val = sst_parse_fw_image(fw);
if (ret_val)
return ret_val;
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_FW_LOADED;
mutex_unlock(&sst_drv_ctx->sst_lock);
/* 7. ask scu to reset the bypass bits */
/* 8.bring sst out of reset */
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
ret_val = sst_start_mrst();
else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID)
ret_val = sst_start_medfield();
if (ret_val)
return ret_val;
pr_debug("fw loaded successful!!!\n");
return ret_val;
}
/*This function is called when any codec/post processing library
needs to be downloaded*/
static int sst_download_library(const struct firmware *fw_lib,
struct snd_sst_lib_download_info *lib)
{
/* send IPC message and wait */
int i;
u8 pvt_id;
struct ipc_post *msg = NULL;
union config_status_reg csr;
struct snd_sst_str_type str_type = {0};
int retval = 0;
if (sst_create_large_msg(&msg))
return -ENOMEM;
pvt_id = sst_assign_pvt_id(sst_drv_ctx);
i = sst_get_block_stream(sst_drv_ctx);
pr_debug("alloc block allocated = %d, pvt_id %d\n", i, pvt_id);
if (i < 0) {
kfree(msg);
return -ENOMEM;
}
sst_drv_ctx->alloc_block[i].sst_id = pvt_id;
sst_fill_header(&msg->header, IPC_IA_PREP_LIB_DNLD, 1, pvt_id);
msg->header.part.data = sizeof(u32) + sizeof(str_type);
str_type.codec_type = lib->dload_lib.lib_info.lib_type;
/*str_type.pvt_id = pvt_id;*/
memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
memcpy(msg->mailbox_data + sizeof(u32), &str_type, sizeof(str_type));
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_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]);
if (retval) {
/* error */
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
pr_err("Prep codec downloaded failed %d\n",
retval);
return -EIO;
}
pr_debug("FW responded, ready for download now...\n");
/* downloading on success */
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_FW_LOADED;
mutex_unlock(&sst_drv_ctx->sst_lock);
csr.full = readl(sst_drv_ctx->shim + SST_CSR);
csr.part.run_stall = 1;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.bypass = 0x7;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
sst_parse_fw_image(fw_lib);
/* set the FW to running again */
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.bypass = 0x0;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.part.run_stall = 0;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
/* send download complete and wait */
if (sst_create_large_msg(&msg)) {
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
return -ENOMEM;
}
sst_fill_header(&msg->header, IPC_IA_LIB_DNLD_CMPLT, 1, pvt_id);
sst_drv_ctx->alloc_block[i].sst_id = pvt_id;
msg->header.part.data = sizeof(u32) + sizeof(*lib);
lib->pvt_id = pvt_id;
memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
memcpy(msg->mailbox_data + sizeof(u32), lib, sizeof(*lib));
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);
pr_debug("Waiting for FW response Download complete\n");
sst_drv_ctx->alloc_block[i].ops_block.condition = false;
retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]);
if (retval) {
/* error */
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
return -EIO;
}
pr_debug("FW success on Download complete\n");
sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_FW_RUNNING;
mutex_unlock(&sst_drv_ctx->sst_lock);
return 0;
}
/* This function is called before downloading the codec/postprocessing
library is set for download to SST DSP*/
static int sst_validate_library(const struct firmware *fw_lib,
struct lib_slot_info *slot,
u32 *entry_point)
{
struct fw_header *header;
struct fw_module_header *module;
struct dma_block_info *block;
unsigned int n_blk, isize = 0, dsize = 0;
int err = 0;
header = (struct fw_header *)fw_lib->data;
if (header->modules != 1) {
pr_err("Module no mismatch found\n");
err = -EINVAL;
goto exit;
}
module = (void *)fw_lib->data + sizeof(*header);
*entry_point = module->entry_point;
pr_debug("Module entry point 0x%x\n", *entry_point);
pr_debug("Module Sign %s, Size 0x%x, Blocks 0x%x Type 0x%x\n",
module->signature, module->mod_size,
module->blocks, module->type);
block = (void *)module + sizeof(*module);
for (n_blk = 0; n_blk < module->blocks; n_blk++) {
switch (block->type) {
case SST_IRAM:
isize += block->size;
break;
case SST_DRAM:
dsize += block->size;
break;
default:
pr_err("Invalid block type for 0x%x\n", n_blk);
err = -EINVAL;
goto exit;
}
block = (void *)block + sizeof(*block) + block->size;
}
if (isize > slot->iram_size || dsize > slot->dram_size) {
pr_err("library exceeds size allocated\n");
err = -EINVAL;
goto exit;
} else
pr_debug("Library is safe for download...\n");
pr_debug("iram 0x%x, dram 0x%x, iram 0x%x, dram 0x%x\n",
isize, dsize, slot->iram_size, slot->dram_size);
exit:
return err;
}
/* This function is called when FW requests for a particular library download
This function prepares the library to download*/
int sst_load_library(struct snd_sst_lib_download *lib, u8 ops)
{
char buf[20];
const char *type, *dir;
int len = 0, error = 0;
u32 entry_point;
const struct firmware *fw_lib;
struct snd_sst_lib_download_info dload_info = {{{0},},};
memset(buf, 0, sizeof(buf));
pr_debug("Lib Type 0x%x, Slot 0x%x, ops 0x%x\n",
lib->lib_info.lib_type, lib->slot_info.slot_num, ops);
pr_debug("Version 0x%x, name %s, caps 0x%x media type 0x%x\n",
lib->lib_info.lib_version, lib->lib_info.lib_name,
lib->lib_info.lib_caps, lib->lib_info.media_type);
pr_debug("IRAM Size 0x%x, offset 0x%x\n",
lib->slot_info.iram_size, lib->slot_info.iram_offset);
pr_debug("DRAM Size 0x%x, offset 0x%x\n",
lib->slot_info.dram_size, lib->slot_info.dram_offset);
switch (lib->lib_info.lib_type) {
case SST_CODEC_TYPE_MP3:
type = "mp3_";
break;
case SST_CODEC_TYPE_AAC:
type = "aac_";
break;
case SST_CODEC_TYPE_AACP:
type = "aac_v1_";
break;
case SST_CODEC_TYPE_eAACP:
type = "aac_v2_";
break;
case SST_CODEC_TYPE_WMA9:
type = "wma9_";
break;
default:
pr_err("Invalid codec type\n");
error = -EINVAL;
goto wake;
}
if (ops == STREAM_OPS_CAPTURE)
dir = "enc_";
else
dir = "dec_";
len = strlen(type) + strlen(dir);
strncpy(buf, type, sizeof(buf)-1);
strncpy(buf + strlen(type), dir, sizeof(buf)-strlen(type)-1);
len += snprintf(buf + len, sizeof(buf) - len, "%d",
lib->slot_info.slot_num);
len += snprintf(buf + len, sizeof(buf) - len, ".bin");
pr_debug("Requesting %s\n", buf);
error = request_firmware(&fw_lib, buf, &sst_drv_ctx->pci->dev);
if (error) {
pr_err("library load failed %d\n", error);
goto wake;
}
error = sst_validate_library(fw_lib, &lib->slot_info, &entry_point);
if (error)
goto wake_free;
lib->mod_entry_pt = entry_point;
memcpy(&dload_info.dload_lib, lib, sizeof(*lib));
error = sst_download_library(fw_lib, &dload_info);
if (error)
goto wake_free;
/* lib is downloaded and init send alloc again */
pr_debug("Library is downloaded now...\n");
wake_free:
/* sst_wake_up_alloc_block(sst_drv_ctx, pvt_id, error, NULL); */
release_firmware(fw_lib);
wake:
return error;
}

View File

@ -1,416 +0,0 @@
#ifndef __INTEL_SST_FW_IPC_H__
#define __INTEL_SST_FW_IPC_H__
/*
* intel_sst_fw_ipc.h - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* Author: 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 has definitions shared between the firmware and driver
*/
#define MAX_NUM_STREAMS_MRST 3
#define MAX_NUM_STREAMS_MFLD 6
#define MAX_NUM_STREAMS 6
#define MAX_DBG_RW_BYTES 80
#define MAX_NUM_SCATTER_BUFFERS 8
#define MAX_LOOP_BACK_DWORDS 8
/* IPC base address and mailbox, timestamp offsets */
#define SST_MAILBOX_SIZE 0x0400
#define SST_MAILBOX_SEND 0x0000
#define SST_MAILBOX_RCV 0x0804
#define SST_TIME_STAMP 0x1800
#define SST_RESERVED_OFFSET 0x1A00
#define SST_CHEKPOINT_OFFSET 0x1C00
#define REPLY_MSG 0x80
/* Message ID's for IPC messages */
/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */
/* I2L Firmware/Codec Download msgs */
#define IPC_IA_PREP_LIB_DNLD 0x01
#define IPC_IA_LIB_DNLD_CMPLT 0x02
#define IPC_IA_SET_PMIC_TYPE 0x03
#define IPC_IA_GET_FW_VERSION 0x04
#define IPC_IA_GET_FW_BUILD_INF 0x05
#define IPC_IA_GET_FW_INFO 0x06
#define IPC_IA_GET_FW_CTXT 0x07
#define IPC_IA_SET_FW_CTXT 0x08
/* I2L Codec Config/control msgs */
#define IPC_IA_SET_CODEC_PARAMS 0x10
#define IPC_IA_GET_CODEC_PARAMS 0x11
#define IPC_IA_SET_PPP_PARAMS 0x12
#define IPC_IA_GET_PPP_PARAMS 0x13
#define IPC_IA_PLAY_FRAMES 0x14
#define IPC_IA_CAPT_FRAMES 0x15
#define IPC_IA_PLAY_VOICE 0x16
#define IPC_IA_CAPT_VOICE 0x17
#define IPC_IA_DECODE_FRAMES 0x18
#define IPC_IA_ALG_PARAMS 0x1A
#define IPC_IA_TUNING_PARAMS 0x1B
/* I2L Stream config/control msgs */
#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */
#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */
#define IPC_IA_SET_STREAM_PARAMS 0x22
#define IPC_IA_GET_STREAM_PARAMS 0x23
#define IPC_IA_PAUSE_STREAM 0x24
#define IPC_IA_RESUME_STREAM 0x25
#define IPC_IA_DROP_STREAM 0x26
#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */
#define IPC_IA_TARGET_DEV_SELECT 0x28
#define IPC_IA_CONTROL_ROUTING 0x29
#define IPC_IA_SET_STREAM_VOL 0x2A /*Vol for stream, pre mixer */
#define IPC_IA_GET_STREAM_VOL 0x2B
#define IPC_IA_SET_STREAM_MUTE 0x2C
#define IPC_IA_GET_STREAM_MUTE 0x2D
#define IPC_IA_ENABLE_RX_TIME_SLOT 0x2E /* Enable Rx time slot 0 or 1 */
#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */
/* Debug msgs */
#define IPC_IA_DBG_MEM_READ 0x40
#define IPC_IA_DBG_MEM_WRITE 0x41
#define IPC_IA_DBG_LOOP_BACK 0x42
/* L2I Firmware/Codec Download msgs */
#define IPC_IA_FW_INIT_CMPLT 0x81
#define IPC_IA_LPE_GETTING_STALLED 0x82
#define IPC_IA_LPE_UNSTALLED 0x83
/* L2I Codec Config/control msgs */
#define IPC_SST_GET_PLAY_FRAMES 0x90 /* Request IA more data */
#define IPC_SST_GET_CAPT_FRAMES 0x91 /* Request IA more data */
#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */
#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */
#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */
#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */
#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */
#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */
#define IPC_IA_TARGET_DEV_CHNGD 0x98 /* error in processing a stream */
#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */
/* L2S messages */
#define IPC_SC_DDR_LINK_UP 0xC0
#define IPC_SC_DDR_LINK_DOWN 0xC1
#define IPC_SC_SET_LPECLK_REQ 0xC2
#define IPC_SC_SSP_BIT_BANG 0xC3
/* L2I Error reporting msgs */
#define IPC_IA_MEM_ALLOC_FAIL 0xE0
#define IPC_IA_PROC_ERR 0xE1 /* error in processing a
stream can be used by playback and
capture modules */
/* L2I Debug msgs */
#define IPC_IA_PRINT_STRING 0xF0
/* Command Response or Acknowledge message to any IPC message will have
* same message ID and stream ID information which is sent.
* There is no specific Ack message ID. The data field is used as response
* meaning.
*/
enum ackData {
IPC_ACK_SUCCESS = 0,
IPC_ACK_FAILURE
};
enum sst_error_codes {
/* Error code,response to msgId: Description */
/* Common error codes */
SST_SUCCESS = 0, /* Success */
SST_ERR_INVALID_STREAM_ID = 1,
SST_ERR_INVALID_MSG_ID = 2,
SST_ERR_INVALID_STREAM_OP = 3,
SST_ERR_INVALID_PARAMS = 4,
SST_ERR_INVALID_CODEC = 5,
SST_ERR_INVALID_MEDIA_TYPE = 6,
SST_ERR_STREAM_ERR = 7,
/* IPC specific error codes */
SST_IPC_ERR_CALL_BACK_NOT_REGD = 8,
SST_IPC_ERR_STREAM_NOT_ALLOCATED = 9,
SST_IPC_ERR_STREAM_ALLOC_FAILED = 10,
SST_IPC_ERR_GET_STREAM_FAILED = 11,
SST_ERR_MOD_NOT_AVAIL = 12,
SST_ERR_MOD_DNLD_RQD = 13,
SST_ERR_STREAM_STOPPED = 14,
SST_ERR_STREAM_IN_USE = 15,
/* Capture specific error codes */
SST_CAP_ERR_INCMPLTE_CAPTURE_MSG = 16,
SST_CAP_ERR_CAPTURE_FAIL = 17,
SST_CAP_ERR_GET_DDR_NEW_SGLIST = 18,
SST_CAP_ERR_UNDER_RUN = 19,
SST_CAP_ERR_OVERFLOW = 20,
/* Playback specific error codes*/
SST_PB_ERR_INCMPLTE_PLAY_MSG = 21,
SST_PB_ERR_PLAY_FAIL = 22,
SST_PB_ERR_GET_DDR_NEW_SGLIST = 23,
/* Codec manager specific error codes */
SST_LIB_ERR_LIB_DNLD_REQUIRED = 24,
SST_LIB_ERR_LIB_NOT_SUPPORTED = 25,
/* Library manager specific error codes */
SST_SCC_ERR_PREP_DNLD_FAILED = 26,
SST_SCC_ERR_LIB_DNLD_RES_FAILED = 27,
/* Scheduler specific error codes */
SST_SCH_ERR_FAIL = 28,
/* DMA specific error codes */
SST_DMA_ERR_NO_CHNL_AVAILABLE = 29,
SST_DMA_ERR_INVALID_INPUT_PARAMS = 30,
SST_DMA_ERR_CHNL_ALREADY_SUSPENDED = 31,
SST_DMA_ERR_CHNL_ALREADY_STARTED = 32,
SST_DMA_ERR_CHNL_NOT_ENABLED = 33,
SST_DMA_ERR_TRANSFER_FAILED = 34,
SST_SSP_ERR_ALREADY_ENABLED = 35,
SST_SSP_ERR_ALREADY_DISABLED = 36,
SST_SSP_ERR_NOT_INITIALIZED = 37,
SST_SSP_ERR_SRAM_NO_DMA_DATA = 38,
/* Other error codes */
SST_ERR_MOD_INIT_FAIL = 39,
/* FW init error codes */
SST_RDR_ERR_IO_DEV_SEL_NOT_ALLOWED = 40,
SST_RDR_ERR_ROUTE_ALREADY_STARTED = 41,
SST_RDR_ERR_IO_DEV_SEL_FAILED = 42,
SST_RDR_PREP_CODEC_DNLD_FAILED = 43,
/* Memory debug error codes */
SST_ERR_DBG_MEM_READ_FAIL = 44,
SST_ERR_DBG_MEM_WRITE_FAIL = 45,
SST_ERR_INSUFFICIENT_INPUT_SG_LIST = 46,
SST_ERR_INSUFFICIENT_OUTPUT_SG_LIST = 47,
SST_ERR_BUFFER_NOT_AVAILABLE = 48,
SST_ERR_BUFFER_NOT_ALLOCATED = 49,
SST_ERR_INVALID_REGION_TYPE = 50,
SST_ERR_NULL_PTR = 51,
SST_ERR_INVALID_BUFFER_SIZE = 52,
SST_ERR_INVALID_BUFFER_INDEX = 53,
/*IIPC specific error codes */
SST_IIPC_QUEUE_FULL = 54,
SST_IIPC_ERR_MSG_SND_FAILED = 55,
SST_PB_ERR_UNDERRUN_OCCURED = 56,
SST_RDR_INSUFFICIENT_MIXER_BUFFER = 57,
SST_INVALID_TIME_SLOTS = 58,
};
enum dbg_mem_data_type {
/* Data type of debug read/write */
DATA_TYPE_U32,
DATA_TYPE_U16,
DATA_TYPE_U8,
};
/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/
/* IPC Header */
union ipc_header {
struct {
u32 msg_id:8; /* Message ID - Max 256 Message Types */
u32 str_id:5;
u32 large:1; /* Large Message if large = 1 */
u32 reserved:2; /* Reserved for future use */
u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */
u32 done:1; /* bit 30 */
u32 busy:1; /* bit 31 */
} part;
u32 full;
} __attribute__ ((packed));
/* Firmware build info */
struct sst_fw_build_info {
unsigned char date[16]; /* Firmware build date */
unsigned char time[16]; /* Firmware build time */
} __attribute__ ((packed));
struct ipc_header_fw_init {
struct snd_sst_fw_version fw_version;/* Firmware version details */
struct sst_fw_build_info build_info;
u16 result; /* Fw init result */
u8 module_id; /* Module ID in case of error */
u8 debug_info; /* Debug info from Module ID in case of fail */
} __attribute__ ((packed));
/* Address and size info of a frame buffer in DDR */
struct sst_address_info {
u32 addr; /* Address at IA */
u32 size; /* Size of the buffer */
} __attribute__ ((packed));
/* Time stamp */
struct snd_sst_tstamp {
u64 samples_processed;/* capture - data in DDR */
u64 samples_rendered;/* playback - data rendered */
u64 bytes_processed;/* bytes decoded or encoded */
u32 sampling_frequency;/* eg: 48000, 44100 */
u32 dma_base_address;/* DMA base address */
u16 dma_channel_no;/* DMA Channel used for the data transfer*/
u16 reserved;/* 32 bit alignment */
};
/* Frame info to play or capture */
struct sst_frame_info {
u16 num_entries; /* number of entries to follow */
u16 rsrvd;
struct sst_address_info addr[MAX_NUM_SCATTER_BUFFERS];
} __attribute__ ((packed));
/* Frames info for decode */
struct snd_sst_decode_info {
unsigned long long input_bytes_consumed;
unsigned long long output_bytes_produced;
struct sst_frame_info frames_in;
struct sst_frame_info frames_out;
} __attribute__ ((packed));
/* SST to IA print debug message*/
struct ipc_sst_ia_print_params {
u32 string_size;/* Max value is 160 */
u8 prt_string[160];/* Null terminated Char string */
} __attribute__ ((packed));
/* Voice data message */
struct snd_sst_voice_data {
u16 num_bytes;/* Number of valid voice data bytes */
u8 pcm_wd_size;/* 0=8 bit, 1=16 bit 2=32 bit */
u8 reserved;/* Reserved */
u8 voice_data_buf[0];/* Voice data buffer in bytes, little endian */
} __attribute__ ((packed));
/* SST to IA memory read debug message */
struct ipc_sst_ia_dbg_mem_rw {
u16 num_bytes;/* Maximum of MAX_DBG_RW_BYTES */
u16 data_type;/* enum: dbg_mem_data_type */
u32 address; /* Memory address of data memory of data_type */
u8 rw_bytes[MAX_DBG_RW_BYTES];/* Maximum of 64 bytes can be RW */
} __attribute__ ((packed));
struct ipc_sst_ia_dbg_loop_back {
u16 num_dwords; /* Maximum of MAX_DBG_RW_BYTES */
u16 increment_val;/* Increments dwords by this value, 0- no increment */
u32 lpbk_dwords[MAX_LOOP_BACK_DWORDS];/* Maximum of 8 dwords loopback */
} __attribute__ ((packed));
/* Stream type params struture for Alloc stream */
struct snd_sst_str_type {
u8 codec_type; /* Codec type */
u8 str_type; /* 1 = voice 2 = music */
u8 operation; /* Playback or Capture */
u8 protected_str; /* 0=Non DRM, 1=DRM */
u8 time_slots;
u8 reserved; /* Reserved */
u16 result; /* Result used for acknowledgment */
} __attribute__ ((packed));
/* Library info structure */
struct module_info {
u32 lib_version;
u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/
u32 media_type;
u8 lib_name[12];
u32 lib_caps;
unsigned char b_date[16]; /* Lib build date */
unsigned char b_time[16]; /* Lib build time */
} __attribute__ ((packed));
/* Library slot info */
struct lib_slot_info {
u8 slot_num; /* 1 or 2 */
u8 reserved1;
u16 reserved2;
u32 iram_size; /* slot size in IRAM */
u32 dram_size; /* slot size in DRAM */
u32 iram_offset; /* starting offset of slot in IRAM */
u32 dram_offset; /* starting offset of slot in DRAM */
} __attribute__ ((packed));
struct snd_sst_lib_download {
struct module_info lib_info; /* library info type, capabilities etc */
struct lib_slot_info slot_info; /* slot info to be downloaded */
u32 mod_entry_pt;
};
struct snd_sst_lib_download_info {
struct snd_sst_lib_download dload_lib;
u16 result; /* Result used for acknowledgment */
u8 pvt_id; /* Private ID */
u8 reserved; /* for alignment */
};
/* Alloc stream params structure */
struct snd_sst_alloc_params {
struct snd_sst_str_type str_type;
struct snd_sst_stream_params stream_params;
};
struct snd_sst_fw_get_stream_params {
struct snd_sst_stream_params codec_params;
struct snd_sst_pmic_config pcm_params;
};
/* Alloc stream response message */
struct snd_sst_alloc_response {
struct snd_sst_str_type str_type; /* Stream type for allocation */
struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */
};
/* Drop response */
struct snd_sst_drop_response {
u32 result;
u32 bytes;
};
/* CSV Voice call routing structure */
struct snd_sst_control_routing {
u8 control; /* 0=start, 1=Stop */
u8 reserved[3]; /* Reserved- for 32 bit alignment */
};
struct ipc_post {
struct list_head node;
union ipc_header header; /* driver specific */
char *mailbox_data;
};
struct snd_sst_ctxt_params {
u32 address; /* Physical Address in DDR where the context is stored */
u32 size; /* size of the context */
};
#endif /* __INTEL_SST_FW_IPC_H__ */

View File

@ -1,440 +0,0 @@
#ifndef __INTEL_SST_IOCTL_H__
#define __INTEL_SST_IOCTL_H__
/*
* intel_sst_ioctl.h - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* 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 file defines all sst ioctls
*/
/* codec and post/pre processing related info */
#include <linux/types.h>
enum sst_codec_types {
/* AUDIO/MUSIC CODEC Type Definitions */
SST_CODEC_TYPE_UNKNOWN = 0,
SST_CODEC_TYPE_PCM, /* Pass through Audio codec */
SST_CODEC_TYPE_MP3,
SST_CODEC_TYPE_MP24,
SST_CODEC_TYPE_AAC,
SST_CODEC_TYPE_AACP,
SST_CODEC_TYPE_eAACP,
SST_CODEC_TYPE_WMA9,
SST_CODEC_TYPE_WMA10,
SST_CODEC_TYPE_WMA10P,
SST_CODEC_TYPE_RA,
SST_CODEC_TYPE_DDAC3,
SST_CODEC_TYPE_STEREO_TRUE_HD,
SST_CODEC_TYPE_STEREO_HD_PLUS,
/* VOICE CODEC Type Definitions */
SST_CODEC_TYPE_VOICE_PCM = 0x21, /* Pass through voice codec */
};
enum sst_algo_types {
SST_CODEC_SRC = 0x64,
SST_CODEC_MIXER = 0x65,
SST_CODEC_DOWN_MIXER = 0x66,
SST_CODEC_VOLUME_CONTROL = 0x67,
SST_CODEC_OEM1 = 0xC8,
SST_CODEC_OEM2 = 0xC9,
};
enum snd_sst_stream_ops {
STREAM_OPS_PLAYBACK = 0, /* Decode */
STREAM_OPS_CAPTURE, /* Encode */
STREAM_OPS_PLAYBACK_DRM, /* Play Audio/Voice */
STREAM_OPS_PLAYBACK_ALERT, /* Play Audio/Voice */
STREAM_OPS_CAPTURE_VOICE_CALL, /* CSV Voice recording */
};
enum stream_mode {
SST_STREAM_MODE_NONE = 0,
SST_STREAM_MODE_DNR = 1,
SST_STREAM_MODE_FNF = 2,
SST_STREAM_MODE_CAPTURE = 3
};
enum stream_type {
SST_STREAM_TYPE_NONE = 0,
SST_STREAM_TYPE_MUSIC = 1,
SST_STREAM_TYPE_NORMAL = 2,
SST_STREAM_TYPE_LONG_PB = 3,
SST_STREAM_TYPE_LOW_LATENCY = 4,
};
enum snd_sst_audio_device_type {
SND_SST_DEVICE_HEADSET = 1,
SND_SST_DEVICE_IHF,
SND_SST_DEVICE_VIBRA,
SND_SST_DEVICE_HAPTIC,
SND_SST_DEVICE_CAPTURE,
};
/* Firmware Version info */
struct snd_sst_fw_version {
__u8 build; /* build number*/
__u8 minor; /* minor number*/
__u8 major; /* major number*/
__u8 type; /* build type */
};
/* Port info structure */
struct snd_sst_port_info {
__u16 port_type;
__u16 reserved;
};
/* Mixer info structure */
struct snd_sst_mix_info {
__u16 max_streams;
__u16 reserved;
};
/* PCM Parameters */
struct snd_pcm_params {
__u16 codec; /* codec type */
__u8 num_chan; /* 1=Mono, 2=Stereo */
__u8 pcm_wd_sz; /* 16/24 - bit*/
__u32 reserved; /* Bitrate in bits per second */
__u32 sfreq; /* Sampling rate in Hz */
__u32 ring_buffer_size;
__u32 period_count; /* period elapsed in samples*/
__u32 ring_buffer_addr;
};
/* MP3 Music Parameters Message */
struct snd_mp3_params {
__u16 codec;
__u8 num_chan; /* 1=Mono, 2=Stereo */
__u8 pcm_wd_sz; /* 16/24 - bit*/
__u32 brate; /* Use the hard coded value. */
__u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
__u8 crc_check; /* crc_check - disable (0) or enable (1) */
__u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB*/
__u16 reserved; /* Unused */
};
#define AAC_BIT_STREAM_ADTS 0
#define AAC_BIT_STREAM_ADIF 1
#define AAC_BIT_STREAM_RAW 2
/* AAC Music Parameters Message */
struct snd_aac_params {
__u16 codec;
__u8 num_chan; /* 1=Mono, 2=Stereo*/
__u8 pcm_wd_sz; /* 16/24 - bit*/
__u32 brate;
__u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
__u32 aac_srate; /* Plain AAC decoder operating sample rate */
__u8 mpg_id; /* 0=MPEG-2, 1=MPEG-4 */
__u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */
__u8 aac_profile; /* 0=Main Profile, 1=LC profile, 3=SSR profile */
__u8 ext_chl; /* No.of external channels */
__u8 aot; /* Audio object type. 1=Main , 2=LC , 3=SSR, 4=SBR*/
__u8 op_align; /* output alignment 0=16 bit , 1=MSB, 2= LSB align */
__u8 brate_type; /* 0=CBR, 1=VBR */
__u8 crc_check; /* crc check 0= disable, 1=enable */
__s8 bit_stream_format[8]; /* input bit stream format adts/adif/raw */
__u8 jstereo; /* Joint stereo Flag */
__u8 sbr_present; /* 1 = SBR Present, 0 = SBR absent, for RAW */
__u8 downsample; /* 1 = Downsampling ON, 0 = Downsampling OFF */
__u8 num_syntc_elems; /* 1- Mono/stereo, 0 - Dual Mono, 0 - for raw */
__s8 syntc_id[2]; /* 0 for ID_SCE(Dula Mono), -1 for raw */
__s8 syntc_tag[2]; /* raw - -1 and 0 -16 for rest of the streams */
__u8 pce_present; /* Flag. 1- present 0 - not present, for RAW */
__u8 sbr_type; /* sbr_type: 0-plain aac, 1-aac-v1, 2-aac-v2 */
__u8 outchmode; /*0- mono, 1-stereo, 2-dual mono 3-Parametric stereo */
__u8 ps_present;
};
/* WMA Music Parameters Message */
struct snd_wma_params {
__u16 codec;
__u8 num_chan; /* 1=Mono, 2=Stereo */
__u8 pcm_wd_sz; /* 16/24 - bit*/
__u32 brate; /* Use the hard coded value. */
__u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
__u32 channel_mask; /* Channel Mask */
__u16 format_tag; /* Format Tag */
__u16 block_align; /* packet size */
__u16 wma_encode_opt;/* Encoder option */
__u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */
__u8 pcm_src; /* input pcm bit width */
};
/* Pre processing param structure */
struct snd_prp_params {
__u32 reserved; /* No pre-processing defined yet */
};
/* Pre and post processing params structure */
struct snd_ppp_params {
__u8 algo_id;/* Post/Pre processing algorithm ID */
__u8 str_id; /*Only 5 bits used 0 - 31 are valid*/
__u8 enable; /* 0= disable, 1= enable*/
__u8 reserved;
__u32 size; /*Size of parameters for all blocks*/
void *params;
} __attribute__ ((packed));
struct snd_sst_postproc_info {
__u32 src_min; /* Supported SRC Min sampling freq */
__u32 src_max; /* Supported SRC Max sampling freq */
__u8 src; /* 0=Not supported, 1=Supported */
__u8 bass_boost; /* 0=Not Supported, 1=Supported */
__u8 stereo_widening; /* 0=Not Supported, 1=Supported */
__u8 volume_control; /* 0=Not Supported, 1=Supported */
__s16 min_vol; /* Minimum value of Volume in dB */
__s16 max_vol; /* Maximum value of Volume in dB */
__u8 mute_control; /* 0=No Mute, 1=Mute */
__u8 reserved1;
__u16 reserved2;
};
/* pre processing Capability info structure */
struct snd_sst_prp_info {
__s16 min_vol; /* Minimum value of Volume in dB */
__s16 max_vol; /* Maximum value of Volume in dB */
__u8 volume_control; /* 0=Not Supported, 1=Supported */
__u8 reserved1; /* for 32 bit alignment */
__u16 reserved2; /* for 32 bit alignment */
} __attribute__ ((packed));
/*Pre / Post processing algorithms support*/
struct snd_sst_ppp_info {
__u32 src:1; /* 0=Not supported, 1=Supported */
__u32 mixer:1; /* 0=Not supported, 1=Supported */
__u32 volume_control:1; /* 0=Not Supported, 1=Supported */
__u32 mute_control:1; /* 0=Not Supported, 1=Supported */
__u32 anc:1; /* 0=Not Supported, 1=Supported */
__u32 side_tone:1; /* 0=Not Supported, 1=Supported */
__u32 dc_removal:1; /* 0=Not Supported, 1=Supported */
__u32 equalizer:1; /* 0=Not Supported, 1=Supported */
__u32 spkr_prot:1; /* 0=Not Supported, 1=Supported */
__u32 bass_boost:1; /* 0=Not Supported, 1=Supported */
__u32 stereo_widening:1;/* 0=Not Supported, 1=Supported */
__u32 rsvd1:21;
__u32 rsvd2;
};
/* Firmware capabilities info */
struct snd_sst_fw_info {
struct snd_sst_fw_version fw_version; /* Firmware version */
__u8 audio_codecs_supported[8]; /* Codecs supported by FW */
__u32 recommend_min_duration; /* Min duration for Lowpower Playback */
__u8 max_pcm_streams_supported; /* Max num of PCM streams supported */
__u8 max_enc_streams_supported; /* Max number of Encoded streams */
__u16 reserved; /* 32 bit alignment*/
struct snd_sst_ppp_info ppp_info; /* pre_processing mod cap info */
struct snd_sst_postproc_info pop_info; /* Post processing cap info*/
struct snd_sst_port_info port_info[3]; /* Port info */
struct snd_sst_mix_info mix_info;/* Mixer info */
__u32 min_input_buf; /* minmum i/p buffer for decode */
};
/* Codec params struture */
union snd_sst_codec_params {
struct snd_pcm_params pcm_params;
struct snd_mp3_params mp3_params;
struct snd_aac_params aac_params;
struct snd_wma_params wma_params;
};
struct snd_sst_stream_params {
union snd_sst_codec_params uc;
} __attribute__ ((packed));
struct snd_sst_params {
__u32 result;
__u32 stream_id;
__u8 codec;
__u8 ops;
__u8 stream_type;
__u8 device_type;
struct snd_sst_stream_params sparams;
};
struct snd_sst_vol {
__u32 stream_id;
__s32 volume;
__u32 ramp_duration;
__u32 ramp_type; /* Ramp type, default=0 */
};
struct snd_sst_mute {
__u32 stream_id;
__u32 mute;
};
/* ioctl related stuff here */
struct snd_sst_pmic_config {
__u32 sfreq; /* Sampling rate in Hz */
__u16 num_chan; /* Mono =1 or Stereo =2 */
__u16 pcm_wd_sz; /* Number of bits per sample */
} __attribute__ ((packed));
struct snd_sst_get_stream_params {
struct snd_sst_params codec_params;
struct snd_sst_pmic_config pcm_params;
};
enum snd_sst_target_type {
SND_SST_TARGET_PMIC = 1,
SND_SST_TARGET_LPE,
SND_SST_TARGET_MODEM,
SND_SST_TARGET_BT,
SND_SST_TARGET_FM,
SND_SST_TARGET_NONE,
};
enum snd_sst_device_type {
SND_SST_DEVICE_SSP = 1,
SND_SST_DEVICE_PCM,
SND_SST_DEVICE_OTHER,
};
enum snd_sst_device_mode {
SND_SST_DEV_MODE_PCM_MODE1 = 1, /*(16-bit word, bit-length frame sync)*/
SND_SST_DEV_MODE_PCM_MODE2,
SND_SST_DEV_MODE_PCM_MODE3,
SND_SST_DEV_MODE_PCM_MODE4_RIGHT_JUSTIFIED,
SND_SST_DEV_MODE_PCM_MODE4_LEFT_JUSTIFIED,
SND_SST_DEV_MODE_PCM_MODE4_I2S, /*(I2S mode, 16-bit words)*/
SND_SST_DEV_MODE_PCM_MODE5,
SND_SST_DEV_MODE_PCM_MODE6,
};
enum snd_sst_port_action {
SND_SST_PORT_PREPARE = 1,
SND_SST_PORT_ACTIVATE,
};
/* Target selection per device structure */
struct snd_sst_slot_info {
__u8 mix_enable; /* Mixer enable or disable */
__u8 device_type;
__u8 device_instance; /* 0, 1, 2 */
__u8 target_device;
__u16 target_sink;
__u8 slot[2];
__u8 master;
__u8 action;
__u8 device_mode;
__u8 reserved;
struct snd_sst_pmic_config pcm_params;
} __attribute__ ((packed));
#define SST_MAX_TARGET_DEVICES 3
/* Target device list structure */
struct snd_sst_target_device {
__u32 device_route;
struct snd_sst_slot_info devices[SST_MAX_TARGET_DEVICES];
} __attribute__ ((packed));
struct snd_sst_driver_info {
__u32 version; /* Version of the driver */
__u32 active_pcm_streams;
__u32 active_enc_streams;
__u32 max_pcm_streams;
__u32 max_enc_streams;
__u32 buf_per_stream;
};
enum snd_sst_buff_type {
SST_BUF_USER = 1,
SST_BUF_MMAP,
SST_BUF_RAR,
};
struct snd_sst_mmap_buff_entry {
unsigned int offset;
unsigned int size;
};
struct snd_sst_mmap_buffs {
unsigned int entries;
enum snd_sst_buff_type type;
struct snd_sst_mmap_buff_entry *buff;
};
struct snd_sst_buff_entry {
void *buffer;
unsigned int size;
};
struct snd_sst_buffs {
unsigned int entries;
__u8 type;
struct snd_sst_buff_entry *buff_entry;
};
struct snd_sst_dbufs {
unsigned long long input_bytes_consumed;
unsigned long long output_bytes_produced;
struct snd_sst_buffs *ibufs;
struct snd_sst_buffs *obufs;
};
struct snd_sst_tuning_params {
__u8 type;
__u8 str_id;
__u8 size;
__u8 rsvd;
__aligned_u64 addr;
} __attribute__ ((packed));
/*IOCTL defined here */
/*SST MMF IOCTLS only */
#define SNDRV_SST_STREAM_SET_PARAMS _IOR('L', 0x00, \
struct snd_sst_stream_params *)
#define SNDRV_SST_STREAM_GET_PARAMS _IOWR('L', 0x01, \
struct snd_sst_get_stream_params *)
#define SNDRV_SST_STREAM_GET_TSTAMP _IOWR('L', 0x02, __u64 *)
#define SNDRV_SST_STREAM_DECODE _IOWR('L', 0x03, struct snd_sst_dbufs *)
#define SNDRV_SST_STREAM_BYTES_DECODED _IOWR('L', 0x04, __u64 *)
#define SNDRV_SST_STREAM_START _IO('A', 0x42)
#define SNDRV_SST_STREAM_DROP _IO('A', 0x43)
#define SNDRV_SST_STREAM_DRAIN _IO('A', 0x44)
#define SNDRV_SST_STREAM_PAUSE _IOW('A', 0x45, int)
#define SNDRV_SST_STREAM_RESUME _IO('A', 0x47)
#define SNDRV_SST_MMAP_PLAY _IOW('L', 0x05, struct snd_sst_mmap_buffs *)
#define SNDRV_SST_MMAP_CAPTURE _IOW('L', 0x06, struct snd_sst_mmap_buffs *)
/*SST common ioctls */
#define SNDRV_SST_DRIVER_INFO _IOR('L', 0x10, struct snd_sst_driver_info *)
#define SNDRV_SST_SET_VOL _IOW('L', 0x11, struct snd_sst_vol *)
#define SNDRV_SST_GET_VOL _IOW('L', 0x12, struct snd_sst_vol *)
#define SNDRV_SST_MUTE _IOW('L', 0x13, struct snd_sst_mute *)
/*AM Ioctly only */
#define SNDRV_SST_FW_INFO _IOR('L', 0x20, struct snd_sst_fw_info *)
#define SNDRV_SST_SET_TARGET_DEVICE _IOW('L', 0x21, \
struct snd_sst_target_device *)
/*DSP Ioctls on /dev/intel_sst_ctrl only*/
#define SNDRV_SST_SET_ALGO _IOW('L', 0x30, struct snd_ppp_params *)
#define SNDRV_SST_GET_ALGO _IOWR('L', 0x31, struct snd_ppp_params *)
#define SNDRV_SST_TUNING_PARAMS _IOW('L', 0x32, struct snd_sst_tuning_params *)
#endif /* __INTEL_SST_IOCTL_H__ */

View File

@ -1,774 +0,0 @@
/*
* intel_sst_ipc.c - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* 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 file defines all ipc functions
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.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_send_sound_card_type - send sound card type
*
* this function sends the sound card type to sst dsp engine
*/
static void sst_send_sound_card_type(void)
{
struct ipc_post *msg = NULL;
if (sst_create_short_msg(&msg))
return;
sst_fill_header(&msg->header, IPC_IA_SET_PMIC_TYPE, 0, 0);
msg->header.part.data = sst_drv_ctx->pmic_vendor;
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_post_message - Posts message to SST
*
* @work: Pointer to work structure
*
* This function is called by any component in driver which
* wants to send an IPC message. This will post message only if
* busy bit is free
*/
void sst_post_message(struct work_struct *work)
{
struct ipc_post *msg;
union ipc_header header;
union interrupt_reg imr;
int retval = 0;
imr.full = 0;
/*To check if LPE is in stalled state.*/
retval = sst_stalled();
if (retval < 0) {
pr_err("in stalled state\n");
return;
}
pr_debug("post message called\n");
spin_lock(&sst_drv_ctx->list_spin_lock);
/* check list */
if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
/* list is empty, mask imr */
pr_debug("Empty msg queue... masking\n");
imr.full = readl(sst_drv_ctx->shim + SST_IMRX);
imr.part.done_interrupt = 1;
/* dummy register for shim workaround */
sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
spin_unlock(&sst_drv_ctx->list_spin_lock);
return;
}
/* check busy bit */
header.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCX);
if (header.part.busy) {
/* busy, unmask */
pr_debug("Busy not free... unmasking\n");
imr.full = readl(sst_drv_ctx->shim + SST_IMRX);
imr.part.done_interrupt = 0;
/* dummy register for shim workaround */
sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
spin_unlock(&sst_drv_ctx->list_spin_lock);
return;
}
/* copy msg from list */
msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
struct ipc_post, node);
list_del(&msg->node);
pr_debug("Post message: header = %x\n", msg->header.full);
pr_debug("size: = %x\n", msg->header.part.data);
if (msg->header.part.large)
memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
msg->mailbox_data, msg->header.part.data);
/* dummy register for shim workaround */
sst_shim_write(sst_drv_ctx->shim, SST_IPCX, msg->header.full);
spin_unlock(&sst_drv_ctx->list_spin_lock);
kfree(msg->mailbox_data);
kfree(msg);
return;
}
/*
* sst_clear_interrupt - clear the SST FW interrupt
*
* This function clears the interrupt register after the interrupt
* bottom half is complete allowing next interrupt to arrive
*/
void sst_clear_interrupt(void)
{
union interrupt_reg isr;
union interrupt_reg imr;
union ipc_header clear_ipc;
imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX);
isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX);
/* write 1 to clear */;
isr.part.busy_interrupt = 1;
sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
/* Set IA done bit */
clear_ipc.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCD);
clear_ipc.part.busy = 0;
clear_ipc.part.done = 1;
clear_ipc.part.data = IPC_ACK_SUCCESS;
sst_shim_write(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
/* un mask busy interrupt */
imr.part.busy_interrupt = 0;
sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
}
void sst_restore_fw_context(void)
{
struct snd_sst_ctxt_params fw_context;
struct ipc_post *msg = NULL;
pr_debug("restore_fw_context\n");
/*check cpu type*/
if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID)
return;
/*not supported for rest*/
if (!sst_drv_ctx->fw_cntx_size)
return;
/*nothing to restore*/
pr_debug("restoring context......\n");
/*send msg to fw*/
if (sst_create_large_msg(&msg))
return;
sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0);
msg->header.part.data = sizeof(fw_context) + sizeof(u32);
fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx);
fw_context.size = sst_drv_ctx->fw_cntx_size;
memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
memcpy(msg->mailbox_data + sizeof(u32),
&fw_context, sizeof(fw_context));
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;
}
/*
* process_fw_init - process the FW init msg
*
* @msg: IPC message from FW
*
* This function processes the FW init msg from FW
* marks FW state and prints debug info of loaded FW
*/
int process_fw_init(struct sst_ipc_msg_wq *msg)
{
struct ipc_header_fw_init *init =
(struct ipc_header_fw_init *)msg->mailbox;
int retval = 0;
pr_debug("*** FW Init msg came***\n");
if (init->result) {
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_ERROR;
mutex_unlock(&sst_drv_ctx->sst_lock);
pr_debug("FW Init failed, Error %x\n", init->result);
pr_err("FW Init failed, Error %x\n", init->result);
retval = -init->result;
return retval;
}
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
sst_send_sound_card_type();
mutex_lock(&sst_drv_ctx->sst_lock);
sst_drv_ctx->sst_state = SST_FW_RUNNING;
sst_drv_ctx->lpe_stalled = 0;
mutex_unlock(&sst_drv_ctx->sst_lock);
pr_debug("FW Version %02x.%02x.%02x\n", init->fw_version.major,
init->fw_version.minor, init->fw_version.build);
pr_debug("Build Type %x\n", init->fw_version.type);
pr_debug(" Build date %s Time %s\n",
init->build_info.date, init->build_info.time);
sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL);
sst_restore_fw_context();
return retval;
}
/**
* sst_process_message - Processes message from SST
*
* @work: Pointer to work structure
*
* This function is scheduled by ISR
* It take a msg from process_queue and does action based on msg
*/
void sst_process_message(struct work_struct *work)
{
struct sst_ipc_msg_wq *msg =
container_of(work, struct sst_ipc_msg_wq, wq);
int str_id = msg->header.part.str_id;
pr_debug("IPC process for %x\n", msg->header.full);
/* based on msg in list call respective handler */
switch (msg->header.part.msg_id) {
case IPC_SST_BUF_UNDER_RUN:
case IPC_SST_BUF_OVER_RUN:
if (sst_validate_strid(str_id)) {
pr_err("stream id %d invalid\n", str_id);
break;
}
pr_err("Buffer under/overrun for %d\n",
msg->header.part.str_id);
pr_err("Got Underrun & not to send data...ignore\n");
break;
case IPC_SST_GET_PLAY_FRAMES:
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
struct stream_info *stream ;
if (sst_validate_strid(str_id)) {
pr_err("strid %d invalid\n", str_id);
break;
}
/* call sst_play_frame */
stream = &sst_drv_ctx->streams[str_id];
pr_debug("sst_play_frames for %d\n",
msg->header.part.str_id);
mutex_lock(&sst_drv_ctx->streams[str_id].lock);
sst_play_frame(msg->header.part.str_id);
mutex_unlock(&sst_drv_ctx->streams[str_id].lock);
break;
} else
pr_err("sst_play_frames for Penwell!!\n");
case IPC_SST_GET_CAPT_FRAMES:
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
struct stream_info *stream;
/* call sst_capture_frame */
if (sst_validate_strid(str_id)) {
pr_err("str id %d invalid\n", str_id);
break;
}
stream = &sst_drv_ctx->streams[str_id];
pr_debug("sst_capture_frames for %d\n",
msg->header.part.str_id);
mutex_lock(&stream->lock);
if (stream->mmapped == false &&
stream->src == SST_DRV) {
pr_debug("waking up block for copy.\n");
stream->data_blk.ret_code = 0;
stream->data_blk.condition = true;
stream->data_blk.on = false;
wake_up(&sst_drv_ctx->wait_queue);
} else
sst_capture_frame(msg->header.part.str_id);
mutex_unlock(&stream->lock);
} else
pr_err("sst_play_frames for Penwell!!\n");
break;
case IPC_IA_PRINT_STRING:
pr_debug("been asked to print something by fw\n");
/* TBD */
break;
case IPC_IA_FW_INIT_CMPLT: {
/* send next data to FW */
process_fw_init(msg);
break;
}
case IPC_SST_STREAM_PROCESS_FATAL_ERR:
if (sst_validate_strid(str_id)) {
pr_err("stream id %d invalid\n", str_id);
break;
}
pr_err("codec fatal error %x stream %d...\n",
msg->header.full, msg->header.part.str_id);
pr_err("Dropping the stream\n");
sst_drop_stream(msg->header.part.str_id);
break;
case IPC_IA_LPE_GETTING_STALLED:
sst_drv_ctx->lpe_stalled = 1;
break;
case IPC_IA_LPE_UNSTALLED:
sst_drv_ctx->lpe_stalled = 0;
break;
default:
/* Illegal case */
pr_err("Unhandled msg %x header %x\n",
msg->header.part.msg_id, msg->header.full);
}
sst_clear_interrupt();
return;
}
/**
* sst_process_reply - Processes reply message from SST
*
* @work: Pointer to work structure
*
* This function is scheduled by ISR
* It take a reply msg from response_queue and
* does action based on msg
*/
void sst_process_reply(struct work_struct *work)
{
struct sst_ipc_msg_wq *msg =
container_of(work, struct sst_ipc_msg_wq, wq);
int str_id = msg->header.part.str_id;
struct stream_info *str_info;
switch (msg->header.part.msg_id) {
case IPC_IA_TARGET_DEV_SELECT:
if (!msg->header.part.data) {
sst_drv_ctx->tgt_dev_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
sst_drv_ctx->tgt_dev_blk.ret_code =
-msg->header.part.data;
}
if (sst_drv_ctx->tgt_dev_blk.on == true) {
sst_drv_ctx->tgt_dev_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_ALG_PARAMS: {
pr_debug("sst:IPC_ALG_PARAMS response %x\n", msg->header.full);
pr_debug("sst: data value %x\n", msg->header.part.data);
pr_debug("sst: large value %x\n", msg->header.part.large);
if (!msg->header.part.large) {
if (!msg->header.part.data) {
pr_debug("sst: alg set success\n");
sst_drv_ctx->ppp_params_blk.ret_code = 0;
} else {
pr_debug("sst: alg set failed\n");
sst_drv_ctx->ppp_params_blk.ret_code =
-msg->header.part.data;
}
} else if (msg->header.part.data) {
struct snd_ppp_params *mailbox_params, *get_params;
char *params;
pr_debug("sst: alg get success\n");
mailbox_params = (struct snd_ppp_params *)msg->mailbox;
get_params = kzalloc(sizeof(*get_params), GFP_KERNEL);
if (get_params == NULL) {
pr_err("sst: out of memory for ALG PARAMS");
break;
}
memcpy_fromio(get_params, mailbox_params,
sizeof(*get_params));
get_params->params = kzalloc(mailbox_params->size,
GFP_KERNEL);
if (get_params->params == NULL) {
kfree(get_params);
pr_err("sst: out of memory for ALG PARAMS block");
break;
}
params = msg->mailbox;
params = params + sizeof(*mailbox_params) - sizeof(u32);
memcpy_fromio(get_params->params, params,
get_params->size);
sst_drv_ctx->ppp_params_blk.ret_code = 0;
sst_drv_ctx->ppp_params_blk.data = get_params;
}
if (sst_drv_ctx->ppp_params_blk.on == true) {
sst_drv_ctx->ppp_params_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
}
case IPC_IA_TUNING_PARAMS: {
pr_debug("sst:IPC_TUNING_PARAMS resp: %x\n", msg->header.full);
pr_debug("data value %x\n", msg->header.part.data);
if (msg->header.part.large) {
pr_debug("alg set failed\n");
sst_drv_ctx->ppp_params_blk.ret_code =
-msg->header.part.data;
} else {
pr_debug("alg set success\n");
sst_drv_ctx->ppp_params_blk.ret_code = 0;
}
if (sst_drv_ctx->ppp_params_blk.on == true) {
sst_drv_ctx->ppp_params_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
}
case IPC_IA_GET_FW_INFO: {
struct snd_sst_fw_info *fw_info =
(struct snd_sst_fw_info *)msg->mailbox;
if (msg->header.part.large) {
int major = fw_info->fw_version.major;
int minor = fw_info->fw_version.minor;
int build = fw_info->fw_version.build;
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
pr_debug("INFO: ***FW*** = %02d.%02d.%02d\n",
major, minor, build);
memcpy_fromio(sst_drv_ctx->fw_info_blk.data,
((struct snd_sst_fw_info *)(msg->mailbox)),
sizeof(struct snd_sst_fw_info));
sst_drv_ctx->fw_info_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
sst_drv_ctx->fw_info_blk.ret_code =
-msg->header.part.data;
}
if (sst_drv_ctx->fw_info_blk.on == true) {
pr_debug("Memcopy succeeded\n");
sst_drv_ctx->fw_info_blk.on = false;
sst_drv_ctx->fw_info_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
}
case IPC_IA_SET_STREAM_MUTE:
if (!msg->header.part.data) {
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
sst_drv_ctx->mute_info_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
sst_drv_ctx->mute_info_blk.ret_code =
-msg->header.part.data;
}
if (sst_drv_ctx->mute_info_blk.on == true) {
sst_drv_ctx->mute_info_blk.on = false;
sst_drv_ctx->mute_info_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_SET_STREAM_VOL:
if (!msg->header.part.data) {
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
sst_drv_ctx->vol_info_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id,
msg->header.part.data);
sst_drv_ctx->vol_info_blk.ret_code =
-msg->header.part.data;
}
if (sst_drv_ctx->vol_info_blk.on == true) {
sst_drv_ctx->vol_info_blk.on = false;
sst_drv_ctx->vol_info_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_GET_STREAM_VOL:
if (msg->header.part.large) {
pr_debug("Large Msg Received Successfully\n");
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
memcpy_fromio(sst_drv_ctx->vol_info_blk.data,
(void *) msg->mailbox,
sizeof(struct snd_sst_vol));
sst_drv_ctx->vol_info_blk.ret_code = 0;
} else {
pr_err("Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
sst_drv_ctx->vol_info_blk.ret_code =
-msg->header.part.data;
}
if (sst_drv_ctx->vol_info_blk.on == true) {
sst_drv_ctx->vol_info_blk.on = false;
sst_drv_ctx->vol_info_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_GET_STREAM_PARAMS:
if (sst_validate_strid(str_id)) {
pr_err("stream id %d invalid\n", str_id);
break;
}
str_info = &sst_drv_ctx->streams[str_id];
if (msg->header.part.large) {
pr_debug("Get stream large success\n");
memcpy_fromio(str_info->ctrl_blk.data,
((void *)(msg->mailbox)),
sizeof(struct snd_sst_fw_get_stream_params));
str_info->ctrl_blk.ret_code = 0;
} else {
pr_err("Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
str_info->ctrl_blk.ret_code = -msg->header.part.data;
}
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_DECODE_FRAMES:
if (sst_validate_strid(str_id)) {
pr_err("stream id %d invalid\n", str_id);
break;
}
str_info = &sst_drv_ctx->streams[str_id];
if (msg->header.part.large) {
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
memcpy_fromio(str_info->data_blk.data,
((void *)(msg->mailbox)),
sizeof(struct snd_sst_decode_info));
str_info->data_blk.ret_code = 0;
} else {
pr_err("Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
str_info->data_blk.ret_code = -msg->header.part.data;
}
if (str_info->data_blk.on == true) {
str_info->data_blk.on = false;
str_info->data_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_DRAIN_STREAM:
if (sst_validate_strid(str_id)) {
pr_err("stream id %d invalid\n", str_id);
break;
}
str_info = &sst_drv_ctx->streams[str_id];
if (!msg->header.part.data) {
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
str_info->ctrl_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
str_info->ctrl_blk.ret_code = -msg->header.part.data;
}
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->data_blk.on == true) {
str_info->data_blk.on = false;
str_info->data_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_DROP_STREAM:
if (sst_validate_strid(str_id)) {
pr_err("str id %d invalid\n", str_id);
break;
}
str_info = &sst_drv_ctx->streams[str_id];
if (msg->header.part.large) {
struct snd_sst_drop_response *drop_resp =
(struct snd_sst_drop_response *)msg->mailbox;
pr_debug("Drop ret bytes %x\n", drop_resp->bytes);
str_info->curr_bytes = drop_resp->bytes;
str_info->ctrl_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id, msg->header.part.data);
str_info->ctrl_blk.ret_code = -msg->header.part.data;
}
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_ENABLE_RX_TIME_SLOT:
if (!msg->header.part.data) {
pr_debug("RX_TIME_SLOT success\n");
sst_drv_ctx->hs_info_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id,
msg->header.part.data);
sst_drv_ctx->hs_info_blk.ret_code =
-msg->header.part.data;
}
if (sst_drv_ctx->hs_info_blk.on == true) {
sst_drv_ctx->hs_info_blk.on = false;
sst_drv_ctx->hs_info_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_PAUSE_STREAM:
case IPC_IA_RESUME_STREAM:
case IPC_IA_SET_STREAM_PARAMS:
str_info = &sst_drv_ctx->streams[str_id];
if (!msg->header.part.data) {
pr_debug("Msg succeeded %x\n",
msg->header.part.msg_id);
str_info->ctrl_blk.ret_code = 0;
} else {
pr_err(" Msg %x reply error %x\n",
msg->header.part.msg_id,
msg->header.part.data);
str_info->ctrl_blk.ret_code = -msg->header.part.data;
}
if (sst_validate_strid(str_id)) {
pr_err(" stream id %d invalid\n", str_id);
break;
}
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_FREE_STREAM:
str_info = &sst_drv_ctx->streams[str_id];
if (!msg->header.part.data) {
pr_debug("Stream %d freed\n", str_id);
} else {
pr_err("Free for %d ret error %x\n",
str_id, msg->header.part.data);
}
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break;
case IPC_IA_ALLOC_STREAM: {
/* map to stream, call play */
struct snd_sst_alloc_response *resp =
(struct snd_sst_alloc_response *)msg->mailbox;
if (resp->str_type.result)
pr_err("error alloc stream = %x\n",
resp->str_type.result);
sst_alloc_stream_response(str_id, resp);
break;
}
case IPC_IA_PLAY_FRAMES:
case IPC_IA_CAPT_FRAMES:
if (sst_validate_strid(str_id)) {
pr_err("stream id %d invalid\n", str_id);
break;
}
pr_debug("Ack for play/capt frames received\n");
break;
case IPC_IA_PREP_LIB_DNLD: {
struct snd_sst_str_type *str_type =
(struct snd_sst_str_type *)msg->mailbox;
pr_debug("Prep Lib download %x\n",
msg->header.part.msg_id);
if (str_type->result)
pr_err("Prep lib download %x\n", str_type->result);
else
pr_debug("Can download codec now...\n");
sst_wake_up_alloc_block(sst_drv_ctx, str_id,
str_type->result, NULL);
break;
}
case IPC_IA_LIB_DNLD_CMPLT: {
struct snd_sst_lib_download_info *resp =
(struct snd_sst_lib_download_info *)msg->mailbox;
int retval = resp->result;
pr_debug("Lib downloaded %x\n", msg->header.part.msg_id);
if (resp->result) {
pr_err("err in lib dload %x\n", resp->result);
} else {
pr_debug("Codec download complete...\n");
pr_debug("codec Type %d Ver %d Built %s: %s\n",
resp->dload_lib.lib_info.lib_type,
resp->dload_lib.lib_info.lib_version,
resp->dload_lib.lib_info.b_date,
resp->dload_lib.lib_info.b_time);
}
sst_wake_up_alloc_block(sst_drv_ctx, str_id,
retval, NULL);
break;
}
case IPC_IA_GET_FW_VERSION: {
struct ipc_header_fw_init *version =
(struct ipc_header_fw_init *)msg->mailbox;
int major = version->fw_version.major;
int minor = version->fw_version.minor;
int build = version->fw_version.build;
dev_info(&sst_drv_ctx->pci->dev,
"INFO: ***LOADED SST FW VERSION*** = %02d.%02d.%02d\n",
major, minor, build);
break;
}
case IPC_IA_GET_FW_BUILD_INF: {
struct sst_fw_build_info *build =
(struct sst_fw_build_info *)msg->mailbox;
pr_debug("Build date:%sTime:%s", build->date, build->time);
break;
}
case IPC_IA_SET_PMIC_TYPE:
break;
case IPC_IA_START_STREAM:
pr_debug("reply for START STREAM %x\n", msg->header.full);
break;
case IPC_IA_GET_FW_CTXT:
pr_debug("reply for get fw ctxt %x\n", msg->header.full);
if (msg->header.part.data)
sst_drv_ctx->fw_cntx_size = 0;
else
sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx;
pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size);
sst_wake_up_alloc_block(
sst_drv_ctx, str_id, msg->header.part.data, NULL);
break;
default:
/* Illegal case */
pr_err("process reply:default = %x\n", msg->header.full);
}
sst_clear_interrupt();
return;
}

View File

@ -1,313 +0,0 @@
/*
* 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;
}

View File

@ -1,583 +0,0 @@
/*
* intel_sst_stream.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 file contains the stream operations of SST driver
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include "intel_sst_ioctl.h"
#include "intel_sst.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
/*
* sst_check_device_type - Check the medfield device type
*
* @device: Device to be checked
* @num_ch: Number of channels queried
* @pcm_slot: slot to be enabled for this device
*
* This checks the deivce against the map and calculates pcm_slot value
*/
int sst_check_device_type(u32 device, u32 num_chan, u32 *pcm_slot)
{
if (device >= MAX_NUM_STREAMS_MFLD) {
pr_debug("device type invalid %d\n", device);
return -EINVAL;
}
if (sst_drv_ctx->streams[device].status == STREAM_UN_INIT) {
if (device == SND_SST_DEVICE_VIBRA && num_chan == 1)
*pcm_slot = 0x10;
else if (device == SND_SST_DEVICE_HAPTIC && num_chan == 1)
*pcm_slot = 0x20;
else if (device == SND_SST_DEVICE_IHF && num_chan == 1)
*pcm_slot = 0x04;
else if (device == SND_SST_DEVICE_IHF && num_chan == 2)
*pcm_slot = 0x0C;
else if (device == SND_SST_DEVICE_HEADSET && num_chan == 1)
*pcm_slot = 0x01;
else if (device == SND_SST_DEVICE_HEADSET && num_chan == 2)
*pcm_slot = 0x03;
else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 1)
*pcm_slot = 0x01;
else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 2)
*pcm_slot = 0x03;
else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 3)
*pcm_slot = 0x07;
else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 4)
*pcm_slot = 0x0F;
else if (device == SND_SST_DEVICE_CAPTURE && num_chan > 4)
*pcm_slot = 0x1F;
else {
pr_debug("No condition satisfied.. ret err\n");
return -EINVAL;
}
} else {
pr_debug("this stream state is not uni-init, is %d\n",
sst_drv_ctx->streams[device].status);
return -EBADRQC;
}
pr_debug("returning slot %x\n", *pcm_slot);
return 0;
}
/**
* get_mrst_stream_id - gets a new stream id for use
*
* This functions searches the current streams and allocated an empty stream
* lock stream_lock required to be held before calling this
*/
static unsigned int get_mrst_stream_id(void)
{
int i;
for (i = 1; i <= MAX_NUM_STREAMS_MRST; i++) {
if (sst_drv_ctx->streams[i].status == STREAM_UN_INIT)
return i;
}
pr_debug("Didn't find empty stream for mrst\n");
return -EBUSY;
}
/**
* sst_alloc_stream - Send msg for a new stream ID
*
* @params: stream params
* @stream_ops: operation of stream PB/capture
* @codec: codec for stream
* @device: device stream to be allocated for
*
* 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, unsigned int stream_ops,
u8 codec, unsigned int device)
{
struct ipc_post *msg = NULL;
struct snd_sst_alloc_params alloc_param;
unsigned int pcm_slot = 0, num_ch;
int str_id;
struct snd_sst_stream_params *sparams;
struct stream_info *str_info;
pr_debug("SST DBG:entering sst_alloc_stream\n");
pr_debug("SST DBG:%d %d %d\n", stream_ops, codec, device);
BUG_ON(!params);
sparams = (struct snd_sst_stream_params *)params;
num_ch = sparams->uc.pcm_params.num_chan;
/*check the device type*/
if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) {
if (sst_check_device_type(device, num_ch, &pcm_slot))
return -EINVAL;
mutex_lock(&sst_drv_ctx->stream_lock);
str_id = device;
mutex_unlock(&sst_drv_ctx->stream_lock);
pr_debug("SST_DBG: slot %x\n", pcm_slot);
} else {
mutex_lock(&sst_drv_ctx->stream_lock);
str_id = get_mrst_stream_id();
mutex_unlock(&sst_drv_ctx->stream_lock);
if (str_id <= 0)
return -EBUSY;
}
/*allocate device type context*/
sst_init_stream(&sst_drv_ctx->streams[str_id], codec,
str_id, stream_ops, pcm_slot, device);
/* send msg to FW to allocate a stream */
if (sst_create_large_msg(&msg))
return -ENOMEM;
sst_fill_header(&msg->header, IPC_IA_ALLOC_STREAM, 1, str_id);
msg->header.part.data = sizeof(alloc_param) + sizeof(u32);
alloc_param.str_type.codec_type = codec;
alloc_param.str_type.str_type = SST_STREAM_TYPE_MUSIC;
alloc_param.str_type.operation = stream_ops;
alloc_param.str_type.protected_str = 0; /* non drm */
alloc_param.str_type.time_slots = pcm_slot;
alloc_param.str_type.result = alloc_param.str_type.reserved = 0;
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));
str_info = &sst_drv_ctx->streams[str_id];
str_info->ctrl_blk.condition = false;
str_info->ctrl_blk.ret_code = 0;
str_info->ctrl_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);
pr_debug("SST DBG:alloc stream done\n");
return str_id;
}
/*
* sst_alloc_stream_response - process alloc reply
*
* @str_id: stream id for which the stream has been allocated
* @resp the stream response from firware
*
* This function is called by firmware as a response to stream allcoation
* request
*/
int sst_alloc_stream_response(unsigned int str_id,
struct snd_sst_alloc_response *resp)
{
int retval = 0;
struct stream_info *str_info;
struct snd_sst_lib_download *lib_dnld;
pr_debug("SST DEBUG: stream number given = %d\n", str_id);
str_info = &sst_drv_ctx->streams[str_id];
if (resp->str_type.result == SST_LIB_ERR_LIB_DNLD_REQUIRED) {
lib_dnld = kzalloc(sizeof(*lib_dnld), GFP_KERNEL);
memcpy(lib_dnld, &resp->lib_dnld, sizeof(*lib_dnld));
} else
lib_dnld = NULL;
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.data = lib_dnld;
str_info->ctrl_blk.condition = true;
str_info->ctrl_blk.ret_code = resp->str_type.result;
pr_debug("SST DEBUG: sst_alloc_stream_response: waking up.\n");
wake_up(&sst_drv_ctx->wait_queue);
}
return retval;
}
/**
* sst_get_fw_info - Send msg to query for firmware configurations
* @info: out param that holds the firmare configurations
*
* This function is called when the firmware configurations are queiried for
*/
int sst_get_fw_info(struct snd_sst_fw_info *info)
{
int retval = 0;
struct ipc_post *msg = NULL;
pr_debug("SST DBG:sst_get_fw_info called\n");
if (sst_create_short_msg(&msg)) {
pr_err("SST ERR: message creation failed\n");
return -ENOMEM;
}
sst_fill_header(&msg->header, IPC_IA_GET_FW_INFO, 0, 0);
sst_drv_ctx->fw_info_blk.condition = false;
sst_drv_ctx->fw_info_blk.ret_code = 0;
sst_drv_ctx->fw_info_blk.on = true;
sst_drv_ctx->fw_info_blk.data = info;
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->fw_info_blk, SST_BLOCK_TIMEOUT);
if (retval) {
pr_err("SST ERR: error in fw_info = %d\n", retval);
retval = -EIO;
}
return retval;
}
/**
* sst_pause_stream - Send msg for a pausing stream
* @str_id: stream ID
*
* This function is called by any function which wants to pause
* an already running stream.
*/
int sst_start_stream(int str_id)
{
int retval = 0;
struct ipc_post *msg = NULL;
struct stream_info *str_info;
pr_debug("sst_start_stream for %d\n", str_id);
retval = sst_validate_strid(str_id);
if (retval)
return retval;
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->status != STREAM_INIT)
return -EBADRQC;
if (sst_create_short_msg(&msg))
return -ENOMEM;
sst_fill_header(&msg->header, IPC_IA_START_STREAM, 0, str_id);
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 retval;
}
/*
* sst_pause_stream - Send msg for a pausing stream
* @str_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;
pr_debug("SST DBG:sst_pause_stream for %d\n", str_id);
retval = sst_validate_strid(str_id);
if (retval)
return retval;
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->status == STREAM_PAUSED)
return 0;
if (str_info->status == STREAM_RUNNING ||
str_info->status == STREAM_INIT) {
if (str_info->prev == STREAM_UN_INIT)
return -EBADRQC;
if (str_info->ctrl_blk.on == true) {
pr_err("SST ERR: control path is in use\n");
return -EINVAL;
}
if (sst_create_short_msg(&msg))
return -ENOMEM;
sst_fill_header(&msg->header, IPC_IA_PAUSE_STREAM, 0, str_id);
str_info->ctrl_blk.condition = false;
str_info->ctrl_blk.ret_code = 0;
str_info->ctrl_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,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
if (retval == 0) {
str_info->prev = str_info->status;
str_info->status = STREAM_PAUSED;
} else if (retval == SST_ERR_INVALID_STREAM_ID) {
retval = -EINVAL;
mutex_lock(&sst_drv_ctx->stream_lock);
sst_clean_stream(str_info);
mutex_unlock(&sst_drv_ctx->stream_lock);
}
} else {
retval = -EBADRQC;
pr_err("SST ERR: BADQRC for stream\n");
}
return retval;
}
/**
* sst_resume_stream - Send msg for resuming stream
* @str_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;
pr_debug("SST DBG:sst_resume_stream for %d\n", str_id);
retval = sst_validate_strid(str_id);
if (retval)
return retval;
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->status == STREAM_RUNNING)
return 0;
if (str_info->status == STREAM_PAUSED) {
if (str_info->ctrl_blk.on == true) {
pr_err("SST ERR: control path in use\n");
return -EINVAL;
}
if (sst_create_short_msg(&msg)) {
pr_err("SST ERR: mem allocation failed\n");
return -ENOMEM;
}
sst_fill_header(&msg->header, IPC_IA_RESUME_STREAM, 0, str_id);
str_info->ctrl_blk.condition = false;
str_info->ctrl_blk.ret_code = 0;
str_info->ctrl_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,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
if (!retval) {
if (str_info->prev == STREAM_RUNNING)
str_info->status = STREAM_RUNNING;
else
str_info->status = STREAM_INIT;
str_info->prev = STREAM_PAUSED;
} else if (retval == -SST_ERR_INVALID_STREAM_ID) {
retval = -EINVAL;
mutex_lock(&sst_drv_ctx->stream_lock);
sst_clean_stream(str_info);
mutex_unlock(&sst_drv_ctx->stream_lock);
}
} else {
retval = -EBADRQC;
pr_err("SST ERR: BADQRC for stream\n");
}
return retval;
}
/**
* sst_drop_stream - Send msg for stopping stream
* @str_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;
struct stream_info *str_info;
pr_debug("SST DBG:sst_drop_stream for %d\n", str_id);
retval = sst_validate_strid(str_id);
if (retval)
return retval;
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->status != STREAM_UN_INIT &&
str_info->status != STREAM_DECODE) {
if (str_info->ctrl_blk.on == true) {
pr_err("SST ERR: control path in use\n");
return -EINVAL;
}
if (sst_create_short_msg(&msg)) {
pr_err("SST ERR: mem allocation failed\n");
return -ENOMEM;
}
sst_fill_header(&msg->header, IPC_IA_DROP_STREAM, 0, str_id);
str_info->ctrl_blk.condition = false;
str_info->ctrl_blk.ret_code = 0;
str_info->ctrl_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,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
if (!retval) {
pr_debug("SST DBG:drop success\n");
str_info->prev = STREAM_UN_INIT;
str_info->status = STREAM_INIT;
if (str_info->src != MAD_DRV) {
mutex_lock(&str_info->lock);
list_for_each_entry_safe(bufs, _bufs,
&str_info->bufs, node) {
list_del(&bufs->node);
kfree(bufs);
}
mutex_unlock(&str_info->lock);
}
str_info->cumm_bytes += str_info->curr_bytes;
} else if (retval == -SST_ERR_INVALID_STREAM_ID) {
retval = -EINVAL;
mutex_lock(&sst_drv_ctx->stream_lock);
sst_clean_stream(str_info);
mutex_unlock(&sst_drv_ctx->stream_lock);
}
if (str_info->data_blk.on == true) {
str_info->data_blk.condition = true;
str_info->data_blk.ret_code = retval;
wake_up(&sst_drv_ctx->wait_queue);
}
} else {
retval = -EBADRQC;
pr_err("SST ERR: BADQRC for stream\n");
}
return retval;
}
/**
* sst_drain_stream - Send msg for draining stream
* @str_id: stream ID
*
* This function is called by any function which wants to drain
* a stream.
*/
int sst_drain_stream(int str_id)
{
int retval = 0;
struct ipc_post *msg = NULL;
struct stream_info *str_info;
pr_debug("SST DBG:sst_drain_stream for %d\n", str_id);
retval = sst_validate_strid(str_id);
if (retval)
return retval;
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->status != STREAM_RUNNING &&
str_info->status != STREAM_INIT &&
str_info->status != STREAM_PAUSED) {
pr_err("SST ERR: BADQRC for stream = %d\n",
str_info->status);
return -EBADRQC;
}
if (str_info->status == STREAM_INIT) {
if (sst_create_short_msg(&msg)) {
pr_err("SST ERR: mem allocation failed\n");
return -ENOMEM;
}
sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, 0, str_id);
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);
} else
str_info->need_draining = true;
str_info->data_blk.condition = false;
str_info->data_blk.ret_code = 0;
str_info->data_blk.on = true;
retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk);
str_info->need_draining = false;
return retval;
}
/**
* sst_free_stream - Frees a stream
* @str_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;
pr_debug("SST DBG:sst_free_stream for %d\n", str_id);
retval = sst_validate_strid(str_id);
if (retval)
return retval;
str_info = &sst_drv_ctx->streams[str_id];
if (str_info->status != STREAM_UN_INIT) {
if (sst_create_short_msg(&msg)) {
pr_err("SST ERR: mem allocation failed\n");
return -ENOMEM;
}
sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, 0, str_id);
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);
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_drv_ctx->wait_queue);
}
str_info->data_blk.on = true;
str_info->data_blk.condition = false;
retval = sst_wait_interruptible_timeout(sst_drv_ctx,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
pr_debug("wait for free returned %d\n", retval);
msleep(100);
mutex_lock(&sst_drv_ctx->stream_lock);
sst_clean_stream(str_info);
mutex_unlock(&sst_drv_ctx->stream_lock);
pr_debug("SST DBG:Stream freed\n");
} else {
retval = -EBADRQC;
pr_debug("SST DBG:BADQRC for stream\n");
}
return retval;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,209 +0,0 @@
/*
* intelmid.h - Intel Sound card driver for MID
*
* Copyright (C) 2008-10 Intel Corp
* Authors: Harsha Priya <priya.harsha@intel.com>
* Vinod Koul <vinod.koul@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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ALSA driver header for Intel MAD chipset
*/
#ifndef __INTELMID_H
#define __INTELMID_H
#include <linux/time.h>
#include <sound/jack.h>
#define DRIVER_NAME_MFLD "msic_audio"
#define DRIVER_NAME_MRST "pmic_audio"
#define DRIVER_NAME "intelmid_audio"
#define PMIC_SOUND_IRQ_TYPE_MASK (1 << 15)
#define AUDINT_BASE (0xFFFFEFF8 + (6 * sizeof(u8)))
#define REG_IRQ
/* values #defined */
/* will differ for different hw - to be taken from config */
#define MAX_DEVICES 1
#define MIN_RATE 8000
#define MAX_RATE 48000
#define MAX_BUFFER (800*1024) /* for PCM */
#define MIN_BUFFER (800*1024)
#define MAX_PERIODS (1024*2)
#define MIN_PERIODS 2
#define MAX_PERIOD_BYTES MAX_BUFFER
#define MIN_PERIOD_BYTES 32
/*#define MIN_PERIOD_BYTES 160*/
#define MAX_MUTE 1
#define MIN_MUTE 0
#define MONO_CNTL 1
#define STEREO_CNTL 2
#define MIN_CHANNEL 1
#define MAX_CHANNEL_AMIC 2
#define MAX_CHANNEL_DMIC 5
#define FIFO_SIZE 0 /* fifo not being used */
#define INTEL_MAD "Intel MAD"
#define MAX_CTRL_MRST 8
#define MAX_CTRL_MFLD 7
#define MAX_CTRL 8
#define MAX_VENDORS 4
/* TODO +6 db */
#define MAX_VOL 64
/* TODO -57 db */
#define MIN_VOL 0
#define PLAYBACK_COUNT 1
#define CAPTURE_COUNT 1
#define ADC_ONE_LSB_MULTIPLIER 2346
#define MID_JACK_HS_LONG_PRESS SND_JACK_BTN_0
#define MID_JACK_HS_SHORT_PRESS SND_JACK_BTN_1
extern int sst_card_vendor_id;
struct mad_jack {
struct snd_jack jack;
int jack_status;
int jack_dev_state;
struct timeval buttonpressed;
struct timeval buttonreleased;
};
struct mad_jack_msg_wq {
u8 intsts;
struct snd_intelmad *intelmaddata;
struct work_struct wq;
};
struct snd_intelmad_probe_info {
unsigned int cpu_id;
unsigned int irq_cache;
unsigned int size;
};
/**
* struct snd_intelmad - intelmad driver structure
*
* @card: ptr to the card details
* @card_index: sound card index
* @card_id: sound card id detected
* @sstdrv_ops: ptr to sst driver ops
* @pdev: ptr to platform device
* @irq: interrupt number detected
* @pmic_status: Device status of sound card
* @int_base: ptr to MMIO interrupt region
* @output_sel: device selected as o/p
* @input_sel: device selected as i/p
* @master_mute: master mute status
* @jack: jack status
* @playback_cnt: active pb streams
* @capture_cnt: active cp streams
* @mad_jack_msg: wq struct for jack interrupt processing
* @mad_jack_wq: wq for jack interrupt processing
* @jack_prev_state: Previos state of jack detected
* @cpu_id: current cpu id loaded for
*/
struct snd_intelmad {
struct snd_card *card; /* ptr to the card details */
int card_index;/* card index */
char *card_id; /* card id */
struct intel_sst_card_ops *sstdrv_ops;/* ptr to sst driver ops */
struct platform_device *pdev;
int irq;
int pmic_status;
void __iomem *int_base;
int output_sel;
int input_sel;
int lineout_sel;
int master_mute;
struct mad_jack jack[4];
int playback_cnt;
int capture_cnt;
u16 adc_address;
struct mad_jack_msg_wq mad_jack_msg;
struct workqueue_struct *mad_jack_wq;
u8 jack_prev_state;
unsigned int cpu_id;
};
struct snd_control_val {
int playback_vol_max;
int playback_vol_min;
int capture_vol_max;
int capture_vol_min;
int master_vol_max;
int master_vol_min;
};
struct mad_stream_pvt {
int stream_status;
int stream_ops;
struct snd_pcm_substream *substream;
struct pcm_stream_info stream_info;
ssize_t dbg_cum_bytes;
enum snd_sst_device_type device;
};
enum mad_drv_status {
INIT = 1,
STARTED,
RUNNING,
PAUSED,
DROPPED,
};
enum mad_pmic_status {
PMIC_UNINIT = 1,
PMIC_INIT,
};
enum _widget_ctrl {
OUTPUT_SEL = 1,
INPUT_SEL,
PLAYBACK_VOL,
PLAYBACK_MUTE,
CAPTURE_VOL,
CAPTURE_MUTE,
MASTER_VOL,
MASTER_MUTE
};
enum _widget_ctrl_mfld {
LINEOUT_SEL_MFLD = 3,
};
enum hw_chs {
HW_CH0 = 0,
HW_CH1,
HW_CH2,
HW_CH3
};
void period_elapsed(void *mad_substream);
int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream);
int snd_intelmad_init_stream(struct snd_pcm_substream *substream);
int sst_sc_reg_access(struct sc_reg_access *sc_access,
int type, int num_val);
#define CPU_CHIP_LINCROFT 1 /* System running lincroft */
#define CPU_CHIP_PENWELL 2 /* System running penwell */
extern struct snd_control_val intelmad_ctrl_val[];
extern struct snd_kcontrol_new snd_intelmad_controls_mrst[];
extern struct snd_kcontrol_new snd_intelmad_controls_mfld[];
extern struct snd_pmic_ops *intelmad_vendor_ops[];
void sst_mad_send_jack_report(struct snd_jack *jack,
int buttonpressevent , int status);
#endif /* __INTELMID_H */

View File

@ -1,193 +0,0 @@
#ifndef __INTELMID_ADC_CONTROL_H__
#define __INTELMID_ADC_CONTROL_H_
/*
* intelmid_adc_control.h - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* Authors: R Durgadadoss <r.durgadoss@intel.com>
* Dharageswari R <dharageswari.r@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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Common private ADC declarations for SST
*/
#define MSIC_ADC1CNTL1 0x1C0
#define MSIC_ADC_ENBL 0x10
#define MSIC_ADC_START 0x08
#define MSIC_ADC1CNTL3 0x1C2
#define MSIC_ADCTHERM_ENBL 0x04
#define MSIC_ADCRRDATA_ENBL 0x05
#define MSIC_STOPBIT_MASK 16
#define MSIC_ADCTHERM_MASK 4
#define ADC_CHANLS_MAX 15 /* Number of ADC channels */
#define ADC_LOOP_MAX (ADC_CHANLS_MAX - 1)
/* ADC channel code values */
#define AUDIO_DETECT_CODE 0x06
/* ADC base addresses */
#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
/**
* configure_adc - enables/disables the ADC for conversion
* @val: zero: disables the ADC non-zero:enables the ADC
*
* Enable/Disable the ADC depending on the argument
*
* Can sleep
*/
static inline int configure_adc(int val)
{
int ret;
struct sc_reg_access sc_access = {0,};
sc_access.reg_addr = MSIC_ADC1CNTL1;
ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1);
if (ret)
return ret;
if (val)
/* Enable and start the ADC */
sc_access.value |= (MSIC_ADC_ENBL | MSIC_ADC_START);
else
/* Just stop the ADC */
sc_access.value &= (~MSIC_ADC_START);
sc_access.reg_addr = MSIC_ADC1CNTL1;
return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1);
}
/**
* reset_stopbit - sets the stop bit to 0 on the given channel
* @addr: address of the channel
*
* Can sleep
*/
static inline int reset_stopbit(uint16_t addr)
{
int ret;
struct sc_reg_access sc_access = {0,};
sc_access.reg_addr = addr;
ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1);
if (ret)
return ret;
/* Set the stop bit to zero */
sc_access.reg_addr = addr;
sc_access.value = (sc_access.value) & 0xEF;
return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1);
}
/**
* find_free_channel - finds an empty channel for conversion
*
* If the ADC is not enabled then start using 0th channel
* itself. Otherwise find an empty channel by looking for a
* channel in which the stopbit is set to 1. returns the index
* of the first free channel if succeeds or an error code.
*
* Context: can sleep
*
*/
static inline int find_free_channel(void)
{
int ret;
int i;
struct sc_reg_access sc_access = {0,};
/* check whether ADC is enabled */
sc_access.reg_addr = MSIC_ADC1CNTL1;
ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1);
if (ret)
return ret;
if ((sc_access.value & MSIC_ADC_ENBL) == 0)
return 0;
/* ADC is already enabled; Looking for an empty channel */
for (i = 0; i < ADC_CHANLS_MAX; i++) {
sc_access.reg_addr = ADC_CHNL_START_ADDR + i;
ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1);
if (ret)
return ret;
if (sc_access.value & MSIC_STOPBIT_MASK) {
ret = i;
break;
}
}
return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret;
}
/**
* mid_initialize_adc - initializing the ADC
* @dev: our device structure
*
* Initialize the ADC for reading thermistor values. Can sleep.
*/
static inline int mid_initialize_adc(void)
{
int base_addr, chnl_addr;
int ret;
static int channel_index;
struct sc_reg_access sc_access = {0,};
/* Index of the first channel in which the stop bit is set */
channel_index = find_free_channel();
if (channel_index < 0) {
pr_err("No free ADC channels");
return channel_index;
}
base_addr = ADC_CHNL_START_ADDR + channel_index;
if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) {
/* Reset stop bit for channels other than 0 and 12 */
ret = reset_stopbit(base_addr);
if (ret)
return ret;
/* Index of the first free channel */
base_addr++;
channel_index++;
}
/* Since this is the last channel, set the stop bit
to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
sc_access.reg_addr = base_addr;
sc_access.value = AUDIO_DETECT_CODE | 0x10;
ret = sst_sc_reg_access(&sc_access, PMIC_WRITE, 1);
if (ret) {
pr_err("unable to enable ADC");
return ret;
}
chnl_addr = ADC_DATA_START_ADDR + 2 * channel_index;
pr_debug("mid_initialize : %x", chnl_addr);
configure_adc(1);
return chnl_addr;
}
#endif

View File

@ -1,921 +0,0 @@
/*
* intelmid_ctrl.c - Intel Sound card driver for MID
*
* Copyright (C) 2008-10 Intel Corp
* Authors: Harsha Priya <priya.harsha@intel.com>
* Vinod Koul <vinod.koul@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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ALSA driver handling mixer controls for Intel MAD chipset
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <sound/core.h>
#include <sound/control.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intelmid_snd_control.h"
#include "intelmid.h"
#define HW_CH_BASE 4
#define HW_CH_0 "Hw1"
#define HW_CH_1 "Hw2"
#define HW_CH_2 "Hw3"
#define HW_CH_3 "Hw4"
static char *router_dmics[] = { "DMIC1",
"DMIC2",
"DMIC3",
"DMIC4",
"DMIC5",
"DMIC6"
};
static char *out_names_mrst[] = {"Headphones",
"Internal speakers"};
static char *in_names_mrst[] = {"AMIC",
"DMIC",
"HS_MIC"};
static char *line_out_names_mfld[] = {"Headset",
"IHF ",
"Vibra1 ",
"Vibra2 ",
"NONE "};
static char *out_names_mfld[] = {"Headset ",
"EarPiece "};
static char *in_names_mfld[] = {"AMIC",
"DMIC"};
struct snd_control_val intelmad_ctrl_val[MAX_VENDORS] = {
{
.playback_vol_max = 63,
.playback_vol_min = 0,
.capture_vol_max = 63,
.capture_vol_min = 0,
},
{
.playback_vol_max = 0,
.playback_vol_min = -31,
.capture_vol_max = 0,
.capture_vol_min = -20,
},
{
.playback_vol_max = 0,
.playback_vol_min = -31,
.capture_vol_max = 0,
.capture_vol_min = -31,
.master_vol_max = 0,
.master_vol_min = -126,
},
};
/* control path functionalities */
static inline int snd_intelmad_volume_info(struct snd_ctl_elem_info *uinfo,
int control_type, int max, int min)
{
WARN_ON(!uinfo);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = control_type;
uinfo->value.integer.min = min;
uinfo->value.integer.max = max;
return 0;
}
/**
* snd_intelmad_mute_info - provides information about the mute controls
*
* @kcontrol: pointer to the control
* @uinfo: pointer to the structure where the control's info need
* to be filled
*
* This function is called when a mixer application requests for control's info
*/
static int snd_intelmad_mute_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
WARN_ON(!uinfo);
WARN_ON(!kcontrol);
/* set up the mute as a boolean mono control with min-max values */
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = MONO_CNTL;
uinfo->value.integer.min = MIN_MUTE;
uinfo->value.integer.max = MAX_MUTE;
return 0;
}
/**
* snd_intelmad_capture_volume_info - provides info about the volume control
*
* @kcontrol: pointer to the control
* @uinfo: pointer to the structure where the control's info need
* to be filled
*
* This function is called when a mixer application requests for control's info
*/
static int snd_intelmad_capture_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
snd_intelmad_volume_info(uinfo, MONO_CNTL,
intelmad_ctrl_val[sst_card_vendor_id].capture_vol_max,
intelmad_ctrl_val[sst_card_vendor_id].capture_vol_min);
return 0;
}
/**
* snd_intelmad_playback_volume_info - provides info about the volume control
*
* @kcontrol: pointer to the control
* @uinfo: pointer to the structure where the control's info need
* to be filled
*
* This function is called when a mixer application requests for control's info
*/
static int snd_intelmad_playback_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
snd_intelmad_volume_info(uinfo, STEREO_CNTL,
intelmad_ctrl_val[sst_card_vendor_id].playback_vol_max,
intelmad_ctrl_val[sst_card_vendor_id].playback_vol_min);
return 0;
}
static int snd_intelmad_master_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
snd_intelmad_volume_info(uinfo, STEREO_CNTL,
intelmad_ctrl_val[sst_card_vendor_id].master_vol_max,
intelmad_ctrl_val[sst_card_vendor_id].master_vol_min);
return 0;
}
/**
* snd_intelmad_device_info_mrst - provides information about the devices available
*
* @kcontrol: pointer to the control
* @uinfo: pointer to the structure where the devices's info need
* to be filled
*
* This function is called when a mixer application requests for device's info
*/
static int snd_intelmad_device_info_mrst(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
WARN_ON(!kcontrol);
WARN_ON(!uinfo);
/* setup device select as drop down controls with different values */
if (kcontrol->id.numid == OUTPUT_SEL)
uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mrst);
else
uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mrst);
uinfo->count = MONO_CNTL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = 1;
if (kcontrol->id.numid == OUTPUT_SEL)
strncpy(uinfo->value.enumerated.name,
out_names_mrst[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name)-1);
else
strncpy(uinfo->value.enumerated.name,
in_names_mrst[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name)-1);
return 0;
}
static int snd_intelmad_device_info_mfld(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_pmic_ops *scard_ops;
struct snd_intelmad *intelmaddata;
WARN_ON(!kcontrol);
WARN_ON(!uinfo);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
/* setup device select as drop down controls with different values */
if (kcontrol->id.numid == OUTPUT_SEL)
uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mfld);
else if (kcontrol->id.numid == INPUT_SEL)
uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mfld);
else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) {
uinfo->value.enumerated.items = ARRAY_SIZE(line_out_names_mfld);
scard_ops->line_out_names_cnt = uinfo->value.enumerated.items;
} else
return -EINVAL;
uinfo->count = MONO_CNTL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = 1;
if (kcontrol->id.numid == OUTPUT_SEL)
strncpy(uinfo->value.enumerated.name,
out_names_mfld[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name)-1);
else if (kcontrol->id.numid == INPUT_SEL)
strncpy(uinfo->value.enumerated.name,
in_names_mfld[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name)-1);
else if (kcontrol->id.numid == LINEOUT_SEL_MFLD)
strncpy(uinfo->value.enumerated.name,
line_out_names_mfld[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name)-1);
else
return -EINVAL;
return 0;
}
/**
* snd_intelmad_volume_get - gets the current volume for the control
*
* @kcontrol: pointer to the control
* @uval: pointer to the structure where the control's info need
* to be filled
*
* This function is called when .get function of a control is invoked from app
*/
static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
int ret_val = 0, cntl_list[2] = {0,};
int value = 0;
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
pr_debug("snd_intelmad_volume_get called\n");
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
switch (kcontrol->id.numid) {
case PLAYBACK_VOL:
cntl_list[0] = PMIC_SND_RIGHT_PB_VOL;
cntl_list[1] = PMIC_SND_LEFT_PB_VOL;
break;
case CAPTURE_VOL:
cntl_list[0] = PMIC_SND_CAPTURE_VOL;
break;
case MASTER_VOL:
cntl_list[0] = PMIC_SND_RIGHT_MASTER_VOL;
cntl_list[1] = PMIC_SND_LEFT_MASTER_VOL;
break;
default:
return -EINVAL;
}
ret_val = scard_ops->get_vol(cntl_list[0], &value);
uval->value.integer.value[0] = value;
if (ret_val)
return ret_val;
if (kcontrol->id.numid == PLAYBACK_VOL ||
kcontrol->id.numid == MASTER_VOL) {
ret_val = scard_ops->get_vol(cntl_list[1], &value);
uval->value.integer.value[1] = value;
}
return ret_val;
}
/**
* snd_intelmad_mute_get - gets the current mute status for the control
*
* @kcontrol: pointer to the control
* @uval: pointer to the structure where the control's info need
* to be filled
*
* This function is called when .get function of a control is invoked from app
*/
static int snd_intelmad_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
int cntl_list = 0, ret_val = 0;
u8 value = 0;
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
pr_debug("Mute_get called\n");
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
switch (kcontrol->id.numid) {
case PLAYBACK_MUTE:
if (intelmaddata->output_sel == STEREO_HEADPHONE)
cntl_list = PMIC_SND_LEFT_HP_MUTE;
else if ((intelmaddata->output_sel == INTERNAL_SPKR) ||
(intelmaddata->output_sel == MONO_EARPIECE))
cntl_list = PMIC_SND_LEFT_SPEAKER_MUTE;
break;
case CAPTURE_MUTE:
if (intelmaddata->input_sel == DMIC)
cntl_list = PMIC_SND_DMIC_MUTE;
else if (intelmaddata->input_sel == AMIC)
cntl_list = PMIC_SND_AMIC_MUTE;
else if (intelmaddata->input_sel == HS_MIC)
cntl_list = PMIC_SND_HP_MIC_MUTE;
break;
case MASTER_MUTE:
uval->value.integer.value[0] = intelmaddata->master_mute;
return 0;
default:
return -EINVAL;
}
ret_val = scard_ops->get_mute(cntl_list, &value);
uval->value.integer.value[0] = value;
return ret_val;
}
/**
* snd_intelmad_volume_set - sets the volume control's info
*
* @kcontrol: pointer to the control
* @uval: pointer to the structure where the control's info is
* available to be set
*
* This function is called when .set function of a control is invoked from app
*/
static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
int ret_val, cntl_list[2] = {0,};
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
pr_debug("volume set called:%ld %ld\n",
uval->value.integer.value[0],
uval->value.integer.value[1]);
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
switch (kcontrol->id.numid) {
case PLAYBACK_VOL:
cntl_list[0] = PMIC_SND_LEFT_PB_VOL;
cntl_list[1] = PMIC_SND_RIGHT_PB_VOL;
break;
case CAPTURE_VOL:
cntl_list[0] = PMIC_SND_CAPTURE_VOL;
break;
case MASTER_VOL:
cntl_list[0] = PMIC_SND_LEFT_MASTER_VOL;
cntl_list[1] = PMIC_SND_RIGHT_MASTER_VOL;
break;
default:
return -EINVAL;
}
ret_val = scard_ops->set_vol(cntl_list[0],
uval->value.integer.value[0]);
if (ret_val)
return ret_val;
if (kcontrol->id.numid == PLAYBACK_VOL ||
kcontrol->id.numid == MASTER_VOL)
ret_val = scard_ops->set_vol(cntl_list[1],
uval->value.integer.value[1]);
return ret_val;
}
/**
* snd_intelmad_mute_set - sets the mute control's info
*
* @kcontrol: pointer to the control
* @uval: pointer to the structure where the control's info is
* available to be set
*
* This function is called when .set function of a control is invoked from app
*/
static int snd_intelmad_mute_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
int cntl_list[2] = {0,}, ret_val;
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
pr_debug("snd_intelmad_mute_set called\n");
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
kcontrol->private_value = uval->value.integer.value[0];
switch (kcontrol->id.numid) {
case PLAYBACK_MUTE:
if (intelmaddata->output_sel == STEREO_HEADPHONE) {
cntl_list[0] = PMIC_SND_LEFT_HP_MUTE;
cntl_list[1] = PMIC_SND_RIGHT_HP_MUTE;
} else if ((intelmaddata->output_sel == INTERNAL_SPKR) ||
(intelmaddata->output_sel == MONO_EARPIECE)) {
cntl_list[0] = PMIC_SND_LEFT_SPEAKER_MUTE;
cntl_list[1] = PMIC_SND_RIGHT_SPEAKER_MUTE;
}
break;
case CAPTURE_MUTE:/*based on sel device mute the i/p dev*/
if (intelmaddata->input_sel == DMIC)
cntl_list[0] = PMIC_SND_DMIC_MUTE;
else if (intelmaddata->input_sel == AMIC)
cntl_list[0] = PMIC_SND_AMIC_MUTE;
else if (intelmaddata->input_sel == HS_MIC)
cntl_list[0] = PMIC_SND_HP_MIC_MUTE;
break;
case MASTER_MUTE:
cntl_list[0] = PMIC_SND_MUTE_ALL;
intelmaddata->master_mute = uval->value.integer.value[0];
break;
default:
return -EINVAL;
}
ret_val = scard_ops->set_mute(cntl_list[0],
uval->value.integer.value[0]);
if (ret_val)
return ret_val;
if (kcontrol->id.numid == PLAYBACK_MUTE)
ret_val = scard_ops->set_mute(cntl_list[1],
uval->value.integer.value[0]);
return ret_val;
}
/**
* snd_intelmad_device_get - get the device select control's info
*
* @kcontrol: pointer to the control
* @uval: pointer to the structure where the control's info is
* to be filled
*
* This function is called when .get function of a control is invoked from app
*/
static int snd_intelmad_device_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
pr_debug("device_get called\n");
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) {
if (kcontrol->id.numid == OUTPUT_SEL)
uval->value.enumerated.item[0] =
scard_ops->output_dev_id;
else if (kcontrol->id.numid == INPUT_SEL)
uval->value.enumerated.item[0] =
scard_ops->input_dev_id;
else if (kcontrol->id.numid == LINEOUT_SEL_MFLD)
uval->value.enumerated.item[0] =
scard_ops->lineout_dev_id;
else
return -EINVAL;
} else if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) {
if (kcontrol->id.numid == OUTPUT_SEL)
/* There is a mismatch here.
* ALSA expects 1 for internal speaker.
* But internally, we may give 2 for internal speaker.
*/
if (scard_ops->output_dev_id == MONO_EARPIECE ||
scard_ops->output_dev_id == INTERNAL_SPKR)
uval->value.enumerated.item[0] = MONO_EARPIECE;
else if (scard_ops->output_dev_id == STEREO_HEADPHONE)
uval->value.enumerated.item[0] =
STEREO_HEADPHONE;
else
return -EINVAL;
else if (kcontrol->id.numid == INPUT_SEL)
uval->value.enumerated.item[0] =
scard_ops->input_dev_id;
else
return -EINVAL;
} else
uval->value.enumerated.item[0] = kcontrol->private_value;
return 0;
}
/**
* snd_intelmad_device_set - set the device select control's info
*
* @kcontrol: pointer to the control
* @uval: pointer to the structure where the control's info is
* available to be set
*
* This function is called when .set function of a control is invoked from app
*/
static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
int ret_val = 0, vendor, status;
struct intel_sst_pcm_control *pcm_control;
pr_debug("snd_intelmad_device_set called\n");
WARN_ON(!uval);
WARN_ON(!kcontrol);
status = -1;
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
/* store value with driver */
kcontrol->private_value = uval->value.enumerated.item[0];
switch (kcontrol->id.numid) {
case OUTPUT_SEL:
ret_val = scard_ops->set_output_dev(
uval->value.enumerated.item[0]);
intelmaddata->output_sel = uval->value.enumerated.item[0];
break;
case INPUT_SEL:
vendor = intelmaddata->sstdrv_ops->vendor_id;
if ((vendor == SND_MX) || (vendor == SND_FS)) {
pcm_control = intelmaddata->sstdrv_ops->pcm_control;
if (uval->value.enumerated.item[0] == HS_MIC)
status = 1;
else
status = 0;
pcm_control->device_control(
SST_ENABLE_RX_TIME_SLOT, &status);
}
ret_val = scard_ops->set_input_dev(
uval->value.enumerated.item[0]);
intelmaddata->input_sel = uval->value.enumerated.item[0];
break;
case LINEOUT_SEL_MFLD:
ret_val = scard_ops->set_lineout_dev(
uval->value.enumerated.item[0]);
intelmaddata->lineout_sel = uval->value.enumerated.item[0];
break;
default:
return -EINVAL;
}
kcontrol->private_value = uval->value.enumerated.item[0];
return ret_val;
}
static int snd_intelmad_device_dmic_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
if (scard_ops->input_dev_id != DMIC) {
pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id);
return 0;
}
if (intelmaddata->cpu_id == CPU_CHIP_PENWELL)
uval->value.enumerated.item[0] = kcontrol->private_value;
else
pr_debug(" CPU id = 0x%xis invalid.\n",
intelmaddata->cpu_id);
return 0;
}
void msic_set_bit(u8 index, unsigned int *available_dmics)
{
*available_dmics |= (1 << index);
}
void msic_clear_bit(u8 index, unsigned int *available_dmics)
{
*available_dmics &= ~(1 << index);
}
int msic_is_set_bit(u8 index, unsigned int *available_dmics)
{
int ret_val;
ret_val = (*available_dmics & (1 << index));
return ret_val;
}
static int snd_intelmad_device_dmic_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uval)
{
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
int i, dmic_index;
unsigned int available_dmics;
int jump_count;
int max_dmics = ARRAY_SIZE(router_dmics);
WARN_ON(!uval);
WARN_ON(!kcontrol);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
if (scard_ops->input_dev_id != DMIC) {
pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id);
return 0;
}
available_dmics = scard_ops->available_dmics;
if (kcontrol->private_value > uval->value.enumerated.item[0]) {
pr_debug("jump count -1.\n");
jump_count = -1;
} else {
pr_debug("jump count 1.\n");
jump_count = 1;
}
dmic_index = uval->value.enumerated.item[0];
pr_debug("set function. dmic_index = %d, avl_dmic = 0x%x\n",
dmic_index, available_dmics);
for (i = 0; i < max_dmics; i++) {
pr_debug("set function. loop index = 0x%x. dmic_index = 0x%x\n",
i, dmic_index);
if (!msic_is_set_bit(dmic_index, &available_dmics)) {
msic_clear_bit(kcontrol->private_value,
&available_dmics);
msic_set_bit(dmic_index, &available_dmics);
kcontrol->private_value = dmic_index;
scard_ops->available_dmics = available_dmics;
scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] =
kcontrol->private_value;
scard_ops->set_hw_dmic_route
(kcontrol->id.numid-HW_CH_BASE);
return 0;
}
dmic_index += jump_count;
if (dmic_index > (max_dmics - 1) && jump_count == 1) {
pr_debug("Resettingthe dmic index to 0.\n");
dmic_index = 0;
} else if (dmic_index == -1 && jump_count == -1) {
pr_debug("Resetting the dmic index to 5.\n");
dmic_index = max_dmics - 1;
}
}
return -EINVAL;
}
static int snd_intelmad_device_dmic_info_mfld(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_intelmad *intelmaddata;
struct snd_pmic_ops *scard_ops;
uinfo->count = MONO_CNTL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->value.enumerated.items = ARRAY_SIZE(router_dmics);
intelmaddata = kcontrol->private_data;
WARN_ON(!intelmaddata->sstdrv_ops);
scard_ops = intelmaddata->sstdrv_ops->scard_ops;
WARN_ON(!scard_ops);
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strncpy(uinfo->value.enumerated.name,
router_dmics[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name)-1);
msic_set_bit(kcontrol->private_value, &scard_ops->available_dmics);
pr_debug("info function. avl_dmic = 0x%x",
scard_ops->available_dmics);
scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] =
kcontrol->private_value;
return 0;
}
struct snd_kcontrol_new snd_intelmad_controls_mrst[MAX_CTRL] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_info_mrst,
.get = snd_intelmad_device_get,
.put = snd_intelmad_device_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_info_mrst,
.get = snd_intelmad_device_get,
.put = snd_intelmad_device_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_playback_volume_info,
.get = snd_intelmad_volume_get,
.put = snd_intelmad_volume_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_mute_info,
.get = snd_intelmad_mute_get,
.put = snd_intelmad_mute_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_capture_volume_info,
.get = snd_intelmad_volume_get,
.put = snd_intelmad_volume_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_mute_info,
.get = snd_intelmad_mute_get,
.put = snd_intelmad_mute_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_master_volume_info,
.get = snd_intelmad_volume_get,
.put = snd_intelmad_volume_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_mute_info,
.get = snd_intelmad_mute_get,
.put = snd_intelmad_mute_set,
.private_value = 0,
},
};
struct snd_kcontrol_new
snd_intelmad_controls_mfld[MAX_CTRL_MFLD] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_info_mfld,
.get = snd_intelmad_device_get,
.put = snd_intelmad_device_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_info_mfld,
.get = snd_intelmad_device_get,
.put = snd_intelmad_device_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line out",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_info_mfld,
.get = snd_intelmad_device_get,
.put = snd_intelmad_device_set,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = HW_CH_0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_dmic_info_mfld,
.get = snd_intelmad_device_dmic_get,
.put = snd_intelmad_device_dmic_set,
.private_value = 0
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = HW_CH_1,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_dmic_info_mfld,
.get = snd_intelmad_device_dmic_get,
.put = snd_intelmad_device_dmic_set,
.private_value = 1
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = HW_CH_2,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_dmic_info_mfld,
.get = snd_intelmad_device_dmic_get,
.put = snd_intelmad_device_dmic_set,
.private_value = 2
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = HW_CH_3,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_intelmad_device_dmic_info_mfld,
.get = snd_intelmad_device_dmic_get,
.put = snd_intelmad_device_dmic_set,
.private_value = 3
}
};

File diff suppressed because it is too large Load Diff

View File

@ -1,173 +0,0 @@
/*
* intelmid_pvt.h - Intel Sound card driver for MID
*
* Copyright (C) 2008-10 Intel Corp
* Authors: Harsha Priya <priya.harsha@intel.com>
* Vinod Koul <vinod.koul@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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ALSA driver for Intel MID sound card chipset - holding private functions
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <asm/intel_scu_ipc.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intelmid_snd_control.h"
#include "intelmid.h"
void period_elapsed(void *mad_substream)
{
struct snd_pcm_substream *substream = mad_substream;
struct mad_stream_pvt *stream;
if (!substream || !substream->runtime)
return;
stream = substream->runtime->private_data;
if (!stream)
return;
if (stream->stream_status != RUNNING)
return;
pr_debug("calling period elapsed\n");
snd_pcm_period_elapsed(substream);
return;
}
int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream)
{
struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream);
struct mad_stream_pvt *stream = substream->runtime->private_data;
struct snd_sst_stream_params param = {{{0,},},};
struct snd_sst_params str_params = {0};
int ret_val;
/* set codec params and inform SST driver the same */
param.uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
param.uc.pcm_params.num_chan = (u8) substream->runtime->channels;
param.uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
param.uc.pcm_params.reserved = 0;
param.uc.pcm_params.sfreq = substream->runtime->rate;
param.uc.pcm_params.ring_buffer_size =
snd_pcm_lib_buffer_bytes(substream);
param.uc.pcm_params.period_count = substream->runtime->period_size;
param.uc.pcm_params.ring_buffer_addr =
virt_to_phys(substream->runtime->dma_area);
pr_debug("period_cnt = %d\n", param.uc.pcm_params.period_count);
pr_debug("sfreq= %d, wd_sz = %d\n",
param.uc.pcm_params.sfreq, param.uc.pcm_params.pcm_wd_sz);
str_params.sparams = param;
str_params.codec = SST_CODEC_TYPE_PCM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
str_params.ops = STREAM_OPS_PLAYBACK;
pr_debug("Playbck stream,Device %d\n", stream->device);
} else {
str_params.ops = STREAM_OPS_CAPTURE;
stream->device = SND_SST_DEVICE_CAPTURE;
pr_debug("Capture stream,Device %d\n", stream->device);
}
str_params.device_type = stream->device;
ret_val = intelmaddata->sstdrv_ops->pcm_control->open(&str_params);
pr_debug("sst: SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
if (ret_val < 0)
return ret_val;
stream->stream_info.str_id = ret_val;
stream->stream_status = INIT;
stream->stream_info.buffer_ptr = 0;
pr_debug("str id : %d\n", stream->stream_info.str_id);
return ret_val;
}
int snd_intelmad_init_stream(struct snd_pcm_substream *substream)
{
struct mad_stream_pvt *stream = substream->runtime->private_data;
struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream);
int ret_val;
pr_debug("setting buffer ptr param\n");
stream->stream_info.period_elapsed = period_elapsed;
stream->stream_info.mad_substream = substream;
stream->stream_info.buffer_ptr = 0;
stream->stream_info.sfreq = substream->runtime->rate;
ret_val = intelmaddata->sstdrv_ops->pcm_control->device_control(
SST_SND_STREAM_INIT, &stream->stream_info);
if (ret_val)
pr_err("control_set ret error %d\n", ret_val);
return ret_val;
}
/**
* sst_sc_reg_access - IPC read/write wrapper
*
* @sc_access: array of data, addresses and mask
* @type: operation type
* @num_val: number of reg to opertae on
*
* Reads/writes/read-modify operations on registers accessed through SCU (sound
* card and few SST DSP regsisters that are not accissible to IA)
*/
int sst_sc_reg_access(struct sc_reg_access *sc_access,
int type, int num_val)
{
int i, retval = 0;
if (type == PMIC_WRITE) {
for (i = 0; i < num_val; i++) {
retval = intel_scu_ipc_iowrite8(sc_access[i].reg_addr,
sc_access[i].value);
if (retval)
goto err;
}
} else if (type == PMIC_READ) {
for (i = 0; i < num_val; i++) {
retval = intel_scu_ipc_ioread8(sc_access[i].reg_addr,
&(sc_access[i].value));
if (retval)
goto err;
}
} else {
for (i = 0; i < num_val; i++) {
retval = intel_scu_ipc_update_register(
sc_access[i].reg_addr, sc_access[i].value,
sc_access[i].mask);
if (retval)
goto err;
}
}
return 0;
err:
pr_err("IPC failed for cmd %d, %d\n", retval, type);
pr_err("reg:0x%2x addr:0x%2x\n",
sc_access[i].reg_addr, sc_access[i].value);
return retval;
}

View File

@ -1,123 +0,0 @@
#ifndef __INTELMID_SND_CTRL_H__
#define __INTELMID_SND_CTRL_H__
/*
* intelmid_snd_control.h - Intel Sound card driver for MID
*
* Copyright (C) 2008-10 Intel Corporation
* 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 file defines all snd control functions
*/
/*
Mask bits
*/
#define MASK0 0x01 /* 0000 0001 */
#define MASK1 0x02 /* 0000 0010 */
#define MASK2 0x04 /* 0000 0100 */
#define MASK3 0x08 /* 0000 1000 */
#define MASK4 0x10 /* 0001 0000 */
#define MASK5 0x20 /* 0010 0000 */
#define MASK6 0x40 /* 0100 0000 */
#define MASK7 0x80 /* 1000 0000 */
/*
value bits
*/
#define VALUE0 0x01 /* 0000 0001 */
#define VALUE1 0x02 /* 0000 0010 */
#define VALUE2 0x04 /* 0000 0100 */
#define VALUE3 0x08 /* 0000 1000 */
#define VALUE4 0x10 /* 0001 0000 */
#define VALUE5 0x20 /* 0010 0000 */
#define VALUE6 0x40 /* 0100 0000 */
#define VALUE7 0x80 /* 1000 0000 */
#define MUTE 0 /* ALSA Passes 0 for mute */
#define UNMUTE 1 /* ALSA Passes 1 for unmute */
#define MAX_VOL_PMIC_VENDOR0 0x3f /* max vol in dB for stereo & voice DAC */
#define MIN_VOL_PMIC_VENDOR0 0 /* min vol in dB for stereo & voice DAC */
/* Head phone volume control */
#define MAX_HP_VOL_PMIC_VENDOR1 6 /* max volume in dB for HP */
#define MIN_HP_VOL_PMIC_VENDOR1 (-84) /* min volume in dB for HP */
#define MAX_HP_VOL_INDX_PMIC_VENDOR1 40 /* Number of HP volume control values */
/* Mono Earpiece Volume control */
#define MAX_EP_VOL_PMIC_VENDOR1 0 /* max volume in dB for EP */
#define MIN_EP_VOL_PMIC_VENDOR1 (-75) /* min volume in dB for EP */
#define MAX_EP_VOL_INDX_PMIC_VENDOR1 32 /* Number of EP volume control values */
int sst_sc_reg_access(struct sc_reg_access *sc_access,
int type, int num_val);
extern struct snd_pmic_ops snd_pmic_ops_fs;
extern struct snd_pmic_ops snd_pmic_ops_mx;
extern struct snd_pmic_ops snd_pmic_ops_nc;
extern struct snd_pmic_ops snd_msic_ops;
/* device */
enum SND_INPUT_DEVICE {
AMIC,
DMIC,
HS_MIC,
IN_UNDEFINED
};
enum SND_LINE_OUT_DEVICE {
HEADSET,
IHF,
VIBRA1,
VIBRA2,
NONE,
};
enum SND_OUTPUT_DEVICE {
STEREO_HEADPHONE,
MONO_EARPIECE,
INTERNAL_SPKR,
RECEIVER,
OUT_UNDEFINED
};
enum pmic_controls {
PMIC_SND_HP_MIC_MUTE = 0x0001,
PMIC_SND_AMIC_MUTE = 0x0002,
PMIC_SND_DMIC_MUTE = 0x0003,
PMIC_SND_CAPTURE_VOL = 0x0004,
/* Output controls */
PMIC_SND_LEFT_PB_VOL = 0x0010,
PMIC_SND_RIGHT_PB_VOL = 0x0011,
PMIC_SND_LEFT_HP_MUTE = 0x0012,
PMIC_SND_RIGHT_HP_MUTE = 0x0013,
PMIC_SND_LEFT_SPEAKER_MUTE = 0x0014,
PMIC_SND_RIGHT_SPEAKER_MUTE = 0x0015,
PMIC_SND_RECEIVER_VOL = 0x0016,
PMIC_SND_RECEIVER_MUTE = 0x0017,
PMIC_SND_LEFT_MASTER_VOL = 0x0018,
PMIC_SND_RIGHT_MASTER_VOL = 0x0019,
/* Other controls */
PMIC_SND_MUTE_ALL = 0x0020,
PMIC_MAX_CONTROLS = 0x0020,
};
#endif /* __INTELMID_SND_CTRL_H__ */

View File

@ -1,866 +0,0 @@
/*
* intel_sst_v0_control.c - Intel SST Driver for audio engine
*
* Copyright (C) 2008-10 Intel Corporation
* 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 file contains the control operations of vendor 1
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/file.h>
#include <sound/control.h>
#include "intel_sst.h"
#include "intelmid_snd_control.h"
#include "intelmid.h"
enum _reg_v1 {
VOICEPORT1 = 0x180,
VOICEPORT2 = 0x181,
AUDIOPORT1 = 0x182,
AUDIOPORT2 = 0x183,
MISCVOICECTRL = 0x184,
MISCAUDCTRL = 0x185,
DMICCTRL1 = 0x186,
AUDIOBIAS = 0x187,
MICCTRL = 0x188,
MICLICTRL1 = 0x189,
MICLICTRL2 = 0x18A,
MICLICTRL3 = 0x18B,
VOICEDACCTRL1 = 0x18C,
STEREOADCCTRL = 0x18D,
AUD15 = 0x18E,
AUD16 = 0x18F,
AUD17 = 0x190,
AUD18 = 0x191,
RMIXOUTSEL = 0x192,
ANALOGLBR = 0x193,
ANALOGLBL = 0x194,
POWERCTRL1 = 0x195,
POWERCTRL2 = 0x196,
HEADSETDETECTINT = 0x197,
HEADSETDETECTINTMASK = 0x198,
TRIMENABLE = 0x199,
};
int rev_id = 0x20;
static bool jack_det_enabled;
/****
* fs_init_card - initialize the sound card
*
* This initializes the audio paths to know values in case of this sound card
*/
static int fs_init_card(void)
{
struct sc_reg_access sc_access[] = {
{0x180, 0x00, 0x0},
{0x181, 0x00, 0x0},
{0x182, 0xF8, 0x0},
{0x183, 0x08, 0x0},
{0x184, 0x00, 0x0},
{0x185, 0x40, 0x0},
{0x186, 0x06, 0x0},
{0x187, 0x80, 0x0},
{0x188, 0x40, 0x0},
{0x189, 0x39, 0x0},
{0x18a, 0x39, 0x0},
{0x18b, 0x1F, 0x0},
{0x18c, 0x00, 0x0},
{0x18d, 0x00, 0x0},
{0x18e, 0x39, 0x0},
{0x18f, 0x39, 0x0},
{0x190, 0x39, 0x0},
{0x191, 0x11, 0x0},
{0x192, 0x0E, 0x0},
{0x193, 0x00, 0x0},
{0x194, 0x00, 0x0},
{0x195, 0x00, 0x0},
{0x196, 0x7C, 0x0},
{0x197, 0x00, 0x0},
{0x198, 0x0B, 0x0},
{0x199, 0x00, 0x0},
{0x037, 0x3F, 0x0},
};
snd_pmic_ops_fs.card_status = SND_CARD_INIT_DONE;
snd_pmic_ops_fs.master_mute = UNMUTE;
snd_pmic_ops_fs.mute_status = UNMUTE;
snd_pmic_ops_fs.num_channel = 2;
return sst_sc_reg_access(sc_access, PMIC_WRITE, 27);
}
static int fs_enable_audiodac(int value)
{
struct sc_reg_access sc_access[3];
sc_access[0].reg_addr = AUD16;
sc_access[1].reg_addr = AUD17;
sc_access[2].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask = sc_access[2].mask = MASK7;
if (snd_pmic_ops_fs.mute_status == MUTE)
return 0;
if (value == MUTE) {
sc_access[0].value = sc_access[1].value =
sc_access[2].value = 0x80;
} else {
sc_access[0].value = sc_access[1].value =
sc_access[2].value = 0x0;
}
if (snd_pmic_ops_fs.num_channel == 1)
sc_access[1].value = sc_access[2].value = 0x80;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3);
}
static int fs_power_up_pb(unsigned int port)
{
struct sc_reg_access sc_access[] = {
{AUDIOBIAS, 0x00, MASK7},
{POWERCTRL1, 0xC6, 0xC6},
{POWERCTRL2, 0x30, 0x30},
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
retval = fs_enable_audiodac(MUTE);
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3);
if (retval)
return retval;
pr_debug("in fs power up pb\n");
return fs_enable_audiodac(UNMUTE);
}
static int fs_power_down_pb(unsigned int device)
{
struct sc_reg_access sc_access[] = {
{POWERCTRL1, 0x00, 0xC6},
{POWERCTRL2, 0x00, 0x30},
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
retval = fs_enable_audiodac(MUTE);
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
if (retval)
return retval;
pr_debug("in fsl power down pb\n");
return fs_enable_audiodac(UNMUTE);
}
static int fs_power_up_cp(unsigned int port)
{
struct sc_reg_access sc_access[] = {
{POWERCTRL2, 0x32, 0x32}, /*NOTE power up A ADC only as*/
{AUDIOBIAS, 0x00, MASK7},
/*as turning on V ADC causes noise*/
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
}
static int fs_power_down_cp(unsigned int device)
{
struct sc_reg_access sc_access[] = {
{POWERCTRL2, 0x00, 0x03},
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
}
static int fs_power_down(void)
{
int retval = 0;
struct sc_reg_access sc_access[] = {
{AUDIOBIAS, MASK7, MASK7},
};
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
}
static int fs_set_pcm_voice_params(void)
{
struct sc_reg_access sc_access[] = {
{0x180, 0xA0, 0},
{0x181, 0x04, 0},
{0x182, 0x0, 0},
{0x183, 0x0, 0},
{0x184, 0x18, 0},
{0x185, 0x40, 0},
{0x186, 0x06, 0},
{0x187, 0x0, 0},
{0x188, 0x10, 0},
{0x189, 0x39, 0},
{0x18a, 0x39, 0},
{0x18b, 0x02, 0},
{0x18c, 0x0, 0},
{0x18d, 0x0, 0},
{0x18e, 0x39, 0},
{0x18f, 0x0, 0},
{0x190, 0x0, 0},
{0x191, 0x20, 0},
{0x192, 0x20, 0},
{0x193, 0x0, 0},
{0x194, 0x0, 0},
{0x195, 0x06, 0},
{0x196, 0x25, 0},
{0x197, 0x0, 0},
{0x198, 0xF, 0},
{0x199, 0x0, 0},
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
return sst_sc_reg_access(sc_access, PMIC_WRITE, 26);
}
static int fs_set_audio_port(int status)
{
struct sc_reg_access sc_access[2];
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
if (status == DEACTIVATE) {
/* Deactivate audio port-tristate and power */
sc_access[0].value = 0x00;
sc_access[0].mask = MASK6|MASK7;
sc_access[0].reg_addr = AUDIOPORT1;
sc_access[1].value = 0x00;
sc_access[1].mask = MASK4|MASK5;
sc_access[1].reg_addr = POWERCTRL2;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
} else if (status == ACTIVATE) {
/* activate audio port */
sc_access[0].value = 0xC0;
sc_access[0].mask = MASK6|MASK7;
sc_access[0].reg_addr = AUDIOPORT1;
sc_access[1].value = 0x30;
sc_access[1].mask = MASK4|MASK5;
sc_access[1].reg_addr = POWERCTRL2;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
} else
return -EINVAL;
}
static int fs_set_voice_port(int status)
{
struct sc_reg_access sc_access[2];
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
if (status == DEACTIVATE) {
/* Deactivate audio port-tristate and power */
sc_access[0].value = 0x00;
sc_access[0].mask = MASK6|MASK7;
sc_access[0].reg_addr = VOICEPORT1;
sc_access[1].value = 0x00;
sc_access[1].mask = MASK0|MASK1;
sc_access[1].reg_addr = POWERCTRL2;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
} else if (status == ACTIVATE) {
/* activate audio port */
sc_access[0].value = 0xC0;
sc_access[0].mask = MASK6|MASK7;
sc_access[0].reg_addr = VOICEPORT1;
sc_access[1].value = 0x03;
sc_access[1].mask = MASK0|MASK1;
sc_access[1].reg_addr = POWERCTRL2;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
} else
return -EINVAL;
}
static int fs_set_pcm_audio_params(int sfreq, int word_size, int num_channel)
{
u8 config1 = 0;
struct sc_reg_access sc_access[4];
int retval = 0, num_value = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
switch (sfreq) {
case 8000:
config1 = 0x00;
break;
case 11025:
config1 = 0x01;
break;
case 12000:
config1 = 0x02;
break;
case 16000:
config1 = 0x03;
break;
case 22050:
config1 = 0x04;
break;
case 24000:
config1 = 0x05;
break;
case 26000:
config1 = 0x06;
break;
case 32000:
config1 = 0x07;
break;
case 44100:
config1 = 0x08;
break;
case 48000:
config1 = 0x09;
break;
}
snd_pmic_ops_fs.num_channel = num_channel;
if (snd_pmic_ops_fs.num_channel == 1) {
sc_access[0].reg_addr = AUD17;
sc_access[1].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask = MASK7;
sc_access[0].value = sc_access[1].value = 0x80;
sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
} else {
sc_access[0].reg_addr = AUD17;
sc_access[1].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask = MASK7;
sc_access[0].value = sc_access[1].value = 0x00;
sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
}
pr_debug("sfreq:%d,Register value = %x\n", sfreq, config1);
if (word_size == 24) {
sc_access[0].reg_addr = AUDIOPORT1;
sc_access[0].mask = MASK0|MASK1|MASK2|MASK3;
sc_access[0].value = 0xFB;
sc_access[1].reg_addr = AUDIOPORT2;
sc_access[1].value = config1 | 0x10;
sc_access[1].mask = MASK0 | MASK1 | MASK2 | MASK3
| MASK4 | MASK5 | MASK6;
sc_access[2].reg_addr = MISCAUDCTRL;
sc_access[2].value = 0x02;
sc_access[2].mask = 0x02;
num_value = 3 ;
} else {
sc_access[0].reg_addr = AUDIOPORT2;
sc_access[0].value = config1;
sc_access[0].mask = MASK0|MASK1|MASK2|MASK3;
sc_access[1].reg_addr = MISCAUDCTRL;
sc_access[1].value = 0x00;
sc_access[1].mask = 0x02;
num_value = 2;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_value);
}
static int fs_set_selected_input_dev(u8 value)
{
struct sc_reg_access sc_access_dmic[] = {
{MICCTRL, 0x81, 0xf7},
{MICLICTRL3, 0x00, 0xE0},
};
struct sc_reg_access sc_access_mic[] = {
{MICCTRL, 0x40, MASK2|MASK4|MASK5|MASK6|MASK7},
{MICLICTRL3, 0x00, 0xE0},
};
struct sc_reg_access sc_access_hsmic[] = {
{MICCTRL, 0x10, MASK2|MASK4|MASK5|MASK6|MASK7},
{MICLICTRL3, 0x00, 0xE0},
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
switch (value) {
case AMIC:
pr_debug("Selecting amic not supported in mono cfg\n");
return sst_sc_reg_access(sc_access_mic, PMIC_READ_MODIFY, 2);
break;
case HS_MIC:
pr_debug("Selecting hsmic\n");
return sst_sc_reg_access(sc_access_hsmic,
PMIC_READ_MODIFY, 2);
break;
case DMIC:
pr_debug("Selecting dmic\n");
return sst_sc_reg_access(sc_access_dmic, PMIC_READ_MODIFY, 2);
break;
default:
return -EINVAL;
}
}
static int fs_set_selected_output_dev(u8 value)
{
struct sc_reg_access sc_access_hp[] = {
{0x191, 0x11, 0x0},
{0x192, 0x0E, 0x0},
};
struct sc_reg_access sc_access_is[] = {
{0x191, 0x17, 0xFF},
{0x192, 0x08, 0xFF},
};
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
switch (value) {
case STEREO_HEADPHONE:
pr_debug("SST DBG:Selecting headphone\n");
return sst_sc_reg_access(sc_access_hp, PMIC_WRITE, 2);
break;
case MONO_EARPIECE:
case INTERNAL_SPKR:
pr_debug("SST DBG:Selecting internal spkr\n");
return sst_sc_reg_access(sc_access_is, PMIC_READ_MODIFY, 2);
break;
default:
return -EINVAL;
}
}
static int fs_set_mute(int dev_id, u8 value)
{
struct sc_reg_access sc_access[6] = {{0,},};
int reg_num = 0;
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
pr_debug("dev_id:0x%x value:0x%x\n", dev_id, value);
switch (dev_id) {
case PMIC_SND_DMIC_MUTE:
sc_access[0].reg_addr = MICCTRL;
sc_access[1].reg_addr = MICLICTRL1;
sc_access[2].reg_addr = MICLICTRL2;
sc_access[0].mask = MASK5;
sc_access[1].mask = sc_access[2].mask = MASK6;
if (value == MUTE) {
sc_access[0].value = 0x20;
sc_access[2].value = sc_access[1].value = 0x40;
} else
sc_access[0].value = sc_access[1].value
= sc_access[2].value = 0x0;
reg_num = 3;
break;
case PMIC_SND_HP_MIC_MUTE:
case PMIC_SND_AMIC_MUTE:
sc_access[0].reg_addr = MICLICTRL1;
sc_access[1].reg_addr = MICLICTRL2;
sc_access[0].mask = sc_access[1].mask = MASK6;
if (value == MUTE)
sc_access[0].value = sc_access[1].value = 0x40;
else
sc_access[0].value = sc_access[1].value = 0x0;
reg_num = 2;
break;
case PMIC_SND_LEFT_SPEAKER_MUTE:
case PMIC_SND_LEFT_HP_MUTE:
sc_access[0].reg_addr = AUD16;
sc_access[1].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask = MASK7;
if (value == MUTE)
sc_access[0].value = sc_access[1].value = 0x80;
else
sc_access[0].value = sc_access[1].value = 0x0;
reg_num = 2;
snd_pmic_ops_fs.mute_status = value;
break;
case PMIC_SND_RIGHT_HP_MUTE:
case PMIC_SND_RIGHT_SPEAKER_MUTE:
sc_access[0].reg_addr = AUD17;
sc_access[1].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask = MASK7;
if (value == MUTE)
sc_access[0].value = sc_access[1].value = 0x80;
else
sc_access[0].value = sc_access[1].value = 0x0;
snd_pmic_ops_fs.mute_status = value;
if (snd_pmic_ops_fs.num_channel == 1)
sc_access[0].value = sc_access[1].value = 0x80;
reg_num = 2;
break;
case PMIC_SND_MUTE_ALL:
sc_access[0].reg_addr = AUD16;
sc_access[1].reg_addr = AUD17;
sc_access[2].reg_addr = AUD15;
sc_access[3].reg_addr = MICCTRL;
sc_access[4].reg_addr = MICLICTRL1;
sc_access[5].reg_addr = MICLICTRL2;
sc_access[0].mask = sc_access[1].mask =
sc_access[2].mask = MASK7;
sc_access[3].mask = MASK5;
sc_access[4].mask = sc_access[5].mask = MASK6;
if (value == MUTE) {
sc_access[0].value =
sc_access[1].value = sc_access[2].value = 0x80;
sc_access[3].value = 0x20;
sc_access[4].value = sc_access[5].value = 0x40;
} else {
sc_access[0].value = sc_access[1].value =
sc_access[2].value = sc_access[3].value =
sc_access[4].value = sc_access[5].value = 0x0;
}
if (snd_pmic_ops_fs.num_channel == 1)
sc_access[1].value = sc_access[2].value = 0x80;
reg_num = 6;
snd_pmic_ops_fs.mute_status = value;
snd_pmic_ops_fs.master_mute = value;
break;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, reg_num);
}
static int fs_set_vol(int dev_id, int value)
{
struct sc_reg_access sc_acces, sc_access[4] = {{0},};
int reg_num = 0;
int retval = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
switch (dev_id) {
case PMIC_SND_LEFT_PB_VOL:
pr_debug("PMIC_SND_LEFT_PB_VOL:%d\n", value);
sc_access[0].value = sc_access[1].value = value;
sc_access[0].reg_addr = AUD16;
sc_access[1].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask =
(MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
reg_num = 2;
break;
case PMIC_SND_RIGHT_PB_VOL:
pr_debug("PMIC_SND_RIGHT_PB_VOL:%d\n", value);
sc_access[0].value = sc_access[1].value = value;
sc_access[0].reg_addr = AUD17;
sc_access[1].reg_addr = AUD15;
sc_access[0].mask = sc_access[1].mask =
(MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
if (snd_pmic_ops_fs.num_channel == 1) {
sc_access[0].value = sc_access[1].value = 0x80;
sc_access[0].mask = sc_access[1].mask = MASK7;
}
reg_num = 2;
break;
case PMIC_SND_CAPTURE_VOL:
pr_debug("PMIC_SND_CAPTURE_VOL:%d\n", value);
sc_access[0].reg_addr = MICLICTRL1;
sc_access[1].reg_addr = MICLICTRL2;
sc_access[2].reg_addr = DMICCTRL1;
sc_access[2].value = value;
sc_access[0].value = sc_access[1].value = value;
sc_acces.reg_addr = MICLICTRL3;
sc_acces.value = value;
sc_acces.mask = (MASK0|MASK1|MASK2|MASK3|MASK5|MASK6|MASK7);
retval = sst_sc_reg_access(&sc_acces, PMIC_READ_MODIFY, 1);
sc_access[0].mask = sc_access[1].mask =
sc_access[2].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
reg_num = 3;
break;
default:
return -EINVAL;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, reg_num);
}
static int fs_get_mute(int dev_id, u8 *value)
{
struct sc_reg_access sc_access[6] = {{0,},};
int retval = 0, temp_value = 0, mask = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
switch (dev_id) {
case PMIC_SND_AMIC_MUTE:
case PMIC_SND_HP_MIC_MUTE:
sc_access[0].reg_addr = MICLICTRL1;
mask = MASK6;
retval = sst_sc_reg_access(sc_access, PMIC_READ, 1);
if (sc_access[0].value & mask)
*value = MUTE;
else
*value = UNMUTE;
break;
case PMIC_SND_DMIC_MUTE:
sc_access[0].reg_addr = MICCTRL;
mask = MASK5;
retval = sst_sc_reg_access(sc_access, PMIC_READ, 1);
temp_value = (sc_access[0].value & mask);
if (temp_value == 0)
*value = UNMUTE;
else
*value = MUTE;
break;
case PMIC_SND_LEFT_HP_MUTE:
case PMIC_SND_LEFT_SPEAKER_MUTE:
sc_access[0].reg_addr = AUD16;
mask = MASK7;
retval = sst_sc_reg_access(sc_access, PMIC_READ, 1);
temp_value = sc_access[0].value & mask;
if (temp_value == 0)
*value = UNMUTE;
else
*value = MUTE;
break;
case PMIC_SND_RIGHT_HP_MUTE:
case PMIC_SND_RIGHT_SPEAKER_MUTE:
sc_access[0].reg_addr = AUD17;
mask = MASK7;
retval = sst_sc_reg_access(sc_access, PMIC_READ, 1);
temp_value = sc_access[0].value & mask;
if (temp_value == 0)
*value = UNMUTE;
else
*value = MUTE;
break;
default:
return -EINVAL;
}
return retval;
}
static int fs_get_vol(int dev_id, int *value)
{
struct sc_reg_access sc_access = {0,};
int retval = 0, mask = 0;
if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT)
retval = fs_init_card();
if (retval)
return retval;
switch (dev_id) {
case PMIC_SND_CAPTURE_VOL:
pr_debug("PMIC_SND_CAPTURE_VOL\n");
sc_access.reg_addr = MICLICTRL1;
mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0);
break;
case PMIC_SND_LEFT_PB_VOL:
pr_debug("PMIC_SND_LEFT_PB_VOL\n");
sc_access.reg_addr = AUD16;
mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0);
break;
case PMIC_SND_RIGHT_PB_VOL:
pr_debug("PMIC_SND_RT_PB_VOL\n");
sc_access.reg_addr = AUD17;
mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0);
break;
default:
return -EINVAL;
}
retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1);
pr_debug("value read = 0x%x\n", sc_access.value);
*value = (int) (sc_access.value & mask);
pr_debug("value returned = 0x%x\n", *value);
return retval;
}
static void fs_pmic_irq_enable(void *data)
{
struct snd_intelmad *intelmaddata = data;
struct sc_reg_access sc_access[] = {
{0x187, 0x00, MASK7},
{0x188, 0x10, MASK4},
{0x18b, 0x10, MASK4},
};
struct sc_reg_access sc_access_write[] = {
{0x198, 0x00, 0x0},
};
pr_debug("Audio interrupt enable\n");
sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3);
sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1);
intelmaddata->jack[0].jack_status = 0;
/*intelmaddata->jack[1].jack_status = 0;*/
jack_det_enabled = true;
return;
}
static void fs_pmic_irq_cb(void *cb_data, u8 value)
{
struct mad_jack *mjack = NULL;
struct snd_intelmad *intelmaddata = cb_data;
unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
mjack = &intelmaddata->jack[0];
if (value & 0x4) {
if (!jack_det_enabled)
fs_pmic_irq_enable(intelmaddata);
/* send headphone detect */
pr_debug(":MAD headphone %d\n", value & 0x4);
present = !(mjack->jack_status);
mjack->jack_status = present;
jack_event_flag = 1;
mjack->jack.type = SND_JACK_HEADPHONE;
}
if (value & 0x2) {
/* send short push */
pr_debug(":MAD short push %d\n", value & 0x2);
present = 1;
jack_event_flag = 1;
buttonpressflag = 1;
mjack->jack.type = MID_JACK_HS_SHORT_PRESS;
}
if (value & 0x1) {
/* send long push */
pr_debug(":MAD long push %d\n", value & 0x1);
present = 1;
jack_event_flag = 1;
buttonpressflag = 1;
mjack->jack.type = MID_JACK_HS_LONG_PRESS;
}
if (value & 0x8) {
if (!jack_det_enabled)
fs_pmic_irq_enable(intelmaddata);
/* send headset detect */
pr_debug(":MAD headset = %d\n", value & 0x8);
present = !(mjack->jack_status);
mjack->jack_status = present;
jack_event_flag = 1;
mjack->jack.type = SND_JACK_HEADSET;
}
if (jack_event_flag)
sst_mad_send_jack_report(&mjack->jack,
buttonpressflag, present);
return;
}
static int fs_jack_enable(void)
{
return 0;
}
struct snd_pmic_ops snd_pmic_ops_fs = {
.set_input_dev = fs_set_selected_input_dev,
.set_output_dev = fs_set_selected_output_dev,
.set_mute = fs_set_mute,
.get_mute = fs_get_mute,
.set_vol = fs_set_vol,
.get_vol = fs_get_vol,
.init_card = fs_init_card,
.set_pcm_audio_params = fs_set_pcm_audio_params,
.set_pcm_voice_params = fs_set_pcm_voice_params,
.set_voice_port = fs_set_voice_port,
.set_audio_port = fs_set_audio_port,
.power_up_pmic_pb = fs_power_up_pb,
.power_up_pmic_cp = fs_power_up_cp,
.power_down_pmic_pb = fs_power_down_pb,
.power_down_pmic_cp = fs_power_down_cp,
.power_down_pmic = fs_power_down,
.pmic_irq_cb = fs_pmic_irq_cb,
/*
* Jack detection enabling
* need be delayed till first IRQ happen.
*/
.pmic_irq_enable = NULL,
.pmic_jack_enable = fs_jack_enable,
};

View File

@ -1,978 +0,0 @@
/* intel_sst_v1_control.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 file contains the control operations of vendor 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <asm/mrst.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/control.h>
#include <sound/initval.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intelmid.h"
#include "intelmid_snd_control.h"
#include <linux/gpio.h>
#define KOSKI_VOICE_CODEC_ENABLE 46
enum _reg_v2 {
MASTER_CLOCK_PRESCALAR = 0x205,
SET_MASTER_AND_LR_CLK1 = 0x20b,
SET_MASTER_AND_LR_CLK2 = 0x20c,
MASTER_MODE_AND_DATA_DELAY = 0x20d,
DIGITAL_INTERFACE_TO_DAI2 = 0x20e,
CLK_AND_FS1 = 0x208,
CLK_AND_FS2 = 0x209,
DAI2_TO_DAC_HP = 0x210,
HP_OP_SINGLE_ENDED = 0x224,
ENABLE_OPDEV_CTRL = 0x226,
ENABLE_DEV_AND_USE_XTAL = 0x227,
/* Max audio subsystem (PQ49) MAX 8921 */
AS_IP_MODE_CTL = 0xF9,
AS_LEFT_SPKR_VOL_CTL = 0xFA, /* Mono Earpiece volume control */
AS_RIGHT_SPKR_VOL_CTL = 0xFB,
AS_LEFT_HP_VOL_CTL = 0xFC,
AS_RIGHT_HP_VOL_CTL = 0xFD,
AS_OP_MIX_CTL = 0xFE,
AS_CONFIG = 0xFF,
/* Headphone volume control & mute registers */
VOL_CTRL_LT = 0x21c,
VOL_CTRL_RT = 0x21d,
};
/**
* mx_init_card - initialize the sound card
*
* This initializes the audio paths to know values in case of this sound card
*/
static int mx_init_card(void)
{
struct sc_reg_access sc_access[] = {
{0x200, 0x80, 0x00},
{0x201, 0xC0, 0x00},
{0x202, 0x00, 0x00},
{0x203, 0x00, 0x00},
{0x204, 0x02, 0x00},
{0x205, 0x10, 0x00},
{0x206, 0x60, 0x00},
{0x207, 0x00, 0x00},
{0x208, 0x90, 0x00},
{0x209, 0x51, 0x00},
{0x20a, 0x00, 0x00},
{0x20b, 0x10, 0x00},
{0x20c, 0x00, 0x00},
{0x20d, 0x00, 0x00},
{0x20e, 0x21, 0x00},
{0x20f, 0x00, 0x00},
{0x210, 0x84, 0x00},
{0x211, 0xB3, 0x00},
{0x212, 0x00, 0x00},
{0x213, 0x00, 0x00},
{0x214, 0x41, 0x00},
{0x215, 0x00, 0x00},
{0x216, 0x00, 0x00},
{0x217, 0x00, 0x00},
{0x218, 0x03, 0x00},
{0x219, 0x03, 0x00},
{0x21a, 0x00, 0x00},
{0x21b, 0x00, 0x00},
{0x21c, 0x00, 0x00},
{0x21d, 0x00, 0x00},
{0x21e, 0x00, 0x00},
{0x21f, 0x00, 0x00},
{0x220, 0x20, 0x00},
{0x221, 0x20, 0x00},
{0x222, 0x51, 0x00},
{0x223, 0x20, 0x00},
{0x224, 0x04, 0x00},
{0x225, 0x80, 0x00},
{0x226, 0x0F, 0x00},
{0x227, 0x08, 0x00},
{0xf9, 0x40, 0x00},
{0xfa, 0x1f, 0x00},
{0xfb, 0x1f, 0x00},
{0xfc, 0x1f, 0x00},
{0xfd, 0x1f, 0x00},
{0xfe, 0x00, 0x00},
{0xff, 0x0c, 0x00},
};
snd_pmic_ops_mx.card_status = SND_CARD_INIT_DONE;
snd_pmic_ops_mx.num_channel = 2;
snd_pmic_ops_mx.master_mute = UNMUTE;
snd_pmic_ops_mx.mute_status = UNMUTE;
return sst_sc_reg_access(sc_access, PMIC_WRITE, 47);
}
static int mx_enable_audiodac(int value)
{
struct sc_reg_access sc_access[3];
int mute_val = 0;
int mute_val1 = 0;
int retval = 0;
sc_access[0].reg_addr = AS_LEFT_HP_VOL_CTL;
sc_access[1].reg_addr = AS_RIGHT_HP_VOL_CTL;
if (value == UNMUTE) {
mute_val = 0x1F;
mute_val1 = 0x00;
} else {
mute_val = 0x00;
mute_val1 = 0x40;
}
sc_access[0].mask = sc_access[1].mask = MASK0|MASK1|MASK2|MASK3|MASK4;
sc_access[0].value = sc_access[1].value = (u8)mute_val;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
if (retval)
return retval;
pr_debug("mute status = %d\n", snd_pmic_ops_mx.mute_status);
if (snd_pmic_ops_mx.mute_status == MUTE ||
snd_pmic_ops_mx.master_mute == MUTE)
return retval;
sc_access[0].reg_addr = VOL_CTRL_LT;
sc_access[1].reg_addr = VOL_CTRL_RT;
sc_access[0].mask = sc_access[1].mask = MASK6;
sc_access[0].value = sc_access[1].value = mute_val1;
if (snd_pmic_ops_mx.num_channel == 1)
sc_access[1].value = 0x40;
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
}
static int mx_power_up_pb(unsigned int port)
{
int retval = 0;
struct sc_reg_access sc_access[3];
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
retval = mx_enable_audiodac(MUTE);
if (retval)
return retval;
msleep(10);
sc_access[0].reg_addr = AS_CONFIG;
sc_access[0].mask = MASK7;
sc_access[0].value = 0x80;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
sc_access[0].reg_addr = ENABLE_OPDEV_CTRL;
sc_access[0].mask = 0xff;
sc_access[0].value = 0x3C;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
sc_access[0].reg_addr = ENABLE_DEV_AND_USE_XTAL;
sc_access[0].mask = 0x80;
sc_access[0].value = 0x80;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
return mx_enable_audiodac(UNMUTE);
}
static int mx_power_down_pb(unsigned int device)
{
struct sc_reg_access sc_access[3];
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
retval = mx_enable_audiodac(MUTE);
if (retval)
return retval;
sc_access[0].reg_addr = ENABLE_OPDEV_CTRL;
sc_access[0].mask = MASK3|MASK2;
sc_access[0].value = 0x00;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
return mx_enable_audiodac(UNMUTE);
}
static int mx_power_up_cp(unsigned int port)
{
int retval = 0;
struct sc_reg_access sc_access[] = {
{ENABLE_DEV_AND_USE_XTAL, 0x80, MASK7},
{ENABLE_OPDEV_CTRL, 0x3, 0x3},
};
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
}
static int mx_power_down_cp(unsigned int device)
{
struct sc_reg_access sc_access[] = {
{ENABLE_OPDEV_CTRL, 0x00, MASK1|MASK0},
};
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
}
static int mx_power_down(void)
{
int retval = 0;
struct sc_reg_access sc_access[3];
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
retval = mx_enable_audiodac(MUTE);
if (retval)
return retval;
sc_access[0].reg_addr = AS_CONFIG;
sc_access[0].mask = MASK7;
sc_access[0].value = 0x00;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
sc_access[0].reg_addr = ENABLE_DEV_AND_USE_XTAL;
sc_access[0].mask = MASK7;
sc_access[0].value = 0x00;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
sc_access[0].reg_addr = ENABLE_OPDEV_CTRL;
sc_access[0].mask = MASK3|MASK2;
sc_access[0].value = 0x00;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
return mx_enable_audiodac(UNMUTE);
}
static int mx_set_pcm_voice_params(void)
{
int retval = 0;
struct sc_reg_access sc_access[] = {
{0x200, 0x80, 0x00},
{0x201, 0xC0, 0x00},
{0x202, 0x00, 0x00},
{0x203, 0x00, 0x00},
{0x204, 0x0e, 0x00},
{0x205, 0x20, 0x00},
{0x206, 0x8f, 0x00},
{0x207, 0x21, 0x00},
{0x208, 0x18, 0x00},
{0x209, 0x32, 0x00},
{0x20a, 0x00, 0x00},
{0x20b, 0x5A, 0x00},
{0x20c, 0xBE, 0x00},/* 0x00 -> 0xBE Koski */
{0x20d, 0x00, 0x00}, /* DAI2 'off' */
{0x20e, 0x40, 0x00},
{0x20f, 0x00, 0x00},
{0x210, 0x84, 0x00},
{0x211, 0x33, 0x00}, /* Voice filter */
{0x212, 0x00, 0x00},
{0x213, 0x00, 0x00},
{0x214, 0x41, 0x00},
{0x215, 0x00, 0x00},
{0x216, 0x00, 0x00},
{0x217, 0x20, 0x00},
{0x218, 0x00, 0x00},
{0x219, 0x00, 0x00},
{0x21a, 0x40, 0x00},
{0x21b, 0x40, 0x00},
{0x21c, 0x09, 0x00},
{0x21d, 0x09, 0x00},
{0x21e, 0x00, 0x00},
{0x21f, 0x00, 0x00},
{0x220, 0x00, 0x00}, /* Microphone configurations */
{0x221, 0x00, 0x00}, /* Microphone configurations */
{0x222, 0x50, 0x00}, /* Microphone configurations */
{0x223, 0x21, 0x00}, /* Microphone configurations */
{0x224, 0x00, 0x00},
{0x225, 0x80, 0x00},
{0xf9, 0x40, 0x00},
{0xfa, 0x19, 0x00},
{0xfb, 0x19, 0x00},
{0xfc, 0x12, 0x00},
{0xfd, 0x12, 0x00},
{0xfe, 0x00, 0x00},
};
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
pr_debug("SST DBG:mx_set_pcm_voice_params called\n");
return sst_sc_reg_access(sc_access, PMIC_WRITE, 44);
}
static int mx_set_pcm_audio_params(int sfreq, int word_size, int num_channel)
{
int retval = 0;
int config1 = 0, config2 = 0, filter = 0xB3;
struct sc_reg_access sc_access[5];
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
switch (sfreq) {
case 8000:
config1 = 0x10;
config2 = 0x00;
filter = 0x33;
break;
case 11025:
config1 = 0x16;
config2 = 0x0d;
break;
case 12000:
config1 = 0x18;
config2 = 0x00;
break;
case 16000:
config1 = 0x20;
config2 = 0x00;
break;
case 22050:
config1 = 0x2c;
config2 = 0x1a;
break;
case 24000:
config1 = 0x30;
config2 = 0x00;
break;
case 32000:
config1 = 0x40;
config2 = 0x00;
break;
case 44100:
config1 = 0x58;
config2 = 0x33;
break;
case 48000:
config1 = 0x60;
config2 = 0x00;
break;
}
snd_pmic_ops_mx.num_channel = num_channel;
/*mute the right channel if MONO*/
if (snd_pmic_ops_mx.num_channel == 1) {
sc_access[0].reg_addr = VOL_CTRL_RT;
sc_access[0].value = 0x40;
sc_access[0].mask = MASK6;
sc_access[1].reg_addr = 0x224;
sc_access[1].value = 0x05;
sc_access[1].mask = MASK0|MASK1|MASK2;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
if (retval)
return retval;
} else {
sc_access[0].reg_addr = VOL_CTRL_RT;
sc_access[0].value = 0x00;
sc_access[0].mask = MASK6;
sc_access[1].reg_addr = 0x224;
sc_access[1].value = 0x04;
sc_access[1].mask = MASK0|MASK1|MASK2;
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
if (retval)
return retval;
}
sc_access[0].reg_addr = 0x206;
sc_access[0].value = config1;
sc_access[1].reg_addr = 0x207;
sc_access[1].value = config2;
if (word_size == 16) {
sc_access[2].value = 0x51;
sc_access[3].value = 0x31;
} else if (word_size == 24) {
sc_access[2].value = 0x52;
sc_access[3].value = 0x92;
}
sc_access[2].reg_addr = 0x209;
sc_access[3].reg_addr = 0x20e;
sc_access[4].reg_addr = 0x211;
sc_access[4].value = filter;
return sst_sc_reg_access(sc_access, PMIC_WRITE, 5);
}
static int mx_set_selected_output_dev(u8 dev_id)
{
struct sc_reg_access sc_access[2];
int num_reg = 0;
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
pr_debug("mx_set_selected_output_dev dev_id:0x%x\n", dev_id);
snd_pmic_ops_mx.output_dev_id = dev_id;
switch (dev_id) {
case STEREO_HEADPHONE:
sc_access[0].reg_addr = 0xFF;
sc_access[0].value = 0x8C;
sc_access[0].mask =
MASK2|MASK3|MASK5|MASK6|MASK4;
num_reg = 1;
break;
case MONO_EARPIECE:
case INTERNAL_SPKR:
sc_access[0].reg_addr = 0xFF;
sc_access[0].value = 0xb0;
sc_access[0].mask = MASK2|MASK3|MASK5|MASK6|MASK4;
num_reg = 1;
break;
case RECEIVER:
pr_debug("RECEIVER Koski selected\n");
/* configuration - AS enable, receiver enable */
sc_access[0].reg_addr = 0xFF;
sc_access[0].value = 0x81;
sc_access[0].mask = 0xff;
num_reg = 1;
break;
default:
pr_err("Not a valid output dev\n");
return 0;
}
return sst_sc_reg_access(sc_access, PMIC_WRITE, num_reg);
}
static int mx_set_voice_port(int status)
{
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
if (status == ACTIVATE)
retval = mx_set_pcm_voice_params();
return retval;
}
static int mx_set_audio_port(int status)
{
return 0;
}
static int mx_set_selected_input_dev(u8 dev_id)
{
struct sc_reg_access sc_access[2];
int num_reg = 0;
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
snd_pmic_ops_mx.input_dev_id = dev_id;
pr_debug("mx_set_selected_input_dev dev_id:0x%x\n", dev_id);
switch (dev_id) {
case AMIC:
sc_access[0].reg_addr = 0x223;
sc_access[0].value = 0x00;
sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
sc_access[1].reg_addr = 0x222;
sc_access[1].value = 0x50;
sc_access[1].mask = MASK7|MASK6|MASK5|MASK4;
num_reg = 2;
break;
case HS_MIC:
sc_access[0].reg_addr = 0x223;
sc_access[0].value = 0x20;
sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
sc_access[1].reg_addr = 0x222;
sc_access[1].value = 0x51;
sc_access[1].mask = MASK7|MASK6|MASK5|MASK4;
num_reg = 2;
break;
case DMIC:
sc_access[1].reg_addr = 0x222;
sc_access[1].value = 0x00;
sc_access[1].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
sc_access[0].reg_addr = 0x223;
sc_access[0].value = 0x20;
sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
num_reg = 2;
break;
}
return sst_sc_reg_access(sc_access, PMIC_WRITE, num_reg);
}
static int mx_set_mute(int dev_id, u8 value)
{
struct sc_reg_access sc_access[5];
int num_reg = 0;
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
pr_debug("set_mute dev_id:0x%x , value:%d\n", dev_id, value);
switch (dev_id) {
case PMIC_SND_DMIC_MUTE:
case PMIC_SND_AMIC_MUTE:
case PMIC_SND_HP_MIC_MUTE:
sc_access[0].reg_addr = 0x220;
sc_access[1].reg_addr = 0x221;
sc_access[2].reg_addr = 0x223;
if (value == MUTE) {
sc_access[0].value = 0x00;
sc_access[1].value = 0x00;
if (snd_pmic_ops_mx.input_dev_id == DMIC)
sc_access[2].value = 0x00;
else
sc_access[2].value = 0x20;
} else {
sc_access[0].value = 0x20;
sc_access[1].value = 0x20;
if (snd_pmic_ops_mx.input_dev_id == DMIC)
sc_access[2].value = 0x20;
else
sc_access[2].value = 0x00;
}
sc_access[0].mask = MASK5|MASK6;
sc_access[1].mask = MASK5|MASK6;
sc_access[2].mask = MASK5|MASK6;
num_reg = 3;
break;
case PMIC_SND_LEFT_SPEAKER_MUTE:
case PMIC_SND_LEFT_HP_MUTE:
sc_access[0].reg_addr = VOL_CTRL_LT;
if (value == MUTE)
sc_access[0].value = 0x40;
else
sc_access[0].value = 0x00;
sc_access[0].mask = MASK6;
num_reg = 1;
snd_pmic_ops_mx.mute_status = value;
break;
case PMIC_SND_RIGHT_SPEAKER_MUTE:
case PMIC_SND_RIGHT_HP_MUTE:
sc_access[0].reg_addr = VOL_CTRL_RT;
if (snd_pmic_ops_mx.num_channel == 1)
value = MUTE;
if (value == MUTE)
sc_access[0].value = 0x40;
else
sc_access[0].value = 0x00;
sc_access[0].mask = MASK6;
num_reg = 1;
snd_pmic_ops_mx.mute_status = value;
break;
case PMIC_SND_MUTE_ALL:
sc_access[0].reg_addr = VOL_CTRL_RT;
sc_access[1].reg_addr = VOL_CTRL_LT;
sc_access[2].reg_addr = 0x220;
sc_access[3].reg_addr = 0x221;
sc_access[4].reg_addr = 0x223;
snd_pmic_ops_mx.master_mute = value;
if (value == MUTE) {
sc_access[0].value = sc_access[1].value = 0x40;
sc_access[2].value = 0x00;
sc_access[3].value = 0x00;
if (snd_pmic_ops_mx.input_dev_id == DMIC)
sc_access[4].value = 0x00;
else
sc_access[4].value = 0x20;
} else {
sc_access[0].value = sc_access[1].value = 0x00;
sc_access[2].value = sc_access[3].value = 0x20;
sc_access[4].value = 0x20;
if (snd_pmic_ops_mx.input_dev_id == DMIC)
sc_access[4].value = 0x20;
else
sc_access[4].value = 0x00;
}
if (snd_pmic_ops_mx.num_channel == 1)
sc_access[0].value = 0x40;
sc_access[0].mask = sc_access[1].mask = MASK6;
sc_access[2].mask = MASK5|MASK6;
sc_access[3].mask = MASK5|MASK6|MASK2|MASK4;
sc_access[4].mask = MASK5|MASK6|MASK4;
num_reg = 5;
break;
case PMIC_SND_RECEIVER_MUTE:
sc_access[0].reg_addr = VOL_CTRL_RT;
if (value == MUTE)
sc_access[0].value = 0x40;
else
sc_access[0].value = 0x00;
sc_access[0].mask = MASK6;
num_reg = 1;
break;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg);
}
static int mx_set_vol(int dev_id, int value)
{
struct sc_reg_access sc_access[2] = {{0},};
int num_reg = 0;
int retval = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
pr_debug("set_vol dev_id:0x%x ,value:%d\n", dev_id, value);
switch (dev_id) {
case PMIC_SND_RECEIVER_VOL:
return 0;
break;
case PMIC_SND_CAPTURE_VOL:
sc_access[0].reg_addr = 0x220;
sc_access[1].reg_addr = 0x221;
sc_access[0].value = sc_access[1].value = -value;
sc_access[0].mask = sc_access[1].mask =
(MASK0|MASK1|MASK2|MASK3|MASK4);
num_reg = 2;
break;
case PMIC_SND_LEFT_PB_VOL:
sc_access[0].value = -value;
sc_access[0].reg_addr = VOL_CTRL_LT;
sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
num_reg = 1;
break;
case PMIC_SND_RIGHT_PB_VOL:
sc_access[0].value = -value;
sc_access[0].reg_addr = VOL_CTRL_RT;
sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
if (snd_pmic_ops_mx.num_channel == 1) {
sc_access[0].value = 0x40;
sc_access[0].mask = MASK6;
sc_access[0].reg_addr = VOL_CTRL_RT;
}
num_reg = 1;
break;
}
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg);
}
static int mx_get_mute(int dev_id, u8 *value)
{
struct sc_reg_access sc_access[4] = {{0},};
int retval = 0, num_reg = 0, mask = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
switch (dev_id) {
case PMIC_SND_DMIC_MUTE:
case PMIC_SND_AMIC_MUTE:
case PMIC_SND_HP_MIC_MUTE:
sc_access[0].reg_addr = 0x220;
mask = MASK5|MASK6;
num_reg = 1;
retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg);
if (retval)
return retval;
*value = sc_access[0].value & mask;
if (*value)
*value = UNMUTE;
else
*value = MUTE;
return retval;
case PMIC_SND_LEFT_HP_MUTE:
case PMIC_SND_LEFT_SPEAKER_MUTE:
sc_access[0].reg_addr = VOL_CTRL_LT;
num_reg = 1;
mask = MASK6;
break;
case PMIC_SND_RIGHT_HP_MUTE:
case PMIC_SND_RIGHT_SPEAKER_MUTE:
sc_access[0].reg_addr = VOL_CTRL_RT;
num_reg = 1;
mask = MASK6;
break;
}
retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg);
if (retval)
return retval;
*value = sc_access[0].value & mask;
if (*value)
*value = MUTE;
else
*value = UNMUTE;
return retval;
}
static int mx_get_vol(int dev_id, int *value)
{
struct sc_reg_access sc_access = {0,};
int retval = 0, mask = 0, num_reg = 0;
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
retval = mx_init_card();
if (retval)
return retval;
}
switch (dev_id) {
case PMIC_SND_CAPTURE_VOL:
sc_access.reg_addr = 0x220;
mask = MASK0|MASK1|MASK2|MASK3|MASK4;
num_reg = 1;
break;
case PMIC_SND_LEFT_PB_VOL:
sc_access.reg_addr = VOL_CTRL_LT;
mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5;
num_reg = 1;
break;
case PMIC_SND_RIGHT_PB_VOL:
sc_access.reg_addr = VOL_CTRL_RT;
mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5;
num_reg = 1;
break;
}
retval = sst_sc_reg_access(&sc_access, PMIC_READ, num_reg);
if (retval)
return retval;
*value = -(sc_access.value & mask);
pr_debug("get volume value extracted %d\n", *value);
return retval;
}
static u8 mx_get_jack_status(void)
{
struct sc_reg_access sc_access_read = {0,};
sc_access_read.reg_addr = 0x201;
sst_sc_reg_access(&sc_access_read, PMIC_READ, 1);
pr_debug("value returned = 0x%x\n", sc_access_read.value);
return sc_access_read.value;
}
static void mx_pmic_irq_enable(void *data)
{
struct snd_intelmad *intelmaddata = data;
intelmaddata->jack_prev_state = 0xc0;
return;
}
static void mx_pmic_irq_cb(void *cb_data, u8 intsts)
{
u8 jack_cur_status, jack_prev_state = 0;
struct mad_jack *mjack = NULL;
unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
time_t timediff;
struct snd_intelmad *intelmaddata = cb_data;
mjack = &intelmaddata->jack[0];
if (intsts & 0x2) {
jack_cur_status = mx_get_jack_status();
jack_prev_state = intelmaddata->jack_prev_state;
if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x40)) {
/*headset insert detected. */
pr_debug("MAD headset inserted\n");
present = 1;
jack_event_flag = 1;
mjack->jack_status = 1;
mjack->jack.type = SND_JACK_HEADSET;
}
if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x00)) {
/* headphone insert detected. */
pr_debug("MAD headphone inserted\n");
present = 1;
jack_event_flag = 1;
mjack->jack.type = SND_JACK_HEADPHONE;
}
if ((jack_prev_state == 0x40) && (jack_cur_status == 0xc0)) {
/* headset remove detected. */
pr_debug("MAD headset removed\n");
present = 0;
jack_event_flag = 1;
mjack->jack_status = 0;
mjack->jack.type = SND_JACK_HEADSET;
}
if ((jack_prev_state == 0x00) && (jack_cur_status == 0xc0)) {
/* headphone remove detected. */
pr_debug("MAD headphone removed\n");
present = 0;
jack_event_flag = 1;
mjack->jack.type = SND_JACK_HEADPHONE;
}
if ((jack_prev_state == 0x40) && (jack_cur_status == 0x00)) {
/* button pressed */
do_gettimeofday(&mjack->buttonpressed);
pr_debug("MAD button press detected\n");
}
if ((jack_prev_state == 0x00) && (jack_cur_status == 0x40)) {
if (mjack->jack_status) {
/*button pressed */
do_gettimeofday(
&mjack->buttonreleased);
/*button pressed */
pr_debug("MAD Button Released detected\n");
timediff = mjack->buttonreleased.tv_sec -
mjack->buttonpressed.tv_sec;
buttonpressflag = 1;
if (timediff > 1) {
pr_debug("MAD long press dtd\n");
/* send headphone detect/undetect */
present = 1;
jack_event_flag = 1;
mjack->jack.type =
MID_JACK_HS_LONG_PRESS;
} else {
pr_debug("MAD short press dtd\n");
/* send headphone detect/undetect */
present = 1;
jack_event_flag = 1;
mjack->jack.type =
MID_JACK_HS_SHORT_PRESS;
}
} else {
/***workaround for maxim
hw issue,0x00 t 0x40 is not
a valid transiton for Headset insertion */
/*headset insert detected. */
pr_debug("MAD headset inserted\n");
present = 1;
jack_event_flag = 1;
mjack->jack_status = 1;
mjack->jack.type = SND_JACK_HEADSET;
}
}
intelmaddata->jack_prev_state = jack_cur_status;
pr_debug("mx_pmic_irq_cb prv_state= 0x%x\n",
intelmaddata->jack_prev_state);
}
if (jack_event_flag)
sst_mad_send_jack_report(&mjack->jack,
buttonpressflag, present);
}
static int mx_jack_enable(void)
{
return 0;
}
struct snd_pmic_ops snd_pmic_ops_mx = {
.set_input_dev = mx_set_selected_input_dev,
.set_output_dev = mx_set_selected_output_dev,
.set_mute = mx_set_mute,
.get_mute = mx_get_mute,
.set_vol = mx_set_vol,
.get_vol = mx_get_vol,
.init_card = mx_init_card,
.set_pcm_audio_params = mx_set_pcm_audio_params,
.set_pcm_voice_params = mx_set_pcm_voice_params,
.set_voice_port = mx_set_voice_port,
.set_audio_port = mx_set_audio_port,
.power_up_pmic_pb = mx_power_up_pb,
.power_up_pmic_cp = mx_power_up_cp,
.power_down_pmic_pb = mx_power_down_pb,
.power_down_pmic_cp = mx_power_down_cp,
.power_down_pmic = mx_power_down,
.pmic_irq_cb = mx_pmic_irq_cb,
.pmic_irq_enable = mx_pmic_irq_enable,
.pmic_jack_enable = mx_jack_enable,
};

File diff suppressed because it is too large Load Diff