[alsa-devel] [PATCH] [RFC 2/13] Intel SST driver
Vinod Koul
vinod.koul at intel.com
Fri Jul 3 09:01:33 CEST 2009
This adds the SST driver for the SST DSP engine. This patch
adds the main file intel_sst.c which contains the init, exit
probe, interrupt routine and PCI suspend/resume implementation.
This file also implements the SST Firmware initialization and
firmware and codec libraries download steps
Signed-off-by: Vinod Koul <vinod.koul at intel.com>
Signed-off-by: Harsha Priya <priya.harsha at intel.com>
new file: sound/pci/sst/intel_sst.c
---
sound/pci/sst/intel_sst.c | 877 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 877 insertions(+), 0 deletions(-)
create mode 100644 sound/pci/sst/intel_sst.c
diff --git a/sound/pci/sst/intel_sst.c b/sound/pci/sst/intel_sst.c
new file mode 100644
index 0000000..bd30833
--- /dev/null
+++ b/sound/pci/sst/intel_sst.c
@@ -0,0 +1,877 @@
+/*
+ * intel_sst.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-09 Intel Corp
+ * Authors: Vinod Koul <vinod.koul at intel.com>
+ * Harsha Priya <priya.harsha at intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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
+ */
+
+#include <linux/cdev.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#ifndef CONFIG_SST_IPC_NOT_INCLUDED
+#include <asm/ipc_defs.h>
+#endif
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intel_sst_fw_ipc.h"
+#include "intel_sst_common.h"
+#include "intel_sst_pvt.h"
+#ifdef CONFIG_SST_OSPM_SUPPORT
+#include <linux/intel_mid.h>
+#endif
+
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul at intel.com>");
+MODULE_DESCRIPTION("Intel (R) Moorestown Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(SST_DRIVER_VERSION);
+
+struct intel_sst_drv *sst_ops;
+
+void sst_reset(void)
+{
+ int retval;
+
+ /*Disable Audio Core clock and Audio Fabric clock*/
+ retval = sst_scu_ipc_write(0xff11d83c, 0x80008008);
+ if (retval != 0)
+ sst_err("scu ipc write1 failed %d", retval);
+ /*Reset the Audio subsystem*/
+ retval = sst_scu_ipc_write(0xff11d118, 0x7ffffcff);
+ if (retval != 0)
+ sst_err("scu ipc write2 failed %d", retval);
+ /*Bring it out of Audio subsystem reset*/
+ retval = sst_scu_ipc_write(0xff11d118, 0x7fffffff);
+ if (retval != 0)
+ sst_err("scu ipc write3 failed %d", retval);
+ /*Enable fabric clock at 50MHz*/
+ retval = sst_scu_ipc_write(0xff11d83c, 0x80008301);
+ if (retval != 0)
+ sst_err("scu ipc write4 failed %d", retval);
+ /*Write to the Shim register for ratio 1:1*/
+ retval = sst_scu_ipc_write(0xffae8000, 0x382);
+ if (retval != 0)
+ sst_err("scu ipc write5 failed %d", retval);
+ /*Enable the core clock at 1:2*/
+ retval = sst_scu_ipc_write(0xff11d83c, 0x80000301);
+ if (retval != 0)
+ sst_err("scu ipc write6 failed %d", retval);
+ return;
+}
+
+void sst_start(void)
+{
+ union config_status_reg csr;
+ int retval;
+
+ csr.full = readl(sst_ops->shim + SST_CSR);
+ csr.part.sst_reset = 0;
+ csr.part.run_stall = 0;
+ csr.part.bypass = 0;
+ /*csr.full = 0x1800;*/
+ sst_dbg("Setting SST to execute 0x%x \n", csr.full);
+
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR, csr.full);
+ if (retval != 0)
+ sst_err("scu ipc write start failed %d", retval);
+ return;
+}
+int sst_parse_module(struct fw_module_header *module)
+{
+ struct dma_block_info *block = NULL;
+ u32 count;
+ void __iomem *ram;
+
+ sst_dbg("module sign=%s sz=0x%x blks=0x%x typ=0x%x ep=0x%x sz=0x%x\n",
+ module->signature, module->mod_size,
+ module->blocks, module->type,
+ module->entry_point, sizeof(*module));
+
+ block = (void *)module + sizeof(*module);
+
+ for (count = 0; count < module->blocks; count++) {
+ if (0 >= block->size) {
+ sst_err("block size invalid\n");
+ return -EINVAL;
+ }
+ switch (block->type) {
+ case SST_IRAM:
+ ram = sst_ops->iram;
+ break;
+ case SST_DRAM:
+ ram = sst_ops->dram;
+ break;
+ default:
+ sst_err("wrong ram type 0x%x in block 0x%x-abort\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 - FW parse and load
+* @fw: Pointer to loaded FW
+*
+* This function is called to parse and download the FW image
+*/
+int sst_parse_fw_image(const struct firmware *sst_fw)
+{
+ struct fw_header *header = NULL;
+ u32 count;
+ int ret_val = 0;
+ struct fw_module_header *module = NULL;
+
+ BUG_ON(!sst_fw);
+
+ /*Read the header information from the data pointer*/
+ header = (struct fw_header *)sst_fw->data;
+
+ /*verify FW*/
+ if ((0 != strncmp(header->signature, SST_FW_SIGN, 4)) ||
+ (sst_fw->size != header->file_size + sizeof(*header))) {
+ /*Invalid FW signature*/
+ sst_err("Invalid FW sign/file size mismatch\n");
+ return -EINVAL;
+ }
+ sst_dbg("header sign=%s size=0x%x modules=0x%x fmt=0x%x size=0x%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 (0 != ret_val)
+ return ret_val;
+ module = (void *)module + sizeof(*module) + module->mod_size ;
+ }
+
+ sst_dbg("done....\n");
+ return 0;
+}
+
+/**
+* sst_load_fw - function to reset FW
+* @fw: Pointer to loaded FW
+* This function is called when the FW is loaded
+*/
+void sst_load_fw(const struct firmware *fw, void *context)
+{
+ int ret;
+
+ sst_dbg("called \n");
+ BUG_ON(!fw);
+
+ sst_reset();
+ /*putting the sst state to init*/
+ sst_ops->sst_state = SST_UN_INIT;
+
+ ret = sst_parse_fw_image(fw);
+ if (0 != ret) {
+ sst_err("error in FW dma\n");
+ return;
+ }
+ sst_ops->sst_state = SST_FW_LOADED;
+ /* 7. ask scu to reset the bypass bits*/
+ /* 8.bring sst out of reset */
+ sst_start();
+ sst_dbg("...successful!!!\n");
+ return;
+}
+
+int sst_download_library(const struct firmware *fw_lib,
+ struct snd_sst_lib_download_info *lib)
+{
+ /*send IPC message and wait*/
+ unsigned int i, 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) != 0)
+ return -ENOMEM;
+
+ pvt_id = sst_assign_pvt_id(sst_ops);
+ i = sst_get_block_stream(sst_ops);
+ sst_dbg("alloc block allocated = %d, pvt_id %d\n", i, pvt_id);
+ if (i < 0) {
+ kfree(msg);
+ return -ENOMEM;
+ }
+ sst_ops->alloc_block[i].sst_id = pvt_id;
+ sst_fill_header(&msg->header, IPC_IA_PREP_LIB_DNLD, 1, 0);
+ 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));
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ retval = sst_wait_timeout(sst_ops, &sst_ops->alloc_block[i]);
+ if (retval != 0) {
+ /*error*/
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_err("Prep codec downloaded failed %d\n", retval);
+ return -EIO;
+ }
+ sst_dbg("FW responded, ready for download now...\n");
+ /*downloading on success*/
+ sst_ops->sst_state = SST_FW_LOADED;
+ csr.full = readl(sst_ops->shim + SST_CSR);
+ sst_dbg("CSR reg 0x%x \n", csr.full);
+ csr.part.run_stall = 1;
+ writel(csr.full, sst_ops->shim + SST_ISRX);
+ sst_dbg("HALT CSR reg setting to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR, csr.full);
+ if (retval != 0) {
+ /*error*/
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_err("IPC failed to Halt SST 0x%x\n", retval);
+ return -EAGAIN;
+ }
+ csr.full = readl(sst_ops->shim + SST_CSR);
+ csr.part.bypass = 0x7;
+ writel(csr.full, sst_ops->shim + SST_ISRX);
+ sst_dbg("Bypass CSR reg setting to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR, csr.full);
+ if (retval != 0) {
+ /*error*/
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_err("IPC failed to Bypass SST 0x%x\n", retval);
+ csr.part.bypass = 0x0;
+ /*bring LPE out of run stall*/
+ csr.part.run_stall = 0x0;
+ writel(csr.full, sst_ops->shim + SST_ISRX);
+ sst_dbg("Bypass CSR reg setting to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR,
+ csr.full);
+ if (retval != 0)
+ sst_ops->sst_state = SST_UN_INIT;
+ return -EAGAIN;
+ }
+ sst_parse_fw_image(fw_lib);
+
+ /*set the FW to running again*/
+ csr.full = readl(sst_ops->shim + SST_CSR);
+ csr.part.bypass = 0x0;
+ writel(csr.full, sst_ops->shim + SST_ISRX);
+ sst_dbg("Bypass CSR reg clearing to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR, csr.full);
+ if (retval != 0) {
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_err("Bypass CSR reg clear failed 0x%x \n", retval);
+ /*bring LPE out of run stall*/
+ csr.part.run_stall = 0x0;
+ writel(csr.full, sst_ops->shim + SST_ISRX);
+ sst_dbg("Bypass CSR reg setting to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR,
+ csr.full);
+ if (retval != 0)
+ sst_ops->sst_state = SST_UN_INIT;
+ return -EAGAIN;
+ }
+
+ csr.full = readl(sst_ops->shim + SST_CSR);
+ csr.part.run_stall = 0;
+ writel(csr.full, sst_ops->shim + SST_ISRX);
+ sst_dbg("Stalll CSR reg clearing to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_ops->shim_phy_add + SST_CSR, csr.full);
+ if (retval != 0) {
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_err("Stall CSR reg clear failed 0x%x \n", retval);
+ if (retval != 0)
+ sst_ops->sst_state = SST_UN_INIT;
+ return -EAGAIN;
+ }
+ /*send download complete and wait*/
+ if (sst_create_large_msg(&msg) != 0) {
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ return -ENOMEM;
+ }
+
+ sst_fill_header(&msg->header, IPC_IA_LIB_DNLD_CMPLT, 1, 0);
+ 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));
+
+ list_add_tail(&msg->node, &sst_ops->ipc_dispatch_list);
+ sst_post_message(&sst_ops->ipc_post_msg_wq);
+ sst_dbg("Waiting for FW to respond on Download complete \n");
+ sst_ops->alloc_block[i].ops_block.condition = false;
+ retval = sst_wait_timeout(sst_ops, &sst_ops->alloc_block[i]);
+ if (retval != 0) {
+ /*error*/
+ sst_ops->sst_state = SST_FW_RUNNING;
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ return -EIO;
+ }
+
+ sst_dbg("FW responded sucess on Download complete \n");
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_ops->sst_state = SST_FW_RUNNING;
+ return 0;
+
+}
+
+int sst_validate_library(const struct firmware *fw_lib,
+ struct lib_slot_info *slot,
+ u32 *entry_point)
+{
+ struct fw_header *header = NULL;
+ struct fw_module_header *module = NULL;
+ struct dma_block_info *block = NULL;
+ unsigned int n_blk, isize = 0, dsize = 0;
+ int err = 0;
+
+ header = (struct fw_header *)fw_lib->data;
+ if (header->modules != 1) {
+ sst_err("Module no mismatch found\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ module = (void *)fw_lib->data + sizeof(*header);
+ *entry_point = module->entry_point;
+ sst_dbg("Module entry point 0x%x \n", *entry_point);
+ sst_dbg("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:
+ sst_err("Invalid block type found 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) {
+ sst_err("libarary exceeds size allocated \n");
+ err = -EINVAL;
+ goto exit;
+ } else
+ sst_dbg("Library is safe for download...\n");
+
+ sst_dbg("iram 0x%x, dram 0x%x, allowed iram 0x%x, allowed dram 0x%x\n",
+ isize, dsize, slot->iram_size, slot->dram_size);
+exit:
+ return err;
+
+}
+
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops, u32 pvt_id)
+{
+ char buf[20];
+ 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));
+
+ sst_dbg("Lib Type 0x%x, Slot 0x%x, ops 0x%x \n",
+ lib->lib_info.lib_type, lib->slot_info.slot_num, ops);
+ sst_dbg("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);
+
+ sst_dbg("IRAM Size 0x%x, offset 0x%x, DRAM Size 0x%x, offset 0x%x \n",
+ lib->slot_info.iram_size, lib->slot_info.iram_offset,
+ lib->slot_info.dram_size, lib->slot_info.dram_offset);
+
+ switch (lib->lib_info.lib_type) {
+ case SST_CODEC_TYPE_MP3:
+ len += snprintf(buf + len, sizeof(buf) - len, "mp3_");
+ break;
+ case SST_CODEC_TYPE_AAC:
+ len += snprintf(buf + len, sizeof(buf) - len, "aac_");
+ break;
+ case SST_CODEC_TYPE_WMA9:
+ len += snprintf(buf + len, sizeof(buf) - len, "wma9_");
+ break;
+ default:
+ sst_err("Invalid codec type \n");
+ error = -EINVAL;
+ goto wake;
+ }
+
+ if (ops == STREAM_OPS_CAPTURE)
+ len += snprintf(buf + len, sizeof(buf) - len, "enc_");
+ else
+ len += snprintf(buf + len, sizeof(buf) - len, "dec_");
+
+ len += snprintf(buf + len, sizeof(buf) - len, "%d",
+ lib->slot_info.slot_num);
+ len += snprintf(buf + len, sizeof(buf) - len, ".bin");
+
+ sst_dbg("Requesting %s \n", buf);
+
+ error = request_firmware(&fw_lib, buf, &sst_ops->pci->dev);
+ if (0 != error) {
+ sst_err("library load failed %d \n", error);
+ goto wake;
+ }
+ error = sst_validate_library(fw_lib, &lib->slot_info, &entry_point);
+ if (0 != error) {
+ sst_err("validate lib failed %d \n", 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 (0 != error) {
+ sst_err("download lib failed %d \n", error);
+ goto wake_free;
+ }
+ /*lib is downloaded and init send alloc again*/
+ sst_dbg("Library is downloaded now... \n");
+wake_free:
+ /*sst_wake_up_alloc_block(sst_ops, pvt_id, error, NULL);*/
+ release_firmware(fw_lib);
+wake:
+ return error;
+}
+
+/*fops Routines*/
+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,
+ .ioctl = intel_sst_ioctl,
+ .mmap = intel_sst_mmap,
+ .aio_read = intel_sst_aio_read,
+ .aio_write = intel_sst_aio_write,
+};
+
+/*
+ ISR Routines
+*/
+/**
+* intel_sst_interrupt - Interrupt service routine for SST
+* @irq: irq number of interrupt
+* @dev_id: 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;
+
+ /*Interrupt arrived, check src*/
+ isr.full = readl(drv->shim + SST_ISRX);
+ imr.full = readl(drv->shim + SST_IMRX);
+ if (1 == isr.part.busy_interrupt) {
+ header.full = readl(drv->shim + SST_IPCD);
+ if (1 == header.part.large)
+ size = header.part.data;
+ if (0 != (header.part.msg_id & REPLY_MSG)) {
+ sst_ops->ipc_process_msg.header = header;
+ memcpy_fromio(sst_ops->ipc_process_msg.mailbox,
+ drv->mailbox + SST_MAILBOX_RCV, size);
+ schedule_work(&sst_ops->ipc_process_msg.wq);
+ } else {
+ sst_ops->ipc_process_reply.header = header;
+ memcpy_fromio(sst_ops->ipc_process_reply.mailbox,
+ drv->mailbox + SST_MAILBOX_RCV, size);
+ schedule_work(&sst_ops->ipc_process_reply.wq);
+ }
+ /*mask busy inetrrupt*/
+ imr.part.busy_interrupt = 1;
+ writel(imr.full, drv->shim + SST_IMRX);
+
+ } else if (1 == isr.part.done_interrupt) {
+ /*Clear done bit*/
+ header.full = readl(drv->shim + SST_IPCX);
+ header.part.done = 0;
+ writel(header.full, drv->shim + SST_IPCX);
+ /* write 1 to clear status register*/;
+ isr.part.done_interrupt = 1;
+ writel(isr.full, drv->shim + SST_ISRX);
+ schedule_work(&sst_ops->ipc_post_msg.wq);
+ }
+ return IRQ_HANDLED;
+}
+
+
+/*
+ PCI Routines
+*/
+static struct pci_device_id intel_sst_ids[] = {
+ { 0x8086, 0x080A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, intel_sst_ids);
+
+
+/*
+* 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 ret = 0;
+ u32 bar = 0, size = 0;
+
+ /*Init the device*/
+ ret = pci_enable_device(pci);
+ if (0 != ret) {
+ sst_err("device cant be enabled\n");
+ return ret;
+ }
+ sst_ops->pci = pci;
+ /*map registers*/
+ /*SST Shim*/
+ bar = pci_resource_start(pci, 1);
+ size = pci_resource_len(pci, 1);
+ ret = pci_request_region(pci, 2, SST_DRV_NAME);
+ if (ret != 0)
+ goto err_1;
+ sst_ops->shim_phy_add = bar;
+ sst_ops->shim = ioremap_nocache(bar, size);
+ if (sst_ops->shim == NULL)
+ goto err_2;
+ sst_dbg("SST Shim 0x%x,Size 0x%x,Ptr %p \n", bar, size, sst_ops->shim);
+
+ /*Shared SRAM*/
+ bar = pci_resource_start(pci, 2);
+ size = pci_resource_len(pci, 2);
+ ret = pci_request_region(pci, 3, SST_DRV_NAME);
+ if (ret != 0)
+ goto err_2;
+ sst_ops->mailbox = ioremap_nocache(bar, size);
+ if (NULL == sst_ops->mailbox)
+ goto err_3;
+ sst_dbg("SRAM 0x%x, Size 0x%x, Ptr %p \n", bar, size, sst_ops->mailbox);
+
+ /* IRAM*/
+ bar = pci_resource_start(pci, 3);
+ size = pci_resource_len(pci, 3);
+ ret = pci_request_region(pci, 4, SST_DRV_NAME);
+ if (ret != 0)
+ goto err_3;
+ sst_ops->iram = ioremap_nocache(bar, size);
+ if (sst_ops->iram == NULL)
+ goto err_4;
+ sst_dbg("IRAM 0x%x, Size 0x%x, Ptr %p \n", bar, size, sst_ops->iram);
+
+ /*DRAM*/
+ bar = pci_resource_start(pci, 4);
+ size = pci_resource_len(pci, 4);
+ ret = pci_request_region(pci, 5, SST_DRV_NAME);
+ if (ret != 0)
+ goto err_4;
+ sst_ops->dram = ioremap_nocache(bar, size);
+ if (sst_ops->dram == NULL)
+ goto err_5;
+ sst_dbg("DRAM 0x%x, Size 0x%x, Ptr %p \n", bar, size, sst_ops->dram);
+
+ /*Register the ISR*/
+ ret = request_irq(pci->irq, intel_sst_interrupt,
+ IRQF_SHARED, SST_DRV_NAME, sst_ops);
+ if (0 != ret)
+ goto err_5;
+ sst_dbg("Registered IRQ 0x%x\n", pci->irq);
+ sst_ops->sst_state = SST_UN_INIT;
+
+ /* Register with /dev */
+ ret = register_chrdev(INTEL_SST_MAJOR, SST_DRV_NAME, &intel_sst_fops);
+ if (0 != ret) {
+ sst_err("couldn't register device number\n");
+ goto err_6;
+ }
+
+ sst_dbg("...successfully done!!!\n");
+ return 0;
+
+err_6: free_irq(pci->irq, sst_ops);
+err_5: iounmap(sst_ops->dram);
+err_4: iounmap(sst_ops->iram);
+err_3: iounmap(sst_ops->mailbox);
+err_2: iounmap(sst_ops->shim);
+err_1: sst_err("Probe failed with 0x%x \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)
+{
+ sst_ops->sst_state = SST_UN_INIT;
+ unregister_chrdev(INTEL_SST_MAJOR, SST_DRV_NAME);
+ free_irq(pci->irq, sst_ops);
+ iounmap(sst_ops->dram);
+ iounmap(sst_ops->iram);
+ iounmap(sst_ops->mailbox);
+ iounmap(sst_ops->shim);
+}
+
+#ifdef CONFIG_SST_OSPM_SUPPORT
+/*
+ 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
+*/
+static int intel_sst_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ int i = 0;
+ sst_dbg("intel_sst_suspend called\n");
+
+ /*Pause all running streams*/
+ for (i = 1; i < MAX_NUM_STREAMS; i++) {
+ if (sst_ops->streams[i].status == STREAM_RUNNING) {
+ sst_ops->active_streams[i] = true;
+ sst_pause_stream(i);
+ } else
+ sst_ops->active_streams[i] = false;
+ }
+
+ pci_set_drvdata(pci, sst_ops);
+
+ /*Disable everything*/
+ /*free_irq(pci->irq, sst_ops);*/
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+ return 0;
+}
+
+/**
+* intel_sst_resume - PCI resume function
+* @pci: PCI device structure
+* @state: PM message
+*
+* This function is called by OS when a power event occurs
+*/
+static int intel_sst_resume(struct pci_dev *pci)
+{
+ int i = 0;
+
+ sst_dbg("\nintel_sst_resume called\n");
+ if (pci != NULL)
+ sst_dbg("pci is not null \n");
+ /*Enable*/
+ sst_ops = pci_get_drvdata(pci);
+ if (sst_ops != NULL)
+ sst_dbg("sst_ops is not null\n");
+ if (pci->irq)
+ sst_dbg("irq %d \n", pci->irq);
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+
+ /*ret = request_irq(pci->irq, intel_sst_interrupt,
+ IRQF_SHARED, "intel_sst_engine", sst_ops);
+ if (0 != ret) {
+ sst_err("\tIRQ %d reg error %d\n", pci->irq, ret);
+ return ret;
+ }*/
+
+ /*Start all paused streams*/
+ for (i = 1; i < MAX_NUM_STREAMS; i++) {
+ if (sst_ops->active_streams[i] == true)
+ sst_resume_stream(i);
+ }
+ return 0;
+}
+/**
+* sst_ospm_send_event - Send PM events to OSPM
+* @event: OSPM Event
+*
+* This function is called when stream is started/stopped/paused...
+*/
+int sst_ospm_send_event(int event)
+{
+ return ospm_generate_netlink_event(AUDIO_SUBSYTEM_ID, event);
+
+}
+#endif /* CONFIG_SST_OSPM_SUPPORT */
+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_SST_OSPM_SUPPORT
+ .suspend = intel_sst_suspend,
+ .resume = intel_sst_resume,
+#endif
+};
+
+/**
+* intel_sst_init - Module init function
+*
+* Registers with PCI
+* Registers with /dev
+*Init all data strutures
+*/
+/*Module init function*/
+static int __init intel_sst_init(void)
+{
+ /*
+ Init all variables, data structure etc....
+ */
+ int ret = 0, i = 0;
+ struct stream_info *stream = NULL;
+
+ sst_info("******** SST DRIVER loading.. Ver: %s\n", SST_DRIVER_VERSION);
+
+ sst_ops = kzalloc(sizeof(*sst_ops), GFP_KERNEL);
+ if (NULL == sst_ops) {
+ sst_err("intel_sst malloc fail\n");
+ return -ENOMEM;
+ }
+
+ sst_ops->pmic_state = PMIC_SND_UN_INIT;
+ sst_ops->stream_cnt = 0;
+ sst_ops->active_cnt = 0;
+ sst_ops->am_cnt = 0;
+ sst_ops->unique_id = 0;
+
+ INIT_LIST_HEAD(&sst_ops->ipc_dispatch_list);
+ INIT_WORK(&sst_ops->ipc_post_msg.wq, sst_post_message);
+ INIT_WORK(&sst_ops->ipc_process_msg.wq, sst_process_message);
+ INIT_WORK(&sst_ops->ipc_process_reply.wq, sst_process_reply);
+ init_waitqueue_head(&sst_ops->wait_queue);
+
+ for (i = 0; i < MAX_ACTIVE_STREAM; i++) {
+ sst_ops->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_ops->alloc_block[i].ops_block.condition = false;
+ }
+ mutex_init(&sst_ops->list_lock);
+
+ for (i = 1; i < MAX_NUM_STREAMS; i++) {
+ stream = &sst_ops->streams[i];
+ INIT_LIST_HEAD(&stream->bufs);
+ mutex_init(&stream->lock);
+ }
+ sst_ops->mmap_mem = NULL;
+ sst_ops->mmap_len = SST_MMAP_PAGES * PAGE_SIZE;
+ while (sst_ops->mmap_len > 0) {
+ sst_ops->mmap_mem = kzalloc(sst_ops->mmap_len, GFP_KERNEL);
+ if (sst_ops->mmap_mem != NULL) {
+ sst_dbg("Got memory %p size 0x%x \n", sst_ops->mmap_mem,
+ sst_ops->mmap_len);
+ break;
+ }
+ sst_ops->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE);
+ if (sst_ops->mmap_len <= 0) {
+ sst_err("Couldnt get any mem...abort!!\n");
+ goto exit_p;
+ }
+ sst_dbg("Failed...trying %d\n", sst_ops->mmap_len);
+ }
+ /* Register with PCI */
+ ret = pci_register_driver(&driver);
+ if (0 != ret) {
+ sst_err("PCI register failed, unregister char drv\n");
+ goto exit;
+ }
+
+
+
+ sst_dbg("...sucessful\n");
+ return 0;
+
+exit:
+ pci_unregister_driver(&driver);
+exit_p:
+ kfree(sst_ops->mmap_mem);
+ kfree(sst_ops);
+ sst_err("Error in init %d\n", ret);
+
+ return ret;
+}
+
+/*Module exit function*/
+static void __exit intel_sst_exit(void)
+{
+ int i;
+ struct stream_info *stream = NULL;
+
+ for (i = 0; i < MAX_NUM_STREAMS; i++) {
+ stream = &sst_ops->streams[i];
+ sst_free_stream(i);
+ }
+ sst_ops->pmic_state = PMIC_SND_UN_INIT;
+ pci_unregister_driver(&driver);
+
+ flush_scheduled_work();
+ kfree(sst_ops->mmap_mem);
+ kfree(sst_ops);
+
+ sst_dbg("...unloaded\n");
+ return;
+}
+
+module_init(intel_sst_init);
+module_exit(intel_sst_exit);
--
1.5.4.5
More information about the Alsa-devel
mailing list