/**********************************************************************
*
*  nvhw.c
*
*  Descripion  - hw access details and common functions needed for channel
*
*  Copyright (c) 2002-2003 NVIDIA Corporation
*
***********************************************************************
*/
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/version.h>
#include <asm/uaccess.h>

#include "nvhw.h"
#include "nvrec.h"
#include "nvwavout.h"
#include "nvspdif.h"

extern int mastervolright;
extern int mastervolleft;
extern int pcmvolright;
extern int pcmvolleft;

extern short SPDIFLeftovers[6];
extern u32   SPDIFLeftoverCount;
extern short AnalogLeftovers[6];
extern u32   AnalogLeftoverCount;

static u32   SRCLeftoverCount = 0;
static short SRCLeftovers[6]  = {0};
static u32   SRCrepcount      = 0;

void Reset_wavoutnode(struct Nvaudio_card *card);
void Reset_spoutnode(struct Nvaudio_card *card);

/**********************************************************************
* allocate DMA buffer, playback and recording buffer
* should be allocated seperately
**********************************************************************/
int alloc_dmabuf(struct Nvaudio_state *state)
{
    void *rawbuf = NULL;
    int order, size;
    struct page *page, *pend;
    struct dmabuf *dmabuf = &state->dmabuffer;

    /* If we don't have any oss frag params, then use our default ones */
    if(dmabuf->ossmaxfrags == 0)
        dmabuf->ossmaxfrags = 4;

    if(dmabuf->ossfragsize == 0)
        dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags;

    size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;

    if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size){
        return 0;
    }

    /* alloc enough to satisfy the oss params */
    for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {

        if ( (PAGE_SIZE<<order) > size )
            continue;

        if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
                           PAGE_SIZE << order,
                           &dmabuf->dma_handle)))
            break;

    }

    if (!rawbuf)
        return -ENOMEM;


#ifdef NV_DEBUG_NORMAL
    printk("Nvaudio: allocated %ld (order = %d) bytes at %p\n",
           PAGE_SIZE << order, order, rawbuf);
#endif

    state->ready     = state->mapped = 0;
    dmabuf->rawbuf   = rawbuf;
    dmabuf->buforder = order;

    /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */

    pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
    for (page = virt_to_page(rawbuf); page <= pend; page++)
        mem_map_reserve(page);

    return 0;
}


/**********************************************************************
* free DMA buffer
**********************************************************************/
void dealloc_dmabuf(struct Nvaudio_state *state)
{
    struct page *page, *pend;
    struct dmabuf *dmabuf = &state->dmabuffer;

    if (dmabuf->rawbuf) {
        /* undo marking the pages as reserved */

        pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
        for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
            mem_map_unreserve(page);

        pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,
                    dmabuf->rawbuf, dmabuf->dma_handle);

#ifdef NV_DEBUG_NORMAL
    printk("Nvaudio: dealloc_dmabuf bytes at %p\n",dmabuf->rawbuf);
#endif

    }
    dmabuf->rawbuf = NULL;
    state->mapped  = state->ready = 0;
}

/**********************************************************************
* function to allocate the new linked list for SRC output
**********************************************************************/
int allocate_SRClist(struct Nvaudio_state *state,unsigned index)
{
    int count = 0, nodecnt = 0, prdsize = 0;
    struct Nvaudio_card* card = state->card;
    struct dmabuf *dmabuf = &state->dmabuffer;

    if(state->mapped) return 0;

    if( ((index == 0 )  && card->outList ) ||
        ((index == 2) && (!card->analogstate) && (card->outList)) ){

        /* delete the existing nodes and add now */
        while(card->outList->nodecount){
            Nvaudio_deletenode(card);
        }

        card->outList->head = NULL;
        count   = (int)(dmabuf->dmasize * card->nCurbuffStep) ;

#ifdef NV_DEBUG_SRC
        printk(" dmasize %d dmafrgsize %d \n", dmabuf->dmasize, dmabuf->fragsize);
#endif
        prdsize = Nvaudio_outbuffersize(card,dmabuf->fragsize);
        count   = (count / prdsize) + 1;

#ifdef NV_DEBUG_SRC
        printk(" Allocate nodes %d of size %d \n", count, prdsize);
#endif
        nodecnt = 0;
        while(nodecnt < count) {
            Nvaudio_allocatenode(card,prdsize);
            nodecnt += 1;
        }
        memset(dmabuf->rawbuf, '\0', dmabuf->dmasize);
    }

    SRCLeftoverCount = 0;
    SRCrepcount      = 0;

    return 0;
}

/**********************************************************************
* function that program the prds for the channels
**********************************************************************/
int prog_dmabuf(struct Nvaudio_state *state,unsigned rec)
{
    struct sg_item *sg;
    unsigned long flags;
    int ret = 0, i = 0, channelnum = 0;
    unsigned fragint = 0;
    unsigned long port = 0;
    struct dmabuf *dmabuf = &state->dmabuffer;

    spin_lock_irqsave(&state->lock, flags);

    if(state->enable & DAC_RUNNING){
        __stop_dac(state);
    }

    if(state->enable & ADC_RUNNING){
        __stop_adc(state);
    }

    if(state->enable & SPDIF_RUNNING) {
        __stop_spdif(state);
    }

    dmabuf->total_bytes = 0;
    dmabuf->count = dmabuf->error = 0;
    dmabuf->swptr = dmabuf->hwptr = 0;
    spin_unlock_irqrestore(&state->lock, flags);

    /* allocate DMA buffer, let alloc_dmabuf determine if we are already
     * allocated well enough or if we should replace the current buffer
     * (assuming one is already allocated, if it isn't, then allocate it).
     */
    if ((ret = alloc_dmabuf(state)))
        return ret;

    dmabuf->dmasize      = PAGE_SIZE << dmabuf->buforder;
    dmabuf->numfrag      = SG_LEN;
    dmabuf->fragsize     = dmabuf->dmasize/dmabuf->numfrag;
    memset(dmabuf->rawbuf, '\0', dmabuf->dmasize);

    /* change the prdsize based on channel number */
    i  = dmabuf->fragsize / (state->card->numchannels << 1);
    i *= state->card->numchannels;
    dmabuf->fragsize     = (i << 1);
    dmabuf->dmasize      = dmabuf->fragsize * dmabuf->numfrag;

    dmabuf->fragsamples  = dmabuf->fragsize >> 1;
    dmabuf->userfragsize = dmabuf->ossfragsize;
    dmabuf->userfrags    = dmabuf->dmasize/dmabuf->ossfragsize;

    if(dmabuf->ossmaxfrags == 4) {
        fragint = 8;
        dmabuf->fragshift = 2;
    } else if (dmabuf->ossmaxfrags == 8) {
        fragint = 4;
        dmabuf->fragshift = 3;
    } else if (dmabuf->ossmaxfrags == 16) {
        fragint = 2;
        dmabuf->fragshift = 4;
    } else {
        fragint = 1;
        dmabuf->fragshift = 5;
    }

#ifdef NV_DEBUG_NORMAL
        printk("Nvaudio: prog_dmabuf, fragint = %x \n",fragint);
#endif

    port = state->card->iobase;

    switch(rec)    {
     case 0:  /*audio out */
        port += PO_BDBAR;
        channelnum = 0;
        break;
     case 1:  /*audio in */
        port += PI_BDBAR;
        channelnum = 1;
        break;
     case 2:  /*spdif out */
        port += SP_BDBAR;
        channelnum = 2;
        break;
    }

    if(! state->card->channel) return ret;

    sg=&state->card->channel[channelnum].sg[0];

    for(i=0;i<dmabuf->numfrag;i++)
    {
        sg->busaddr=(u32)dmabuf->dma_handle+ (dmabuf->fragsize*i);

        // the card will always be doing 16bit stereo
        sg->control=dmabuf->fragsamples;

        sg->control|=CON_BUFPAD;

        // set us up to get IOC interrupts as often as needed to
        // satisfy numfrag requirements, no more
        if( ((i+1) % fragint) == 0) {
            sg->control|=CON_IOC;
        }

        sg++;
    }

    spin_lock_irqsave(&state->lock, flags);
    outb(2, port+OFF_CR);         /* reset DMA machine */
    while(inb(port+OFF_CR)&0x02); /* wait until reset done */
    outl((u32)(state->card->chandma + (channelnum * sizeof(struct Nvaudio_channel))),
                port+OFF_BDBAR);
    spin_unlock_irqrestore(&state->lock, flags);

    /* set the ready flag for the dma buffer */
    state->ready = 1;

#ifdef NV_DEBUG_NORMAL
    printk("Nvaudio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, "
           "fragsize = %d dmasize = %d\n",
           state->rate, state->fmt, dmabuf->numfrag,
           dmabuf->fragsize, dmabuf->dmasize);
#endif

    allocate_SRClist(state,rec);
    return 0;
}

/**********************************************************************
* Write AC97 codec registers
* TO DO :- check whether to use card spinlock on accessing codec registers
**********************************************************************/
u16 Nvaudio_ac97_get(struct ac97_codec *dev, u8 reg)
{
    struct Nvaudio_card *card = dev->private_data;
    int count  = 100;
    u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));

    while(count-- && (inb(card->iobase + CAS) & 1))
        udelay(1);

    return inw(card->ac97base + reg_set);
}

void Nvaudio_ac97_set(struct ac97_codec *dev, u8 reg, u16 data)
{
    struct Nvaudio_card *card = dev->private_data;
    int count  = 100;
    u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));

    while(count-- && (inb(card->iobase + CAS) & 1))
        udelay(1);
    outw(data, card->ac97base + reg_set);
}

/**********************************************************************
* Function to recalculate the free space based on the downmix info
* check whether we need this
**********************************************************************/
int Nvaudio_Recalfreespace(struct Nvaudio_card* card,int cnt)
{
    int result = 0;
    unsigned int i_glob_cnt = 0;
    switch( card->numchannels) {
        case CHANNEL_51:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    result = cnt + (((cnt >> 1) / CHANNEL_51) * ( 4 * sizeof(short)));
                    break;
                case NV_SPKR_QUAD:
                    result = cnt + (((cnt >> 1) / CHANNEL_51) * ( 2 * sizeof(short)));
                    break;
                case NV_SPKR_51:
                    result = cnt;
                    break;
            }
            break;
        case CHANNEL_QUAD:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    result = cnt + (((cnt >> 1) / CHANNEL_QUAD) *( 2 * sizeof(short)));
                    break;
                case NV_SPKR_QUAD:
                    result = cnt;
                    break;
                case NV_SPKR_51:
                    result = cnt;
                    break;
            }
            break;
        case CHANNEL_STEREO:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    result = cnt;
                    break;
                case NV_SPKR_QUAD:
                    i_glob_cnt = inl(card->iobase + GLOB_CNT);
                    outl((i_glob_cnt & 0xcfffff) | 0x100000,card->iobase + GLOB_CNT);
                    result = cnt >> 1;
                    break;
                case NV_SPKR_51:
                    i_glob_cnt = inl(card->iobase + GLOB_CNT);
                    outl((i_glob_cnt & 0xcfffff) | 0x100000,card->iobase + GLOB_CNT);
                    result = cnt >> 1;
                    break;
            }
            break;
    }

    return result;

}

/**********************************************************************
* Function to get the swap info for micin and linein
**********************************************************************/
int Nvaudio_getswapinfo(struct Nvaudio_card *card, int index)
{
	int regvalue = 0, found = 0;
	if(card->codecType == REALTEK_CODEC) {
	    regvalue = inw(card->ac97base + REALTEK_MULTI_CHANNEL);
		switch(index) {
			case SWAP_LINEIN:
				regvalue &= REALTEK_SWAP_LINEIN;
				break;
			case SWAP_MICIN:
				regvalue &= REALTEK_SWAP_MICIN;
				break;
		}	
		if(regvalue) found = 1;
	}
	return found;
}

/**********************************************************************
* Function to set the swap info for micin and linein
**********************************************************************/
int Nvaudio_setswapinfo(struct Nvaudio_card *card, int index, int value)
{
	int regvalue = 0;
	if(card->codecType == REALTEK_CODEC) {
		regvalue = inw(card->ac97base + REALTEK_MULTI_CHANNEL);
		switch(index) {
		    case SWAP_LINEIN:
                regvalue &= ~REALTEK_SWAP_LINEIN;
			    if(value) {
				    regvalue |= REALTEK_SWAP_LINEIN;
			    }
			    break;
		    case SWAP_MICIN:
                regvalue &= ~REALTEK_SWAP_MICIN;
			    if(value) {
				    regvalue |= REALTEK_SWAP_MICIN;
			    }
			    break;
        }
        outw(regvalue,card->ac97base + REALTEK_MULTI_CHANNEL);
	}
    return 0;
}

/**********************************************************************
* Function to set the bus master properly
* check whether to mute & unmute during this process to avoid clippings
**********************************************************************/
int Nvaudio_setbusmaster(struct Nvaudio_card* card,int index, int value)
{
    int ret = -1;
    unsigned long flags = 0;
    struct Nvaudio_state *state = NULL;
    struct dmabuf *dmabuf       = NULL;

    switch(index) {
        case ANALOG_OUT:
            state = card->wavout;
            ret   = 0;
            if(state) {
                spin_lock_irqsave(&state->lock, flags);
            }
            card->analogstate = value;
            if(value && state) {
                __start_dac(state);
            }
            if(!value && state )  {
                dmabuf = &state->dmabuffer;
                __stop_dac(state);
                mdelay(100);
                Reset_wavoutnode(card);
                memset(dmabuf->rawbuf, '\0', dmabuf->dmasize);
            }
            if(state) {
                spin_unlock_irqrestore(&state->lock, flags);
            }
            break;
        case DIGITAL_OUT:
            state = card->spout;
            ret   = 0;
            if(state) {
                spin_lock_irqsave(&state->lock, flags);
            }
            card->digitalstate = value;
            if(value && state) {
                __start_spdif(state);
            }
            if(!value && state) {
                dmabuf = &state->dmabuffer;
                __stop_spdif(state);
                mdelay(100);
                Reset_spoutnode(card);
                memset(dmabuf->rawbuf, '\0', dmabuf->dmasize);

            }
            if(state) {
                spin_unlock_irqrestore(&state->lock, flags);
            }
            break;
    }

    return ret;
}

/**********************************************************************
* Function to  pass the hardware information
**********************************************************************/
int Nvaudio_getinfo(struct Nvaudio_card* card, int index, char* name)
{
    int value = 0;
    char  buf[20] = {0};
    switch(index) {
        case LINUX_INFO:
            value = LINUX_VERSION_CODE;
            break;
        case DRIVER_INFO:
            {
                sprintf(buf,"%s",DRIVERVER);
                strcpy(name,buf);
            }
            break;
        case CODEC_INFO:
            value = card->codecType;
            break;
    }
    return value;
}

/**********************************************************************
* Function to  set the mute for the mixer
**********************************************************************/
int Nvaudio_set_mixermute(struct Nvaudio_card* card,int regIndex, int value)
{
    int index = 0, regindex = AC97_MASTER_VOL_STEREO;
    struct ac97_codec *codec    = card->ac97_codec[0];
    struct ac97_codec *seccodec = card->ac97_codec[1];
    int scale = 1 << codec->bit_resolution;
    int adireg74 = 0;

    if(card->codecType == ADI_CODEC) {
        /* read register 0x74 */
        adireg74 = Nvaudio_ac97_get(codec,AC97_ADI_MULTICHN);
    }

    switch(regIndex) {
        case NV_MASTER_MUTE:
            /* set the mute for all */
            for(index  = PREMIX_LEFT; index < (PREMIX_SUB + 1); index++ )  {
                if((index == PREMIX_LEFT) || (index == PREMIX_RIGHT)
                    ||(index == PREMIX_RLEFT) || (index == PREMIX_SUB)) {
                    card->premix_volume[index] &= ~(AC97_MUTE);
                    if(value) {
                        card->premix_volume[index] |= AC97_MUTE;
                    }
                }else {
                    card->premix_volume[index] &= ~(PREMIX_MUTE);
                    if(value) {
                        card->premix_volume[index] |= PREMIX_MUTE;
                    }
                }
                if((index == PREMIX_RLEFT) || (index == PREMIX_RRIGHT)){
                    if(card->codecType == SIGMATEL_CODEC) {
                        codec  = seccodec;
                    }else if(card->codecType == ADI_CODEC) {
                        Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_SURRSPKRS);
                        regindex = AC97_HEADPHONE_VOL;
                    }else {
                        regindex = AC97_SURROUND_MASTER;
                    }
                }else if((index == PREMIX_CENTER) || (index == PREMIX_SUB)) {
                    if(card->codecType == SIGMATEL_CODEC) {
                        codec    = seccodec;
                        regindex = AC97_SURROUND_MASTER;
                    }else if(card->codecType == ADI_CODEC) {
                        Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_CENLFESPKRS);
                        regindex = AC97_HEADPHONE_VOL;
                    }else {
                        regindex = AC97_CENTER_LFE_MASTER;
                    }
                }

                if((index == PREMIX_RLEFT) || (index == PREMIX_CENTER)) {
                    card->premix_volume[index + 1] = card->premix_volume[index];
                }
                else if((index == PREMIX_RRIGHT) || (index == PREMIX_SUB)) {
                    card->premix_volume[index - 1] = card->premix_volume[index];
                }

                if((card->codecType == ADI_CODEC) &&
                    ((index == PREMIX_LEFT) || (index == PREMIX_RIGHT))) {
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_FRONTSPKRS);
                }
                Nvaudio_ac97_set(codec,regindex,card->premix_volume[index]);
            }
            card->mastermute = value;
            if(value) {
                mastervolright = 0;
                mastervolleft  = 0;
            }else {
                mastervolright =    card->premix_volume[index] & 0xff;
                mastervolleft  =    (card->premix_volume[index] >> 8) & 0xff;
                mastervolright = 100 - ( (mastervolright * 100) / scale );
                mastervolleft  = 100 - ( (mastervolleft * 100) / scale );
            }
            break;
    }

    if(card->codecType == ADI_CODEC) {
        /* setback  register 0x74 */
        Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,adireg74);
    }
    mdelay(10);
    return 0;
}

/**********************************************************************
* Function to  get the mute for the mixer
**********************************************************************/
int Nvaudio_get_mixermute(struct Nvaudio_card* card,int regIndex)
{
    int mute = FALSE, index = 0;
    switch(regIndex) {
        case NV_MASTER_MUTE:
            /* check all the premix volume registers and set accordingly */
            for(index  = PREMIX_LEFT; index < (PREMIX_SUB + 1); index++ )  {
                if((index == PREMIX_LEFT) || (index == PREMIX_RIGHT)
                    ||(index == PREMIX_RLEFT) || (index == PREMIX_SUB)){
                    if(card->premix_volume[index] & (AC97_MUTE))
                        mute = TRUE;
                }else {
                    if(card->premix_volume[index] &(PREMIX_MUTE))
                        mute = TRUE;
                }
                if(mute == FALSE) break;
            }
            break;
    }

    return mute;
}

/**********************************************************************
* Get the premix volume registers
**********************************************************************/
int Nvaudio_getpremix_volume(struct Nvaudio_card* card, int premixreg, int *pmute)
{
    int regvalue = 0;
    struct ac97_codec *codec    = card->ac97_codec[0];
    struct ac97_codec *seccodec = card->ac97_codec[1];
    int scale = 1 << codec->bit_resolution;
    int adireg74 = 0;

    *pmute = FALSE;

    if(card->codecType == ADI_CODEC) {
        /* read register 0x74 */
        adireg74 = Nvaudio_ac97_get(codec,AC97_ADI_MULTICHN);
    }

    switch (premixreg){
        case PREMIX_LEFT:
        case PREMIX_RIGHT:
            if(card->codecType == ADI_CODEC) {
                Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_FRONTSPKRS);
            }
            regvalue = Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO);
            card->premix_volume[premixreg] = regvalue;
            if((regvalue & AC97_MUTE) == AC97_MUTE){
                *pmute   = TRUE;
                regvalue &= ~(AC97_MUTE);
            }
            break;
        case PREMIX_RLEFT:
        case PREMIX_RRIGHT:
            if((card->codecType  != SIGMATEL_CODEC) &&
                (card->codecType != ADI_CODEC)) {
                regvalue = Nvaudio_ac97_get(codec,AC97_SURROUND_MASTER);
                card->premix_volume[premixreg] = regvalue;
                if(premixreg == PREMIX_RRIGHT){
                    if(regvalue & PREMIX_MUTE){
                        *pmute   = TRUE;
                        regvalue &= ~(PREMIX_MUTE);
                    }
                }else{
                    if((regvalue >> 8) & PREMIX_MUTE){
                        *pmute   = TRUE;
                        regvalue &= ~(AC97_MUTE);
                    }
                }
            }else {
                if(card->codecType == SIGMATEL_CODEC) {
                    regvalue = Nvaudio_ac97_get(seccodec,AC97_MASTER_VOL_STEREO);
                }else if(card->codecType == ADI_CODEC) {
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_SURRSPKRS);
                    regvalue = Nvaudio_ac97_get(codec,AC97_HEADPHONE_VOL);
                }
                card->premix_volume[premixreg] = regvalue;
                if((regvalue & AC97_MUTE) == AC97_MUTE){
                    *pmute   = TRUE;
                    regvalue &= ~(AC97_MUTE);
                }
            }
            break;
        case PREMIX_CENTER:
        case PREMIX_SUB:
            switch(card->codecType) {
                case SIGMATEL_CODEC:
                    regvalue = Nvaudio_ac97_get(seccodec,AC97_SURROUND_MASTER);
                    break;
                case ADI_CODEC:
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_CENLFESPKRS);
                    regvalue = Nvaudio_ac97_get(codec,AC97_HEADPHONE_VOL);
                    break;
                default:
                    regvalue = Nvaudio_ac97_get(codec,AC97_CENTER_LFE_MASTER);
                    break;
            }
            card->premix_volume[premixreg] = regvalue;
            if(card->codecType != ADI_CODEC) {
                if(premixreg == PREMIX_CENTER){
                    if(regvalue & PREMIX_MUTE){
                        *pmute   = TRUE;
                        regvalue &= ~(PREMIX_MUTE);
                    }
                }else{
                    if((regvalue >> 8) & PREMIX_MUTE){
                        *pmute   = TRUE;
                        regvalue &= ~(AC97_MUTE);
                    }
                }
            }else {
                if((regvalue & AC97_MUTE) == AC97_MUTE){
                    *pmute   = TRUE;
                    regvalue &= ~(AC97_MUTE);
                }
            }
            break;
    }


    if((premixreg == PREMIX_LEFT) || (premixreg == PREMIX_RLEFT) || (premixreg == PREMIX_SUB)) {
        regvalue &= 0xff00;
        regvalue = regvalue >> 8;
    }
    else {
        regvalue &= 0x00ff;
    }

    regvalue  = 100 - ( (regvalue * 100) / scale );


    if(card->codecType == ADI_CODEC) {
        /* setback  register 0x74 */
        Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,adireg74);
    }
    mdelay(10);
    return regvalue;


}
/**********************************************************************
* Function to  set the regvalue
**********************************************************************/
int set_regvalue(int regvalue, int mask, int newvalue)
{
    int value = (regvalue & mask) | newvalue;
    return value;
}

/**********************************************************************
* Function to  Set the premix volume registers
**********************************************************************/
int Nvaudio_setpremix_volume(struct Nvaudio_card* card, int premixreg, int value)
{
    struct ac97_codec *codec    = card->ac97_codec[0];
    struct ac97_codec *seccodec = card->ac97_codec[1];
    int regvalue = 0, regIndex = 0 , newvalue =0 , mask = 0;
    int adireg74 = 0;

    int scale = 1 << codec->bit_resolution;

    if(card->codecType == ADI_CODEC) {
        /* read register 0x74 */
        adireg74 = Nvaudio_ac97_get(codec,AC97_ADI_MULTICHN);
    }
    if(value != 0) {
        newvalue = ((100 - value) * scale) / 100;
        if (newvalue >= scale)
            newvalue = scale-1;
    }

    switch (premixreg){
        case PREMIX_RIGHT:
            if(card->codecType == ADI_CODEC) {
                Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_FRONTSPKRS);
            }
            regvalue = Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO);
            mask  = 0xff00;
            if(value == 0) {
                /* mute only if left and right are minimum */
                if(((regvalue & AC97_MUTE) == AC97_MUTE) ||
                    ((regvalue & mask) == PREMIX_LEFTMIN)) {
                    newvalue  = AC97_MUTE;
                }  else {
                    newvalue  = PREMIX_RIGHTMIN;
                }
                mask      =  0xffff;
            }else {
                /* check whether master is muted */
                if(!card->mastermute) {
                    regvalue &= ~AC97_MUTE;
                }

            }
            mastervolright = value;
            regIndex = AC97_MASTER_VOL_STEREO;
            break;

        case PREMIX_LEFT:

            if(card->codecType == ADI_CODEC) {
                Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_FRONTSPKRS);
            }

            regvalue = Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO);
            mask     =  0x80ff;
            if(value == 0) {
                /* mute only if left and right are minimum */
                if(((regvalue & AC97_MUTE) == AC97_MUTE) ||
                    ((regvalue & mask) == PREMIX_RIGHTMIN)) {
                    newvalue = PREMIX_MUTE;
                }
                else{
                    newvalue  = PREMIX_MIN;
                }
                mask      =  0xffff;
            }else {
                /* check whether master is muted */
                if(!card->mastermute) {
                    regvalue &= ~AC97_MUTE;
                }
            }
            mastervolleft = value;
            newvalue  = (newvalue << 8);
            regIndex = AC97_MASTER_VOL_STEREO;
            break;

        case PREMIX_RRIGHT:
            if( (card->codecType != SIGMATEL_CODEC) &&
                (card->codecType != ADI_CODEC)) {
                mask = 0xff80;
                regvalue = Nvaudio_ac97_get(codec,AC97_SURROUND_MASTER);
                regIndex = AC97_SURROUND_MASTER;
                if(value == 0 ) {
                    mask     = 0xffff;
                    newvalue  = PREMIX_MUTE;
                }else {
                    /* check whether master is muted */
                    if(!card->mastermute) {
                        regvalue &= ~PREMIX_MUTE;
                    }
                }
            } else {
                if(card->codecType == SIGMATEL_CODEC) {
                    codec    = seccodec;
                    regIndex = AC97_MASTER_VOL_STEREO;
                }else if(card->codecType == ADI_CODEC) {
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_SURRSPKRS);
                    regIndex  = AC97_HEADPHONE_VOL;
                }
                mask = 0xff00;
                regvalue = Nvaudio_ac97_get(codec,regIndex);

                if(value == 0){
                    /* mute only if left and right are minimum */
                    if(((regvalue & AC97_MUTE) == AC97_MUTE) ||
                        ((regvalue & mask) == PREMIX_LEFTMIN)) {
                        newvalue  = AC97_MUTE;
                    }  else {
                        newvalue  = PREMIX_RIGHTMIN;
                    }
                    mask      = 0xffff;
                }else {
                    /* check whether master is muted */
                    if(!card->mastermute) {
                        regvalue &= ~AC97_MUTE;
                    }
                }
            }
            break;

        case PREMIX_RLEFT:
            mask = 0x80ff;
            if( (card->codecType != SIGMATEL_CODEC) &&
                (card->codecType != ADI_CODEC)) {
                regvalue = Nvaudio_ac97_get(codec,AC97_SURROUND_MASTER);
                regIndex = AC97_SURROUND_MASTER;
                if(value == 0 ) {
                    newvalue  = PREMIX_MUTE;
                    mask     = 0xffff;
                }
            } else {
                if(card->codecType == SIGMATEL_CODEC) {
                    codec    = seccodec;
                    regIndex = AC97_MASTER_VOL_STEREO;
                }else if(card->codecType == ADI_CODEC) {
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_SURRSPKRS);
                    regIndex = AC97_HEADPHONE_VOL;
                }
                regvalue = Nvaudio_ac97_get(codec,regIndex);
                if(value == 0 ) {
                 /* mute only if left and right are minimum */
                     if(((regvalue & AC97_MUTE) == AC97_MUTE) ||
                           ((regvalue & mask) == PREMIX_RIGHTMIN)) {
                        newvalue  = PREMIX_MUTE;
                     }  else {
                        newvalue  = PREMIX_MIN;
                     }
                     mask     = 0xffff;
                }

            }
            if(value) {
                /* check whether master is muted */
                if(!card->mastermute) {
                    regvalue &= ~AC97_MUTE;
                }
            }
            newvalue = (newvalue << 8);
            break;

        case PREMIX_CENTER:
            mask = 0xff80;
            switch(card->codecType) {
                case ADI_CODEC:
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_CENLFESPKRS);
                    regIndex = AC97_HEADPHONE_VOL;
                    break;
                case SIGMATEL_CODEC:
                    codec    = seccodec;
                    regIndex = AC97_SURROUND_MASTER;
                    break;
                default:
                    regIndex = AC97_CENTER_LFE_MASTER;
                    break;
            }

            regvalue = Nvaudio_ac97_get(codec,regIndex);

            if(card->codecType != ADI_CODEC) {
                if(value == 0 ) {
                    newvalue = PREMIX_MUTE;
                    mask     = 0xffff;
                }else {
                    /* check whether master is muted */
                    if(!card->mastermute) {
                        regvalue &= ~PREMIX_MUTE;
                    }
                }
            }else {
                mask = 0xff00;
                if(value == 0){
                    /* mute only if left and right are minimum */
                    if(((regvalue & AC97_MUTE) == AC97_MUTE) ||
                        ((regvalue & mask) == PREMIX_LEFTMIN)) {
                        newvalue  = AC97_MUTE;
                    }  else {
                        newvalue  = PREMIX_RIGHTMIN;
                    }
                    mask      = 0xffff;
                }else {
                    /* check whether master is muted */
                    if(!card->mastermute) {
                        regvalue &= ~AC97_MUTE;
                    }
                }
            }

            break;

        case PREMIX_SUB:
            mask = 0x80ff;
            switch(card->codecType) {
                case ADI_CODEC:
                    Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,ADI_CENLFESPKRS);
                    regIndex = AC97_HEADPHONE_VOL;
                    break;
                case SIGMATEL_CODEC:
                    codec    = seccodec;
                    regIndex = AC97_SURROUND_MASTER;
                    break;
                default:
                    regIndex = AC97_CENTER_LFE_MASTER;
                    break;
            }
            regvalue = Nvaudio_ac97_get(codec,regIndex);

            if(card->codecType != ADI_CODEC) {
                if(value == 0 ) {
                    newvalue  = PREMIX_MUTE;
                    mask      = 0xffff;
                }
            }else {
                if(value == 0 ) {
                 /* mute only if left and right are minimum */
                 if(((regvalue & AC97_MUTE) == AC97_MUTE) ||
                       ((regvalue & mask) == PREMIX_RIGHTMIN)) {
                    newvalue  = PREMIX_MUTE;
                 }  else {
                    newvalue  = PREMIX_MIN;
                 }
                 mask     = 0xffff;
                }
            }
            if(value) {
                /* check whether master is muted */
                if(!card->mastermute) {
                    regvalue &= ~AC97_MUTE;
                }
            }
            newvalue  = (newvalue << 8);
            break;
    }

    regvalue = set_regvalue(regvalue,mask,newvalue);

    Nvaudio_ac97_set(codec,regIndex,regvalue);

    /*update both right & left */
    if((premixreg == PREMIX_LEFT) || (premixreg == PREMIX_RLEFT) ||
        (premixreg == PREMIX_CENTER)) {
        card->premix_volume[premixreg] = regvalue;
        card->premix_volume[premixreg + 1] = regvalue;
    }else{
        card->premix_volume[premixreg] = regvalue;
        card->premix_volume[premixreg - 1] = regvalue;
    }

    if(card->codecType == ADI_CODEC) {
        /* setback  register 0x74 */
        Nvaudio_ac97_set(codec,AC97_ADI_MULTICHN,adireg74);
    }
    mdelay(10);
    return 0;

}
/**********************************************************************
* Set the Multichannel Volume registers - temp
* check whether we need this anymore
**********************************************************************/
int Nvaudio_MultiChannel_volume(struct Nvaudio_card* card, int channel)
{
    struct ac97_codec *codec    = card->ac97_codec[0];
    struct ac97_codec *seccodec = NULL;

     switch (card->codecType){
        case ADI_CODEC:
            break;
        case SIGMATEL_CODEC:
            {
                seccodec = card->ac97_codec[1];
                if(seccodec) {
                    if(channel >= CHANNEL_QUAD) {
                         Nvaudio_ac97_set(seccodec,AC97_MASTER_VOL_STEREO,
                            Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO));
                    }

                    if(channel == CHANNEL_51) {
                         Nvaudio_ac97_set(seccodec,AC97_SURROUND_MASTER,
                            Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO));
                    }
                }
            }
            break;
        default:
            if(channel >= CHANNEL_QUAD) {
                Nvaudio_ac97_set(codec,AC97_SURROUND_MASTER,
                    Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO));
                Nvaudio_ac97_set(codec,0x64,
                    Nvaudio_ac97_get(codec,AC97_PCMOUT_VOL));
            }

            if(channel == CHANNEL_51) {
                Nvaudio_ac97_set(codec,AC97_CENTER_LFE_MASTER,
                    Nvaudio_ac97_get(codec,AC97_MASTER_VOL_STEREO));
                Nvaudio_ac97_set(codec,0x66,
                    Nvaudio_ac97_get(codec,AC97_PCMOUT_VOL));
            }

            break;
    }
    return 0;
}

/**********************************************************************
* Function to  set the GLOBAL CTRL register based on Speaker Selection
**********************************************************************/
void Nvaudio_SetSpkrRegister(struct Nvaudio_card* card)
{
    unsigned int   i_glob_cnt;
    int mastermute = 0;
    struct Nvaudio_state *state  = card->wavout;

    if(card->spkrselect == 0 ) return;

    mastermute = card->mastermute;

    if(state && !mastermute)   {
        /* mute */
        Nvaudio_set_mixermute(card,NV_MASTER_MUTE,TRUE);
    }

    i_glob_cnt = inl(card->iobase + GLOB_CNT);

    switch (card->spkrselect) {
        case NV_SPKR_QUAD:
            i_glob_cnt = (i_glob_cnt & 0xcfffff) | 0x100000;
            break;
        case  NV_SPKR_51:
            i_glob_cnt = (i_glob_cnt & 0xcfffff) | 0x200000;
            break;
        case NV_SPKR_STEREO:
            i_glob_cnt = (i_glob_cnt & 0xcfffff);
            break;
        default:
            break;
    }
    /* Do we need to set the Mixer regs here */

    /* Temp hack added for few selection now - will remove later */
    if(state && (((card->numchannels == CHANNEL_QUAD) ||
         (card->numchannels == CHANNEL_STEREO)) && (card->spkrselect == NV_SPKR_51))) {
        /* set to four spkr now - until center channel is created */
        i_glob_cnt = (i_glob_cnt & 0xcfffff) | 0x100000;
    }

    /* For remapped data - set the spkr as stereo now as we donot support software SRC & downmixing for those */
    if(state) {
        if(state->mapped)
            i_glob_cnt = (i_glob_cnt & 0xcfffff);
    }

    outl(i_glob_cnt,card->iobase + GLOB_CNT);

    if(state && !mastermute) {
        mdelay(50);
        Nvaudio_set_mixermute(card,NV_MASTER_MUTE,FALSE);
    }

}

/**********************************************************************
* Function to Store the leftover data for copying
**********************************************************************/
void Nvaudio_StoreLeftOver(int loop, int loopend, short** pSrc, int bSpdif)
{
    int newloop = 0, diff =0;
    short *pSource = NULL;

    pSource = (short *) *pSrc;

    if(loop != loopend) {

        diff     = loopend - loop;

        if(bSpdif) {  // spdif
            newloop  = SPDIFLeftoverCount;
            diff    += SPDIFLeftoverCount;
            for(; newloop < diff; newloop++) {
                SPDIFLeftovers[newloop] = *pSource++;
                SPDIFLeftoverCount += 1;
            }
        }

        else { //Analog
            newloop  = AnalogLeftoverCount;
            diff    += AnalogLeftoverCount;
            for(; newloop < diff; newloop++) {
                AnalogLeftovers[newloop] = *pSource++;
                AnalogLeftoverCount += 1;
            }

        }
    } else  {

        if(bSpdif) {
            SPDIFLeftoverCount = 0;
        }
        else {
            AnalogLeftoverCount = 0;
        }
    }

    //make sure we  update pointer
    *pSrc = pSource;

}

/**********************************************************************
* Function to use the Leftover with Spkrselect as Quad and 2 channel wave
**********************************************************************/
u32 Nvaudio_UseLeftOverToStereo(short** pIn, short** pOut,struct dmabuf *dmabuf, int loopend,int carrylimit, u32* StartIndex)
{
    u32 SampleCnt  = 0;
    int shiftdone  = FALSE, count = 0;
    long left=0,   right=0;
    short *pDest    = NULL, *pSource = NULL;
    short  *pLimit  = NULL, *pStart = NULL;

    pStart  = (short *) dmabuf->rawbuf;
    pLimit  = (short *) (dmabuf->rawbuf + dmabuf->dmasize);

    pSource = (short *) *pIn;
    pDest   = (short *) *pOut;


    if(AnalogLeftoverCount > 0) {
        left = AnalogLeftovers[0];

        switch(AnalogLeftoverCount)
        {
            case 1:
                if(loopend >= (carrylimit - 1)){
                    right = *pSource++;
                    shiftdone = TRUE;
                }
                break;
        }

        if(shiftdone == FALSE) {
            return SampleCnt;
        }

        do {

            *pDest++ = (short)left;
            RECHECK_DMA(pDest,pStart,pLimit);

            *pDest++ = (short)right;
            RECHECK_DMA(pDest,pStart,pLimit);

            count += 1;
        }while (count < 2);


        *StartIndex = carrylimit - AnalogLeftoverCount;
        SampleCnt   = 2;
        AnalogLeftoverCount = 0;

    }

    // Make sure we update the pointers
    *pIn  = pSource;
    *pOut = pDest;

    return SampleCnt;

}

/**********************************************************************
* Function to use the Leftover with Spkrselect as Quad and 5.1 channel
**********************************************************************/
u32 Nvaudio_UseLeftOverToQuad(short** pIn, short** pOut,struct dmabuf *dmabuf, int loopend,int carrylimit, u32* StartIndex, int bSpdif)
{
    u32 SampleCnt  = 0;
    int shiftdone  = FALSE;
    long left=0,right=0,center=0,lfe=0,sleft=0,sright=0;
    short *pDest = NULL, *pSource = NULL;
    float sMix1  = 0.0, sMix2 = 0.0;
    short  *pLimit  = NULL, *pStart = NULL;

    pStart  = (short *) dmabuf->rawbuf;
    pLimit  = (short *) (dmabuf->rawbuf + dmabuf->dmasize);

    pSource = (short *) *pIn;
    pDest   = (short *) *pOut;

    if((AnalogLeftoverCount > 0) && (!bSpdif)) {

        left = AnalogLeftovers[0];

        switch(AnalogLeftoverCount)
        {
            case 1:
                if(loopend >= (carrylimit - 1)) {
                    right  = *pSource++;
                    center = *pSource++;
                    lfe    = *pSource++;
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;

            case 2:
                if(loopend >= (carrylimit - 2)) {
                    right  = AnalogLeftovers[1];
                    center = *pSource++;
                    lfe    = *pSource++;
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 3:
                if(loopend >= (carrylimit - 3)) {
                    right  = AnalogLeftovers[1];
                    center = AnalogLeftovers[2];
                    lfe    = *pSource++;
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 4:
                if(loopend >= (carrylimit - 4)) {
                    right  = AnalogLeftovers[1];
                    center = AnalogLeftovers[2];
                    lfe    = AnalogLeftovers[3];
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 5:
                if(loopend >= (carrylimit - 5)) {
                    right  = AnalogLeftovers[1];
                    center = AnalogLeftovers[2];
                    lfe    = AnalogLeftovers[3];
                    sleft  = AnalogLeftovers[4];
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            default:
                break;

        } // switch

        if(shiftdone == FALSE) {
            return SampleCnt;
        }

        sMix1 = 0.707f * center;
        sMix2 = 0.707f * lfe;
        left   += (long)(sMix1 + sMix2);
        right  += (long)(sMix1 + sMix2);
        sleft  += (long)sMix2;
        sright += (long)sMix2;

        RANGECHECK(left,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(right,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(sleft,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(sright,MINVOLRANGE,MAXVOLRANGE);

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)sleft;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)sright;
        RECHECK_DMA(pDest,pStart,pLimit);

        *StartIndex = carrylimit - AnalogLeftoverCount;
        SampleCnt   = 2;
        AnalogLeftoverCount = 0;
    } // if

    // Make sure we update the pointers
    *pIn  = pSource;
    *pOut = pDest;

    return SampleCnt;

}

/**********************************************************************
* Function to use the left over data
**********************************************************************/
u32 Nvaudio_UseLeftOver(short** pIn, short** pOut,struct dmabuf *dmabuf, int loopend,int carrylimit, u32* StartIndex, int bSpdif, int ToQuad)
{
    u32 SampleCnt  = 0;
    int shiftdone  = FALSE;
    long left=0,right=0,center=0,lfe=0,sleft=0,sright=0;
    short *pDest = NULL, *pSource = NULL;
    short  *pLimit  = NULL, *pStart = NULL;

    pStart  = (short *) dmabuf->rawbuf;
    pLimit  = (short *) (dmabuf->rawbuf + dmabuf->dmasize);

    pSource = (short *) *pIn;
    pDest   = (short *) *pOut;

    if((AnalogLeftoverCount > 0) && (!bSpdif)) {

        left = AnalogLeftovers[0];

        switch(AnalogLeftoverCount)
        {
            case 1:
                if(loopend >= (carrylimit - 1)) {
                    right = *pSource++;
                    if(carrylimit == CHANNEL_51) {
                        center = *pSource++;
                        lfe    = *pSource++;
                    }
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;

            case 2:
                if(loopend >= (carrylimit - 2)) {
                    right  = AnalogLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = *pSource++;
                        lfe    = *pSource++;
                    }
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 3:
                if(loopend >= (carrylimit - 3)) {
                    right  = AnalogLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = AnalogLeftovers[2];
                        lfe    = *pSource++;
                        sleft  = *pSource++;
                    }
                    else if(carrylimit == CHANNEL_QUAD) {
                        sleft = AnalogLeftovers[2];
                    }
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 4:
                if(loopend >= (carrylimit - 4)) {
                    right  = AnalogLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = AnalogLeftovers[2];
                        lfe    = AnalogLeftovers[3];
                        sleft  = *pSource++;
                        sright = *pSource++;
                    }
                    shiftdone = TRUE;
                }
                break;
            case 5:
                if(loopend >= (carrylimit - 5)) {
                    right  = AnalogLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = AnalogLeftovers[2];
                        lfe    = AnalogLeftovers[3];
                        sleft  = AnalogLeftovers[4];
                        sright = *pSource++;
                    }
                    shiftdone = TRUE;
                }
                break;
            default:
                break;

        } // switch

        if(shiftdone == FALSE) {
            return SampleCnt;
        }

        if(ToQuad == NV_SPKR_STEREO) {

            left  = Nvaudio_MixDown(left,center,lfe,sleft);
            right = Nvaudio_MixDown(right,center,lfe,sright);

            RANGECHECK(left,MINVOLRANGE,MAXVOLRANGE);
            RANGECHECK(right,MINVOLRANGE,MAXVOLRANGE);

        }

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);

        *StartIndex = carrylimit - AnalogLeftoverCount;
        SampleCnt = 1;
        AnalogLeftoverCount = 0;

        if(ToQuad == NV_SPKR_51) {
            *pDest++ = (short)center;
            RECHECK_DMA(pDest,pStart,pLimit);
            *pDest++ = (short)lfe;
            RECHECK_DMA(pDest,pStart,pLimit);
            SampleCnt += 1;
        }

        if((ToQuad == NV_SPKR_QUAD) || (ToQuad == NV_SPKR_51)) {
            *pDest++ = (short)sleft;
            RECHECK_DMA(pDest,pStart,pLimit);
            *pDest++ = (short)sright;
            RECHECK_DMA(pDest,pStart,pLimit);
            SampleCnt += 1;
        }

    } // if

    else if( (SPDIFLeftoverCount > 0) && (bSpdif)) {

        left = SPDIFLeftovers[0];

        switch(SPDIFLeftoverCount)
        {
            case 1:
                if(loopend >= (carrylimit - 1)) {
                    right = *pSource++;
                    if(carrylimit == CHANNEL_51) {
                        center = *pSource++;
                        lfe    = *pSource++;
                    }
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;

            case 2:
                if(loopend >= (carrylimit - 2)) {
                    right  = SPDIFLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = *pSource++;
                        lfe    = *pSource++;
                    }
                    sleft  = *pSource++;
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 3:
                if(loopend >= (carrylimit - 3)) {
                    right  = SPDIFLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = SPDIFLeftovers[2];
                        lfe    = *pSource++;
                        sleft  = *pSource++;
                    }
                    else if(carrylimit == CHANNEL_QUAD) {
                        sleft = SPDIFLeftovers[2];
                    }
                    sright = *pSource++;
                    shiftdone = TRUE;
                }
                break;
            case 4:
                if(loopend >= (carrylimit - 4)) {
                    right  = SPDIFLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = SPDIFLeftovers[2];
                        lfe    = SPDIFLeftovers[3];
                        sleft  = *pSource++;
                        sright = *pSource++;
                    }
                    shiftdone = TRUE;
                }
                break;
            case 5:
                if(loopend >= (carrylimit - 5)) {
                    right  = SPDIFLeftovers[1];
                    if(carrylimit == CHANNEL_51) {
                        center = SPDIFLeftovers[2];
                        lfe    = SPDIFLeftovers[3];
                        sleft  = SPDIFLeftovers[4];
                        sright = *pSource++;
                    }
                    shiftdone = TRUE;
                }
                break;
            default:
                break;

        } // switch

        if(shiftdone == FALSE) {
            return SampleCnt;
        }

        left  = Nvaudio_MixDown(left,center,lfe,sleft);
        right = Nvaudio_MixDown(right,center,lfe,sright);

        RANGECHECK(left,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(right,MINVOLRANGE,MAXVOLRANGE);

        /* apply the volume */
        left   = ( left * mastervolleft * pcmvolleft) / 10000;
        right  = ( right * mastervolright * pcmvolright) / 10000;

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);

        *StartIndex = carrylimit - SPDIFLeftoverCount;
        SampleCnt = 1;
        SPDIFLeftoverCount = 0;

    } // if
    // Make sure we update the pointers
    *pIn  = pSource;
    *pOut = pDest;

    return SampleCnt;

}

/**********************************************************************
* Function that helps to mix down the data -
* used for spdif and analog with speaker selection
**********************************************************************/
long Nvaudio_MixDown( long channel, long center, long lfe, long schannel)
{
     float result;
     result  = channel + 0.7f * ( schannel + center + lfe);
     return result;
}

/**********************************************************************
* Function to mix down - QUAD TO STEREO
**********************************************************************/
u32  Nvaudio_MixDownQuadToStereo(const char* pIn,struct Nvaudio_state *state,int cnt, int carrylimit, int bSpdif)
{
    u32 Samplecnt = 0 ,StartIndex = 0;
    long left=0,right=0,sleft=0,sright=0;
    short *pDest    = NULL, *pSource = NULL;
    int loop = 0;

    short  *pLimit  = NULL, *pStart = NULL;
    struct dmabuf *dmabuf         = &state->dmabuffer;

    pDest   = (short *)(dmabuf->rawbuf + dmabuf->swptr);
    pLimit  = (short *)(dmabuf->rawbuf + dmabuf->dmasize);
    pStart  = (short *) dmabuf->rawbuf;
    pSource = (short *) pIn;

    Samplecnt = Nvaudio_UseLeftOver(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex,bSpdif, NV_SPKR_STEREO);

    for(loop= StartIndex;loop <= (cnt - carrylimit);loop += carrylimit) {
        left   = *pSource++;
        right  = *pSource++;
        sleft  = *pSource++;
        sright = *pSource++;

        left  = Nvaudio_MixDown(left,0,0,sleft);
        right = Nvaudio_MixDown(right,0,0,sright);

        RANGECHECK(left,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(right,MINVOLRANGE,MAXVOLRANGE);

        if(bSpdif) {
            /* apply the volume */
            left   = ( left * mastervolleft * pcmvolleft) / 10000;
            right  = ( right * mastervolright * pcmvolright) / 10000;
        }

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);

        Samplecnt += 1;
    }

    //Check for leftovers
    Nvaudio_StoreLeftOver(loop,cnt,&pSource,bSpdif);

    return Samplecnt;
}

/**********************************************************************
* Function to mix down - 5.1 TO STEREO
**********************************************************************/
u32  Nvaudio_MixDown5_1ToStereo(const char* pIn,struct Nvaudio_state *state,int cnt, int carrylimit, int bSpdif)
{
    u32 Samplecnt = 0 ,StartIndex = 0;
    long left=0,right=0,center=0, lfe=0, sleft=0,sright=0;
    short *pDest = NULL, *pSource = NULL;
    int loop = 0;

    short  *pLimit  = NULL, *pStart = NULL;
    struct dmabuf *dmabuf           = &state->dmabuffer;

    pDest   = (short *)(dmabuf->rawbuf + dmabuf->swptr);
    pLimit  = (short *)(dmabuf->rawbuf + dmabuf->dmasize);
    pStart  = (short *) dmabuf->rawbuf;
    pSource = (short *) pIn;


    Samplecnt = Nvaudio_UseLeftOver(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex,bSpdif,NV_SPKR_STEREO);

    for(loop= StartIndex;loop <= (cnt - carrylimit);loop += carrylimit) {
        left   = *pSource++;
        right  = *pSource++;
        center = *pSource++;
        lfe    = *pSource++;
        sleft  = *pSource++;
        sright = *pSource++;

        left  = Nvaudio_MixDown(left,center,lfe,sleft);
        right = Nvaudio_MixDown(right,center,lfe,sright);

        RANGECHECK(left,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(right,MINVOLRANGE,MAXVOLRANGE);

        if(bSpdif) {
            /* apply the volume */
            left   = ( left * mastervolleft * pcmvolleft) / 10000;
            right  = ( right * mastervolright * pcmvolright) / 10000;
        }

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);

        Samplecnt += 1;
    }

    //Check for leftovers
    Nvaudio_StoreLeftOver(loop,cnt,&pSource,bSpdif);

    return Samplecnt;
}

/**********************************************************************
* Function to do normal mix down
**********************************************************************/
u32  Nvaudio_MixNormal(const char* pIn,struct Nvaudio_state *state,int cnt, int carrylimit, int bSpdif)
{
    u32 Samplecnt = 0 ,StartIndex = 0;
    long left=0,right=0;
    short *pDest = NULL, *pSource = NULL;
    int loop = 0;

    short  *pLimit  = NULL, *pStart = NULL;
    struct dmabuf *dmabuf           = &state->dmabuffer;

    pDest   = (short *)(dmabuf->rawbuf + dmabuf->swptr);
    pLimit  = (short *)(dmabuf->rawbuf + dmabuf->dmasize);
    pStart  = (short *) dmabuf->rawbuf;
    pSource = (short *) pIn;

    switch(carrylimit) {
        case CHANNEL_STEREO:
            Samplecnt = Nvaudio_UseLeftOver(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex,bSpdif,NV_SPKR_STEREO);
            break;
        case CHANNEL_QUAD:
            Samplecnt = Nvaudio_UseLeftOver(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex,bSpdif,NV_SPKR_QUAD);
            break;
        case CHANNEL_51:
            Samplecnt = Nvaudio_UseLeftOver(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex,bSpdif,NV_SPKR_51);
            break;
    }

    for(loop= StartIndex;loop <= (cnt - carrylimit);loop += carrylimit) {
        left  = *pSource++;
        right = *pSource++;

        if(bSpdif) {
            /* apply the volume */
            left   = ( left * mastervolleft * pcmvolleft) / 10000;
            right  = ( right * mastervolright * pcmvolright) / 10000;
        }

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);
        Samplecnt += 1;

        if(carrylimit == CHANNEL_51) {
            *pDest++ = *pSource++;
            RECHECK_DMA(pDest,pStart,pLimit);
            *pDest++ = *pSource++;
            RECHECK_DMA(pDest,pStart,pLimit);
            Samplecnt += 1;
        }

        if((carrylimit == CHANNEL_51) || (carrylimit == CHANNEL_QUAD)) {
            *pDest++ = *pSource++;
            RECHECK_DMA(pDest,pStart,pLimit);
            *pDest++ = *pSource++;
            RECHECK_DMA(pDest,pStart,pLimit);
            Samplecnt += 1;
        }

    }

    //Check for leftovers
    Nvaudio_StoreLeftOver(loop,cnt,&pSource,bSpdif);

    return Samplecnt;
}

/**********************************************************************
* Function to mix down - 5.1 TO Quad
**********************************************************************/
u32  Nvaudio_MixDown5_1ToQuad(const char* pIn,struct Nvaudio_state *state,int cnt, int carrylimit, int bSpdif)
{
    u32 Samplecnt = 0 ,StartIndex = 0;
    long left=0,right=0,center=0, lfe=0, sleft=0,sright=0;
    short *pDest = NULL, *pSource = NULL;
    int loop = 0;
    float sMix1 = 0.0, sMix2 = 0.0;

    short  *pLimit  = NULL, *pStart = NULL;
    struct dmabuf *dmabuf           = &state->dmabuffer;

    pDest   = (short *)(dmabuf->rawbuf + dmabuf->swptr);
    pLimit  = (short *)(dmabuf->rawbuf + dmabuf->dmasize);
    pStart  = (short *) dmabuf->rawbuf;
    pSource = (short *) pIn;


    Samplecnt = Nvaudio_UseLeftOverToQuad(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex,bSpdif);

    for(loop= StartIndex;loop <= (cnt - carrylimit);loop += carrylimit) {
        left   = *pSource++;
        right  = *pSource++;
        center = *pSource++;
        lfe    = *pSource++;
        sleft  = *pSource++;
        sright = *pSource++;

        sMix1   = 0.707f * center;
        sMix2   = 0.707f * lfe;
        left   += (long)(sMix1 + sMix2);
        right  += (long)(sMix1 + sMix2);
        sleft  += (long)sMix2;
        sright += (long)sMix2;

        RANGECHECK(left,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(right,MINVOLRANGE,MAXVOLRANGE);

        RANGECHECK(sleft,MINVOLRANGE,MAXVOLRANGE);
        RANGECHECK(sright,MINVOLRANGE,MAXVOLRANGE);

        *pDest++ = (short)left;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)right;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)sleft;
        RECHECK_DMA(pDest,pStart,pLimit);
        *pDest++ = (short)sright;
        RECHECK_DMA(pDest,pStart,pLimit);

        Samplecnt += 2;  // Two samples generated
    }

    //Check for leftovers
    Nvaudio_StoreLeftOver(loop,cnt,&pSource,bSpdif);

    return Samplecnt;
}

/**********************************************************************
* Function to Replicate Stereo To Rears
**********************************************************************/
u32  Nvaudio_ReplicateStereotoRear(const char* pIn,struct Nvaudio_state *state,int cnt,int carrylimit)
{
    u32 Samplecnt = 0, StartIndex = 0;
    long left = 0, right = 0;
    int loop  = 0, count = 0;

    short *pDest   = NULL, *pSource = NULL;
    short *pLimit  = NULL, *pStart  = NULL;
    struct dmabuf *dmabuf           = &state->dmabuffer;

    pSource = (short *) pIn;
    pStart  = (short *) dmabuf->rawbuf;
    pLimit  = (short *)(dmabuf->rawbuf + dmabuf->dmasize);
    pDest   = (short *)(dmabuf->rawbuf + dmabuf->swptr);


    Samplecnt = Nvaudio_UseLeftOverToStereo(&pSource,&pDest,dmabuf,cnt,carrylimit,&StartIndex);

    for(loop= StartIndex;loop <= (cnt - carrylimit);loop += carrylimit) {

        left  = *pSource++;
        right = *pSource++;
        count = 0;

        do{

            *pDest++ = (short)left;
            RECHECK_DMA(pDest,pStart,pLimit);

            *pDest++ = (short)right;
            RECHECK_DMA(pDest,pStart,pLimit);

            count += 1;

        }while(count < 2 );

        Samplecnt += 2;  // Two samples generated
    }

    //Check for leftovers
    Nvaudio_StoreLeftOver(loop,cnt,&pSource,FALSE);

    return Samplecnt;
}

/**********************************************************************
* Function to insert the node to the list
**********************************************************************/
void Nvaudio_Insertnode(struct Nvaudio_card *card, nodePtr node)
{
    if((card->outList) && node){

        node->next     = NULL;
        node->position = 0;

        if(card->outList->head == NULL ) {/* set head and tail the same */
            card->outList->tail       = node;
            card->outList->head       = node;
            card->outList->curnode    = node;
            card->outList->wavoutnode = node;
            card->outList->spoutnode  = node;
        }else {
            card->outList->tail->next = node;
            card->outList->tail = node;
            card->outList->tail->next = card->outList->head;
        }

        card->outList->nodecount += 1;

#ifdef NV_DEBUG_SRC
        printk(" HEAD %p , TAIL %p \n",card->outList->head,card->outList->tail);
#endif
    }
}

/**********************************************************************
* Function to allocate the node and buffer
**********************************************************************/
nodePtr Nvaudio_allocatenode(struct Nvaudio_card *card,int prdsize)
{
    nodePtr newnode   = NULL;
    char * databuffer = NULL;

    /* allocate the outbuffer based on prd size */
    newnode = kmalloc(sizeof(struct ListNode), GFP_KERNEL);
    if(!newnode){
        printk(KERN_INFO "Unable to allocate anymore node \n");
        return 0;
    }
    memset(newnode,0,sizeof(struct ListNode));

    databuffer = kmalloc(prdsize,GFP_KERNEL);
    if(!databuffer) {
        printk(KERN_INFO "Unable to allocate memory for o/p buffer \n");
        kfree(newnode);
        return 0;
    }
    memset(databuffer,'\0',prdsize);

    newnode->data = databuffer;
    newnode->size = prdsize;
    newnode->wavoutused = 1;
    newnode->spoutused  = 1;

    card->outList->newnode = NULL;

    Nvaudio_Insertnode(card, newnode);
    return newnode;
}

/**********************************************************************
* Function to reset the spoutlist contents
**********************************************************************/
void Reset_spoutnode(struct Nvaudio_card *card)
{
    nodePtr curnode = NULL;
    while(card->outList->spoutcount > 0){
        curnode = card->outList->curnode;
        curnode->spoutused = 1;
        card->outList->spoutcount -= 1;
        curnode = curnode->next;
    }

}
/**********************************************************************
* Function to reset the waveoutlist contents
**********************************************************************/
void Reset_wavoutnode(struct Nvaudio_card *card)
{
    nodePtr curnode = NULL;
    while(card->outList->wavoutcount > 0){
        curnode = card->outList->curnode;
        curnode->wavoutused = 1;
        card->outList->wavoutcount -= 1;
        curnode = curnode->next;
    }
}
/**********************************************************************
* Function to reset the list contents
**********************************************************************/
void Nvaudio_resetnode(struct Nvaudio_card *card)
{
    Reset_wavoutnode(card);
    Reset_spoutnode(card);
    card->outList->curnode    = card->outList->head;
    if(card->analogstate)
        card->outList->wavoutnode = card->outList->head;
    if(card->digitalstate)
        card->outList->spoutnode  = card->outList->head;

}

/**********************************************************************
* Function to delete the List contents
**********************************************************************/
nodePtr Nvaudio_deletenode(struct Nvaudio_card *card)
{
    nodePtr curnode   = NULL;
    curnode = card->outList->head;
    if(curnode) {

#ifdef NV_DEBUG_SRC
        printk("delete curnode %p \n", curnode);
#endif
        curnode = curnode->next;
        card->outList->head->next = NULL;
        kfree(card->outList->head->data);
        kfree(card->outList->head);
        card->outList->head = curnode;
        card->outList->nodecount -= 1;
    }

    if(card->outList->nodecount <= 0 ) {
        card->outList->head = NULL;
        card->outList->tail = NULL;
        card->outList->wavoutnode = NULL;
        card->outList->spoutnode  = NULL;
        card->outList->curnode   = NULL;
        card->outList->nodecount = 0;
    }

    if(card->outList->wavoutcount > 0) {
        card->outList->wavoutcount -= 1;
    }

    if(card->outList->spoutcount > 0) {
        card->outList->spoutcount -= 1;
    }
    return card->outList->head;
}

/**********************************************************************
* Function to recalculate the outbuffer Size
**********************************************************************/
int Nvaudio_outbuffersize( struct Nvaudio_card *card, int buffersize)
{
    int newsize = buffersize;
    unsigned int i_glob_cnt = 0;

    switch( card->numchannels) {
        case CHANNEL_51:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    newsize = (newsize >> 1) * card->numchannels;
                    break;
                case NV_SPKR_QUAD:
                    newsize = (newsize / CHANNEL_QUAD) * card->numchannels;
                    break;
                case NV_SPKR_51:
                    break;
            }
            break;
        case CHANNEL_QUAD:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    newsize = (newsize >> 1 ) * card->numchannels;
                    break;
                case NV_SPKR_QUAD:
                case NV_SPKR_51:
                    break;
            }
            break;
        case CHANNEL_STEREO:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                    i_glob_cnt = inl(card->iobase + GLOB_CNT);
                    outl((i_glob_cnt & 0xcfffff) | 0x100000,card->iobase + GLOB_CNT);
                    newsize = newsize >> 1;
                    break;
                case NV_SPKR_51:
                    i_glob_cnt = inl(card->iobase + GLOB_CNT);
                    outl((i_glob_cnt & 0xcfffff) | 0x100000,card->iobase + GLOB_CNT);
                    newsize = newsize >> 1;
                    break;
            }
            break;
    }

    card->curspkr = card->spkrselect;

    return newsize;
}

/**********************************************************************
* Function to set the nodes value correctly
**********************************************************************/
nodePtr Nvaudio_setnode(struct Nvaudio_card *card, nodePtr currentnode)
{
    currentnode->position      = 0;

    if(card->analogstate){
        currentnode->analogposn    = 0;
        card->outList->wavoutcount += 1;
        card->outList->curnode->wavoutused = 0;
    }else{
        card->outList->wavoutnode = currentnode;
    }
    if(card->digitalstate){
        currentnode->digitalposn   = 0;
        card->outList->spoutcount  += 1;
        card->outList->curnode->spoutused  = 0;
    }else{
        card->outList->spoutnode = currentnode;
    }

    card->outList->curnode = card->outList->curnode->next;

    /*if((card->outList->curnode->wavoutused) ||
       (card->outList->curnode->spoutused)) {

         //CHECK what to do here
         //printk(KERN_INFO " NOT CLEARED YET \n");
    }*/

    return card->outList->curnode;
}

/**********************************************************************
* Function to apply Linear SRC
**********************************************************************/
char* Nvaudio_linearSRC(struct Nvaudio_card *card, const char *pInBuffer, int cnt)
{
    int nCurTimeI=0, lIndex=0, lnextIndex=0;
    float nCurTimeF = 0.0f, lFract = 0.0f;

    nodePtr newnode  = NULL;
    short *pOutData  = NULL, *pInData  = NULL;

    /* suppose to be 16bit stereo */
    int prdsize = 0,inbufdone=0, nchannels=0, count=0;
    pInData     = (short *)pInBuffer;

    newnode = card->outList->curnode;
    prdsize = newnode->size;

    if(!newnode) {
       printk(KERN_INFO "newnode failure \n");
       return 0;
    }

    if((!newnode->wavoutused) ||
       (!newnode->spoutused) ) {
        /* check what to do */
        /*printk(KERN_INFO " NOT CLEARED YET \n");*/
    }

    if(SRCrepcount) {

        pOutData = (short *)newnode->data + newnode->position;

        while(SRCrepcount > 0) {

            nCurTimeI = (int)card->nCurTimeInt;
            nCurTimeF = card->nCurTimeInt - nCurTimeI;

            for(nchannels = 0; nchannels < card->numchannels; nchannels++) {

                lFract  = (pInData[nchannels] - SRCLeftovers[nchannels]) * nCurTimeF;

                lFract += SRCLeftovers[nchannels];
                *pOutData++ = (short)lFract;
                newnode->position += 1;

                if( newnode->position >= (prdsize >> 1)) {
                    newnode = Nvaudio_setnode(card,newnode);
                    pOutData = (short *)newnode->data;
                }
            }
            card->nCurTimeInt += card->ncurstepsize;
            SRCrepcount -= 1;
        }
    }


    nCurTimeI = (int)card->nCurTimeInt;
    nCurTimeF = card->nCurTimeInt - nCurTimeI;

    card->nCurTimeInt =  nCurTimeF;
    SRCrepcount = 0;

    pOutData = (short *)newnode->data + newnode->position ;

    while(!inbufdone) {

        nCurTimeI = (int)card->nCurTimeInt;
        nCurTimeF = card->nCurTimeInt - nCurTimeI;

        for(nchannels = 0; nchannels < card->numchannels; nchannels++) {

            lIndex     = (nCurTimeI * card->numchannels) + nchannels;
            lnextIndex = lIndex + card->numchannels;

            if(lIndex >= (cnt >> 1) ) {
                printk("LINDEX out of range \n");
                inbufdone = 1;
                card->outList->newnode = newnode;
                break;
            }

            if(lnextIndex >= (cnt >> 1)) {

                inbufdone = 1;

                if((nchannels > 0 ) &&  (nchannels < 5)) {
                    //printk(KERN_INFO " loop over happended at odd position nchannel %d \n",nchannels);
                }

                SRCrepcount = 1;
                for(count = 1;;count++) {
                    if(nCurTimeI == (int)(card->nCurTimeInt + (count*card->ncurstepsize))) {
                        SRCrepcount += 1;
                    }else{
                        break;
                    }
                }

                /*problem if not channel aligned */
                for(count=0;count < card->numchannels; count++) {

                    SRCLeftovers[count] =  pInData[lIndex];
                    SRCLeftoverCount++;
                    lIndex++;
                    if((lIndex >= (cnt >> 1)) && (count < (card->numchannels - 1 )  )) {
                        printk("LINDEX out of range \n");
                        break;
                    }
                }
                card->outList->newnode      = newnode;
                break;
            }

            lFract  = (pInData[lnextIndex] - pInData[lIndex]) * nCurTimeF;
            lFract += pInData[lIndex];
            *pOutData++ = (short)lFract;
            newnode->position += 1;

            if( newnode->position >= (prdsize >> 1)) {
                newnode = Nvaudio_setnode(card,newnode);
                pOutData = (short *)newnode->data;
            }
        }

        if(!inbufdone)
            card->nCurTimeInt += card->ncurstepsize;

    }
    return (char *)newnode;

}

/**********************************************************************
* Function that help to copy the data to Spdif prds
**********************************************************************/
int Nvaudio_copydatatospprd(struct Nvaudio_card *card,int count)
{
    u32 samplecount = 0;
    unsigned long flags;
    unsigned int swptr = 0;
    int cnt    = 0, loopend = 0;
    struct Nvaudio_state *state = card ? card->spout: 0;
    struct dmabuf *dmabuf       = &state->dmabuffer;
    char  *outbuf   = NULL;
    nodePtr curnode = NULL;

    while(count > 0 ) {

        cnt = count;

        if(card->outList->spoutcount > 0) {

            swptr   = dmabuf->swptr;
            curnode = card->outList->spoutnode;

#ifdef NV_DEBUG_SRC
            printk("curnode the node %p posn %d swptr %d \n ",curnode, curnode->digitalposn, swptr);
#endif

            if(curnode->digitalposn) {
#ifdef NV_DEBUG_SRC
                printk(" POSN HIT %d \n ", curnode->digitalposn);
#endif
                if(cnt >= (curnode->size - curnode->digitalposn)) {
                    cnt = (curnode->size - curnode->digitalposn);
                }
            }else {
                if(cnt >= dmabuf->fragsize)  {
                    cnt =  curnode->size;
                } else {
#ifdef NV_DEBUG_SRC
                    printk(" SIZE CNT LESS than fragsize %d \n", cnt);
#endif

                    if(cnt >= curnode->size) {
                        cnt =  curnode->size;
                        /* stereo replication will result in error here */
                    }
                }
            }

            outbuf  = curnode->data + curnode->digitalposn;

#ifdef NV_DEBUG_SRC
            printk("Nvaudio_write copying from buffer %p count %x to location %p\n",
                    outbuf,cnt,dmabuf->rawbuf + swptr);
#endif

            loopend = cnt  >> 1; /* now we supporting only 16bit Samples*/

            switch(card->numchannels) {
                   case CHANNEL_STEREO:
                        samplecount = Nvaudio_MixNormal(outbuf,state,
                                            loopend, CHANNEL_STEREO, TRUE);
                        break;

                    case CHANNEL_QUAD:
                        samplecount = Nvaudio_MixDownQuadToStereo(outbuf,state,
                                            loopend,CHANNEL_QUAD,TRUE);
                        break;

                    case CHANNEL_51:
                        samplecount = Nvaudio_MixDown5_1ToStereo(outbuf,state,
                                            loopend,CHANNEL_51,TRUE);
                        break;
                } //switch for channel num

              samplecount = (samplecount << 2); /* 16bit stereo to get in Bytes*/

#ifdef NV_DEBUG_SRC
            printk("samplecount %d \n", samplecount);
#endif

            curnode->digitalposn += cnt;

            if(curnode->digitalposn >=  curnode->size) {
                curnode->digitalposn = 0;
            }

            swptr  = (swptr + samplecount) % dmabuf->dmasize;

            spin_lock_irqsave(&state->lock, flags);

            if (PM_SUSPENDED(card)) {
                spin_unlock_irqrestore(&state->lock, flags);
                continue;
            }

            dmabuf->swptr = swptr;
            dmabuf->count += samplecount;
            spin_unlock_irqrestore(&state->lock, flags);

            count  -= samplecount;

            if(outbuf && samplecount) {
                if(curnode->digitalposn == 0) {
#ifdef NV_DEBUG_SRC
                    printk(" free the node %p \n ",curnode);
#endif
                    card->outList->spoutnode->spoutused = 1;
                    card->outList->spoutcount -= 1;
                    card->outList->spoutnode = card->outList->spoutnode->next;
                    outbuf   = NULL;
                }

                if(card->outList->spoutcount <=0 ) {
                    card->outList->spoutcount = 0;
                    if (count >=0 ) count = 0;
                }
                if(!card->analogstate) {
                    card->outList->wavoutnode = card->outList->spoutnode;
                }
            }

        }
    }

    return 0;
}

/**********************************************************************
* Function to recalculate the outbuffer Size
**********************************************************************/
int resizecount(struct Nvaudio_card *card, int buffersize)
{
    int newsize = buffersize;
    switch( card->numchannels) {
        case CHANNEL_51:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                    break;
                case NV_SPKR_51:
                    break;
            }
            break;
        case CHANNEL_QUAD:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                case NV_SPKR_51:
                    break;
            }
            break;
        case CHANNEL_STEREO:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                    newsize = (newsize >> 1);
                    break;
                case NV_SPKR_51:
                    newsize = (newsize >> 1);
                    break;
            }
            break;
    }

    return newsize;
}
/**********************************************************************
* Function to recalculate count to copy based on speaker selection
**********************************************************************/
int wavcopyresize(struct Nvaudio_card *card, int count, int totalcount)
{
    int newsize = count;
    switch( card->numchannels) {
        case CHANNEL_51:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                    break;
                case NV_SPKR_51:
                    break;
            }
            break;
        case CHANNEL_QUAD:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                case NV_SPKR_51:
                    break;
            }
            break;
        case CHANNEL_STEREO:
            switch(card->spkrselect) {
                case NV_SPKR_STEREO:
                    break;
                case NV_SPKR_QUAD:
                case NV_SPKR_51:
                    /* skip if they are same now */
                    if((totalcount >= (count << 1)) ||
                      (card->curspkr == NV_SPKR_QUAD) ||
                      (card->curspkr == NV_SPKR_51) ) {
                        break;
                    } else {
                        newsize = (totalcount >> 1);
                    }
                    break;
            }
            break;
    }

    return newsize;
}

/**********************************************************************
* Function to copy data to wave prds
**********************************************************************/
int Nvaudio_copydatatowavprd(struct Nvaudio_card *card,int count)
{
    u32 samplecount = 0;
    unsigned long flags;
    unsigned int swptr = 0;
    int cnt = 0;
    struct Nvaudio_state *state = card ? card->wavout: 0;
    struct dmabuf *dmabuf       = &state->dmabuffer;
    char * outbuf  = NULL;
    nodePtr curnode = NULL;

    while(count > 0 ) {

        cnt = count;

        if(card->outList->wavoutcount > 0 ) {

            swptr   = dmabuf->swptr;
            curnode = card->outList->wavoutnode;
#ifdef NV_DEBUG_SRC
            printk("curnode the node %p posn %d swptr %d \n ",curnode, curnode->analogposn, swptr);
#endif

            if(curnode->analogposn) {
#ifdef NV_DEBUG_SRC
                printk(" POSN HIT %d \n ", curnode->analogposn);
#endif
                if(cnt >= (curnode->size - curnode->analogposn)) {
                    cnt = (curnode->size - curnode->analogposn);
                }
            }else {
                if(cnt >= dmabuf->fragsize)  {
                    cnt =  curnode->size;
                } else {
#ifdef NV_DEBUG_SRC
                    printk(" SIZE CNT LESS than fragsize %d \n", cnt);
#endif
                    if(cnt >= curnode->size) {
                        cnt =  curnode->size;
                    }else {
                        cnt = resizecount(card,cnt);
                    }
                }
            }

            if(card->curspkr != card->spkrselect) {
                cnt = wavcopyresize(card,cnt,count);
            }
            outbuf  = curnode->data + curnode->analogposn;

#ifdef NV_DEBUG_SRC
            printk("Nvaudio_write copying from buffer %p count %x to location %p\n",
                   outbuf,cnt,dmabuf->rawbuf + swptr);
#endif
            samplecount = Nvaudio_wavoutwrite(card, outbuf, (void *)(dmabuf->rawbuf+swptr), cnt);

#ifdef NV_DEBUG_SRC
            printk("samplecount %d \n", samplecount);
#endif

            curnode->analogposn += cnt;

            if(curnode->analogposn >=  curnode->size) {
                curnode->analogposn = 0;
            }

            swptr  = (swptr + samplecount) % dmabuf->dmasize;

            spin_lock_irqsave(&state->lock, flags);

            if (PM_SUSPENDED(card)) {
                spin_unlock_irqrestore(&state->lock, flags);
                continue;
            }

            dmabuf->swptr = swptr;
            dmabuf->count += samplecount;

            spin_unlock_irqrestore(&state->lock, flags);

            count  -= samplecount;

            if(outbuf && samplecount) {
                if(curnode->analogposn == 0) {
#ifdef NV_DEBUG_SRC
                    printk(" free the node %p \n ",curnode);
#endif
                    card->outList->wavoutnode->wavoutused = 1;
                    card->outList->wavoutcount -= 1;
                    card->outList->wavoutnode = card->outList->wavoutnode->next;
                    outbuf   = NULL;
                }

                if(card->outList->wavoutcount <=0 ) {
                    card->outList->wavoutcount = 0;
                    if (count >=0 ) count = 0;
                }

                if(!card->digitalstate) {
                   card->outList->spoutnode = card->outList->wavoutnode;
                }
            }

        }else {
            count = 0;
        }
    }
    return 0;
}

