Files
poky/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-1-8.patch

1434 lines
41 KiB
Diff

From c3cccfca9868aaf6e67a77c46a859a18d6384492 Mon Sep 17 00:00:00 2001
From: R, Dharageswari <dharageswari.r@intel.com>
Date: Thu, 29 Apr 2010 20:14:06 +0530
Subject: [PATCH] ADR-Post-Beta-0.05.002.03-1/8-Adding Moorestown Audio Drivers - SST driver
This patch is the first patch in the series of eight patches which add up SST
driver and MAD driver. The SST driver is a driver for the SST DSP engine.This
patch adds the SST driver main file intel_sst.c which contains the init, exit,
probe, interrupt routine, as well as PCI suspend and resume implementations.
intel_sst_dsp.c file implements the SST FW initialization as well as FW and
library download steps.This patch also contains the intel_lpe.h header file
which is placed in include folder for MAD driver (ALSA Sound card driver for
Moorestown given in patch 7/8) to use. This file contains the definition of
interfaces exposed by SST drivers along with definition of all the controls
for the sound card to be used by MAD Driver
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
new file: include/sound/intel_lpe.h
new file: sound/pci/sst/intel_sst.c
new file: sound/pci/sst/intel_sst_dsp.c
Patch-mainline: 2.6.35?
---
include/sound/intel_lpe.h | 148 +++++++++
sound/pci/sst/intel_sst.c | 527 ++++++++++++++++++++++++++++++
sound/pci/sst/intel_sst_dsp.c | 706 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1381 insertions(+), 0 deletions(-)
create mode 100644 include/sound/intel_lpe.h
create mode 100644 sound/pci/sst/intel_sst.c
create mode 100644 sound/pci/sst/intel_sst_dsp.c
diff --git a/include/sound/intel_lpe.h b/include/sound/intel_lpe.h
new file mode 100644
index 0000000..70e7a1f
--- /dev/null
+++ b/include/sound/intel_lpe.h
@@ -0,0 +1,148 @@
+#ifndef __INTEL_SST_H__
+#define __INTEL_SST_H__
+/*
+ * intel_lpe.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
+ */
+
+#define SST_CARD_NAMES "intel_mid_card"
+
+/* 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,
+
+};
+
+struct pcm_stream_info {
+ int str_id;
+ void *mad_substream;
+ void (*period_elapsed) (void *mad_substream);
+ unsigned long long buffer_ptr;
+ int sfreq;
+};
+
+struct stream_buffer {
+ unsigned long addr;
+ int length;
+};
+
+static inline long sst_get_time(void)
+{
+ struct timeval t;
+ do_gettimeofday(&t);
+ return t.tv_usec;
+}
+
+#ifndef CONFIG_SND_AUDIO_DBG_PRINT
+#define printk(format, arg...) do {} while (0);
+#endif
+
+struct snd_pmic_ops {
+ int card_status;
+ int num_channel;
+ int input_dev_id;
+ int mute_status;
+ int output_dev_id;
+ int (*set_input_dev) (u8 value);
+ int (*set_output_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, u8 value);
+ int (*get_vol) (int dev_id, u8 *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) (void);
+ int (*power_down_pmic_cp) (void);
+ int (*power_down_pmic) (void);
+};
+
+struct intel_sst_card_ops {
+ char *module_name;
+ int vendor_id;
+ int (*control_set) (int control_element, void *value);
+ int (*send_buffer) (int str_id, struct stream_buffer *mad_buf);
+ struct snd_pmic_ops *scard_ops;
+};
+
+/* periphral interrupt interface */
+enum lpe_periphral {
+ LPE_DMA = 1,
+ LPE_SSP0,
+ LPE_SSP1
+};
+
+/* 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);
+int lpe_mask_periphral_intr(enum lpe_periphral device);
+int lpe_unmask_periphral_intr(enum lpe_periphral device);
+int lpe_periphral_intr_status(enum lpe_periphral device, int *status);
+#endif /* __INTEL_SST_H__ */
diff --git a/sound/pci/sst/intel_sst.c b/sound/pci/sst/intel_sst.c
new file mode 100644
index 0000000..c6e68b8
--- /dev/null
+++ b/sound/pci/sst/intel_sst.c
@@ -0,0 +1,527 @@
+/*
+ * 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
+ */
+
+#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>
+#ifdef CONFIG_MSTWN_POWER_MGMT
+#include <linux/intel_mid.h>
+#endif
+#include <sound/intel_lpe.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intel_sst_fw_ipc.h"
+#include "intel_sst_common.h"
+
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_DESCRIPTION("Intel (R) Moorestown Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(SST_DRIVER_VERSION);
+
+struct intel_sst_drv *sst_drv_ctx;
+
+/* 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,
+ .ioctl = intel_sst_ioctl,
+ .mmap = intel_sst_mmap,
+ .aio_read = intel_sst_aio_read,
+ .aio_write = intel_sst_aio_write,
+};
+
+spinlock_t pe_slock;
+unsigned int pe_inprogress = 0;
+
+/**
+* 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;
+ int str_id;
+ struct stream_info *stream ;
+
+
+ /* Interrupt arrived, check src */
+ isr.full = readl(drv->shim + SST_ISRX);
+
+ if (isr.part.busy_interrupt) {
+ header.full = readl(drv->shim + SST_IPCD);
+ if (header.part.large)
+ size = header.part.data;
+ if (header.part.msg_id & REPLY_MSG) {
+ sst_drv_ctx->ipc_process_msg.header = header;
+
+ if (header.part.msg_id == IPC_SST_PERIOD_ELAPSED) {
+ sst_clear_interrupt();
+
+ /*spin_lock(&pe_slock);*/
+ if (pe_inprogress == 1) {
+ /*spin_unlock(&pe_slock);*/
+ return IRQ_HANDLED;
+ }
+
+ pe_inprogress = 1;
+ //spin_unlock(&pe_slock);
+
+ str_id = header.part.str_id;
+ stream = &sst_drv_ctx->streams[str_id];
+ if (stream->period_elapsed)
+ stream->period_elapsed(stream->pcm_substream);
+ //spin_lock(&pe_slock);
+ pe_inprogress = 0;
+ //spin_unlock(&pe_slock);
+ return IRQ_HANDLED;
+ } else {
+ 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 = readl(drv->shim + SST_IMRX);
+ imr.part.busy_interrupt = 1;
+ /* dummy register for shim workaround */
+ writel(imr.full, sst_drv_ctx->shim + SST_ISRD);
+ writel(imr.full, drv->shim + SST_IMRX);
+ return IRQ_HANDLED;
+ } else if (isr.part.done_interrupt) {
+ /* Clear done bit */
+ header.full = readl(drv->shim + SST_IPCX);
+ header.part.done = 0;
+ /* dummy register for shim workaround */
+ writel(header.full, sst_drv_ctx->shim + SST_ISRD);
+ writel(header.full, drv->shim + SST_IPCX);
+ /* write 1 to clear status register */;
+ isr.part.done_interrupt = 1;
+ /* dummy register for shim workaround */
+ writel(isr.full, sst_drv_ctx->shim + SST_ISRD);
+ writel(isr.full, drv->shim + SST_ISRX);
+ queue_work(sst_drv_ctx->post_msg_wq,
+ &sst_drv_ctx->ipc_post_msg.wq);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+
+}
+
+
+/* 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 i, ret = 0;
+ static struct mutex drv_ctx_lock;
+ mutex_init(&drv_ctx_lock);
+
+ mutex_lock(&drv_ctx_lock);
+ if (sst_drv_ctx) {
+ printk(KERN_ERR
+ "SST 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) {
+ printk(KERN_ERR
+ "SST ERR: intel_sst malloc fail\n");
+ mutex_unlock(&drv_ctx_lock);
+ return -ENOMEM;
+ }
+ mutex_unlock(&drv_ctx_lock);
+
+ mutex_init(&sst_drv_ctx->stream_cnt_lock);
+ sst_drv_ctx->pmic_state = SND_MAD_UN_INIT;
+
+ mutex_lock(&sst_drv_ctx->stream_cnt_lock);
+ sst_drv_ctx->stream_cnt = 0;
+ sst_drv_ctx->encoded_cnt = 0;
+ sst_drv_ctx->am_cnt = 0;
+ mutex_unlock(&sst_drv_ctx->stream_cnt_lock);
+ 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;
+ }
+ mutex_init(&sst_drv_ctx->list_lock);
+
+ for (i = 1; i < MAX_NUM_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);
+ }
+ 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) {
+ printk(KERN_DEBUG "SST DBG:Got memory %p size 0x%x \n",
+ sst_drv_ctx->mmap_mem,
+ sst_drv_ctx->mmap_len);
+ break;
+ }
+ sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE);
+ if (sst_drv_ctx->mmap_len <= 0) {
+ printk(KERN_ERR
+ "SST ERR: Couldnt get any +\
+ mem...abort!!\n");
+ ret = -ENOMEM;
+ goto free_process_reply_wq;
+ }
+ printk(KERN_DEBUG "SST DBG:Failed...trying %d\n", \
+ sst_drv_ctx->mmap_len);
+ }
+
+ /* Init the device */
+ ret = pci_enable_device(pci);
+ if (ret) {
+ printk(KERN_ERR
+ "SST ERR: device cant 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 =
+ (unsigned long) pci_resource_start(pci, 1);
+ sst_drv_ctx->shim = pci_ioremap_bar(pci, 1);
+ if (!sst_drv_ctx->shim)
+ goto do_release_regions;
+ printk(KERN_DEBUG "SST DBG: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;
+ printk(KERN_DEBUG "SST DBG: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;
+ printk(KERN_DEBUG "SST DBG: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;
+ printk(KERN_DEBUG "SST DBG:DRAM Ptr %p \n", sst_drv_ctx->dram);
+
+ sst_drv_ctx->sst_state = SST_UN_INIT;
+ /* 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;
+ printk(KERN_DEBUG "SST DBG:Registered IRQ 0x%x\n", pci->irq);
+
+ /* Register with /dev */
+ ret = register_chrdev(INTEL_SST_MAJOR, SST_DRV_NAME, &intel_sst_fops);
+ if (ret) {
+ printk(KERN_ERR
+ "SST ERR: couldn't register +\
+ device number\n");
+ goto do_free_irq;
+ }
+
+ sst_drv_ctx->lpe_stalled = 0;
+ printk(KERN_DEBUG "SST DBG:...successfully done!!!\n");
+ return ret;
+
+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);
+ printk(KERN_ERR
+ "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)
+{
+ pci_dev_put(sst_drv_ctx->pci);
+ sst_drv_ctx->sst_state = SST_UN_INIT;
+ unregister_chrdev(INTEL_SST_MAJOR, SST_DRV_NAME);
+ 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;
+ kfree(sst_drv_ctx->mmap_mem);
+ 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(sst_drv_ctx);
+ pci_release_region(pci, 2);
+ pci_release_region(pci, 3);
+ pci_release_region(pci, 4);
+ pci_release_region(pci, 5);
+ pci_set_drvdata(pci, NULL);
+}
+
+/* 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;
+ printk(KERN_DEBUG "SST DBG:intel_sst_suspend called\n");
+
+ /* Pause all running streams */
+ for (i = 1; i < MAX_NUM_STREAMS; i++) {
+ if (sst_drv_ctx->streams[i].status == STREAM_RUNNING) {
+ sst_drv_ctx->active_streams[i] = true;
+ sst_pause_stream(i);
+ } else
+ sst_drv_ctx->active_streams[i] = false;
+ }
+
+ pci_set_drvdata(pci, sst_drv_ctx);
+
+ /* Send msg to FW FIXME */
+
+ /* Disable everything */
+ /* free_irq(pci->irq, sst_drv_ctx); */
+ 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;
+
+ printk(KERN_DEBUG "SST DBG:\nintel_sst_resume called\n");
+ sst_drv_ctx = pci_get_drvdata(pci);
+ if (pci->irq)
+ printk(KERN_DEBUG "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_drv_ctx);
+ if (ret) {
+ return ret;
+ } */
+
+ /* Send msg to FW FIXME */
+
+ /* Start all paused streams */
+ for (i = 1; i < MAX_NUM_STREAMS; i++) {
+ if (sst_drv_ctx->active_streams[i] == true)
+ sst_resume_stream(i);
+ }
+ return 0;
+}
+
+
+static struct pci_driver driver = {
+ .name = SST_DRV_NAME,
+ .id_table = intel_sst_ids,
+ .probe = intel_sst_probe,
+ .remove = __devexit_p(intel_sst_remove),
+ .suspend = intel_sst_suspend,
+ .resume = intel_sst_resume,
+};
+
+/**
+* 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;
+ printk(KERN_ERR
+ "INFO: ******** SST DRIVER +\
+ loading.. Ver: %s\n", SST_DRIVER_VERSION);
+
+ /* Register with PCI */
+ ret = pci_register_driver(&driver);
+ if (ret)
+ printk(KERN_ERR
+ "SST ERR: PCI register failed \n");
+ sst_spi_mode_enable();
+ 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)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_STREAMS; i++)
+ sst_free_stream(i);
+
+ /* Send msg to FW TBD */
+ pci_unregister_driver(&driver);
+
+ flush_scheduled_work();
+ printk(KERN_DEBUG "SST DBG:...unloaded\n");
+ return;
+}
+
+module_init(intel_sst_init);
+module_exit(intel_sst_exit);
diff --git a/sound/pci/sst/intel_sst_dsp.c b/sound/pci/sst/intel_sst_dsp.c
new file mode 100644
index 0000000..bc78918
--- /dev/null
+++ b/sound/pci/sst/intel_sst_dsp.c
@@ -0,0 +1,706 @@
+/*
+ * intel_sst_dsp.h - 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
+ */
+#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>
+#include <asm/ipc_defs.h>
+#include <sound/intel_lpe.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intel_sst_fw_ipc.h"
+#include "intel_sst_common.h"
+
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_DESCRIPTION("Intel (R) Moorestown Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(SST_DRIVER_VERSION);
+
+/**
+* this function writes registers through SCU IPC
+*/
+static int sst_scu_ipc_write(u32 addr, u32 value)
+{
+ int retval = 0, retry = 3;
+ struct ipc_reg_data ipc_reg = {0};
+
+ ipc_reg.address = addr;
+ ipc_reg.data = value;
+ ipc_reg.ioc = 1;
+
+ while (retry) {
+ retval = mrst_ipc_write32(&ipc_reg);
+ if (!retval)
+ break;
+ retry--;
+ /* error */
+ printk(KERN_ERR "SST ERR: IPC +\
+ write failed %x\n", retval);
+ }
+ return retval;
+}
+
+/**
+* this function reads registers through SCU IPC
+*/
+static int sst_scu_ipc_read(u32 addr, u32 *value)
+{
+ int retval = 0, retry = 3;
+ struct ipc_reg_data ipc_reg = {0};
+
+ ipc_reg.address = addr;
+ ipc_reg.data = 0;
+ ipc_reg.ioc = 1;
+
+ while (retry) {
+ retval = mrst_ipc_read32(&ipc_reg);
+ if (!retval)
+ break;
+ retry--;
+ printk(KERN_ERR
+ "SST ERR: IPC read failed %x\n ", retval);
+ }
+ *value = ipc_reg.data;
+ printk(KERN_DEBUG "SST DBG:The read value +\
+ from the mrst_ipc is ::0x%08x\n", *value);
+ return retval;
+}
+/**
+* Resetting SST DSP
+*/
+static int intel_sst_reset_dsp(void)
+{
+ union config_status_reg csr;
+ int retval;
+ unsigned int value = 0;
+
+ retval = sst_scu_ipc_read(CHIP_REV_REG, &value);
+ if (retval)
+ return retval;
+
+#if 0
+ /* A2-CHANGES */
+ if (((value & CHIP_REV_ADDR) >> 3) == CHIP_REV_A1) {
+ sst_drv_ctx->chip_rev_id = CHIP_A1_50;
+ /* SCU FW Changes*/
+ retval = sst_scu_ipc_write(AUD_SHIM_BASE_ADDR,
+ AUD_SHIM_RATIO_1_1);
+ } else {
+ if (DSP_CLOCK_SPEED == CLK_100MHZ) {
+ sst_drv_ctx->chip_rev_id = CHIP_A2_100;
+ /* SCU FW Changes*/
+ retval = sst_scu_ipc_write(AUD_SHIM_BASE_ADDR,
+ AUD_SHIM_RATIO);
+ } else if (DSP_CLOCK_SPEED == CLK_50MHZ) {
+ sst_drv_ctx->chip_rev_id = CHIP_A2_50;
+ /* SCU FW Changes*/
+
+ retval = sst_scu_ipc_write(AUD_SHIM_BASE_ADDR,
+ AUD_SHIM_RATIO_1_1);
+ } else {
+ printk(KERN_ERR "SST ERR: +\
+ Invalid clock speed\n ");
+ return -EIO;
+ }
+ } /*else {
+ printk(KERN_ERR
+ "SST ERR: Invalid chip revision Type\n");
+ return -EIO;
+ }*/
+#endif
+ /* to fix SPI driver bug, which sets to 1 */
+ csr.full = 0x3a2;
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+ /* ----------- */
+
+ csr.full = readl(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;
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+
+ printk(KERN_DEBUG "SST DBG:Chip version +\
+ is:: %d\n", value);
+ return retval;
+}
+
+/**
+* Start the SST DSP processor
+*/
+static int sst_start(void)
+{
+ union config_status_reg csr;
+
+ csr.full = readl(sst_drv_ctx->shim + SST_CSR);
+ csr.part.bypass = 0;
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+
+#if 0
+ retval = sst_scu_ipc_write(
+ sst_drv_ctx->shim_phy_add + SST_CSR, csr.full);
+ if (retval != 0)
+ printk(KERN_ERR
+ "SST ERR: scu ipc write start failed %d ", retval);
+ csr.full = readl(sst_drv_ctx->shim + SST_CSR);
+ csr.part.sst_reset = 0;
+ csr.part.run_stall = 0;
+ if ((sst_drv_ctx->chip_rev_id == CHIP_A2_50) ||
+ (sst_drv_ctx->chip_rev_id == CHIP_A2_100)) {
+ csr.part.strb_cntr_rst = 1;
+ if (sst_drv_ctx->chip_rev_id == CHIP_A2_100)
+ csr.part.sst_clk = 1;
+ }
+#endif
+ csr.part.run_stall = 0;
+ csr.part.sst_reset = 0;
+ csr.part.strb_cntr_rst = 1;
+ printk(KERN_DEBUG "SST DBG:Setting SST to execute 0x%x \n", csr.full);
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+
+#if 0
+ return sst_scu_ipc_write(
+ sst_drv_ctx->shim_phy_add + SST_CSR, csr.full);
+#endif
+ return 0;
+}
+
+/**
+* Parse modules that need to be placed in SST IRAM and DRAM
+*/
+static int sst_parse_module(struct fw_module_header *module)
+{
+ struct dma_block_info *block;
+ u32 count;
+ void __iomem *ram;
+
+ printk(KERN_DEBUG "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 (block->size <= 0) {
+ printk(KERN_ERR "SST 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:
+ printk(KERN_ERR
+ "SST ERR:wrng ram typ0x%x +\
+ inblock0x%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 - FW parse and load
+* 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 */
+ printk(KERN_ERR
+ "SST ERR: InvalidFW sgn/filesiz mismtch\n ");
+ return -EINVAL;
+ }
+ printk(KERN_DEBUG "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 (ret_val)
+ return ret_val;
+ module = (void *)module + sizeof(*module) + module->mod_size ;
+ }
+
+ printk(KERN_DEBUG "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
+*/
+int sst_load_fw(const struct firmware *fw, void *context)
+{
+ int ret_val;
+
+ printk(KERN_DEBUG "SST DBG:called \n");
+ BUG_ON(!fw);
+
+ /* TBD: Checksum, tamper check etc */
+ ret_val = intel_sst_reset_dsp();
+ if (ret_val)
+ return ret_val;
+ /* putting the sst state to init */
+ sst_drv_ctx->sst_state = SST_UN_INIT;
+
+ ret_val = sst_parse_fw_image(fw);
+ if (ret_val)
+ return ret_val;
+
+ sst_drv_ctx->sst_state = SST_FW_LOADED;
+ /* 7. ask scu to reset the bypass bits */
+ /* 8.bring sst out of reset */
+ ret_val = sst_start();
+ if (ret_val)
+ return ret_val;
+
+ printk(KERN_DEBUG "SST DBG:...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);
+ printk(KERN_DEBUG "SST DBG: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, 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));
+ mutex_lock(&sst_drv_ctx->list_lock);
+ list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list);
+ mutex_unlock(&sst_drv_ctx->list_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;
+ printk(KERN_ERR
+ "SST ERR: Prep codec downloaded failed %d\n", retval);
+ return -EIO;
+ }
+ printk(KERN_DEBUG "SST DBG:FW responded, ready for download now...\n");
+ /* downloading on success */
+ sst_drv_ctx->sst_state = SST_FW_LOADED;
+ csr.full = readl(sst_drv_ctx->shim + SST_CSR);
+ printk(KERN_DEBUG "SST DBG:CSR reg 0x%x \n", csr.full);
+ csr.part.run_stall = 1;
+ printk(KERN_DEBUG "SST DBG:HALT CSR reg setting to 0x%x \n", csr.full);
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+#if 0
+ retval = sst_scu_ipc_write(
+ sst_drv_ctx->shim_phy_add + SST_CSR, csr.full);
+ if (retval) {
+ /* error */
+ sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ printk(KERN_ERR
+ "SST ERR: IPC failed to Halt SST 0x%x\n", retval);
+ return -EAGAIN;
+ }
+#endif
+ csr.full = readl(sst_drv_ctx->shim + SST_CSR);
+ csr.part.bypass = 0x7;
+ printk(KERN_DEBUG "SST DBG:Bypass CSR reg +\
+ setting to 0x%x \n", csr.full);
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+#if 0
+ retval = sst_scu_ipc_write(
+ sst_drv_ctx->shim_phy_add + SST_CSR, csr.full);
+ if (retval) {
+ /* error */
+ sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ printk(KERN_ERR
+ "SST ERR: IPC failed to Bypass SST 0x%x\n", retval);
+ csr.part.bypass = 0x0;
+ /* bring LPE out of run stall */
+ /* send error mesages to FW- TBD FIXME */
+ csr.part.run_stall = 0x0;
+ printk(KERN_DEBUG "SST DBG:Bypass CSR reg +\
+ setting to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_drv_ctx->shim_phy_add + SST_CSR,
+ csr.full);
+ if (retval) {
+ /* prepare to download firmware again
+ for the next time TBD FIXME
+ sst_drv_ctx->sst_state = SST_UN_INIT;*/
+ }
+ return -EAGAIN;
+ }
+#endif
+ sst_parse_fw_image(fw_lib);
+
+ /* set the FW to running again */
+ csr.full = readl(sst_drv_ctx->shim + SST_CSR);
+ csr.part.bypass = 0x0;
+ printk(KERN_DEBUG "SST DBG:Bypass CSR reg +\
+ setting to 0x%x \n", csr.full);
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+#if 0
+ retval = sst_scu_ipc_write(
+ sst_drv_ctx->shim_phy_add + SST_CSR, csr.full);
+ if (retval) {
+ sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ printk(KERN_ERR\
+ "SST ERR: BypassCSR regclear failed 0x%x\n", retval);
+ /* bring LPE out of run stall */
+ /* send error mesages to FW- TBD FIXME */
+ csr.part.run_stall = 0x0;
+ printk(KERN_DEBUG "SST DBG:Bypass CSR +\
+ reg setting to 0x%x \n", csr.full);
+ retval = sst_scu_ipc_write(sst_drv_ctx->shim_phy_add + SST_CSR,
+ csr.full);
+ if (retval) {
+ /* prepare to download firmware again
+ for the next time TBD FIXME
+ sst_drv_ctx->sst_state = SST_UN_INIT;*/
+ }
+ return -EAGAIN;
+ }
+#endif
+ csr.full = readl(sst_drv_ctx->shim + SST_CSR);
+ csr.part.run_stall = 0;
+ printk(KERN_DEBUG "SST DBG:Stalll CSR reg +\
+ setting to 0x%x \n", csr.full);
+ writel(csr.full, sst_drv_ctx->shim + SST_ISRX);
+ writel(csr.full, sst_drv_ctx->shim + SST_CSR);
+#if 0
+ retval = sst_scu_ipc_write(
+ sst_drv_ctx->shim_phy_add + SST_CSR, csr.full);
+ if (retval) {
+ sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ printk(KERN_ERR
+ "SST ERR: Stall CSR reg clear failed 0x%x \n", retval);
+ if (retval) {
+ /* prepare to download firmware again
+ for the next time TBD FIXME
+ sst_drv_ctx->sst_state = SST_UN_INIT;*/
+ }
+ return -EAGAIN;
+ }
+#endif
+ /* 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, 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));
+ mutex_lock(&sst_drv_ctx->list_lock);
+ list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list);
+ mutex_unlock(&sst_drv_ctx->list_lock);
+ sst_post_message(&sst_drv_ctx->ipc_post_msg_wq);
+ printk(KERN_DEBUG "SST DBG:Waiting for FW to respond on 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 */
+ sst_drv_ctx->sst_state = SST_FW_RUNNING;
+ /* shouldnt we set it to error state??? TBD */
+ sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ return -EIO;
+ }
+
+ printk(KERN_DEBUG "SST DBG:FW responded sucess on Download complete \n");
+ sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_drv_ctx->sst_state = SST_FW_RUNNING;
+ return 0;
+
+}
+
+/**
+* This function is called befoer 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) {
+ printk(KERN_ERR\
+ "SST ERR: Module no mismatch found\n ");
+ err = -EINVAL;
+ goto exit;
+ }
+ module = (void *)fw_lib->data + sizeof(*header);
+ *entry_point = module->entry_point;
+ printk(KERN_DEBUG "SST DBG:Module entry point 0x%x \n", *entry_point);
+ printk(KERN_DEBUG "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:
+ printk(KERN_ERR
+ "SST ERR: Invalid blk 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) {
+ printk(KERN_ERR
+ "SST ERR: library exceeds size allocated \n");
+ err = -EINVAL;
+ goto exit;
+ } else
+ printk(KERN_DEBUG "SST DBG:Library is safe for download...\n");
+
+ printk(KERN_DEBUG "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;
+
+}
+
+/**
+* This function is called when FW requests for a particular libary download
+* This function prepares the library to download
+*/
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops, u32 pvt_id)
+{
+ 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));
+
+ printk(KERN_DEBUG "SST DBG:Lib Type 0x%x, Slot 0x%x, ops 0x%x \n",
+ lib->lib_info.lib_type, lib->slot_info.slot_num, ops);
+ printk(KERN_DEBUG "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);
+
+ printk(KERN_DEBUG "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:
+ 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:
+ printk(KERN_ERR "SST ERR: +\
+ Invalid codec type \n");
+ error = -EINVAL;
+ goto wake;
+ }
+
+ if (ops == STREAM_OPS_CAPTURE)
+ dir = "enc_";
+ else
+ dir = "dec_";
+ strncpy(buf, type, strlen(type));
+ strncpy(buf + strlen(type), dir, strlen(dir));
+ len = strlen(type) + strlen(dir);
+ len += snprintf(buf + len, sizeof(buf) - len, "%d",
+ lib->slot_info.slot_num);
+ len += snprintf(buf + len, sizeof(buf) - len, ".bin");
+
+ printk(KERN_DEBUG "SST DBG:Requesting %s \n", buf);
+
+ error = request_firmware(&fw_lib, buf, &sst_drv_ctx->pci->dev);
+ if (error) {
+ printk(KERN_ERR
+ "SST 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 */
+ printk(KERN_DEBUG "SST DBG: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;
+}
+
+/* This Function set the bit banging*/
+int sst_spi_mode_enable()
+{
+
+ void __iomem *logical_ptr_to_bang;
+ u32 regbase = SPI_MODE_ENABLE_BASE_ADDR, range = 0x38;
+ u32 data;
+ u32 mask = 0x400000;
+ int retval;
+ int i = 0;
+
+
+ logical_ptr_to_bang = ioremap_nocache(regbase, range);
+ if (!logical_ptr_to_bang) {
+ dev_err(&sst_drv_ctx->pci->dev, \
+ "SST ERR: SSP0 bit bang -IOREMAP Failed \n");
+ return -1;
+ }
+
+ /* spi mode enable */
+ iowrite32(0x0000000f, logical_ptr_to_bang);
+ iowrite32(0x33301dc3, logical_ptr_to_bang + 0x4);
+ iowrite32(0x02010007, logical_ptr_to_bang + 0x2c);
+ iowrite32(0x00000000, logical_ptr_to_bang + 0x30);
+ iowrite32(0x00000000, logical_ptr_to_bang + 0x34);
+ iowrite32(0x0000008f, logical_ptr_to_bang);
+
+
+ retval = sst_scu_ipc_write(0xff12b004, 0x3);
+ retval = sst_scu_ipc_write(0xff12b000, 0x01070034);
+ retval = sst_scu_ipc_write(0xff12b004, 0x99);
+ retval = sst_scu_ipc_write(0xff12b000, 0x01070038);
+
+ data = ioread32(logical_ptr_to_bang+0x8);
+ dev_err(&sst_drv_ctx->pci->dev,\
+ "SST ERR: SSP0 bit bang SSCR val = 0x%08x \n", data);
+ data = data & mask;
+ while (data == mask) {
+ retval = sst_scu_ipc_write(0xff12b004, 0x3);
+ retval = sst_scu_ipc_write(0xff12b000, 0x01070034);
+ retval = sst_scu_ipc_write(0xff12b004, 0x2);
+ retval = sst_scu_ipc_write(0xff12b000, 0x01070034);
+ data = ioread32(logical_ptr_to_bang+0x8);
+ data = data & mask;
+ i++;
+ }
+ dev_err(&sst_drv_ctx->pci->dev, \
+ "SST ERR: SSP0 bit bang while loop counter= %4d \n ", i);
+ retval = sst_scu_ipc_write(0xff12b004, 0x0);
+ retval = sst_scu_ipc_write(0xff12b000, 0x01070038);
+
+ return retval;
+}
--
1.6.2.2