mirror of
https://git.yoctoproject.org/poky
synced 2026-05-01 06:32:11 +02:00
1434 lines
41 KiB
Diff
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
|
|
|