
void playback_es1938_rtirq(void)
{
   unsigned char temp, status;

   status = inb(es1938_io_base + 0x07);
   if(status & 0x20)
    {
         /* debug */
         if((es1938_irq_count<=DEBUG_ARRAY_SIZE)&&(es1938_irq_count>=0)) {
	       es1938_dma_count[es1938_irq_count] = inw(es1938_io_base + 0x04);
	       es1938_dma_addr[es1938_irq_count]  = inl(es1938_io_base + 0x00);
	 }

         outb(0x7a,es1938_sbio_base + 0x04);
         if(inb(es1938_sbio_base + 0x05)& 0x80)  /* check for ESS produced interrupt */
         {
            inb(es1938_sbio_base + 0x0e);  /* reset any interrupt request */

            rt_spin_lock(&reg_lock);
 
            /* clear interrupt bit */
            outb(0x7a,es1938_sbio_base + 0x04); 
            temp = inb(es1938_sbio_base + 0x05);
            temp &= 0x47;
            outb(0x7a,es1938_sbio_base + 0x04);
            outb(temp, es1938_sbio_base + 0x05);

            rt_spin_unlock(&reg_lock);
            es1938_irq_count++;      
          }

          //outb(0x20,es1938_io_base + 0x07); /* enable audio 2 interrupt */

    }
}




unsigned int es1938_set_dac_rate(unsigned int rate)
{
  int x0, x1, r0, r1, which;
  unsigned int f;

  x0 = 793800 / rate;
  x1 = 768000 / rate;
  r0 = 793800 / x0;
  r1 = 768000 / x1;
  which = abs(r0 - rate) < abs(r1 - rate) ? 0 : 128;
  if(which) {
	x0 = x1;
	r0 = r1;
  }
  x0 = which | (128 - x0);
  
  f = (rate * 9) / 20;
  f = 256 - 7160000 / (82 * f);

  /* set AUDIO2 SAMPLE RATE AND FILTER registers */
  outb(0x70,es1938_sbio_base + 0x04); /* sample rate */
  outb(x0,es1938_sbio_base + 0x05);
  outb(0x72,es1938_sbio_base + 0x04); /* filter clock rate */
  outb(f,es1938_sbio_base + 0x05);

  printk("Solo DEBUG: sample rate = %d, filter = %d \n", x0, f);
  return r0;
}


int init_sound_dev(void)
{
  int es1938_ireq, i;
  unsigned short es1938_vendor_id;
  unsigned short es1938_device_id;
  unsigned char temp;
  unsigned long flags;
  unsigned short es1938_command, es1938_legacy_audio, es1938_ddma_control;
  unsigned int   es1938_solo1_config;

  es1938_present = 0;
  es1938_irq_count = 0;

  /* Find sound card */
  if((pdev = pci_find_device(ES1938_VENDOR_ID,ES1938_DEVICE_ID,pdev)))
  {
     es1938_present = 1;
     es1938_vendor_id = (pdev->vendor);
     es1938_device_id = (pdev->device);
     es1938_io_base =  pci_resource_start(pdev,0);
     es1938_sbio_base= pci_resource_start(pdev,1);
     es1938_irq = (pdev->irq);

     printk("\n  Found : Es1938 Solo-1 Sound card! \n");
     printk("  Card Vendor ID : 0x%x\t  Card Device ID : 0x%x\n",es1938_vendor_id,es1938_device_id);
     printk("  IO Base Address for Sound Card: 0x%x\n",es1938_io_base);
     printk("  SB IO Base Address for Sound Card: 0x%x\n", es1938_sbio_base);
     printk("  Card IRQ : %d\n",es1938_irq);

     if(pci_set_dma_mask(pdev,0xffffffff)){
        printk("Error: No suitable DMA available \n");
        return -1;
     }

    spin_lock_init(&reg_lock);
   
    /* Allocate buffer Memory and get DMA address for buffer */
     es1938_psnd_buf = (unsigned char*)pci_alloc_consistent(pdev,ES1938_BUFFER_SIZE * sizeof(unsigned char),&dma_handle);
     
     if(es1938_psnd_buf == NULL){
        printk("Error: Cannot allocate enough memory to the Sound Buffer \n");
        return -1;
     }

     for(i = 0; i<ES1938_BUFFER_SIZE ; i++)
         es1938_psnd_buf[i] = 0;

     if(!dma_handle){
        printk("Error: Cannot map buffer memory to DMA memory \n");
        return -1;
     }
     printk("  The DMA Handle is : 0x%x \n", dma_handle);

     es1938_ireq = rt_request_global_irq(es1938_irq,playback_es1938_rtirq);
     rt_enable_irq(es1938_irq);


   /*--------------------  RESET CHIP--------------------------*/
 
     /* for Software reset */
     temp = inb(es1938_sbio_base + 0x06);
     temp |= 0x03;
     outb(temp,es1938_sbio_base + 0x06);
     udelay(10);

     inb(es1938_sbio_base + 0x06);

     temp = inb(es1938_sbio_base + 0x06);
     temp &= 0xfc;
     outb(temp,es1938_sbio_base + 0x06);
     udelay(10);

     for(i=0; i<0x10000; i++) {
         if(inb(es1938_sbio_base + 0x0e) & 0x80) {
                if(inb(es1938_sbio_base + 0x0a) == 0xaa)
			goto __next;
	 }
     }
     printk("ESS Solo-1 reset failed.\n");


__next:
     /* unmask audio2 channel */
     outb(0x7a, es1938_sbio_base +0x04);
     temp = inb(es1938_sbio_base + 0x05);
     temp |= 0x40;
     outb(0x7a, es1938_sbio_base +0x04);
     outb(temp , es1938_sbio_base + 0x05);


     /* Configure native mode */

     /* enable bus master and I/O space */
     pci_write_config_word(pdev, PCI_COMMAND, 5);  
     
     /* disable legacy audio */
     pci_write_config_word(pdev, 0x40, 0x805f);

     /* set DMA/IRQ policy */
     pci_write_config_dword(pdev, 0x50, 0);

     /* Enable audio 2 IRQ */
     outb(0x20,es1938_io_base + 0x07);

     /* enable bus mastering */
     pci_set_master(pdev);

     /* -------------------- DAC OPERATIONS ---------------------- */

     flags = rt_spin_lock_irqsave(&reg_lock);

     /* STEP 1 : Reset fifo */
     outb(0x02,es1938_sbio_base + 0x06); 
     udelay(10);
     outb(0x00,es1938_sbio_base + 0x06);
     udelay(10);
       
     outb(0x7c,es1938_sbio_base + 0x04); /* Mute the sound by initializing mixer register */
     outb(0x00,es1938_sbio_base + 0x05);

     /* STEP 2 : Initialize DMA to Auto-Initialised mode */
     outb(0x78,es1938_sbio_base + 0x04);
     outb(0x10,es1938_sbio_base + 0x05); 

     /* STEP 3 : Initialize all clocks and counters */
     es1938_set_dac_rate(48000);  /* set sample rate and filter clock rate */
     outb(0x74,es1938_sbio_base + 0x04); /* Audio 2 transfer count reload register low */
     outb(0x00,es1938_sbio_base + 0x05);
     outb(0x76,es1938_sbio_base + 0x04); /* Audio 2 transfer count reload register high */
     outb(0x80 ,es1938_sbio_base + 0x05);
     

     /* STEP 4 : Initialize and configure DAC */
     outb(0x7a, es1938_sbio_base + 0x04);
     temp = inb(es1938_sbio_base + 0x05);
     temp |= 0x03;
     outb(0x7a,es1938_sbio_base + 0x04);
     outb(temp,es1938_sbio_base + 0x05);
  
     /* reset FIFO again */
     outb(0x02,es1938_sbio_base + 0x06);
     udelay(10);
     outb(0x00,es1938_sbio_base + 0x06);
     udelay(10);

    
     /* STEP 5 : Initialize IRQ channel */
     /*
     outb(0x7a, es1938_sbio_base +0x04);
     temp = inb(es1938_sbio_base + 0x05);
     temp |= 0x40;
     outb(0x7a, es1938_sbio_base +0x04);
     outb(temp , es1938_sbio_base + 0x05);
     */ 

     //outb(0x20,es1938_io_base + 0x07); /* IRQ control register sets A2IRQ bit */

     /* STEP 6 : Set DMA Address, DMA Count */
     temp = inb(es1938_io_base +0x06);        /* DMA mode : disable DMA  */
     temp |= 0xfd;
     outb(temp , es1938_io_base + 0x06);

     outl(dma_handle,es1938_io_base + 0x0);   /* DMA base address        */
     outw(0x8000,es1938_io_base + 0x04);      /* DMA count:32KB          */
     temp = inb(es1938_io_base +0x06);        /* DMA mode : enable DMA   */
     temp |= 0x0a; 
     outb(temp , es1938_io_base + 0x06);
    
     
     /* STEP 7 : Start DMA */
     outb(0x78 , es1938_sbio_base + 0x04);
     temp = inb(es1938_sbio_base + 0x05);
     temp |= 0x03;
     outb(0x78 , es1938_sbio_base + 0x04);
     outb(temp , es1938_sbio_base + 0x05);
     
 
     /* STEP 8 : allow delay of 100 miliseconds for analog circuits to be settled */
     mdelay(100);
 
     /* Turn up the volume */
     outb(0x7c,es1938_sbio_base + 0x04);
     outb(0x88,es1938_sbio_base + 0x05);   
    
     rt_spin_unlock_irqrestore(flags , &reg_lock);
  }
  else
  {
     printk("No ES 1938 Sound Card Found ... !\n");
     return -1;
  } 
  return 0;
}



int init_module(void)
{
  /* Initialize sound card */
  if(init_sound_dev()){
	printk("\nIniting sound device failed. \n");
	return -1;
  }
  return 0;
}  


void cleanup_module(void)
{
  int  i;
  unsigned char temp;

  /* free interrupt service for the ES1938 */
  if(es1938_present){
     /* stop DMA and disable interrupts */
     outb(0x78, es1938_sbio_base + 0x04);
     temp = inb(es1938_sbio_base + 0x05);
     temp &= 0xfc;  /* disable DMA and FIFO */
     outb(0x78,es1938_sbio_base + 0x04);
     outb(temp,es1938_sbio_base + 0x05);

     /* mask audio2 IRQ */
     outb(0x00,es1938_io_base + 0x07);

     /* restore DMA controller */
     outb(0x7a,es1938_sbio_base + 0x04);
     outb(0x00,es1938_sbio_base + 0x05);

     /* wait for fifo empty */
     while(!(inb(es1938_sbio_base + 0x0c) & 0x10 ));
     
     mdelay(25);

     /* turn off the volume */
     outb(0x7c,es1938_sbio_base + 0x04);
     outb(0x00,es1938_sbio_base + 0x05);
     
     /* for Software reset */
     temp = inb(es1938_sbio_base + 0x06);
     temp |= 0x01;
     outb(temp,es1938_sbio_base + 0x06);

     inb(es1938_sbio_base + 0x06);

     temp = inb(es1938_sbio_base + 0x06);
     temp &= 0xfe;
     outb(temp,es1938_sbio_base + 0x06);

     while(!(inb(es1938_sbio_base + 0x0e) & 0x80) || (inb(es1938_sbio_base + 0x0a) != 0xaa));

     rt_disable_irq(es1938_irq);
     rt_free_global_irq(es1938_irq);

     pci_free_consistent(pdev,ES1938_BUFFER_SIZE * sizeof(unsigned char),es1938_psnd_buf,dma_handle);
  }

  printk("\n\ncleanup_module says goodbye \n\n"); 
  for(i=0; i<es1938_irq_count; i++) {
      printk("Solodebug - audio channel 2 interrupt\n");
      printk("Solodebug - audio channel 2 DMAC DMA count: %u\n",   es1938_dma_count[i]);
      printk("Solodebug - audio channel 2 DMAC DMA base : 0x%x\n", es1938_dma_addr[i]);
  }

  printk("\n  es1938 Interrupt Count : %d \n",es1938_irq_count);
  return;
}

