/***************************************************************************
*	NAME:  IWINIT.C $Revision: 1.29 $
**	COPYRIGHT:
**	"Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: iwinit.c $
* Revision 1.29  1995/11/21 17:29:00  sdsmith
* Changes for new Windows init.
* Revision 1.28  1995/11/20 14:10:20  sdsmith
* Kernel and Windows init changes
* Revision 1.27  1995/10/26 14:59:29  mleibow
* Added mutes for software mixer controls.
* Revision 1.26  1995/10/16 15:58:35  unknown
* Fixed copyright
* Revision 1.25  1995/10/16 15:37:05  unknown
* Added copyright
* Added code to detect busy InterWave card.
* Revision 1.24  1995/10/15 18:40:47  sdsmith
* 
* Revision 1.23  1995/10/13 17:19:12  mleibow
* Added base level GS effects for reverb and chorus.
* Revision 1.22  1995/10/07 15:42:36  mleibow
* DOn't disable line-out on kernel reset.
* Revision 1.21  1995/07/27 13:46:08  mleibow
* changed iw_reset_interface to leave synth irq lines and dma lines active on ISA bus.
* Revision 1.20  1995/06/09 14:13:54  sdsmith
* Mike's changes
* Revision 1.19  1995/06/09 05:00:48  mleibow
* Changed iw_load_kernel to automatically call iw_unload_kernel on failure.
* Revision 1.18  1995/05/26 16:17:41  mleibow
* Changed UMCR to leave all audio settings enabled.
* Revision 1.17  1995/05/25 15:13:41  mleibow
* Added multiple language support.
* Revision 1.16  1995/05/25 01:21:00  sdsmith
* Tried to fix click by disabling line out in iw_init_kernel and
* reenable in iw_auto_init
* Revision 1.15  1995/05/17 01:38:49  sdsmith
* Revision 1.14  1995/05/15 22:52:32  sdsmith
* Fixed GP pin to IRQ code
* Revision 1.13  1995/05/03 08:24:13  sdsmith
* Changed names of iwl_midi globals to iwl_uart
* Revision 1.12  1995/04/28 12:18:09  sdsmith
* Moved init_ports to run before any registers are accessed
* Revision 1.11  1995/04/26 16:24:11  sdsmith
* Fixed public function commentary and made sure the close
* functions return the chip to a good known state
* Revision 1.10  1995/04/14 09:19:57  sdsmith
* Added support for B0 silicon
* Revision 1.9  1995/03/29 10:01:47  sdsmith
* Removed the debug.h include
* Revision 1.8  1995/03/24 20:14:39  mleibow
* took out debugging statements
* Revision 1.7  1995/03/24 09:44:12  mleibow
* Added initialization for MPU401 and other emulations
* Revision 1.6  1995/03/22 14:57:37  mleibow
* moved #include "codec.h" to iwl.h
* Revision 1.5  1995/03/22 14:57:27  sdsmith
* Removed CODEC.H
* Revision 1.4  1995/03/08 15:37:18  mleibow
* added chip revision check for pre-release versions of the chip
* Revision 1.3  1995/03/01 14:04:10  sdsmith
* Fixed bits for UMCR on A3 silicon release
* Revision 1.2  1995/02/24 14:00:58  mleibow
* fixed initialization of IDECI and added iw_reset_interface().  Also added
* auto deinitialization
* Revision 1.1  1995/02/23 11:07:06  unknown
* Initial revision
***************************************************************************/

#include <dos.h>
#include "iw.h"
#include "globals.h"
#include "iwl.h"
#include "digital.h"

static char *cpr="Copyright 1992, 1995 Advanced Gravis, Inc. All rights reserved";

#if !defined(__WATCOMC__) && !defined(__FLAT__) && !defined(_WINDOWS)
/* external private functions */
//extern int iwl_multiplex_init(void);
//extern void iw_free_multiplex(void);
#endif

/* local variables */
OS_PINTERRUPT old_iwl_service;
OS_PINTERRUPT old_midi_service;
int os_loaded = 0;
static unsigned char iwl_olduici;
static unsigned char iwl_oldudci;
static unsigned char iwl_oldiveri;
static unsigned char iwl_oldicmpti;
static unsigned char iwl_oldieirqi;

/***************************************************************************

FUNCTION DEFINITION:
iwl_init_ports - initialize the I/O port addresses

DESCRIPITON:
This routine adjusts all of the global I/O port addresses to be relative
to the base port locations

RETURNS: void
*/
void iwl_init_ports(void)
{
    /* set up global register address variables */
	iwl_mix_control     = iwl_base_port;         //0x2X0
	iwl_status_register = iwl_base_port + 0x06;  //0x2X6
	iwl_timer_control   = iwl_base_port + 0x08;  //0x2X8
	iwl_timer_data      = iwl_base_port + 0x09;  //0x2X9
	iwl_irqdma_control  = iwl_base_port + 0x0B;  //0x2Xb
	iwl_reg_control     = iwl_base_port + 0x0F;  //0x2Xf
	iwl_uart_control    = iwl_synth_base;        //0x3X0
	iwl_uart_data       = iwl_synth_base + 0x01; //0x3X1
	iwl_page_register   = iwl_synth_base + 0x02; //0x3X2
	iwl_register_select = iwl_synth_base + 0x03; //0x3X3
	iwl_data_low        = iwl_synth_base + 0x04; //0x3X4
	iwl_data_high       = iwl_synth_base + 0x05; //0x3X5
	iwl_dram_io         = iwl_synth_base + 0x07; //0x3X7
	iwl_mpu_data	= iwl_mpu_base + 0;
	iwl_mpu_status	= iwl_mpu_base + 1;
	iwl_mpu_command	= iwl_mpu_base + 1;
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_set_interface - setup the synthesizer's PC interface

DESCRIPITON:
This routine sets registers in the synthesizer that control the interface
to the PC, i.e. DMA channel, ports, and irq's.

RETURNS: void
*/
static int iwl_set_interface(void)
{
//	if (iwl_emulation && iwl_irq1 == iwl_irq2) return(IW_EMULATION_FAIL);
	iwl_iveri = 5;
	IWL_OUT_B(IW_IVERI, iwl_iveri);	/* disable hidden register lock */

	OS_OUTPORTB(iwl_reg_control,0x5);		/* set up access to ULCLRII */
	OS_OUTPORTB(iwl_mix_control,iwl_umcr);
	OS_OUTPORTB(iwl_irqdma_control,0x0);	/* clears UCLRII */

#ifdef IWL_NMI_FOR_EMULATION
	 {
		unsigned char val;
	/* Reset PC's NMI latch */
		val = OS_INPORTB(0x61);
		val |= 0x08;
		OS_OUTPORTB(0x61,val);
		val &= ~0x08;
		OS_OUTPORTB(0x61,val);
	}
#endif

   /* select IRQ/DMA control hidden registers */
	iwl_urcr = 0;
	OS_OUTPORTB(iwl_reg_control, iwl_urcr);

   /* Check if IRQ and DMA channels need to be combined */
	OS_OUTPORTB(iwl_mix_control, iwl_umcr|IWL_SELECT_UICI);
	iwl_uici = OS_INPORTB(iwl_irqdma_control);
	iwl_olduici = iwl_uici;
	if (iwl_emulation) {
		iwl_uici |= IWL_ALSB; /* enable NMI's for adlib/sb/mpu401 emulation */
	} else {
		iwl_uici &= ~IWL_ALSB; /* disable adlib sound-blaster NMI generation */
	}
#ifndef IWL_MIDI_IRQ1
	if (iwl_irq1 == iwl_irq2) {
#endif
		iwl_uici |= IWL_COMBINE_IRQ;
#ifndef IWL_MIDI_IRQ1
	}
#endif
#ifdef IWL_NMI_FOR_EMULATION
	if (iwl_emulation) {
		iwl_uici |= IWL_COMBINE_IRQ;
	}
	if (iwl_emulation & EMUL_MPU401) {
		IWL_INP_B(IW_ICMPTI, iwl_icmpti);

		iwl_urcr |= IWL_URCR_EGPRA|IWL_URCR_GP1IRQ|IWL_URCR_GP2IRQ;
	/* set lower 8 bits of general purpose reg 1 address */
		OS_OUTPORTB(iwl_reg_control,0x3 | iwl_urcr);
		OS_OUTPORTB(iwl_mix_control,iwl_umcr);
		OS_OUTPORTB(iwl_irqdma_control,(iwl_mpu_base&0xFF));
	/* set lower 8 bits of general purpose reg 2 address */
		OS_OUTPORTB(iwl_reg_control,0x4 | iwl_urcr);
		OS_OUTPORTB(iwl_mix_control,iwl_umcr);
		OS_OUTPORTB(iwl_irqdma_control,((iwl_mpu_base+1)&0xFF));
	/* set upper 2 bits of general purpose register addresses */
		iwl_icmpti &= ~0x0f;	// clear GPR2A and GPR1A
		iwl_icmpti |= ((iwl_mpu_base>>8) & 0x03);
		iwl_icmpti |= (((iwl_mpu_base+1)>>8) & 0x03) << 2;
		IWL_OUT_B(IW_ICMPTI, iwl_icmpti);
		OS_OUTPORTB(iwl_reg_control,iwl_urcr);
	}
   /* Set the AdLib timers to the proper mode */
	if (iwl_emulation & (EMUL_SB|EMUL_ADLIB)) {
		iwl_uasbci = 0x22;
	} else {
		iwl_uasbci = 0x0;
	}
#else
	iwl_uasbci = 0x0;
#endif
	OS_OUTPORTB(iwl_register_select, ADLIB_CONTROL);
	OS_OUTPORTB(iwl_data_high, iwl_uasbci);

   /* write out final uici and udci values */
	OS_OUTPORTB(iwl_reg_control, iwl_urcr);	/* select UICI */
	OS_OUTPORTB(iwl_mix_control, iwl_umcr|IWL_SELECT_UICI);	/* select UICI */
	OS_OUTPORTB(iwl_irqdma_control, iwl_uici);
   /* now udci */
	OS_OUTPORTB(iwl_reg_control, iwl_urcr);	/* select UDCI */
	OS_OUTPORTB(iwl_mix_control, iwl_umcr);	/* select UDCI */
	iwl_udci = OS_INPORTB(iwl_irqdma_control);
	iwl_oldudci = iwl_udci;
	if (iwl_channel_in == iwl_channel_out) {
		iwl_udci |= IWL_COMBINE_DMA;
		OS_OUTPORTB(iwl_irqdma_control, iwl_udci);
	}

   /* Enable interrupt generation and address decodes */
	IWL_INP_B(IW_IDECI, iwl_oldideci);
	iwl_ideci = IWL_IDECI_EICH1|IWL_IDECI_EICH2|IWL_IDECI_EINMI|IWL_IDECI_ECOD|IWL_IDECI_E3889|IWL_IDECI_EEDC|IWL_IDECI_EA98;

   /* Check for IRQ4 and IRQ10 usage */
	if (iwl_revision >= 4) {
		OS_OUTPORTB(iwl_register_select, IW_IEIRQI);
		iwl_ieirqi = OS_INPORTB(iwl_data_high);
		if ((iwl_ieirqi & IWL_IEIRQI_SELGP) &&
		    ((iwl_irq1 == 4      || iwl_irq1 == 10) ||
			(iwl_irq2 == 4      || iwl_irq2 == 10) ||
			(iwl_mpu_irq == 4   || iwl_mpu_irq == 10 ) ||
			(iwl_adlib_irq == 4 || iwl_adlib_irq == 10))
		   ) {
			return(IW_BAD_IRQ);
		}
	}

   /* Enable DMA and interrupts */
	iwl_umcr |= IWL_ENABLE_IRQ_DMA;
	OS_OUTPORTB(iwl_mix_control, iwl_umcr);

   /* enable writes to UDCI[5:0] and UICI[5:0] */
	IWL_INP_B(IW_ICMPTI, iwl_icmpti);
	iwl_icmpti |= IWL_ICMPTI_CPEN;
	if (iwl_revision <= 1) {
		iwl_icmpti &= ~0xe0;
		iwl_icmpti |= 0x80;
	}
	IWL_OUT_B(IW_ICMPTI, iwl_icmpti);

	return(IW_OK);
}

static void iwl_reset_interface(void)
{
	/* MSL - leave IRQ and DMA channels activated.  iwinit -off can
	** be used to disable our card if necessary */
//   /* Disable DMA and interrupts */
//	iwl_umcr &= ~IWL_ENABLE_IRQ_DMA;
	IWL_OUT_B(IW_IEMUAI, 0x00);
	IWL_OUT_B(IW_IEMUBI, 0x30);

    /* Enable access to all hidden registers */
	iwl_icmpti |= 0x10;
	IWL_OUT_B(IW_ICMPTI, iwl_icmpti);
	iwl_iveri |= 0x01;
	IWL_OUT_B(IW_IVERI, iwl_iveri);

	OS_OUTPORTB(iwl_reg_control, 0);
	OS_OUTPORTB(iwl_mix_control, (iwl_umcr & ~IWL_SELECT_UICI));
	OS_OUTPORTB(iwl_irqdma_control, iwl_oldudci);
	OS_OUTPORTB(iwl_mix_control, (iwl_umcr | IWL_SELECT_UICI));
	OS_OUTPORTB(iwl_irqdma_control, iwl_olduici);

	IWL_OUT_B(IW_IDECI, iwl_oldideci);

	/* Make sure the HR Lock is disabled */
	IWL_OUT_B(IW_IVERI, iwl_oldiveri|0x01);
	IWL_OUT_B(IW_ICMPTI, iwl_oldicmpti);
	IWL_OUT_B(IW_IEIRQI, iwl_oldieirqi);

	IWL_OUT_B(ADLIB_CONTROL, 0x00);
	IWL_OUT_B(MASTER_RESET, 0x7);
	/* AMD complained that CD-ROM was being shut off.  Removing code to */
	/* disable line out.  ON Gus-MAX, line out from codec was not affected */
	/* by this because CODEC and synth were on different chips */
//	OS_OUTPORTB(iwl_mix_control, 0x04); /* this line is coded properly below */
//	if (iwl_revision <= 2) {
//	    iwl_umcr &= ~IWL_DISABLE_OUT;
//	} else {
//	    iwl_umcr |= IWL_DISABLE_OUT;
//	}
	iwl_umcr |= IWL_ENABLE_MIC;
	OS_OUTPORTB(iwl_mix_control, iwl_umcr);
}

/***************************************************************************

FUNCTION DEFINITION:
iw_init_kernel - initalize the InterWave Kernel

DESCRIPITON:
This routine is used to initialize the InterWave kernel in preparation
for initializing the other kernel modules such the synthesizer, CODEC,
and mixer.  This routine alone is NOT sufficient to bring the kernel
into a state for playing digital audio, midi, or digtial music, however,
this must be the first routine called after iw_get_kernel_cfg() to
start the kernel initialization process.

Following this routine a wrapper should call one or more of:
	iw_init_synth()
	iw_init_codec()
	iw_init_mixer()

After that, the wrapper MUST call iw_auto_init().

PARAMETERS:
	cfg - pointer to a load_kernel_cfg structure

RETURNS: int - IW_OK
               IW_CARD_NOT_FOUND
		   other error codes
*/
int	iw_init_kernel(struct load_kernel_cfg RFAR *cfg)
{
	int status;

	if (os_loaded) {
		os_loaded += 1;
		return(IW_OK);
	}
	// One of the first modules that needs to be inited is the
	// multiple language support so that if an error is returned, the
	// correct error message can be pulled from the language file.
#ifdef IW_MODULE_MLS
	iw_mls_init(cfg->mls_path);
#endif
	if (cfg->UseDma == 0) iwl_flags |= F_IW_NO_DMA;
	/* the following line of code is for pre-release chips */
	iwl_revision = 4;
	/* the previous line of code is for pre-release chips */
	iwl_pnp_csn = cfg->pnp_csn;
	iwl_pnp_read = cfg->pnp_read_port;
	iwl_gpusage = (unsigned short)cfg->gpusage;
#ifdef IWL_NMI_FOR_EMULATION
	iwl_emulation = cfg->emulation;
#else
	iwl_emulation = 0;
#endif

	OS_PUSH_DISABLE();

	/* New for InterWave */
	if ((status = iwl_get_pnp_config()) != IW_OK) {
		OS_POP_FLAGS();
		return(status);
	}
	/* kludge until B0 hardware */
	if (iwl_revision < 4) iwl_mpu_base = cfg->mpubase;

	if (OS_INPORTB(iwl_base_port) != 0xFF && OS_INPORTB(iwl_codec_base) != 0xFF) {
	    iwl_init_ports();
    
    
	/* Initialize UMCR */
	    iwl_umcr = IWL_ENABLE_MIC;
	    OS_OUTPORTB(iwl_mix_control, iwl_umcr);
    
	    /* Snag a copy of registers that must be restored */
	    IWL_INP_B(IW_ICMPTI, iwl_oldicmpti);
	    IWL_INP_B(IW_IVERI, iwl_oldiveri);
	    IWL_INP_B(IW_IEIRQI, iwl_oldieirqi);
    
	/* initialize interrupt handlers */
	    iwl_isr_init();

	    if (iwl_gpusage == irq) {
		OS_OUTPORTB(iwl_register_select, IW_IEIRQI);
		iwl_ieirqi = OS_INPORTB(iwl_data_high);
		iwl_ieirqi &= ~IWL_IEIRQI_SELGP;
		OS_OUTPORTB(iwl_data_high,iwl_ieirqi);
	    }
    
	    /* set up pc interface (irqs, dmas, and emulation regs) */
	    if ((status = iwl_set_interface()) != IW_OK) {
		    OS_POP_FLAGS();
		    return(status);
	    }
    
	    iwl_flags |= F_IW_OS_LOADED;
	    os_loaded += 1;
	    OS_POP_FLAGS();
	    if (status) return(status);
    
    
	    return(IW_OK);
	}
	else {
	    OS_POP_FLAGS();
	    return(IW_CARD_IS_BUSY);
	}
}

#ifdef DEBUG
extern void stack_check(void);
#endif

/***************************************************************************

FUNCTION DEFINITION:
iw_close_kernel - shutdown the InterWave kernel

DESCRIPITON:
iw_close_kernel cleans up all flags indicating that the kernel was loaded.
This routine is not a standalone function.  The wrapper must first
shutdown any other kernel modules that were initialized.  There are
corresponding iw_close_xxxxx() functions to shutdown the synthesizer
and CODEC.

RETURNS: void
*/
void	iw_close_kernel(void)
{
	if (!(iwl_flags & F_IW_OS_LOADED)) return;
	if (--os_loaded) return;
	OS_PUSH_DISABLE();

	iwl_reset_interface();
//#if !defined(__WATCOMC__) && !defined(__FLAT__) && !defined(OS2) && !defined(_WINDOWS)
//	iw_free_multiplex();
//#endif
	OS_POP_FLAGS();
	iwl_flags &= ~F_IW_OS_LOADED;
#ifdef DEBUG
	stack_check();
#endif
}

/***************************************************************************

FUNCTION DEFINITION:
iw_auto_init - initialize the InterWave core services

DESCRIPITON:
This function is used to initialize the core services of the interwave
based on which modules were activated at the kernel's compilation time.
The InterWave kernel modules are activated by setting macros in the
config.h file that corresponds to the application the kernel is being
compiled for.

This routine MUST be called after iw_init_kernel() and any other kernel
module init routines have been called.  Until this routine is called,
the kernel is not fully initialized.

NOTE: This routine does not need to be called if iw_load_kernel() is
used to initialize the kernel.

RETURNS: int
			IW_OK
			other error codes from core service inits
			
*/
int iw_auto_init(struct load_kernel_cfg RFAR *cfg)
{
	int rc = IW_OK;

#ifdef IW_MODULE_TIMER
	if (rc == IW_OK) rc = iwl_timer_init();
#endif
#ifdef IW_MODULE_MEM
	if (rc == IW_OK) rc = iwl_mem_init();
#endif
#ifdef IW_MODULE_VOICE
	if (rc == IW_OK) rc = iwl_voice_init();
#endif
#ifdef IW_MODULE_DMA
	if (rc == IW_OK) rc = iwl_dma_init();
#endif
#ifdef IW_MODULE_LFO
	if (rc == IW_OK) rc = iwl_lfo_init();
#endif
#ifdef IW_MODULE_EFFECTS
	if (rc == IW_OK) rc = iwl_effects_init(cfg);
#endif
#ifdef IW_MODULE_UART
	if (rc == IW_OK) rc = iwl_uart_init();
#endif
#ifdef IW_MODULE_RECORD
	if (rc == IW_OK) rc = iwl_record_init();
#endif
#ifdef IW_MODULE_NOTE
	if (rc == IW_OK) rc = iwl_note_init();
#endif
#ifdef IW_MODULE_SOUND
	if (rc == IW_OK) rc = iwl_sound_init();
#endif
#ifdef IW_MODULE_SYNTH_DIGITAL
	if (rc == IW_OK) rc = iwl_synth_dig_init();
#endif
//	if (iwl_revision <= 2) {
//		iwl_umcr |= IWL_DISABLE_OUT; /* backwards in early chips */
//		OS_OUTPORTB(iwl_mix_control, iwl_umcr);
//	} else {
//		iwl_umcr &= ~(IWL_DISABLE_OUT);
//		OS_OUTPORTB(iwl_mix_control, iwl_umcr);
//	}
	return(rc);
}

/***************************************************************************

FUNCTION DEFINITION:
iw_auto_deinit - deactivate the InterWave core services

DESCRIPITON:
This routine must be called before any kernel module shutdown routines
and before iw_close_kernel() in order to shutdown the kernel core
services.

NOTE:
If iw_unload_kernel() is used to shutdown the kernel, this routine does
not need to bne called.

RETURNS: int - IW_OK
*/
int iw_auto_deinit(void)
{
	int rc = IW_OK;

#ifdef IW_MODULE_MEM
	iwl_mem_deinit();
#endif
#ifdef IW_MODULE_DMA
	iwl_dma_deinit();
#endif
#ifdef IW_MODULE_TIMER
	iwl_timer_deinit();
#endif
#ifdef IW_MODULE_NOTE
	iwl_note_deinit();
#endif
#ifdef IW_MODULE_SYNTH_DIGITAL
        iwl_synth_dig_deinit();
#endif
	return(rc);
}
