Maemo patchset 20101501+0m5
[h-e-n] / drivers / dsp / bridge / wmd / io_sm.c
diff --git a/drivers/dsp/bridge/wmd/io_sm.c b/drivers/dsp/bridge/wmd/io_sm.c
new file mode 100644 (file)
index 0000000..d8ae1f1
--- /dev/null
@@ -0,0 +1,2009 @@
+/*
+ * io_sm.c
+ *
+ * DSP-BIOS Bridge driver support functions for TI OMAP processors.
+ *
+ * Copyright (C) 2005-2006 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ *  ======== io_sm.c ========
+ *  Description:
+ *      IO dispatcher for a shared memory channel driver.
+ *
+ *  Public Functions:
+ *      WMD_IO_Create
+ *      WMD_IO_Destroy
+ *      WMD_IO_OnLoaded
+ *      IO_AndSetValue
+ *      IO_BufSize
+ *      IO_CancelChnl
+ *      IO_DPC
+ *      IO_ISR
+ *      IO_IVAISR
+ *      IO_OrSetValue
+ *      IO_ReadValue
+ *      IO_ReadValueLong
+ *      IO_RequestChnl
+ *      IO_Schedule
+ *      IO_WriteValue
+ *      IO_WriteValueLong
+ *
+ *  Channel Invariant:
+ *      There is an important invariant condition which must be maintained per
+ *      channel outside of WMD_CHNL_GetIOC() and IO_Dispatch(), violation of
+ *      which may cause timeouts and/or failure of the WIN32_WaitSingleObject
+ *      function (SYNC_WaitOnEvent).
+ *
+ */
+
+/*  ----------------------------------- Host OS */
+#include <dspbridge/host_os.h>
+#include <linux/workqueue.h>
+
+/*  ----------------------------------- DSP/BIOS Bridge */
+#include <dspbridge/std.h>
+#include <dspbridge/dbdefs.h>
+#include <dspbridge/errbase.h>
+
+/*  ----------------------------------- Trace & Debug */
+#include <dspbridge/dbc.h>
+#include <dspbridge/dbg.h>
+
+/*  ----------------------------------- OS Adaptation Layer */
+#include <dspbridge/cfg.h>
+#include <dspbridge/dpc.h>
+#include <dspbridge/mem.h>
+#include <dspbridge/ntfy.h>
+#include <dspbridge/sync.h>
+#include <dspbridge/reg.h>
+
+/* ------------------------------------ Hardware Abstraction Layer */
+#include <hw_defs.h>
+#include <hw_mmu.h>
+
+/*  ----------------------------------- Mini Driver */
+#include <dspbridge/wmddeh.h>
+#include <dspbridge/wmdio.h>
+#include <dspbridge/wmdioctl.h>
+#include <_tiomap.h>
+#include <tiomap_io.h>
+#include <_tiomap_pwr.h>
+#include <tiomap_io.h>
+
+/*  ----------------------------------- Platform Manager */
+#include <dspbridge/cod.h>
+#include <dspbridge/dev.h>
+#include <dspbridge/chnl_sm.h>
+#include <dspbridge/dbreg.h>
+
+/*  ----------------------------------- Others */
+#include <dspbridge/rms_sh.h>
+#include <dspbridge/mgr.h>
+#include <dspbridge/drv.h>
+#include "_cmm.h"
+
+/*  ----------------------------------- This */
+#include <dspbridge/io_sm.h>
+#include "_msg_sm.h"
+#include <dspbridge/gt.h>
+
+/*  ----------------------------------- Defines, Data Structures, Typedefs */
+#define OUTPUTNOTREADY  0xffff
+#define NOTENABLED      0xffff /* channel(s) not enabled */
+
+#define EXTEND      "_EXT_END"
+
+#define SwapWord(x)     (x)
+#define ulPageAlignSize 0x10000   /* Page Align Size */
+
+#define MAX_PM_REQS 32
+
+/* IO Manager: only one created per board: */
+struct IO_MGR {
+       /* These four fields must be the first fields in a IO_MGR_ struct: */
+       u32 dwSignature;        /* Used for object validation   */
+       struct WMD_DEV_CONTEXT *hWmdContext;    /* WMD device context  */
+       struct WMD_DRV_INTERFACE *pIntfFxns;    /* Function interface to WMD */
+       struct DEV_OBJECT *hDevObject;  /* Device this board represents */
+
+       /* These fields initialized in WMD_IO_Create():    */
+       struct CHNL_MGR *hChnlMgr;
+       struct SHM *pSharedMem;         /* Shared Memory control        */
+       u8 *pInput;             /* Address of input channel     */
+       u8 *pOutput;            /* Address of output channel    */
+       struct MSG_MGR *hMsgMgr;        /* Message manager */
+       struct MSG *pMsgInputCtrl;      /* Msg control for from DSP messages */
+       struct MSG *pMsgOutputCtrl;     /* Msg control for to DSP messages */
+       u8 *pMsgInput;  /* Address of input messages    */
+       u8 *pMsgOutput;         /* Address of output messages   */
+       u32 uSMBufSize;         /* Size of a shared memory I/O channel */
+       bool fSharedIRQ;        /* Is this IRQ shared?    */
+       struct DPC_OBJECT *hDPC;        /* DPC object handle        */
+       struct SYNC_CSOBJECT *hCSObj;   /* Critical section object handle */
+       u32 uWordSize;  /* Size in bytes of DSP word    */
+       u16 wIntrVal;           /* interrupt value            */
+       /* private extnd proc info; mmu setup */
+       struct MGR_PROCESSOREXTINFO extProcInfo;
+       struct CMM_OBJECT *hCmmMgr;     /* Shared Mem Mngr            */
+       struct work_struct io_workq;     /*workqueue */
+       u32 dQuePowerMbxVal[MAX_PM_REQS];
+       u32 iQuePowerHead;
+       u32 iQuePowerTail;
+#ifndef DSP_TRACEBUF_DISABLED
+       u32 ulTraceBufferBegin;         /* Trace message start address */
+       u32 ulTraceBufferEnd;   /* Trace message end address */
+       u32 ulTraceBufferCurrent;       /* Trace message current address */
+       u32 ulGPPReadPointer;   /* GPP Read pointer to Trace buffer */
+       u8 *pMsg;
+       u32 ulGppVa;
+       u32 ulDspVa;
+#endif
+} ;
+
+/*  ----------------------------------- Function Prototypes */
+static void IO_DispatchChnl(IN struct IO_MGR *pIOMgr,
+                          IN OUT struct CHNL_OBJECT *pChnl, u32 iMode);
+static void IO_DispatchMsg(IN struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr);
+static void IO_DispatchPM(struct work_struct *work);
+static void NotifyChnlComplete(struct CHNL_OBJECT *pChnl,
+                               struct CHNL_IRP *pChirp);
+static void InputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl,
+                       u32 iMode);
+static void OutputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl,
+                       u32 iMode);
+static void InputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr);
+static void OutputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr);
+static u32 FindReadyOutput(struct CHNL_MGR *pChnlMgr,
+                            struct CHNL_OBJECT *pChnl, u32 dwMask);
+static u32 ReadData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest,
+                       void *pSrc, u32 uSize);
+static u32 WriteData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest,
+                       void *pSrc, u32 uSize);
+static struct workqueue_struct *bridge_workqueue;
+#ifndef DSP_TRACEBUF_DISABLED
+void PrintDSPDebugTrace(struct IO_MGR *hIOMgr);
+#endif
+
+/* Bus Addr (cached kernel)*/
+static DSP_STATUS registerSHMSegs(struct IO_MGR *hIOMgr,
+                                 struct COD_MANAGER *hCodMan,
+                                 u32 dwGPPBasePA);
+
+#ifdef CONFIG_BRIDGE_DVFS
+/* The maximum number of OPPs that are supported */
+extern s32 dsp_max_opps;
+/* The Vdd1 opp table information */
+extern u32 vdd1_dsp_freq[6][4] ;
+#endif
+
+#if GT_TRACE
+static struct GT_Mask dsp_trace_mask = { NULL, NULL }; /* GT trace variable */
+#endif
+
+/*
+ *  ======== WMD_IO_Create ========
+ *      Create an IO manager object.
+ */
+DSP_STATUS WMD_IO_Create(OUT struct IO_MGR **phIOMgr,
+                        struct DEV_OBJECT *hDevObject,
+                        IN CONST struct IO_ATTRS *pMgrAttrs)
+{
+       DSP_STATUS status = DSP_SOK;
+       struct IO_MGR *pIOMgr = NULL;
+       struct SHM *pSharedMem = NULL;
+       struct WMD_DEV_CONTEXT *hWmdContext = NULL;
+       struct CFG_HOSTRES hostRes;
+       struct CFG_DEVNODE *hDevNode;
+       struct CHNL_MGR *hChnlMgr;
+       static int ref_count;
+       u32 devType;
+       /* Check DBC requirements:  */
+       DBC_Require(phIOMgr != NULL);
+       DBC_Require(pMgrAttrs != NULL);
+       DBC_Require(pMgrAttrs->uWordSize != 0);
+       /* This for the purposes of DBC_Require: */
+       status = DEV_GetChnlMgr(hDevObject, &hChnlMgr);
+       DBC_Require(status != DSP_EHANDLE);
+       DBC_Require(hChnlMgr != NULL);
+       DBC_Require(hChnlMgr->hIOMgr == NULL);
+       /*
+        * Message manager will be created when a file is loaded, since
+        * size of message buffer in shared memory is configurable in
+        * the base image.
+        */
+       DEV_GetWMDContext(hDevObject, &hWmdContext);
+       DBC_Assert(hWmdContext);
+       DEV_GetDevType(hDevObject, &devType);
+       /*
+        * DSP shared memory area will get set properly when
+        * a program is loaded. They are unknown until a COFF file is
+        * loaded. I chose the value -1 because it was less likely to be
+        * a valid address than 0.
+        */
+       pSharedMem = (struct SHM *) -1;
+       if (DSP_FAILED(status))
+               goto func_cont;
+
+       /* Create a Single Threaded Work Queue */
+       if (ref_count == 0)
+               bridge_workqueue = create_workqueue("bridge_work-queue");
+
+       if (!bridge_workqueue)
+               DBG_Trace(DBG_LEVEL1, "Workqueue creation failed!\n");
+
+       /* Allocate IO manager object: */
+       MEM_AllocObject(pIOMgr, struct IO_MGR, IO_MGRSIGNATURE);
+       if (pIOMgr == NULL) {
+               status = DSP_EMEMORY;
+               goto func_cont;
+       }
+
+       /* Intializing Work Element */
+       if (ref_count == 0) {
+               INIT_WORK(&pIOMgr->io_workq, (void *)IO_DispatchPM);
+               ref_count = 1;
+       } else
+               PREPARE_WORK(&pIOMgr->io_workq, (void *)IO_DispatchPM);
+
+       /* Initialize CHNL_MGR object:    */
+#ifndef DSP_TRACEBUF_DISABLED
+       pIOMgr->pMsg = NULL;
+#endif
+       pIOMgr->hChnlMgr = hChnlMgr;
+       pIOMgr->uWordSize = pMgrAttrs->uWordSize;
+       pIOMgr->pSharedMem = pSharedMem;
+       if (DSP_SUCCEEDED(status))
+               status = SYNC_InitializeCS(&pIOMgr->hCSObj);
+
+       if (devType == DSP_UNIT) {
+               /* Create a DPC object: */
+               status = DPC_Create(&pIOMgr->hDPC, IO_DPC, (void *)pIOMgr);
+               if (DSP_SUCCEEDED(status))
+                       status = DEV_GetDevNode(hDevObject, &hDevNode);
+
+               pIOMgr->iQuePowerHead = 0;
+               pIOMgr->iQuePowerTail = 0;
+       }
+       if (DSP_SUCCEEDED(status)) {
+               status = CFG_GetHostResources((struct CFG_DEVNODE *)
+                               DRV_GetFirstDevExtension() , &hostRes);
+       }
+       if (DSP_SUCCEEDED(status)) {
+               pIOMgr->hWmdContext = hWmdContext;
+               pIOMgr->fSharedIRQ = pMgrAttrs->fShared;
+               IO_DisableInterrupt(hWmdContext);
+               if (devType == DSP_UNIT) {
+                       HW_MBOX_initSettings(hostRes.dwMboxBase);
+                       /* Plug the channel ISR:. */
+                       if ((request_irq(INT_MAIL_MPU_IRQ, IO_ISR, 0,
+                               "DspBridge\tmailbox", (void *)pIOMgr)) == 0)
+                               status = DSP_SOK;
+                       else
+                               status = DSP_EFAIL;
+               }
+               if (DSP_SUCCEEDED(status))
+                       DBG_Trace(DBG_LEVEL1, "ISR_IRQ Object 0x%x \n",
+                                       pIOMgr);
+               else
+                       status = CHNL_E_ISR;
+       } else
+               status = CHNL_E_ISR;
+func_cont:
+       if (DSP_FAILED(status)) {
+               /* Cleanup: */
+               WMD_IO_Destroy(pIOMgr);
+               *phIOMgr = NULL;
+       } else {
+               /* Return IO manager object to caller... */
+               hChnlMgr->hIOMgr = pIOMgr;
+               *phIOMgr = pIOMgr;
+       }
+       return status;
+}
+
+/*
+ *  ======== WMD_IO_Destroy ========
+ *  Purpose:
+ *      Disable interrupts, destroy the IO manager.
+ */
+DSP_STATUS WMD_IO_Destroy(struct IO_MGR *hIOMgr)
+{
+       DSP_STATUS status = DSP_SOK;
+       struct WMD_DEV_CONTEXT *hWmdContext;
+       if (MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE)) {
+               /* Unplug IRQ:    */
+               /* Disable interrupts from the board:  */
+               if (DSP_SUCCEEDED(DEV_GetWMDContext(hIOMgr->hDevObject,
+                      &hWmdContext)))
+                               DBC_Assert(hWmdContext);
+               (void)CHNLSM_DisableInterrupt(hWmdContext);
+               destroy_workqueue(bridge_workqueue);
+               /* Linux function to uninstall ISR */
+               free_irq(INT_MAIL_MPU_IRQ, (void *)hIOMgr);
+               (void)DPC_Destroy(hIOMgr->hDPC);
+#ifndef DSP_TRACEBUF_DISABLED
+               if (hIOMgr->pMsg)
+                       MEM_Free(hIOMgr->pMsg);
+#endif
+               SYNC_DeleteCS(hIOMgr->hCSObj);  /* Leak Fix. */
+               /* Free this IO manager object: */
+               MEM_FreeObject(hIOMgr);
+       } else
+               status = DSP_EHANDLE;
+
+       return status;
+}
+
+/*
+ *  ======== WMD_IO_OnLoaded ========
+ *  Purpose:
+ *      Called when a new program is loaded to get shared memory buffer
+ *      parameters from COFF file. ulSharedBufferBase and ulSharedBufferLimit
+ *      are in DSP address units.
+ */
+DSP_STATUS WMD_IO_OnLoaded(struct IO_MGR *hIOMgr)
+{
+       struct COD_MANAGER *hCodMan;
+       struct CHNL_MGR *hChnlMgr;
+       struct MSG_MGR *hMsgMgr;
+       u32 ulShmBase;
+       u32 ulShmBaseOffset;
+       u32 ulShmLimit;
+       u32 ulShmLength = -1;
+       u32 ulMemLength = -1;
+       u32 ulMsgBase;
+       u32 ulMsgLimit;
+       u32 ulMsgLength = -1;
+       u32 ulExtEnd;
+       u32 ulGppPa = 0;
+       u32 ulGppVa = 0;
+       u32 ulDspVa = 0;
+       u32 ulSegSize = 0;
+       u32 ulPadSize = 0;
+       u32 i;
+       DSP_STATUS status = DSP_SOK;
+       u32 uNumProcs = 0;
+       s32 ndx = 0;
+       /* DSP MMU setup table */
+       struct WMDIOCTL_EXTPROC aEProc[WMDIOCTL_NUMOFMMUTLB];
+       struct CFG_HOSTRES hostRes;
+       u32 mapAttrs;
+       u32 ulShm0End;
+       u32 ulDynExtBase;
+       u32 ulSeg1Size = 0;
+       u32 paCurr = 0;
+       u32 vaCurr = 0;
+       u32 gppVaCurr = 0;
+       u32 numBytes = 0;
+       u32 allBits = 0;
+       u32 pgSize[] = { HW_PAGE_SIZE_16MB, HW_PAGE_SIZE_1MB,
+                          HW_PAGE_SIZE_64KB, HW_PAGE_SIZE_4KB };
+
+       status = DEV_GetCodMgr(hIOMgr->hDevObject, &hCodMan);
+       DBC_Assert(DSP_SUCCEEDED(status));
+       hChnlMgr = hIOMgr->hChnlMgr;
+        /*  The message manager is destroyed when the board is stopped.  */
+       DEV_GetMsgMgr(hIOMgr->hDevObject, &hIOMgr->hMsgMgr);
+       hMsgMgr = hIOMgr->hMsgMgr;
+       DBC_Assert(MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE));
+       DBC_Assert(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE));
+       if (hIOMgr->pSharedMem)
+               hIOMgr->pSharedMem = NULL;
+
+       /* Get start and length of channel part of shared memory */
+       status = COD_GetSymValue(hCodMan, CHNL_SHARED_BUFFER_BASE_SYM,
+                                &ulShmBase);
+       if (DSP_FAILED(status)) {
+               status = CHNL_E_NOMEMMAP;
+               goto func_cont1;
+       }
+       status = COD_GetSymValue(hCodMan, CHNL_SHARED_BUFFER_LIMIT_SYM,
+                               &ulShmLimit);
+       if (DSP_FAILED(status)) {
+               status = CHNL_E_NOMEMMAP;
+               goto func_cont1;
+       }
+       if (ulShmLimit <= ulShmBase) {
+               status = CHNL_E_INVALIDMEMBASE;
+       } else {
+               /* get total length in bytes */
+               ulShmLength = (ulShmLimit - ulShmBase + 1) * hIOMgr->uWordSize;
+               /* Calculate size of a PROCCOPY shared memory region */
+               DBG_Trace(DBG_LEVEL7,
+                        "**(proc)PROCCOPY SHMMEM SIZE: 0x%x bytes\n",
+                         (ulShmLength - sizeof(struct SHM)));
+       }
+func_cont1:
+       if (DSP_SUCCEEDED(status)) {
+               /* Get start and length of message part of shared memory */
+               status = COD_GetSymValue(hCodMan, MSG_SHARED_BUFFER_BASE_SYM,
+                                       &ulMsgBase);
+       }
+       if (DSP_SUCCEEDED(status)) {
+               status = COD_GetSymValue(hCodMan, MSG_SHARED_BUFFER_LIMIT_SYM,
+                                       &ulMsgLimit);
+               if (DSP_SUCCEEDED(status)) {
+                       if (ulMsgLimit <= ulMsgBase) {
+                               status = CHNL_E_INVALIDMEMBASE;
+                       } else {
+                               /* Length (bytes) of messaging part of shared
+                                * memory */
+                               ulMsgLength = (ulMsgLimit - ulMsgBase + 1) *
+                                             hIOMgr->uWordSize;
+                               /* Total length (bytes) of shared memory:
+                                * chnl + msg */
+                               ulMemLength = ulShmLength + ulMsgLength;
+                       }
+               } else {
+                       status = CHNL_E_NOMEMMAP;
+               }
+       }
+       if (DSP_SUCCEEDED(status)) {
+#ifndef DSP_TRACEBUF_DISABLED
+               status = COD_GetSymValue(hCodMan, DSP_TRACESEC_END, &ulShm0End);
+               DBG_Trace(DBG_LEVEL7, "_BRIDGE_TRACE_END value = %x \n",
+                        ulShm0End);
+#else
+               status = COD_GetSymValue(hCodMan, SHM0_SHARED_END_SYM,
+                                        &ulShm0End);
+               DBG_Trace(DBG_LEVEL7, "_SHM0_END = %x \n", ulShm0End);
+#endif
+               if (DSP_FAILED(status))
+                       status = CHNL_E_NOMEMMAP;
+
+       }
+       if (DSP_SUCCEEDED(status)) {
+               status = COD_GetSymValue(hCodMan, DYNEXTBASE, &ulDynExtBase);
+               if (DSP_FAILED(status))
+                       status = CHNL_E_NOMEMMAP;
+
+       }
+       if (DSP_SUCCEEDED(status)) {
+               status = COD_GetSymValue(hCodMan, EXTEND, &ulExtEnd);
+               if (DSP_FAILED(status))
+                       status = CHNL_E_NOMEMMAP;
+
+       }
+       if (DSP_SUCCEEDED(status)) {
+               /* Get memory reserved in host resources */
+               (void)MGR_EnumProcessorInfo(0,
+                       (struct DSP_PROCESSORINFO *)&hIOMgr->extProcInfo,
+                       sizeof(struct MGR_PROCESSOREXTINFO), &uNumProcs);
+               CFG_GetHostResources((
+                       struct CFG_DEVNODE *)DRV_GetFirstDevExtension(),
+                       &hostRes);
+               /* The first MMU TLB entry(TLB_0) in DCD is ShmBase. */
+               ndx = 0;
+               ulGppPa = hostRes.dwMemPhys[1];
+               ulGppVa = hostRes.dwMemBase[1];
+               /* THIS IS THE VIRTUAL UNCACHED IOREMAPPED ADDRESS !!! */
+               /* Why can't we directly take the DSPVA from the symbols? */
+               ulDspVa = hIOMgr->extProcInfo.tyTlb[0].ulDspVirt;
+               ulSegSize = (ulShm0End - ulDspVa) * hIOMgr->uWordSize;
+               ulSeg1Size = (ulExtEnd - ulDynExtBase) * hIOMgr->uWordSize;
+               ulSeg1Size = (ulSeg1Size + 0xFFF) & (~0xFFFUL); /* 4K align*/
+               ulSegSize = (ulSegSize + 0xFFFF) & (~0xFFFFUL); /* 64K align*/
+               ulPadSize = ulPageAlignSize - ((ulGppPa + ulSeg1Size) %
+                            ulPageAlignSize);
+                       if (ulPadSize == ulPageAlignSize)
+                               ulPadSize = 0x0;
+
+                DBG_Trace(DBG_LEVEL7, "ulGppPa %x, ulGppVa %x, ulDspVa %x, "
+                         "ulShm0End %x, ulDynExtBase %x, ulExtEnd %x, "
+                         "ulSegSize %x ulSeg1Size %x \n", ulGppPa, ulGppVa,
+                         ulDspVa, ulShm0End, ulDynExtBase, ulExtEnd, ulSegSize,
+                         ulSeg1Size);
+
+               if ((ulSegSize + ulSeg1Size + ulPadSize) >
+                  hostRes.dwMemLength[1]) {
+                       DBG_Trace(DBG_LEVEL7, "ulGppPa %x, ulGppVa %x, ulDspVa "
+                                "%x, ulShm0End %x, ulDynExtBase %x, ulExtEnd "
+                                "%x, ulSegSize %x, ulSeg1Size %x \n", ulGppPa,
+                                ulGppVa, ulDspVa, ulShm0End, ulDynExtBase,
+                                ulExtEnd, ulSegSize, ulSeg1Size);
+                       DBG_Trace(DBG_LEVEL7, "Insufficient SHM Reserved 0x%x. "
+                                "Required 0x%x\n", hostRes.dwMemLength[1],
+                                ulSegSize + ulSeg1Size + ulPadSize);
+                       status = DSP_EMEMORY;
+               }
+       }
+       if (DSP_FAILED(status))
+               goto func_cont;
+
+       paCurr = ulGppPa;
+       vaCurr = ulDynExtBase * hIOMgr->uWordSize;
+       gppVaCurr = ulGppVa;
+       numBytes = ulSeg1Size;
+
+       /*
+        * Try to fit into TLB entries. If not possible, push them to page
+        * tables. It is quite possible that if sections are not on
+        * bigger page boundary, we may end up making several small pages.
+        * So, push them onto page tables, if that is the case.
+        */
+       mapAttrs = 0x00000000;
+       mapAttrs = DSP_MAPLITTLEENDIAN;
+       mapAttrs |= DSP_MAPPHYSICALADDR;
+       mapAttrs |= DSP_MAPELEMSIZE32;
+       mapAttrs |= DSP_MAPDONOTLOCK;
+
+       while (numBytes && DSP_SUCCEEDED(status)) {
+               /* To find the max. page size with which both PA & VA are
+                * aligned */
+               allBits = paCurr | vaCurr;
+               DBG_Trace(DBG_LEVEL1, "allBits %x, paCurr %x, vaCurr %x, "
+                        "numBytes %x\n", allBits, paCurr, vaCurr, numBytes);
+               for (i = 0; i < 4; i++) {
+                       if ((numBytes >= pgSize[i]) && ((allBits &
+                          (pgSize[i] - 1)) == 0)) {
+                               status = hIOMgr->pIntfFxns->pfnBrdMemMap
+                                       (hIOMgr->hWmdContext, paCurr, vaCurr,
+                                       pgSize[i], mapAttrs);
+                               DBC_Assert(DSP_SUCCEEDED(status));
+                               paCurr += pgSize[i];
+                               vaCurr += pgSize[i];
+                               gppVaCurr += pgSize[i];
+                               numBytes -= pgSize[i];
+                               /* Don't try smaller sizes. Hopefully we have
+                                * reached an address aligned to a bigger page
+                                * size*/
+                               break;
+                       }
+               }
+       }
+       paCurr += ulPadSize;
+       vaCurr += ulPadSize;
+       gppVaCurr += ulPadSize;
+
+       /* configure the TLB entries for the next cacheable segment */
+       numBytes = ulSegSize;
+       vaCurr = ulDspVa * hIOMgr->uWordSize;
+       allBits = 0x0;
+       while (numBytes && DSP_SUCCEEDED(status)) {
+               /* To find the max. page size with which both PA & VA are
+                * aligned*/
+               allBits = paCurr | vaCurr;
+               DBG_Trace(DBG_LEVEL1, "allBits for Seg1 %x, paCurr %x, "
+                        "vaCurr %x, numBytes %x\n", allBits, paCurr, vaCurr,
+                        numBytes);
+               for (i = 0; i < 4; i++) {
+                       if (!(numBytes >= pgSize[i]) ||
+                          !((allBits & (pgSize[i]-1)) == 0))
+                               continue;
+                       if (ndx < MAX_LOCK_TLB_ENTRIES) {
+                               /* This is the physical address written to
+                                * DSP MMU */
+                               aEProc[ndx].ulGppPa = paCurr;
+                               /* THIS IS THE VIRTUAL UNCACHED IOREMAPPED
+                                * ADDRESS!!! */
+                               aEProc[ndx].ulGppVa = gppVaCurr;
+                               aEProc[ndx].ulDspVa = vaCurr / hIOMgr->
+                                                     uWordSize;
+                               aEProc[ndx].ulSize = pgSize[i];
+                               aEProc[ndx].endianism = HW_LITTLE_ENDIAN;
+                               aEProc[ndx].elemSize = HW_ELEM_SIZE_16BIT;
+                               aEProc[ndx].mixedMode = HW_MMU_CPUES;
+                               DBG_Trace(DBG_LEVEL1, "SHM MMU TLB entry PA %lx"
+                                        " VA %lx DSP_VA %lx Size %lx\n",
+                                        aEProc[ndx].ulGppPa,
+                                        aEProc[ndx].ulGppVa,
+                                        aEProc[ndx].ulDspVa *
+                                        hIOMgr->uWordSize, pgSize[i]);
+                               ndx++;
+                       } else {
+                               status = hIOMgr->pIntfFxns->pfnBrdMemMap(
+                               hIOMgr->hWmdContext, paCurr, vaCurr, pgSize[i],
+                                       mapAttrs);
+                               DBG_Trace(DBG_LEVEL1, "SHM MMU PTE entry PA %lx"
+                                        " VA %lx DSP_VA %lx Size %lx\n",
+                                        aEProc[ndx].ulGppPa,
+                                        aEProc[ndx].ulGppVa,
+                                        aEProc[ndx].ulDspVa *
+                                        hIOMgr->uWordSize, pgSize[i]);
+                               DBC_Assert(DSP_SUCCEEDED(status));
+                       }
+                       paCurr += pgSize[i];
+                       vaCurr += pgSize[i];
+                       gppVaCurr += pgSize[i];
+                       numBytes -= pgSize[i];
+                       /* Don't try smaller sizes. Hopefully we have reached
+                        an address aligned to a bigger page size*/
+                       break;
+               }
+       }
+
+        /* Copy remaining entries from CDB. All entries are 1 MB and should not
+        * conflict with SHM entries on MPU or DSP side */
+       for (i = 3; i < 7 && ndx < WMDIOCTL_NUMOFMMUTLB &&
+           DSP_SUCCEEDED(status); i++) {
+               if (hIOMgr->extProcInfo.tyTlb[i].ulGppPhys == 0)
+                       continue;
+
+               if ((hIOMgr->extProcInfo.tyTlb[i].ulGppPhys > ulGppPa - 0x100000
+                       && hIOMgr->extProcInfo.tyTlb[i].ulGppPhys <=
+                               ulGppPa + ulSegSize)
+                       || (hIOMgr->extProcInfo.tyTlb[i].ulDspVirt > ulDspVa -
+                               0x100000 / hIOMgr->uWordSize && hIOMgr->
+                               extProcInfo.tyTlb[i].ulDspVirt
+                               <= ulDspVa + ulSegSize / hIOMgr->uWordSize)) {
+                       DBG_Trace(DBG_LEVEL7, "CDB MMU entry %d conflicts with "
+                                "SHM.\n\tCDB: GppPa %x, DspVa %x.\n\tSHM: "
+                                "GppPa %x, DspVa %x, Bytes %x.\n", i,
+                                hIOMgr->extProcInfo.tyTlb[i].ulGppPhys,
+                                hIOMgr->extProcInfo.tyTlb[i].ulDspVirt,
+                                ulGppPa, ulDspVa, ulSegSize);
+                       status = DSP_EFAIL;
+               } else {
+                       if (ndx < MAX_LOCK_TLB_ENTRIES) {
+                               aEProc[ndx].ulDspVa = hIOMgr->extProcInfo.
+                                       tyTlb[i].ulDspVirt;
+                               aEProc[ndx].ulGppPa = hIOMgr->extProcInfo.
+                                       tyTlb[i].ulGppPhys;
+                               aEProc[ndx].ulGppVa = 0;
+                               /* Can't convert, so set to zero*/
+                               aEProc[ndx].ulSize = 0x100000;  /* 1 MB*/
+                               DBG_Trace(DBG_LEVEL1, "SHM MMU entry PA %x "
+                                        "DSP_VA 0x%x\n", aEProc[ndx].ulGppPa,
+                                       aEProc[ndx].ulDspVa);
+                               ndx++;
+                       } else {
+                               status = hIOMgr->pIntfFxns->pfnBrdMemMap
+                                       (hIOMgr->hWmdContext,
+                                       hIOMgr->extProcInfo.tyTlb[i].ulGppPhys,
+                                       hIOMgr->extProcInfo.tyTlb[i].ulDspVirt,
+                                       0x100000, mapAttrs);
+                       }
+               }
+       }
+       if (i < 7 && DSP_SUCCEEDED(status)) {
+               /* All CDB entries could not be made*/
+               status = DSP_EFAIL;
+       }
+func_cont:
+       mapAttrs = 0x00000000;
+       mapAttrs = DSP_MAPLITTLEENDIAN;
+       mapAttrs |= DSP_MAPPHYSICALADDR;
+       mapAttrs |= DSP_MAPELEMSIZE32;
+       mapAttrs |= DSP_MAPDONOTLOCK;
+
+       /* Map the L4 peripherals */
+       i = 0;
+       while (L4PeripheralTable[i].physAddr && DSP_SUCCEEDED(status)) {
+               status = hIOMgr->pIntfFxns->pfnBrdMemMap
+                       (hIOMgr->hWmdContext, L4PeripheralTable[i].physAddr,
+                       L4PeripheralTable[i].dspVirtAddr, HW_PAGE_SIZE_4KB,
+                       mapAttrs);
+               if (DSP_FAILED(status))
+                       break;
+               i++;
+       }
+
+       if (DSP_SUCCEEDED(status)) {
+               for (i = ndx; i < WMDIOCTL_NUMOFMMUTLB; i++) {
+                       aEProc[i].ulDspVa = 0;
+                       aEProc[i].ulGppPa = 0;
+                       aEProc[i].ulGppVa = 0;
+                       aEProc[i].ulSize = 0;
+               }
+               /* Set the SHM physical address entry (grayed out in CDB file)
+                * to the virtual uncached ioremapped address of SHM reserved
+                * on MPU */
+               hIOMgr->extProcInfo.tyTlb[0].ulGppPhys = (ulGppVa + ulSeg1Size +
+                                                        ulPadSize);
+               DBG_Trace(DBG_LEVEL1, "*********extProcInfo *********%x \n",
+                         hIOMgr->extProcInfo.tyTlb[0].ulGppPhys);
+               /* Need SHM Phys addr. IO supports only one DSP for now:
+                * uNumProcs=1 */
+               if ((hIOMgr->extProcInfo.tyTlb[0].ulGppPhys == 0) ||
+                  (uNumProcs != 1)) {
+                       status = CHNL_E_NOMEMMAP;
+                       DBC_Assert(false);
+               } else {
+                       DBC_Assert(aEProc[0].ulDspVa <= ulShmBase);
+                       /* ulShmBase may not be at ulDspVa address */
+                       ulShmBaseOffset = (ulShmBase - aEProc[0].ulDspVa) *
+                           hIOMgr->uWordSize;
+                        /* WMD_BRD_Ctrl() will set dev context dsp-mmu info. In
+                        *   _BRD_Start() the MMU will be re-programed with MMU
+                        *   DSPVa-GPPPa pair info while DSP is in a known
+                        *   (reset) state.  */
+                       DBC_Assert(hIOMgr->pIntfFxns != NULL);
+                       DBC_Assert(hIOMgr->hWmdContext != NULL);
+                       status = hIOMgr->pIntfFxns->pfnDevCntrl(hIOMgr->
+                                hWmdContext, WMDIOCTL_SETMMUCONFIG, aEProc);
+                       ulShmBase = hIOMgr->extProcInfo.tyTlb[0].ulGppPhys;
+                       DBG_Trace(DBG_LEVEL1, "extProcInfo.tyTlb[0].ulGppPhys "
+                                "%x \n ", hIOMgr->extProcInfo.tyTlb[0].
+                                ulGppPhys);
+                       ulShmBase += ulShmBaseOffset;
+                       ulShmBase = (u32)MEM_LinearAddress((void *)ulShmBase,
+                                   ulMemLength);
+                       DBC_Assert(ulShmBase != 0);
+                       if (DSP_SUCCEEDED(status)) {
+                               status = registerSHMSegs(hIOMgr, hCodMan,
+                                        aEProc[0].ulGppPa);
+                               /* Register SM */
+                       }
+               }
+       }
+       if (DSP_SUCCEEDED(status)) {
+               hIOMgr->pSharedMem = (struct SHM *)ulShmBase;
+               hIOMgr->pInput = (u8 *)hIOMgr->pSharedMem +
+                                sizeof(struct SHM);
+               hIOMgr->pOutput = hIOMgr->pInput + (ulShmLength -
+                                 sizeof(struct SHM))/2;
+               hIOMgr->uSMBufSize = hIOMgr->pOutput - hIOMgr->pInput;
+               DBG_Trace(DBG_LEVEL3,
+                        "hIOMgr: pInput %p pOutput %p ulShmLength %x\n",
+                        hIOMgr->pInput, hIOMgr->pOutput, ulShmLength);
+               DBG_Trace(DBG_LEVEL3,
+                        "pSharedMem %p uSMBufSize %x sizeof(SHM) %x\n",
+                        hIOMgr->pSharedMem, hIOMgr->uSMBufSize,
+                        sizeof(struct SHM));
+                /*  Set up Shared memory addresses for messaging. */
+               hIOMgr->pMsgInputCtrl = (struct MSG *)((u8 *)
+                                       hIOMgr->pSharedMem +
+                                       ulShmLength);
+               hIOMgr->pMsgInput = (u8 *)hIOMgr->pMsgInputCtrl +
+                                   sizeof(struct MSG);
+               hIOMgr->pMsgOutputCtrl = (struct MSG *)((u8 *)hIOMgr->
+                                        pMsgInputCtrl + ulMsgLength / 2);
+               hIOMgr->pMsgOutput = (u8 *)hIOMgr->pMsgOutputCtrl +
+                                    sizeof(struct MSG);
+               hMsgMgr->uMaxMsgs = ((u8 *)hIOMgr->pMsgOutputCtrl -
+                                   hIOMgr->pMsgInput) /
+                                   sizeof(struct MSG_DSPMSG);
+               DBG_Trace(DBG_LEVEL7, "IO MGR SHM details : pSharedMem 0x%x, "
+                        "pInput 0x%x, pOutput 0x%x, pMsgInputCtrl 0x%x, "
+                        "pMsgInput 0x%x, pMsgOutputCtrl 0x%x, pMsgOutput "
+                        "0x%x \n", (u8 *)hIOMgr->pSharedMem,
+                        (u8 *)hIOMgr->pInput, (u8 *)hIOMgr->pOutput,
+                        (u8 *)hIOMgr->pMsgInputCtrl,
+                        (u8 *)hIOMgr->pMsgInput,
+                        (u8 *)hIOMgr->pMsgOutputCtrl,
+                        (u8 *)hIOMgr->pMsgOutput);
+               DBG_Trace(DBG_LEVEL7, "** (proc) MAX MSGS IN SHARED MEMORY: "
+                        "0x%x\n", hMsgMgr->uMaxMsgs);
+               memset((void *) hIOMgr->pSharedMem, 0, sizeof(struct SHM));
+       }
+#ifndef DSP_TRACEBUF_DISABLED
+       if (DSP_SUCCEEDED(status)) {
+               /* Get the start address of trace buffer */
+               if (DSP_SUCCEEDED(status)) {
+                       status = COD_GetSymValue(hCodMan, SYS_PUTCBEG,
+                                &hIOMgr->ulTraceBufferBegin);
+                       if (DSP_FAILED(status))
+                               status = CHNL_E_NOMEMMAP;
+
+               }
+               hIOMgr->ulGPPReadPointer = hIOMgr->ulTraceBufferBegin =
+                       (ulGppVa + ulSeg1Size + ulPadSize) +
+                       (hIOMgr->ulTraceBufferBegin - ulDspVa);
+               /* Get the end address of trace buffer */
+               if (DSP_SUCCEEDED(status)) {
+                       status = COD_GetSymValue(hCodMan, SYS_PUTCEND,
+                                &hIOMgr->ulTraceBufferEnd);
+                       if (DSP_FAILED(status))
+                               status = CHNL_E_NOMEMMAP;
+
+               }
+               hIOMgr->ulTraceBufferEnd = (ulGppVa + ulSeg1Size + ulPadSize) +
+                                          (hIOMgr->ulTraceBufferEnd - ulDspVa);
+               /* Get the current address of DSP write pointer */
+               if (DSP_SUCCEEDED(status)) {
+                       status = COD_GetSymValue(hCodMan,
+                                BRIDGE_SYS_PUTC_current,
+                                &hIOMgr->ulTraceBufferCurrent);
+                       if (DSP_FAILED(status))
+                               status = CHNL_E_NOMEMMAP;
+
+               }
+               hIOMgr->ulTraceBufferCurrent = (ulGppVa + ulSeg1Size +
+                                               ulPadSize) + (hIOMgr->
+                                               ulTraceBufferCurrent - ulDspVa);
+               /* Calculate the size of trace buffer */
+               if (hIOMgr->pMsg)
+                       MEM_Free(hIOMgr->pMsg);
+               hIOMgr->pMsg = MEM_Alloc(((hIOMgr->ulTraceBufferEnd -
+                                       hIOMgr->ulTraceBufferBegin) *
+                                       hIOMgr->uWordSize) + 2, MEM_NONPAGED);
+               if (!hIOMgr->pMsg)
+                       status = DSP_EMEMORY;
+
+               DBG_Trace(DBG_LEVEL1, "** hIOMgr->pMsg: 0x%x\n", hIOMgr->pMsg);
+               hIOMgr->ulDspVa = ulDspVa;
+               hIOMgr->ulGppVa = (ulGppVa + ulSeg1Size + ulPadSize);
+    }
+#endif
+       IO_EnableInterrupt(hIOMgr->hWmdContext);
+       return status;
+}
+
+/*
+ *  ======== IO_BufSize ========
+ *      Size of shared memory I/O channel.
+ */
+u32 IO_BufSize(struct IO_MGR *hIOMgr)
+{
+       DBC_Require(MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE));
+
+       return hIOMgr->uSMBufSize;
+}
+
+/*
+ *  ======== IO_CancelChnl ========
+ *      Cancel IO on a given PCPY channel.
+ */
+void IO_CancelChnl(struct IO_MGR *hIOMgr, u32 ulChnl)
+{
+       struct IO_MGR *pIOMgr = (struct IO_MGR *)hIOMgr;
+       struct SHM *sm;
+
+       DBC_Require(MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE));
+       sm = hIOMgr->pSharedMem;
+
+       /* Inform DSP that we have no more buffers on this channel:  */
+       IO_AndValue(pIOMgr->hWmdContext, struct SHM, sm, hostFreeMask,
+                  (~(1 << ulChnl)));
+
+       CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS);
+}
+
+/*
+ *  ======== IO_DispatchChnl ========
+ *      Proc-copy chanl dispatch.
+ */
+static void IO_DispatchChnl(IN struct IO_MGR *pIOMgr,
+                          IN OUT struct CHNL_OBJECT *pChnl, u32 iMode)
+{
+       DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE));
+
+       DBG_Trace(DBG_LEVEL3, "Entering IO_DispatchChnl \n");
+
+       /* See if there is any data available for transfer: */
+       DBC_Assert(iMode == IO_SERVICE);
+
+       /* Any channel will do for this mode: */
+       InputChnl(pIOMgr, pChnl, iMode);
+       OutputChnl(pIOMgr, pChnl, iMode);
+}
+
+/*
+ *  ======== IO_DispatchMsg ========
+ *      Performs I/O dispatch on message queues.
+ */
+static void IO_DispatchMsg(IN struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr)
+{
+       DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE));
+
+       DBG_Trace(DBG_LEVEL3, "Entering IO_DispatchMsg \n");
+
+       /*  We are performing both input and output processing. */
+       InputMsg(pIOMgr, hMsgMgr);
+       OutputMsg(pIOMgr, hMsgMgr);
+}
+
+/*
+ *  ======== IO_DispatchPM ========
+ *      Performs I/O dispatch on PM related messages from DSP
+ */
+static void IO_DispatchPM(struct work_struct *work)
+{
+       struct IO_MGR *pIOMgr =
+                               container_of(work, struct IO_MGR, io_workq);
+       DSP_STATUS status;
+       u32 pArg[2];
+
+       /*DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE));*/
+
+       DBG_Trace(DBG_LEVEL7, "IO_DispatchPM: Entering IO_DispatchPM : \n");
+
+       /*  Perform Power message processing here  */
+       while (pIOMgr->iQuePowerHead != pIOMgr->iQuePowerTail) {
+               pArg[0] = *(u32 *)&(pIOMgr->dQuePowerMbxVal[pIOMgr->
+                         iQuePowerTail]);
+               DBG_Trace(DBG_LEVEL7, "IO_DispatchPM - pArg[0] - 0x%x: \n",
+                        pArg[0]);
+               /* Send the command to the WMD clk/pwr manager to handle */
+               if (pArg[0] ==  MBX_PM_HIBERNATE_EN) {
+                       DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Hibernate "
+                                "command\n");
+                       status = pIOMgr->pIntfFxns->pfnDevCntrl(pIOMgr->
+                                hWmdContext, WMDIOCTL_PWR_HIBERNATE, pArg);
+                       if (DSP_FAILED(status)) {
+                               DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : "
+                                        "Hibernation command failed\n");
+                       }
+               } else if (pArg[0] == MBX_PM_OPP_REQ) {
+                       pArg[1] = pIOMgr->pSharedMem->oppRequest.rqstOppPt;
+                       DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Value of OPP "
+                                "value =0x%x \n", pArg[1]);
+                       status = pIOMgr->pIntfFxns->pfnDevCntrl(pIOMgr->
+                                hWmdContext, WMDIOCTL_CONSTRAINT_REQUEST,
+                                pArg);
+                       if (DSP_FAILED(status)) {
+                               DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Failed "
+                                        "to set constraint = 0x%x \n",
+                                        pArg[1]);
+                       }
+
+               } else {
+                       DBG_Trace(DBG_LEVEL7, "IO_DispatchPM - clock control - "
+                                "value of msg = 0x%x: \n", pArg[0]);
+                       status = pIOMgr->pIntfFxns->pfnDevCntrl(pIOMgr->
+                                hWmdContext, WMDIOCTL_CLK_CTRL, pArg);
+                       if (DSP_FAILED(status)) {
+                               DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Failed "
+                                        "to control the DSP clk = 0x%x \n",
+                                        *pArg);
+                       }
+               }
+               /* increment the tail count here */
+               pIOMgr->iQuePowerTail++;
+               if (pIOMgr->iQuePowerTail >= MAX_PM_REQS)
+                       pIOMgr->iQuePowerTail = 0;
+
+       }
+
+}
+
+/*
+ *  ======== IO_DPC ========
+ *      Deferred procedure call for shared memory channel driver ISR.  Carries
+ *      out the dispatch of I/O as a non-preemptible event.It can only be
+ *      pre-empted      by an ISR.
+ */
+void IO_DPC(IN OUT void *pRefData)
+{
+       struct IO_MGR *pIOMgr = (struct IO_MGR *)pRefData;
+       struct CHNL_MGR *pChnlMgr;
+       struct MSG_MGR *pMsgMgr;
+       struct DEH_MGR *hDehMgr;
+
+       DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE));
+       pChnlMgr = pIOMgr->hChnlMgr;
+       DEV_GetMsgMgr(pIOMgr->hDevObject, &pMsgMgr);
+       DEV_GetDehMgr(pIOMgr->hDevObject, &hDehMgr);
+       DBC_Require(MEM_IsValidHandle(pChnlMgr, CHNL_MGRSIGNATURE));
+       DBG_Trace(DBG_LEVEL7, "Entering IO_DPC(0x%x)\n", pRefData);
+       /* Check value of interrupt register to ensure it is a valid error */
+       if ((pIOMgr->wIntrVal > DEH_BASE) && (pIOMgr->wIntrVal < DEH_LIMIT)) {
+               /* notify DSP/BIOS exception */
+               if (hDehMgr)
+                       WMD_DEH_Notify(hDehMgr, DSP_SYSERROR, pIOMgr->wIntrVal);
+
+       }
+       IO_DispatchChnl(pIOMgr, NULL, IO_SERVICE);
+#ifdef CHNL_MESSAGES
+       if (pMsgMgr) {
+               DBC_Require(MEM_IsValidHandle(pMsgMgr, MSGMGR_SIGNATURE));
+               IO_DispatchMsg(pIOMgr, pMsgMgr);
+       }
+#endif
+#ifndef DSP_TRACEBUF_DISABLED
+       if (pIOMgr->wIntrVal & MBX_DBG_CLASS) {
+               /* notify DSP Trace message */
+               if (pIOMgr->wIntrVal & MBX_DBG_SYSPRINTF)
+                       PrintDSPDebugTrace(pIOMgr);
+       }
+#endif
+
+#ifndef DSP_TRACEBUF_DISABLED
+       PrintDSPDebugTrace(pIOMgr);
+#endif
+}
+
+
+/*
+ *  ======== IO_ISR ========
+ *      Main interrupt handler for the shared memory IO manager.
+ *      Calls the WMD's CHNL_ISR to determine if this interrupt is ours, then
+ *      schedules a DPC to dispatch I/O.
+ */
+irqreturn_t IO_ISR(int irq, IN void *pRefData)
+{
+       struct IO_MGR *hIOMgr = (struct IO_MGR *)pRefData;
+       bool fSchedDPC;
+       DBC_Require(irq == INT_MAIL_MPU_IRQ);
+       DBC_Require(MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE));
+       DBG_Trace(DBG_LEVEL3, "Entering IO_ISR(0x%x)\n", pRefData);
+
+       /* Call WMD's CHNLSM_ISR() to see if interrupt is ours, and process. */
+       if (IO_CALLISR(hIOMgr->hWmdContext, &fSchedDPC, &hIOMgr->wIntrVal)) {
+               {
+                       DBG_Trace(DBG_LEVEL3, "IO_ISR %x\n", hIOMgr->wIntrVal);
+                       if (hIOMgr->wIntrVal & MBX_PM_CLASS) {
+                               hIOMgr->dQuePowerMbxVal[hIOMgr->iQuePowerHead] =
+                                       hIOMgr->wIntrVal;
+                               hIOMgr->iQuePowerHead++;
+                               if (hIOMgr->iQuePowerHead >= MAX_PM_REQS)
+                                       hIOMgr->iQuePowerHead = 0;
+
+                               queue_work(bridge_workqueue, &hIOMgr->io_workq);
+                       }
+                       if (hIOMgr->wIntrVal == MBX_DEH_RESET) {
+                               DBG_Trace(DBG_LEVEL6, "*** DSP RESET ***\n");
+                               hIOMgr->wIntrVal = 0;
+                       } else if (fSchedDPC) {
+                               /* PROC-COPY defer i/o  */
+                               DPC_Schedule(hIOMgr->hDPC);
+                       }
+               }
+       } else
+               /* Ensure that, if WMD didn't claim it, the IRQ is shared. */
+               DBC_Ensure(hIOMgr->fSharedIRQ);
+       return IRQ_HANDLED;
+}
+
+/*
+ *  ======== IO_RequestChnl ========
+ *  Purpose:
+ *      Request chanenel I/O from the DSP. Sets flags in shared memory, then
+ *      interrupts the DSP.
+ */
+void IO_RequestChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl,
+                  u32 iMode, OUT u16 *pwMbVal)
+{
+       struct CHNL_MGR *pChnlMgr;
+       struct SHM *sm;
+       DBC_Require(pChnl != NULL);
+       DBC_Require(pwMbVal != NULL);
+       pChnlMgr = pIOMgr->hChnlMgr;
+       sm = pIOMgr->pSharedMem;
+       if (iMode == IO_INPUT) {
+               /*  Assertion fires if CHNL_AddIOReq() called on a stream
+                * which was cancelled, or attached to a dead board: */
+               DBC_Assert((pChnl->dwState == CHNL_STATEREADY) ||
+                         (pChnl->dwState == CHNL_STATEEOS));
+               /* Indicate to the DSP we have a buffer available for input: */
+               IO_OrValue(pIOMgr->hWmdContext, struct SHM, sm, hostFreeMask,
+                         (1 << pChnl->uId));
+               *pwMbVal = MBX_PCPY_CLASS;
+       } else if (iMode == IO_OUTPUT) {
+               /*  This assertion fails if CHNL_AddIOReq() was called on a
+                * stream which was cancelled, or attached to a dead board: */
+               DBC_Assert((pChnl->dwState & ~CHNL_STATEEOS) ==
+                         CHNL_STATEREADY);
+               /* Record the fact that we have a buffer available for
+                * output: */
+               pChnlMgr->dwOutputMask |= (1 << pChnl->uId);
+       } else {
+               DBC_Assert(iMode);      /* Shouldn't get here. */
+       }
+}
+
+/*
+ *  ======== IO_Schedule ========
+ *      Schedule DPC for IO.
+ */
+void IO_Schedule(struct IO_MGR *pIOMgr)
+{
+       DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE));
+
+       DPC_Schedule(pIOMgr->hDPC);
+}
+
+/*
+ *  ======== FindReadyOutput ========
+ *      Search for a host output channel which is ready to send.  If this is
+ *      called as a result of servicing the DPC, then implement a round
+ *      robin search; otherwise, this was called by a client thread (via
+ *      IO_Dispatch()), so just start searching from the current channel id.
+ */
+static u32 FindReadyOutput(struct CHNL_MGR *pChnlMgr,
+                            struct CHNL_OBJECT *pChnl, u32 dwMask)
+{
+       u32 uRetval = OUTPUTNOTREADY;
+       u32 id, startId;
+       u32 shift;
+
+       id = (pChnl != NULL ? pChnl->uId : (pChnlMgr->dwLastOutput + 1));
+       id = ((id == CHNL_MAXCHANNELS) ? 0 : id);
+       DBC_Assert(id < CHNL_MAXCHANNELS);
+       if (dwMask) {
+               shift = (1 << id);
+               startId = id;
+               do {
+                       if (dwMask & shift) {
+                               uRetval = id;
+                               if (pChnl == NULL)
+                                       pChnlMgr->dwLastOutput = id;
+
+                               break;
+                       }
+                       id = id + 1;
+                       id = ((id == CHNL_MAXCHANNELS) ? 0 : id);
+                       shift = (1 << id);
+               } while (id != startId);
+       }
+       DBC_Ensure((uRetval == OUTPUTNOTREADY) || (uRetval < CHNL_MAXCHANNELS));
+       return uRetval;
+}
+
+/*
+ *  ======== InputChnl ========
+ *      Dispatch a buffer on an input channel.
+ */
+static void InputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl,
+                     u32 iMode)
+{
+       struct CHNL_MGR *pChnlMgr;
+       struct SHM *sm;
+       u32 chnlId;
+       u32 uBytes;
+       struct CHNL_IRP *pChirp = NULL;
+       u32 dwArg;
+       bool fClearChnl = false;
+       bool fNotifyClient = false;
+
+       sm = pIOMgr->pSharedMem;
+       pChnlMgr = pIOMgr->hChnlMgr;
+
+       DBG_Trace(DBG_LEVEL3, "> InputChnl\n");
+
+       /* Attempt to perform input.... */
+       if (!IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, inputFull))
+               goto func_end;
+
+       uBytes = IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, inputSize) *
+                           pChnlMgr->uWordSize;
+       chnlId = IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, inputId);
+       dwArg = IO_GetLong(pIOMgr->hWmdContext, struct SHM, sm, arg);
+       if (chnlId >= CHNL_MAXCHANNELS) {
+               /* Shouldn't be here: would indicate corrupted SHM. */
+               DBC_Assert(chnlId);
+               goto func_end;
+       }
+       pChnl = pChnlMgr->apChannel[chnlId];
+       if ((pChnl != NULL) && CHNL_IsInput(pChnl->uMode)) {
+               if ((pChnl->dwState & ~CHNL_STATEEOS) == CHNL_STATEREADY) {
+                       if (!pChnl->pIORequests)
+                               goto func_end;
+                       /* Get the I/O request, and attempt a transfer:  */
+                       pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->
+                                pIORequests);
+                       if (pChirp) {
+                               pChnl->cIOReqs--;
+                               DBC_Assert(pChnl->cIOReqs >= 0);
+                               /* Ensure we don't overflow the client's
+                                * buffer: */
+                               uBytes = min(uBytes, pChirp->cBytes);
+                               /* Transfer buffer from DSP side: */
+                               uBytes = ReadData(pIOMgr->hWmdContext,
+                                               pChirp->pHostSysBuf,
+                                               pIOMgr->pInput, uBytes);
+                               pChnl->cBytesMoved += uBytes;
+                               pChirp->cBytes = uBytes;
+                               pChirp->dwArg = dwArg;
+                               pChirp->status = CHNL_IOCSTATCOMPLETE;
+                               DBG_Trace(DBG_LEVEL7, "Input Chnl:status= 0x%x "
+                                        "\n", *((RMS_WORD *)(pChirp->
+                                        pHostSysBuf)));
+                               if (uBytes == 0) {
+                                       /* This assertion fails if the DSP
+                                        * sends EOS more than once on this
+                                        * channel: */
+                                       DBC_Assert(!(pChnl->dwState &
+                                                 CHNL_STATEEOS));
+                                        /* Zero bytes indicates EOS. Update
+                                         * IOC status for this chirp, and also
+                                         * the channel state: */
+                                       pChirp->status |= CHNL_IOCSTATEOS;
+                                       pChnl->dwState |= CHNL_STATEEOS;
+                                       /* Notify that end of stream has
+                                        * occurred */
+                                       NTFY_Notify(pChnl->hNtfy,
+                                                  DSP_STREAMDONE);
+                                       DBG_Trace(DBG_LEVEL7, "Input Chnl NTFY "
+                                                "chnl = 0x%x\n", pChnl);
+                               }
+                               /* Tell DSP if no more I/O buffers available: */
+                               if (!pChnl->pIORequests)
+                                       goto func_end;
+                               if (LST_IsEmpty(pChnl->pIORequests)) {
+                                       IO_AndValue(pIOMgr->hWmdContext,
+                                                  struct SHM, sm, hostFreeMask,
+                                                  ~(1 << pChnl->uId));
+                               }
+                               fClearChnl = true;
+                               fNotifyClient = true;
+                       } else {
+                               /* Input full for this channel, but we have no
+                                * buffers available.  The channel must be
+                                * "idling". Clear out the physical input
+                                * channel.  */
+                               fClearChnl = true;
+                       }
+               } else {
+                       /* Input channel cancelled:  clear input channel.  */
+                       fClearChnl = true;
+               }
+       } else {
+               /* DPC fired after host closed channel: clear input channel. */
+               fClearChnl = true;
+       }
+       if (fClearChnl) {
+               /* Indicate to the DSP we have read the input: */
+               IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, inputFull, 0);
+               CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS);
+       }
+       if (fNotifyClient) {
+               /* Notify client with IO completion record:  */
+               NotifyChnlComplete(pChnl, pChirp);
+       }
+func_end:
+       DBG_Trace(DBG_LEVEL3, "< InputChnl\n");
+}
+
+/*
+ *  ======== InputMsg ========
+ *      Copies messages from shared memory to the message queues.
+ */
+static void InputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr)
+{
+       u32 uMsgs;
+       u32 i;
+       u8 *pMsgInput;
+       struct MSG_QUEUE *hMsgQueue;
+       struct MSG_FRAME *pMsg;
+       struct MSG_DSPMSG msg;
+       struct MSG *pCtrl;
+       u32 fInputEmpty;
+       u32 addr;
+
+       pCtrl = pIOMgr->pMsgInputCtrl;
+       /* Get the number of input messages to be read. */
+       fInputEmpty = IO_GetValue(pIOMgr->hWmdContext, struct MSG, pCtrl,
+                                bufEmpty);
+       uMsgs = IO_GetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, size);
+       if (fInputEmpty || uMsgs >= hMsgMgr->uMaxMsgs)
+               return;
+
+       pMsgInput = pIOMgr->pMsgInput;
+       for (i = 0; i < uMsgs; i++) {
+               /* Read the next message */
+               addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->msg.dwCmd);
+               msg.msg.dwCmd = ReadExt32BitDspData(pIOMgr->hWmdContext, addr);
+               addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->msg.dwArg1);
+               msg.msg.dwArg1 = ReadExt32BitDspData(pIOMgr->hWmdContext, addr);
+               addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->msg.dwArg2);
+               msg.msg.dwArg2 = ReadExt32BitDspData(pIOMgr->hWmdContext, addr);
+               addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->dwId);
+               msg.dwId = ReadExt32BitDspData(pIOMgr->hWmdContext, addr);
+               pMsgInput += sizeof(struct MSG_DSPMSG);
+               if (!hMsgMgr->queueList)
+                       goto func_end;
+
+               /* Determine which queue to put the message in */
+               hMsgQueue = (struct MSG_QUEUE *)LST_First(hMsgMgr->queueList);
+               DBG_Trace(DBG_LEVEL7, "InputMsg RECVD: dwCmd=0x%x dwArg1=0x%x "
+                        "dwArg2=0x%x dwId=0x%x \n", msg.msg.dwCmd,
+                        msg.msg.dwArg1, msg.msg.dwArg2, msg.dwId);
+                /*  Interrupt may occur before shared memory and message
+                *  input locations have been set up. If all nodes were
+                *  cleaned up, hMsgMgr->uMaxMsgs should be 0.  */
+               if (hMsgQueue && uMsgs > hMsgMgr->uMaxMsgs)
+                       goto func_end;
+
+               while (hMsgQueue != NULL) {
+                       if (msg.dwId == hMsgQueue->dwId) {
+                               /* Found it */
+                               if (msg.msg.dwCmd == RMS_EXITACK) {
+                                       /* The exit message does not get
+                                        * queued */
+                                       /* Call the node exit notification */
+                                       /* Node handle */ /* status */
+                                       (*hMsgMgr->onExit)((HANDLE)hMsgQueue->
+                                               hArg, msg.msg.dwArg1);
+                               } else {
+                                       /* Not an exit acknowledgement, queue
+                                        * the message */
+                                       if (!hMsgQueue->msgFreeList)
+                                               goto func_end;
+                                       pMsg = (struct MSG_FRAME *)LST_GetHead
+                                               (hMsgQueue->msgFreeList);
+                                       if (hMsgQueue->msgUsedList && pMsg) {
+                                               pMsg->msgData = msg;
+                                               LST_PutTail(hMsgQueue->
+                                                     msgUsedList,
+                                                     (struct LST_ELEM *)pMsg);
+                                               NTFY_Notify(hMsgQueue->hNtfy,
+                                                       DSP_NODEMESSAGEREADY);
+                                               SYNC_SetEvent(hMsgQueue->
+                                                       hSyncEvent);
+                                       } else {
+                                               /* No free frame to copy the
+                                                * message into */
+                                               DBG_Trace(DBG_LEVEL7, "NO FREE "
+                                                       "MSG FRAMES, DISCARDING"
+                                                       " MESSAGE\n");
+                                       }
+                               }
+                               break;
+                       }
+
+                       if (!hMsgMgr->queueList || !hMsgQueue)
+                               goto func_end;
+                       hMsgQueue = (struct MSG_QUEUE *)LST_Next(hMsgMgr->
+                                   queueList, (struct LST_ELEM *)hMsgQueue);
+               }
+       }
+       /* Set the post SWI flag */
+       if (uMsgs > 0) {
+               /* Tell the DSP we've read the messages */
+               IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, bufEmpty,
+                          true);
+               IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, postSWI,
+                          true);
+               CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS);
+       }
+func_end:
+       return;
+
+}
+
+/*
+ *  ======== NotifyChnlComplete ========
+ *  Purpose:
+ *      Signal the channel event, notifying the client that I/O has completed.
+ */
+static void NotifyChnlComplete(struct CHNL_OBJECT *pChnl,
+                             struct CHNL_IRP *pChirp)
+{
+       bool fSignalEvent;
+
+       DBC_Require(MEM_IsValidHandle(pChnl, CHNL_SIGNATURE));
+       DBC_Require(pChnl->hSyncEvent != NULL);
+        /*  Note: we signal the channel event only if the queue of IO
+         *  completions is empty.  If it is not empty, the event is sure to be
+         *  signalled by the only IO completion list consumer:
+         *  WMD_CHNL_GetIOC().  */
+       fSignalEvent = LST_IsEmpty(pChnl->pIOCompletions);
+       /* Enqueue the IO completion info for the client: */
+       LST_PutTail(pChnl->pIOCompletions, (struct LST_ELEM *) pChirp);
+       pChnl->cIOCs++;
+       DBC_Assert(pChnl->cIOCs <= pChnl->cChirps);
+       /* Signal the channel event (if not already set) that IO is complete: */
+       if (fSignalEvent)
+               SYNC_SetEvent(pChnl->hSyncEvent);
+
+       /* Notify that IO is complete */
+       NTFY_Notify(pChnl->hNtfy, DSP_STREAMIOCOMPLETION);
+}
+
+/*
+ *  ======== OutputChnl ========
+ *  Purpose:
+ *      Dispatch a buffer on an output channel.
+ */
+static void OutputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl,
+                       u32 iMode)
+{
+       struct CHNL_MGR *pChnlMgr;
+       struct SHM *sm;
+       u32 chnlId;
+       struct CHNL_IRP *pChirp;
+       u32 dwDspFMask;
+
+       pChnlMgr = pIOMgr->hChnlMgr;
+       sm = pIOMgr->pSharedMem;
+       DBG_Trace(DBG_LEVEL3, "> OutputChnl\n");
+       /* Attempt to perform output: */
+       if (IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, outputFull))
+               goto func_end;
+
+       if (pChnl && !((pChnl->dwState & ~CHNL_STATEEOS) == CHNL_STATEREADY))
+               goto func_end;
+
+       /* Look to see if both a PC and DSP output channel are ready: */
+       dwDspFMask = IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm,
+                                dspFreeMask);
+       chnlId = FindReadyOutput(pChnlMgr, pChnl, (pChnlMgr->dwOutputMask &
+                                dwDspFMask));
+       if (chnlId == OUTPUTNOTREADY)
+               goto func_end;
+
+       pChnl = pChnlMgr->apChannel[chnlId];
+       if (!pChnl || !pChnl->pIORequests) {
+               /* Shouldn't get here: */
+               goto func_end;
+       }
+       /* Get the I/O request, and attempt a transfer:  */
+       pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIORequests);
+       if (!pChirp)
+               goto func_end;
+
+       pChnl->cIOReqs--;
+       if (pChnl->cIOReqs < 0 || !pChnl->pIORequests)
+               goto func_end;
+
+       /* Record fact that no more I/O buffers available:  */
+       if (LST_IsEmpty(pChnl->pIORequests))
+               pChnlMgr->dwOutputMask &= ~(1 << chnlId);
+
+       /* Transfer buffer to DSP side: */
+       pChirp->cBytes = WriteData(pIOMgr->hWmdContext, pIOMgr->pOutput,
+                       pChirp->pHostSysBuf, min(pIOMgr->uSMBufSize, pChirp->
+                       cBytes));
+       pChnl->cBytesMoved += pChirp->cBytes;
+       /* Write all 32 bits of arg */
+       IO_SetLong(pIOMgr->hWmdContext, struct SHM, sm, arg, pChirp->dwArg);
+#if _CHNL_WORDSIZE == 2
+       IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputId,
+                  (u16)chnlId);
+       IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputSize,
+                  (u16)(pChirp->cBytes + (pChnlMgr->uWordSize-1)) /
+                  (u16)pChnlMgr->uWordSize);
+#else
+       IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputId, chnlId);
+       IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputSize,
+                  (pChirp->cBytes + (pChnlMgr->uWordSize - 1)) / pChnlMgr->
+                  uWordSize);
+#endif
+       IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputFull, 1);
+       /* Indicate to the DSP we have written the output: */
+       CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS);
+       /* Notify client with IO completion record (keep EOS) */
+       pChirp->status &= CHNL_IOCSTATEOS;
+       NotifyChnlComplete(pChnl, pChirp);
+       /* Notify if stream is done. */
+       if (pChirp->status & CHNL_IOCSTATEOS)
+               NTFY_Notify(pChnl->hNtfy, DSP_STREAMDONE);
+
+func_end:
+       DBG_Trace(DBG_LEVEL3, "< OutputChnl\n");
+}
+/*
+ *  ======== OutputMsg ========
+ *      Copies messages from the message queues to the shared memory.
+ */
+static void OutputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr)
+{
+       u32 uMsgs = 0;
+       u32 i;
+       u8 *pMsgOutput;
+       struct MSG_FRAME *pMsg;
+       struct MSG *pCtrl;
+       u32 fOutputEmpty;
+       u32 val;
+       u32 addr;
+
+       pCtrl = pIOMgr->pMsgOutputCtrl;
+
+       /* Check if output has been cleared */
+       fOutputEmpty = IO_GetValue(pIOMgr->hWmdContext, struct MSG, pCtrl,
+                                 bufEmpty);
+       if (fOutputEmpty) {
+               uMsgs = (hMsgMgr->uMsgsPending > hMsgMgr->uMaxMsgs) ?
+                        hMsgMgr->uMaxMsgs : hMsgMgr->uMsgsPending;
+               pMsgOutput = pIOMgr->pMsgOutput;
+               /* Copy uMsgs messages into shared memory */
+               for (i = 0; i < uMsgs; i++) {
+                       if (!hMsgMgr->msgUsedList) {
+                               DBG_Trace(DBG_LEVEL3, "msgUsedList is NULL\n");
+                               pMsg = NULL;
+                               goto func_end;
+                       } else
+                               pMsg = (struct MSG_FRAME *)LST_GetHead(
+                                       hMsgMgr->msgUsedList);
+                       if (pMsg != NULL) {
+                               val = (pMsg->msgData).dwId;
+                               addr = (u32)&(((struct MSG_DSPMSG *)
+                                       pMsgOutput)->dwId);
+                               WriteExt32BitDspData(pIOMgr->hWmdContext, addr,
+                                                    val);
+                               val = (pMsg->msgData).msg.dwCmd;
+                               addr = (u32)&((((struct MSG_DSPMSG *)
+                                       pMsgOutput)->msg).dwCmd);
+                               WriteExt32BitDspData(pIOMgr->hWmdContext, addr,
+                                                    val);
+                               val = (pMsg->msgData).msg.dwArg1;
+                               addr =
+                                       (u32)&((((struct MSG_DSPMSG *)
+                                       pMsgOutput)->msg).dwArg1);
+                               WriteExt32BitDspData(pIOMgr->hWmdContext, addr,
+                                                   val);
+                               val = (pMsg->msgData).msg.dwArg2;
+                               addr =
+                                       (u32)&((((struct MSG_DSPMSG *)
+                                       pMsgOutput)->msg).dwArg2);
+                               WriteExt32BitDspData(pIOMgr->hWmdContext, addr,
+                                                   val);
+                               pMsgOutput += sizeof(struct MSG_DSPMSG);
+                               if (!hMsgMgr->msgFreeList)
+                                       goto func_end;
+                               LST_PutTail(hMsgMgr->msgFreeList,
+                                          (struct LST_ELEM *) pMsg);
+                               SYNC_SetEvent(hMsgMgr->hSyncEvent);
+                       } else {
+                               DBG_Trace(DBG_LEVEL3, "pMsg is NULL\n");
+                       }
+               }
+
+               if (uMsgs > 0) {
+                       hMsgMgr->uMsgsPending -= uMsgs;
+#if _CHNL_WORDSIZE == 2
+                       IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl,
+                                  size, (u16)uMsgs);
+#else
+                       IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl,
+                                  size, uMsgs);
+#endif
+                       IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl,
+                                  bufEmpty, false);
+                       /* Set the post SWI flag */
+                       IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl,
+                                  postSWI, true);
+                       /* Tell the DSP we have written the output. */
+                       CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS);
+               }
+       }
+func_end:
+       return;
+
+}
+
+/*
+ *  ======== registerSHMSegs ========
+ *  purpose:
+ *      Registers GPP SM segment with CMM.
+ */
+static DSP_STATUS registerSHMSegs(struct IO_MGR *hIOMgr,
+                                struct COD_MANAGER *hCodMan,
+                                u32 dwGPPBasePA)
+{
+       DSP_STATUS status = DSP_SOK;
+       u32 ulShm0_Base = 0;
+       u32 ulShm0_End = 0;
+       u32 ulShm0_RsrvdStart = 0;
+       u32 ulRsrvdSize = 0;
+       u32 ulGppPhys;
+       u32 ulDspVirt;
+       u32 ulShmSegId0 = 0;
+       u32 dwOffset, dwGPPBaseVA, ulDSPSize;
+
+       /* Read address and size info for first SM region.*/
+       /* Get start of 1st SM Heap region */
+       status = COD_GetSymValue(hCodMan, SHM0_SHARED_BASE_SYM, &ulShm0_Base);
+       DBC_Assert(ulShm0_Base != 0);
+       /* Get end of 1st SM Heap region */
+       if (DSP_SUCCEEDED(status)) {
+               /* Get start and length of message part of shared memory */
+               status = COD_GetSymValue(hCodMan, SHM0_SHARED_END_SYM,
+                                        &ulShm0_End);
+               DBC_Assert(ulShm0_End != 0);
+       }
+       /* start of Gpp reserved region */
+       if (DSP_SUCCEEDED(status)) {
+               /* Get start and length of message part of shared memory */
+               status = COD_GetSymValue(hCodMan, SHM0_SHARED_RESERVED_BASE_SYM,
+                                       &ulShm0_RsrvdStart);
+               DBG_Trace(DBG_LEVEL1, "***ulShm0_RsrvdStart  0x%x \n",
+                        ulShm0_RsrvdStart);
+               DBC_Assert(ulShm0_RsrvdStart != 0);
+       }
+       /* Register with CMM */
+       if (DSP_SUCCEEDED(status)) {
+               status = DEV_GetCmmMgr(hIOMgr->hDevObject, &hIOMgr->hCmmMgr);
+               if (DSP_SUCCEEDED(status)) {
+                       status = CMM_UnRegisterGPPSMSeg(hIOMgr->hCmmMgr,
+                                CMM_ALLSEGMENTS);
+                       if (DSP_FAILED(status)) {
+                               DBG_Trace(DBG_LEVEL7, "ERROR - Unable to "
+                                        "Un-Register SM segments \n");
+                       }
+               } else {
+                       DBG_Trace(DBG_LEVEL7, "ERROR - Unable to get CMM "
+                                "Handle \n");
+               }
+       }
+       /* Register new SM region(s) */
+       if (DSP_SUCCEEDED(status) && (ulShm0_End - ulShm0_Base) > 0) {
+               /* calc size (bytes) of SM the GPP can alloc from */
+               ulRsrvdSize = (ulShm0_End - ulShm0_RsrvdStart + 1) * hIOMgr->
+                             uWordSize;
+               DBC_Assert(ulRsrvdSize > 0);
+               /* calc size of SM DSP can alloc from */
+               ulDSPSize = (ulShm0_RsrvdStart - ulShm0_Base) * hIOMgr->
+                       uWordSize;
+               DBC_Assert(ulDSPSize > 0);
+               /*  First TLB entry reserved for Bridge SM use.*/
+               ulGppPhys = hIOMgr->extProcInfo.tyTlb[0].ulGppPhys;
+               /* get size in bytes */
+               ulDspVirt = hIOMgr->extProcInfo.tyTlb[0].ulDspVirt * hIOMgr->
+                       uWordSize;
+                /* Calc byte offset used to convert GPP phys <-> DSP byte
+                 * address.*/
+               if (dwGPPBasePA > ulDspVirt)
+                       dwOffset = dwGPPBasePA - ulDspVirt;
+               else
+                       dwOffset = ulDspVirt - dwGPPBasePA;
+
+               DBC_Assert(ulShm0_RsrvdStart * hIOMgr->uWordSize >= ulDspVirt);
+               /* calc Gpp phys base of SM region */
+               /* Linux - this is actually uncached kernel virtual address*/
+               dwGPPBaseVA = ulGppPhys + ulShm0_RsrvdStart * hIOMgr->uWordSize
+                               - ulDspVirt;
+               /* calc Gpp phys base of SM region */
+               /* Linux - this is the physical address*/
+               dwGPPBasePA = dwGPPBasePA + ulShm0_RsrvdStart * hIOMgr->
+                             uWordSize - ulDspVirt;
+                /* Register SM Segment 0.*/
+               status = CMM_RegisterGPPSMSeg(hIOMgr->hCmmMgr, dwGPPBasePA,
+                        ulRsrvdSize, dwOffset, (dwGPPBasePA > ulDspVirt) ?
+                        CMM_ADDTODSPPA : CMM_SUBFROMDSPPA,
+                        (u32)(ulShm0_Base * hIOMgr->uWordSize),
+                        ulDSPSize, &ulShmSegId0, dwGPPBaseVA);
+               if (DSP_FAILED(status)) {
+                       DBG_Trace(DBG_LEVEL7, "ERROR - Failed to register SM "
+                                "Seg 0 \n");
+               }
+               /* first SM region is segId = 1 */
+               DBC_Assert(ulShmSegId0 == 1);
+       }
+       return status;
+}
+
+/*
+ *  ======== ReadData ========
+ *      Copies buffers from the shared memory to the host buffer.
+ */
+static u32 ReadData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest,
+                    void *pSrc, u32 uSize)
+{
+       memcpy(pDest, pSrc, uSize);
+       return uSize;
+}
+
+/*
+ *  ======== WriteData ========
+ *      Copies buffers from the host side buffer to the shared memory.
+ */
+static u32 WriteData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest,
+                      void *pSrc, u32 uSize)
+{
+       memcpy(pDest, pSrc, uSize);
+       return uSize;
+}
+
+/* ZCPY IO routines. */
+void IO_IntrDSP2(IN struct IO_MGR *pIOMgr, IN u16 wMbVal)
+{
+       CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, wMbVal);
+}
+
+/*
+ *  ======== IO_SHMcontrol ========
+ *      Sets the requested SHM setting.
+ */
+DSP_STATUS IO_SHMsetting(IN struct IO_MGR *hIOMgr, IN enum SHM_DESCTYPE desc,
+                        IN void *pArgs)
+{
+#ifdef CONFIG_BRIDGE_DVFS
+       u32 i;
+       struct dspbridge_platform_data *pdata =
+                               omap_dspbridge_dev->dev.platform_data;
+
+       switch (desc) {
+       case SHM_CURROPP:
+               /* Update the shared memory with requested OPP information */
+               if (pArgs != NULL)
+                       hIOMgr->pSharedMem->oppTableStruct.currOppPt =
+                               *(u32 *)pArgs;
+               else
+                       return DSP_EFAIL;
+               break;
+       case SHM_OPPINFO:
+               /* Update the shared memory with the voltage, frequency,
+                                  min and max frequency values for an OPP */
+               for (i = 0; i <= dsp_max_opps; i++) {
+                       hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].voltage =
+                               vdd1_dsp_freq[i][0];
+                       DBG_Trace(DBG_LEVEL5, "OPP shared memory -voltage: "
+                                "%d\n", hIOMgr->pSharedMem->oppTableStruct.
+                                oppPoint[i].voltage);
+                       hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].
+                               frequency = vdd1_dsp_freq[i][1];
+                       DBG_Trace(DBG_LEVEL5, "OPP shared memory -frequency: "
+                                "%d\n", hIOMgr->pSharedMem->oppTableStruct.
+                                oppPoint[i].frequency);
+                       hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].minFreq =
+                               vdd1_dsp_freq[i][2];
+                       DBG_Trace(DBG_LEVEL5, "OPP shared memory -min value: "
+                                "%d\n", hIOMgr->pSharedMem->oppTableStruct.
+                                 oppPoint[i].minFreq);
+                       hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].maxFreq =
+                               vdd1_dsp_freq[i][3];
+                       DBG_Trace(DBG_LEVEL5, "OPP shared memory -max value: "
+                                "%d\n", hIOMgr->pSharedMem->oppTableStruct.
+                                oppPoint[i].maxFreq);
+               }
+               hIOMgr->pSharedMem->oppTableStruct.numOppPts = dsp_max_opps;
+               DBG_Trace(DBG_LEVEL5, "OPP shared memory - max OPP number: "
+                        "%d\n", hIOMgr->pSharedMem->oppTableStruct.numOppPts);
+               /* Update the current OPP number */
+               if (pdata->dsp_get_opp)
+                       i = (*pdata->dsp_get_opp)();
+               hIOMgr->pSharedMem->oppTableStruct.currOppPt = i;
+               DBG_Trace(DBG_LEVEL7, "OPP value programmed to shared memory: "
+                        "%d\n", i);
+               break;
+       case SHM_GETOPP:
+               /* Get the OPP that DSP has requested */
+               *(u32 *)pArgs = hIOMgr->pSharedMem->oppRequest.rqstOppPt;
+               break;
+       default:
+               break;
+       }
+#endif
+       return DSP_SOK;
+}
+
+/*
+ *  ======== WMD_IO_GetProcLoad ========
+ *      Gets the Processor's Load information
+ */
+DSP_STATUS WMD_IO_GetProcLoad(IN struct IO_MGR *hIOMgr,
+                            OUT struct DSP_PROCLOADSTAT *pProcStat)
+{
+       pProcStat->uCurrLoad = hIOMgr->pSharedMem->loadMonInfo.currDspLoad;
+       pProcStat->uPredictedLoad = hIOMgr->pSharedMem->loadMonInfo.predDspLoad;
+       pProcStat->uCurrDspFreq = hIOMgr->pSharedMem->loadMonInfo.currDspFreq;
+       pProcStat->uPredictedFreq = hIOMgr->pSharedMem->loadMonInfo.predDspFreq;
+
+       DBG_Trace(DBG_LEVEL4, "Curr Load =%d, Pred Load = %d, Curr Freq = %d, "
+                            "Pred Freq = %d\n", pProcStat->uCurrLoad,
+                            pProcStat->uPredictedLoad, pProcStat->uCurrDspFreq,
+                            pProcStat->uPredictedFreq);
+       return DSP_SOK;
+}
+
+#ifndef DSP_TRACEBUF_DISABLED
+void PrintDSPDebugTrace(struct IO_MGR *hIOMgr)
+{
+       u32 ulNewMessageLength = 0, ulGPPCurPointer;
+
+       GT_0trace(dsp_trace_mask, GT_ENTER, "Entering PrintDSPDebugTrace\n");
+
+       while (true) {
+               /* Get the DSP current pointer */
+               ulGPPCurPointer = *(u32 *) (hIOMgr->ulTraceBufferCurrent);
+               ulGPPCurPointer = hIOMgr->ulGppVa + (ulGPPCurPointer -
+                                 hIOMgr->ulDspVa);
+
+               /* No new debug messages available yet */
+               if (ulGPPCurPointer == hIOMgr->ulGPPReadPointer)
+                       break;
+
+               /* Continuous data */
+               else if (ulGPPCurPointer > hIOMgr->ulGPPReadPointer) {
+                       ulNewMessageLength = ulGPPCurPointer - hIOMgr->
+                                            ulGPPReadPointer;
+
+                       memcpy(hIOMgr->pMsg, (char *)hIOMgr->ulGPPReadPointer,
+                              ulNewMessageLength);
+                       hIOMgr->pMsg[ulNewMessageLength] = '\0';
+                       /* Advance the GPP trace pointer to DSP current
+                        * pointer */
+                       hIOMgr->ulGPPReadPointer += ulNewMessageLength;
+                       /* Print the trace messages */
+                       GT_0trace(dsp_trace_mask, GT_1CLASS, hIOMgr->pMsg);
+               }
+               /* Handle trace buffer wraparound */
+               else if (ulGPPCurPointer < hIOMgr->ulGPPReadPointer) {
+                       memcpy(hIOMgr->pMsg, (char *)hIOMgr->ulGPPReadPointer,
+                               hIOMgr->ulTraceBufferEnd -
+                               hIOMgr->ulGPPReadPointer);
+                       ulNewMessageLength = ulGPPCurPointer -
+                               hIOMgr->ulTraceBufferBegin;
+                       memcpy(&hIOMgr->pMsg[hIOMgr->ulTraceBufferEnd -
+                               hIOMgr->ulGPPReadPointer],
+                               (char *)hIOMgr->ulTraceBufferBegin,
+                               ulNewMessageLength);
+                       hIOMgr->pMsg[hIOMgr->ulTraceBufferEnd -
+                               hIOMgr->ulGPPReadPointer +
+                               ulNewMessageLength] = '\0';
+                       /* Advance the GPP trace pointer to DSP current
+                        * pointer */
+                       hIOMgr->ulGPPReadPointer = hIOMgr->ulTraceBufferBegin +
+                                                  ulNewMessageLength;
+                       /* Print the trace messages */
+                       GT_0trace(dsp_trace_mask, GT_1CLASS, hIOMgr->pMsg);
+               }
+       }
+}
+#endif
+
+/*
+ *  ======== PackTraceBuffer ========
+ *      Removes extra nulls from the trace buffer returned from the DSP.
+ *      Works even on buffers that already are packed (null removed); but has
+ *      one bug in that case -- loses the last character (replaces with '\0').
+ *      Continues through conversion for full set of nBytes input characters.
+ *  Parameters:
+ *    lpBuf:            Pointer to input/output buffer
+ *    nBytes:           Number of characters in the buffer
+ *    ulNumWords:       Number of DSP words in the buffer.  Indicates potential
+ *                      number of extra carriage returns to generate.
+ *  Returns:
+ *      DSP_SOK:        Success.
+ *      DSP_EMEMORY:    Unable to allocate memory.
+ *  Requires:
+ *      lpBuf must be a fully allocated writable block of at least nBytes.
+ *      There are no more than ulNumWords extra characters needed (the number of
+ *      linefeeds minus the number of NULLS in the input buffer).
+ */
+#if (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE
+static DSP_STATUS PackTraceBuffer(char *lpBuf, u32 nBytes, u32 ulNumWords)
+{
+       DSP_STATUS status = DSP_SOK;
+       char *lpTmpBuf;
+       char *lpBufStart;
+       char *lpTmpStart;
+       u32 nCnt;
+       char thisChar;
+
+       /* tmp workspace, 1 KB longer than input buf */
+       lpTmpBuf = MEM_Calloc((nBytes + ulNumWords), MEM_PAGED);
+       if (lpTmpBuf == NULL) {
+               DBG_Trace(DBG_LEVEL7, "PackTrace buffer:OutofMemory \n");
+               status = DSP_EMEMORY;
+       }
+
+       if (DSP_SUCCEEDED(status)) {
+               lpBufStart = lpBuf;
+               lpTmpStart = lpTmpBuf;
+               for (nCnt = nBytes; nCnt > 0; nCnt--) {
+                       thisChar = *lpBuf++;
+                       switch (thisChar) {
+                       case '\0':      /* Skip null bytes */
+                       break;
+                       case '\n':      /* Convert \n to \r\n */
+                       /* NOTE: do not reverse order; Some OS */
+                       /* editors control doesn't understand "\n\r" */
+                       *lpTmpBuf++ = '\r';
+                       *lpTmpBuf++ = '\n';
+                       break;
+                       default:        /* Copy in the actual ascii byte */
+                       *lpTmpBuf++ = thisChar;
+                       break;
+                       }
+               }
+               *lpTmpBuf = '\0';    /* Make sure tmp buf is null terminated */
+               /* Cut output down to input buf size */
+               strncpy(lpBufStart, lpTmpStart, nBytes);
+               /*Make sure output is null terminated */
+               lpBufStart[nBytes - 1] = '\0';
+               MEM_Free(lpTmpStart);
+       }
+
+       return status;
+}
+#endif    /* (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE */
+
+/*
+ *  ======== PrintDspTraceBuffer ========
+ *      Prints the trace buffer returned from the DSP (if DBG_Trace is enabled).
+ *  Parameters:
+ *    hDehMgr:          Handle to DEH manager object
+ *                      number of extra carriage returns to generate.
+ *  Returns:
+ *      DSP_SOK:        Success.
+ *      DSP_EMEMORY:    Unable to allocate memory.
+ *  Requires:
+ *      hDehMgr muse be valid. Checked in WMD_DEH_Notify.
+ */
+DSP_STATUS PrintDspTraceBuffer(struct WMD_DEV_CONTEXT *hWmdContext)
+{
+       DSP_STATUS status = DSP_SOK;
+
+#if (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE
+       struct COD_MANAGER *hCodMgr;
+       u32 ulTraceEnd;
+       u32 ulTraceBegin;
+       u32 ulNumBytes = 0;
+       u32 ulNumWords = 0;
+       u32 ulWordSize = 2;
+       CONST u32 uMaxSize = 512;
+       char *pszBuf;
+       u16 *lpszBuf;
+
+       struct WMD_DEV_CONTEXT *pWmdContext = (struct WMD_DEV_CONTEXT *)
+                                               hWmdContext;
+       struct WMD_DRV_INTERFACE *pIntfFxns;
+       struct DEV_OBJECT *pDevObject = (struct DEV_OBJECT *)
+                                       pWmdContext->hDevObject;
+
+       status = DEV_GetCodMgr(pDevObject, &hCodMgr);
+       if (DSP_FAILED(status))
+               GT_0trace(dsp_trace_mask, GT_2CLASS,
+               "PrintDspTraceBuffer: Failed on DEV_GetCodMgr.\n");
+
+       if (DSP_SUCCEEDED(status)) {
+               /* Look for SYS_PUTCBEG/SYS_PUTCEND: */
+               status = COD_GetSymValue(hCodMgr, COD_TRACEBEG, &ulTraceBegin);
+               GT_1trace(dsp_trace_mask, GT_2CLASS,
+                       "PrintDspTraceBuffer: ulTraceBegin Value 0x%x\n",
+                       ulTraceBegin);
+               if (DSP_FAILED(status))
+                       GT_0trace(dsp_trace_mask, GT_2CLASS,
+                               "PrintDspTraceBuffer: Failed on "
+                               "COD_GetSymValue.\n");
+       }
+       if (DSP_SUCCEEDED(status)) {
+               status = COD_GetSymValue(hCodMgr, COD_TRACEEND, &ulTraceEnd);
+               GT_1trace(dsp_trace_mask, GT_2CLASS,
+                       "PrintDspTraceBuffer: ulTraceEnd Value 0x%x\n",
+                       ulTraceEnd);
+               if (DSP_FAILED(status))
+                       GT_0trace(dsp_trace_mask, GT_2CLASS,
+                               "PrintDspTraceBuffer: Failed on "
+                               "COD_GetSymValue.\n");
+       }
+       if (DSP_SUCCEEDED(status)) {
+               ulNumBytes = (ulTraceEnd - ulTraceBegin) * ulWordSize;
+               /*  If the chip type is 55 then the addresses will be
+               *  byte addresses; convert them to word addresses.  */
+               if (ulNumBytes > uMaxSize)
+                       ulNumBytes = uMaxSize;
+
+               /* make sure the data we request fits evenly */
+               ulNumBytes = (ulNumBytes / ulWordSize) * ulWordSize;
+               GT_1trace(dsp_trace_mask, GT_2CLASS, "PrintDspTraceBuffer: "
+                       "ulNumBytes 0x%x\n", ulNumBytes);
+               ulNumWords = ulNumBytes * ulWordSize;
+               GT_1trace(dsp_trace_mask, GT_2CLASS, "PrintDspTraceBuffer: "
+                       "ulNumWords 0x%x\n", ulNumWords);
+               status = DEV_GetIntfFxns(pDevObject, &pIntfFxns);
+       }
+
+       if (DSP_SUCCEEDED(status)) {
+               pszBuf = MEM_Calloc(uMaxSize, MEM_NONPAGED);
+               lpszBuf = MEM_Calloc(ulNumBytes * 2, MEM_NONPAGED);
+               if (pszBuf != NULL) {
+                       /* Read bytes from the DSP trace buffer... */
+                       status = (*pIntfFxns->pfnBrdRead)(hWmdContext,
+                               (u8 *)pszBuf, (u32)ulTraceBegin,
+                               ulNumBytes, 0);
+                       if (DSP_FAILED(status))
+                               GT_0trace(dsp_trace_mask, GT_2CLASS,
+                                       "PrintDspTraceBuffer: "
+                                       "Failed to Read Trace Buffer.\n");
+
+                       if (DSP_SUCCEEDED(status)) {
+                               /* Pack and do newline conversion */
+                               GT_0trace(dsp_trace_mask, GT_2CLASS,
+                                       "PrintDspTraceBuffer: "
+                                       "before pack and unpack.\n");
+                               PackTraceBuffer(pszBuf, ulNumBytes, ulNumWords);
+                               GT_1trace(dsp_trace_mask, GT_1CLASS,
+                                       "DSP Trace Buffer:\n%s\n", pszBuf);
+                       }
+                       MEM_Free(pszBuf);
+                       MEM_Free(lpszBuf);
+               } else {
+                       GT_0trace(dsp_trace_mask, GT_2CLASS,
+                               "PrintDspTraceBuffer: Failed to "
+                               "allocate trace buffer.\n");
+                       status = DSP_EMEMORY;
+               }
+       }
+#endif
+       return status;
+}
+
+void IO_SM_init(void)
+{
+
+       GT_create(&dsp_trace_mask, "DT"); /* DSP Trace Mask */
+
+}