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