/*
 *  Copyright (c) 1994/95 by Jaroslav Kysela (Perex soft)
 */

#include "gus_dev.h"

unsigned char gf1_min_ramp_8;
unsigned short gf1_min_ramp_16;
unsigned char gf1_max_ramp_8;
unsigned short gf1_max_ramp_16;

int gf1_ramp_auto = GF1_RAMP_AUTO;

static unsigned short voicesTbl[ 32 - 14 + 1 ] = {
44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843,
25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293
};

static inline unsigned short lvol_to_gf1_vol_internal( unsigned vol )
{
  unsigned short e, m, tmp;
  
  if ( vol > 65535 ) 
    PRINTK( "gus: volume error (%d)\n", vol );

  tmp = vol;
  e = 7;
  if ( tmp < 128 )
    {
      while ( e > 0 && tmp < ( 1 << e ) ) e--;
    }
   else
    {
      while ( tmp > 255 ) { tmp >>= 1; e++; }
    }
  m = vol - ( 1 << e );
  if ( m > 0 )
    {
      if ( e > 8 ) m >>= e - 8; else
      if ( e < 8 ) m <<= 8 - e;
      m &= 255;
    }

  return ( e << 12 ) | ( m << 4 );
}

unsigned short lvol_to_gf1_vol( unsigned vol )
{
  vol = ( ( current_mix[ MIX_GF1_MASTER ].left +
            current_mix[ MIX_GF1_MASTER ].right ) >> 1 ) * vol;
  vol *= 5;				/* correction to range 0..65535 */
  return lvol_to_gf1_vol_internal( vol );
}

unsigned short lvol_to_gf1_vol_16( unsigned vol )
{
  vol = ( vol * 
          current_mix[ MIX_GF1_MASTER ].left * 
          current_mix[ MIX_GF1_MASTER ].right ) / 10000L;
  return lvol_to_gf1_vol_internal( vol );
}

unsigned int get_gf1_playback_freq( short voices )
{
  if ( voices < 14 ) voices = 14;
  if ( voices > 32 ) voices = 32;
  return voicesTbl[ voices - 14 ];
}

void recompute_gf1_ramp_ranges( void )
{
  gf1_min_ramp_16 = lvol_to_gf1_vol( 1 );
  gf1_min_ramp_8 = gf1_min_ramp_16 >> 8;
  gf1_max_ramp_16 = lvol_to_gf1_vol( 128 );
  if ( gf1_max_ramp_16 > 0xfc00 ) gf1_max_ramp_16 = 0xfc00;
  gf1_max_ramp_8 = gf1_max_ramp_16 >> 8;
}

short gus_ramp_ok( unsigned char *start, unsigned char *end )
{
  if ( gf1_ramp_auto > 63 ) return 0;
  if ( *start < MIN_RAMP_RANGE_8 ) *start = MIN_RAMP_RANGE_8;
  if ( *end > MAX_RAMP_RANGE_8 ) *end = MAX_RAMP_RANGE_8;
  return *start < *end;
}

short gus_set_ramp( unsigned char start, unsigned char end, unsigned char cntrl )
{
  int tmp;

  if ( gf1_ramp_auto > 63 ) return 0;

  if ( ( cntrl & 0x80 ) == 0 )
    if ( !gus_ramp_ok( &start, &end ) ) return 0;
   else
    cntrl &= ~0x80;

  CLI();
  gus_write8( 0x0d, 3 );
  gus_write8( 7, start );
  gus_write8( 8, end );
  tmp = ( gf1_ramp_auto * 44100L ) / gf1_playback_freq;
  if ( tmp > 0x3f ) tmp = 0x3f;
  gus_write8( 6, tmp );
  gus_write8( 0x0d, cntrl );
  gus_delay();
  gus_write8( 0x0d, cntrl );
  STI();
#if 0
  PRINTK( "vol: 0x%x, rs: 0x%x, re: 0x%x, rr: 0x%x, rc: 0x%x\n",
            gus_read16( 9 ), gus_read8( 7 ), gus_read8( 8 ), gus_read8( 6 ), 
            gus_read8( 0x0d ) );
#endif
  return 1;
}

void gf1_init_dma_transfer( unsigned int addr, unsigned char *buf, int count, 
                            short w_unsigned, short w_16bit )
{
  unsigned int address;
  unsigned char dma_cmd;

  if ( gus_dma1 > 3 )
    {
      address = addr >> 5;
      address &= 0x1fff;
      address |= ( addr >> 4 ) & 0xc000;
    }
   else
    address = addr >> 4;
    
  dma_cmd = 0x21;			/* IRQ enable */
  if ( w_unsigned ) dma_cmd |= 0x80;	/* invert hight bit */
  if ( w_16bit ) 
    {
      dma_cmd |= 0x40;
      count++; count &= ~1;
    }
  if ( gus_dma1 > 3 )
    {
      dma_cmd |= 0x04;
      count++; count &= ~1;
    }
#ifdef LINUX
  CLI();
  if ( max_flag )
    {
      OUTB( max_cntrl_val | 0x10, GUSP( MAXCNTRLPORT ) );
      OUTB( max_cntrl_val & ~0x10, GUSP( MAXCNTRLPORT ) );
      codec_in( CODEC_IRQ_STATUS );
    }
  gus_write8( 0x41, 0 );		/* disable GF1 DMA */
  disable_dma( gus_dma1 );
  INB( GUSP( IRQSTAT ) );		/* touch the IRQ reg */
  gus_look8( 0x41 );			/* DRAM DMA Control Register */
  gus_look8( 0x49 );			/* Sampling Control Register */
  gus_read8( 0x0f );			/* IRQ Source Register */
  gus_write16( 0x42, address );
  clear_dma_ff( gus_dma1 );
  set_dma_mode( gus_dma1, DMA_MODE_WRITE );
  set_dma_addr( gus_dma1, (int)buf );
  set_dma_count( gus_dma1, count );
  enable_dma( gus_dma1 );
  gus_write8( 0x41, dma_cmd );
  STI();
#elif OSF1
  CLI();
  gus_write8( 0x41, 0 );		/* disable GF1 DMA */ 
#endif
}

void gf1_done_dma_transfer( void )
{
  CLI();
#ifdef LINUX
  disable_dma( gus_dma1 );
#endif
  gus_write8( 0x41, 0 );		/* disable GF1 DMA */
  INB( GUSP( IRQSTAT ) );		/* touch the IRQ reg */
  gus_look8( 0x41 );			/* DRAM DMA Control Register */
  gus_look8( 0x49 );			/* Sampling Control Register */
  gus_read8( 0x0f );			/* IRQ Source Register */
  if ( max_flag )
    {
      OUTB( max_cntrl_val | 0x10, GUSP( MAXCNTRLPORT ) );
      OUTB( max_cntrl_val & ~0x10, GUSP( MAXCNTRLPORT ) );
      codec_in( CODEC_IRQ_STATUS );
    }
  STI();
}

void gf1_init_rdma_transfer( unsigned char *buf, int count, unsigned char rctrl_reg )
{
#ifdef LINUX
  CLI();
  disable_dma( gus_dma2 );
#if 0
  INB( GUSP( IRQSTAT ) );		/* touch the IRQ reg */
  gus_look8( 0x41 );			/* DRAM DMA Control Register */
  gus_look8( 0x49 );			/* Sampling Control Register */
  gus_read8( 0x0f );			/* IRQ Source Register */
#endif
  if ( max_flag )
    OUTB( max_cntrl_val & ~0x20, GUSP( MAXCNTRLPORT ) );
  clear_dma_ff( gus_dma2 );
  set_dma_mode( gus_dma2, DMA_MODE_READ );
  set_dma_addr( gus_dma2, (int)buf );
  set_dma_count( gus_dma2, count );
  enable_dma( gus_dma2 );
  gus_write8( 0x49, rctrl_reg );	/* go!!!! */
  STI();
#elif OSF1
  /* code for OSF/1 */
#endif
}

void gf1_done_rdma_transfer( void )
{
  CLI();
#ifdef LINUX
  disable_dma( gus_dma2 );
#endif
  gus_write8( 0x49, 0 );		/* disable sampling */
#if 0
  INB( GUSP( IRQSTAT ) );		/* touch the IRQ reg */
  gus_look8( 0x41 );			/* DRAM DMA Control Register */
  gus_look8( 0x49 );			/* Sampling Control Register */
  gus_read8( 0x0f );			/* IRQ Source Register */
#endif
  STI();
}
