From 5e8c7c54a9b297dae0081dd19a7bb94e23040a3d Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Tue, 18 May 2010 14:51:13 +0100 Subject: linux-moblin: add 2.6.33.2 kernel from MeeGo 1.0 Signed-off-by: Joshua Lock --- ...ux-2.6.34-moorestown-audio-driver-6.0-1-8.patch | 1433 ++++++++++++++++++++ 1 file changed, 1433 insertions(+) create mode 100644 meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-1-8.patch (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-1-8.patch') diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-1-8.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-1-8.patch new file mode 100644 index 000000000..795bf1a3b --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-1-8.patch @@ -0,0 +1,1433 @@ +From c3cccfca9868aaf6e67a77c46a859a18d6384492 Mon Sep 17 00:00:00 2001 +From: R, Dharageswari +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 + + 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 ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_MSTWN_POWER_MGMT ++#include ++#endif ++#include ++#include ++#include "intel_sst_fw_ipc.h" ++#include "intel_sst_common.h" ++ ++ ++MODULE_AUTHOR("Vinod Koul "); ++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 ++ * Harsha Priya ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "intel_sst_fw_ipc.h" ++#include "intel_sst_common.h" ++ ++ ++MODULE_AUTHOR("Vinod Koul "); ++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 + -- cgit v1.2.3