From aff741d60160c6a9d7d39c9e004a25ea3aa13847 Mon Sep 17 00:00:00 2001 From: NicJA Date: Wed, 20 Jul 2011 19:08:32 +0000 Subject: [PATCH] Importing CMI8738-20072011 git-svn-id: https://svn.aros.org/svn/aros/trunk/AROS@40252 fb15a70f-31f2-0310-bbcc-cdcc74a49acc --- workbench/devs/AHI/Drivers/CMI8738/DriverData.h | 215 ++++++ workbench/devs/AHI/Drivers/CMI8738/Makefile | 19 + workbench/devs/AHI/Drivers/CMI8738/README | 87 +++ workbench/devs/AHI/Drivers/CMI8738/accel.c | 105 +++ workbench/devs/AHI/Drivers/CMI8738/cmi8738.c | 801 +++++++++++++++++++++++ workbench/devs/AHI/Drivers/CMI8738/driver-init.c | 276 ++++++++ workbench/devs/AHI/Drivers/CMI8738/interrupt.c | 236 +++++++ workbench/devs/AHI/Drivers/CMI8738/interrupt.h | 28 + workbench/devs/AHI/Drivers/CMI8738/misc.c | 583 +++++++++++++++++ workbench/devs/AHI/Drivers/CMI8738/misc.h | 60 ++ workbench/devs/AHI/Drivers/CMI8738/regs.h | 292 +++++++++ workbench/devs/AHI/Drivers/CMI8738/version.h | 3 + 12 files changed, 2705 insertions(+) create mode 100755 workbench/devs/AHI/Drivers/CMI8738/DriverData.h create mode 100755 workbench/devs/AHI/Drivers/CMI8738/Makefile create mode 100755 workbench/devs/AHI/Drivers/CMI8738/README create mode 100755 workbench/devs/AHI/Drivers/CMI8738/accel.c create mode 100755 workbench/devs/AHI/Drivers/CMI8738/cmi8738.c create mode 100755 workbench/devs/AHI/Drivers/CMI8738/driver-init.c create mode 100755 workbench/devs/AHI/Drivers/CMI8738/interrupt.c create mode 100755 workbench/devs/AHI/Drivers/CMI8738/interrupt.h create mode 100755 workbench/devs/AHI/Drivers/CMI8738/misc.c create mode 100755 workbench/devs/AHI/Drivers/CMI8738/misc.h create mode 100755 workbench/devs/AHI/Drivers/CMI8738/regs.h create mode 100755 workbench/devs/AHI/Drivers/CMI8738/version.h diff --git a/workbench/devs/AHI/Drivers/CMI8738/DriverData.h b/workbench/devs/AHI/Drivers/CMI8738/DriverData.h new file mode 100755 index 0000000000..44f46c399a --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/DriverData.h @@ -0,0 +1,215 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#ifndef AHI_Drivers_Card_DriverData_h +#define AHI_Drivers_Card_DriverData_h + +#include +#include +#include + +#define DRIVER "cmi8738.audio" +#define DRIVER_NEED_GLOBAL_EXECBASE +#include "DriverBase.h" + +struct CardData; + +struct tester +{ + unsigned long diff; + int flip; + int oldflip; + int A; + int Missed; +}; + +struct CardBase +{ + /** Skeleton's variables *************************************************/ + + struct DriverBase driverbase; + + + /** A sempahore used for locking */ + struct SignalSemaphore semaphore; + + /** The number of cards found */ + int cards_found; + + /** A CardData structure for each card found */ + struct CardData** driverdatas; +}; + +#define RECORD_BUFFER_SAMPLES 1024 + + +struct CardData +{ + /*** PCI/Card initialization progress *********************************/ + + struct PCIDevice *pci_dev; + unsigned long iobase; + unsigned long length; + unsigned short model; + unsigned char chiprev; + unsigned int irq; + + /** TRUE if bus mastering is activated */ + BOOL pci_master_enabled; + + /** TRUE if the Card chip has been initialized */ + BOOL card_initialized; + + + + /*** The driverbase ******************************************************/ + + /** This field is also used as a lock and access to is is + * semaphore protected. */ + struct DriverBase* ahisubbase; + + + /*** The AudioCtrl currently using this DriverData structure *************/ + + struct AHIAudioCtrlDrv* audioctrl; + + + + /*** Playback/recording interrupts ***************************************/ + + /** TRUE when playback is enabled */ + BOOL is_playing; + + /** TRUE when recording is enabled */ + BOOL is_recording; + + /** The main (hardware) interrupt */ + struct Interrupt interrupt; + + /** TRUE if the hardware interrupt has been added to the PCI subsystem */ + BOOL interrupt_added; + + /** The playback software interrupt */ + struct Interrupt playback_interrupt; + + /** TRUE if the hardware interrupt may Cause() playback_interrupt */ + BOOL playback_interrupt_enabled; + + /** The recording software interrupt */ + struct Interrupt record_interrupt; + + /** TRUE if the hardware interrupt may Cause() playback_interrupt */ + BOOL record_interrupt_enabled; + + + + /*** CAMD support functions **********************************************/ + + /** CAMD transmitter function wrapped as a Hook */ + struct Hook* camd_transmitfunc; + + /** CAMD receiver function wrapped as a Hook */ + struct Hook* camd_receivefunc; + + /** True if CMAD V40 mode */ + ULONG camd_v40; + + + + /*** Playback interrupt variables ****************************************/ + + APTR playback_buffer; + APTR playback_buffer_nonaligned; + APTR playback_buffer_phys; + + /** The mixing buffer (a cyclic buffer filled by AHI) */ + APTR mix_buffer; + + /** The length of each playback buffer in sample frames */ + ULONG current_frames; + + /** The length of each playback buffer in sample bytes */ + ULONG current_bytesize; + + /** Where (inside the cyclic buffer) we're currently writing */ + APTR current_buffer; + + int flip; + int oldflip; + + + + /*** Recording interrupt variables ***************************************/ + + /** The recording buffer (simple double buffering is used */ + APTR record_buffer; + APTR record_buffer_nonaligned; + APTR record_buffer_phys; + + /** Were (inside the recording buffer) the current data is */ + APTR current_record_buffer; + + /** The length of each record buffer in sample bytes */ + ULONG current_record_bytesize; + + int recflip; + + + + /** Analog mixer variables ***********************************************/ + + unsigned char mixerstate; + + + /** The currently selected input */ + UWORD input; + + /** The currently selected output */ + UWORD output; + + /** The current (recording) monitor volume */ + Fixed monitor_volume; + + /** The current (recording) input gain */ + Fixed input_gain; + + /** The current (playback) output volume */ + Fixed output_volume; + + /** The hardware register value corresponding to monitor_volume */ + UWORD monitor_volume_bits; + + /** The hardware register value corresponding to input_gain */ + UWORD input_gain_bits; + + /** The hardware register value corresponding to output_volume */ + UWORD output_volume_bits; + + /** Saved state for AC97 mike */ + UWORD ac97_mic; + + /** Saved state for AC97 cd */ + UWORD ac97_cd; + + /** Saved state for AC97 vide */ + UWORD ac97_video; + + /** Saved state for AC97 aux */ + UWORD ac97_aux; + + /** Saved state for AC97 line in */ + UWORD ac97_linein; + + /** Saved state for AC97 phone */ + UWORD ac97_phone; +}; + +#endif /* AHI_Drivers_Card_DriverData_h */ diff --git a/workbench/devs/AHI/Drivers/CMI8738/Makefile b/workbench/devs/AHI/Drivers/CMI8738/Makefile new file mode 100755 index 0000000000..9705729730 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/Makefile @@ -0,0 +1,19 @@ +CC = gcc +srcdir = /Drivers +common_srcdir = /Drivers/Common + +DRIVER = cmi8738.audio +MODEFILE = CMI8738 + +OBJECTS = driver-init.o cmi8738.o accel.o misc.o interrupt.o ../common/library_card.o gatestubs.o + +CPPFLAGS = -DCPU="\"603e\"" -I${common_srcdir} -I${srcdir} -DAHI +CFLAGS = -W -Wall -Wno-unused -mcpu=603e -c -I$(srcdir)/CMI8738 -DDRIVER="${DRIVER}" -O2 +LDFLAGS = -nostartfiles -lm + +all: $(DRIVER) + +$(DRIVER): $(OBJECTS) + gcc $(LDFLAGS) -o $@ $^ -lm + strip cmi8738.audio + cp $@ DEVS:AHI diff --git a/workbench/devs/AHI/Drivers/CMI8738/README b/workbench/devs/AHI/Drivers/CMI8738/README new file mode 100755 index 0000000000..bf07addc90 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/README @@ -0,0 +1,87 @@ + +CMI8738 AHI driver for OS4 / A1 +------------------------------------- + +The interrupt of the card must be set to 'Level' in +the U-Boot menu. From version 5.7, please check if +the output in the AHI prefs is set to Front (5.7) or +"Line Out" (5.8 or higher) if you don't hear sound. + +The driver is tested with: + +- Sweex 5.1 +- Hercules Muse +- MicroA1 +- Terratec Aureon 5.1 Fun and 5.1 PCI + +It is more than likely that it will work with other CMI8738 based cards. + +Don't forget to set up Unit 0 in the AHI prefs (freq=44100 Hz, +Channels = 10 or more). + +Please send bug reports to info@audio-evolution.com. + +Davy Wentzler + + + +History: + +5.17 (04 Nov 2006): + - Initialization code now locks the device + +5.16 (06 Apr 2006): + - Muted FM + +5.15 (16 Feb 2006): + - Works with old and new memory system + +5.14 (07 Jan 2006): + - Works with new memory system + +5.13 (10 Dec 2005): + - Handles mono modes correctly now. + +5.12 (18 Sep 2005): + - Did a 'greater than' instead of 'greater or equal than' test to + determine which buffer to choose, which could result in any kind + of crackling. Odd that it only occured here at sample rates other + than 44100 Hz. + - Fixed the date in the version string + +5.11 (13 Sep 2005): + - Fix involving recovery of lost interrupts. The sound could get + distorted after Grim Reapers or when starting the network. + Distortion will still take place for a short while, but it + will recover from that. + +5.10: test version + +5.9 (04 Sep 2005): + - The mic input now has a default gain of +20dB. You can turn this + on and off by using the tool 'cmiboost' in the C directory: + 'cmiboost 1' turns it on and 'cmiboost 0' turns it off. + +5.8 (22 Aug 2005): + - CD playback stopped when playing a PCM sound. Fixed. + - Changed name of first output to "Line Out" instead of + "Front" + +5.7 (20 Aug 2005): + - Added S/PDIF out support. S/PDIF in doesn't work yet. + +5.6 (15 Aug 2005): + - Volumes were all set to max, which wasn't the intention. + When you want to set volumes of CD etc., please user Mixer 1.12. + +5.5 (12 Aug 2005): + - Input selection + - Output volume control + - Aux support + + +5.4 (02 Oct 2004): + + - Code clean-up + +5.3: Added reset handler to stop the sound when doing a software reset diff --git a/workbench/devs/AHI/Drivers/CMI8738/accel.c b/workbench/devs/AHI/Drivers/CMI8738/accel.c new file mode 100755 index 0000000000..fdd2dbd61d --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/accel.c @@ -0,0 +1,105 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#include + +#include +#include + +#include "library_card.h" + +/****************************************************************************** +** AHIsub_SetVol ************************************************************** +******************************************************************************/ + +ULONG +_AHIsub_SetVol( UWORD channel, + Fixed volume, + sposition pan, + struct AHIAudioCtrlDrv* AudioCtrl, + ULONG flags, + struct DriverBase* AHIsubBase ) +{ + return AHIS_UNKNOWN; +} + + +/****************************************************************************** +** AHIsub_SetFreq ************************************************************* +******************************************************************************/ + +ULONG +_AHIsub_SetFreq( UWORD channel, + ULONG freq, + struct AHIAudioCtrlDrv* AudioCtrl, + ULONG flags, + struct DriverBase* AHIsubBase ) +{ + return AHIS_UNKNOWN; +} + + +/****************************************************************************** +** AHIsub_SetSound ************************************************************ +******************************************************************************/ + +ULONG +_AHIsub_SetSound( UWORD channel, + UWORD sound, + ULONG offset, + LONG length, + struct AHIAudioCtrlDrv* AudioCtrl, + ULONG flags, + struct DriverBase* AHIsubBase ) +{ + return AHIS_UNKNOWN; +} + + +/****************************************************************************** +** AHIsub_SetEffect *********************************************************** +******************************************************************************/ + +ULONG +_AHIsub_SetEffect( APTR effect, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + return AHIS_UNKNOWN; +} + + +/****************************************************************************** +** AHIsub_LoadSound *********************************************************** +******************************************************************************/ + +ULONG +_AHIsub_LoadSound( UWORD sound, + ULONG type, + APTR info, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + return AHIS_UNKNOWN; +} + + +/****************************************************************************** +** AHIsub_UnloadSound ********************************************************* +******************************************************************************/ + +ULONG +_AHIsub_UnloadSound( UWORD sound, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + return AHIS_UNKNOWN; +} diff --git a/workbench/devs/AHI/Drivers/CMI8738/cmi8738.c b/workbench/devs/AHI/Drivers/CMI8738/cmi8738.c new file mode 100755 index 0000000000..96287f80c8 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/cmi8738.c @@ -0,0 +1,801 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#include + +#undef __USE_INLINE__ +#include + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "library_card.h" +#include "regs.h" +#include "misc.h" +#include "DriverData.h" + +extern struct UtilityIFace* IUtility; +extern struct AHIsubIFace* IAHIsub; +extern struct MMUIFace* IMMU; +extern int z; + +/****************************************************************************** +** Globals ******************************************************************** +******************************************************************************/ + +#define FREQUENCIES 8 + +static const ULONG Frequencies[ FREQUENCIES ] = +{ + 5512, + 8000, // µ- and A-Law + 11025, // CD/4 + 16000, // DAT/3 + 22050, // CD/2 + 32000, // DAT/1.5 + 44100, // CD + 48000 // DAT +}; + + +static const ULONG FrequencyBits[ FREQUENCIES ] = +{ + 0, + 4, + 1, + 5, + 2, + 6, + 3, + 7 +}; + + + + +#define INPUTS 5 + +static const STRPTR Inputs[ INPUTS ] = +{ + "Line in", + "Mic", + "CD", + "Aux", + "S/PDIF" +}; + +#if 0 +/* Not static since it's used in misc.c too */ +const UWORD InputBits[ INPUTS ] = +{ + AC97_RECMUX_LINE, + AC97_RECMUX_MIC, + AC97_RECMUX_CD, + AC97_RECMUX_VIDEO, + AC97_RECMUX_AUX, + AC97_RECMUX_STEREO_MIX, + AC97_RECMUX_MONO_MIX, + AC97_RECMUX_PHONE +}; +#endif + + +#define OUTPUTS 2 + +static const STRPTR Outputs[ OUTPUTS ] = +{ + "Line Out", + "Digital Out" +}; + + + +/****************************************************************************** +** AHIsub_AllocAudio ********************************************************** +******************************************************************************/ + +ULONG +_AHIsub_AllocAudio( struct TagItem* taglist, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + + int card_num; + ULONG ret; + int i, freq = 6; + + card_num = ( IUtility->GetTagData( AHIDB_AudioID, 0, taglist) & 0x0000f000 ) >> 12; + + if( card_num >= CardBase->cards_found || + CardBase->driverdatas[ card_num ] == NULL ) + { + IExec->DebugPrintF("no date for card = %ld\n", card_num); + Req( "No CardData for card %ld.", card_num ); + return AHISF_ERROR; + } + else + { + struct CardData* card; + BOOL in_use; + struct PCIDevice *dev; + + card = CardBase->driverdatas[ card_num ]; + AudioCtrl->ahiac_DriverData = card; + + IExec->ObtainSemaphore( &CardBase->semaphore ); + in_use = ( card->audioctrl != NULL ); + if( !in_use ) + { + card->audioctrl = AudioCtrl; + } + IExec->ReleaseSemaphore( &CardBase->semaphore ); + + if( in_use ) + { + return AHISF_ERROR; + } + + dev = card->pci_dev; + card->playback_interrupt_enabled = FALSE; + card->record_interrupt_enabled = FALSE; + + for( i = 1; i < FREQUENCIES; i++ ) + { + if( (ULONG) Frequencies[ i ] > AudioCtrl->ahiac_MixFreq ) + { + if ( ( AudioCtrl->ahiac_MixFreq - (LONG) Frequencies[ i - 1 ] ) < ( (LONG) Frequencies[ i ] - AudioCtrl->ahiac_MixFreq ) ) + { + freq = i-1; + break; + } + else + { + freq = i; + break; + } + } + } + + } + + ret = AHISF_KNOWHIFI | AHISF_KNOWSTEREO | AHISF_MIXING | AHISF_TIMING; + + + for( i = 0; i < FREQUENCIES; ++i ) + { + if( AudioCtrl->ahiac_MixFreq == Frequencies[ i ] ) + { + ret |= AHISF_CANRECORD; + break; + } + } + + return ret; +} + + + +/****************************************************************************** +** AHIsub_FreeAudio *********************************************************** +******************************************************************************/ + +void +_AHIsub_FreeAudio( struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + struct CardData* card = (struct CardData*) AudioCtrl->ahiac_DriverData; + + if( card != NULL ) + { + IExec->ObtainSemaphore( &CardBase->semaphore ); + if( card->audioctrl == AudioCtrl ) + { + // Release it if we own it. + card->audioctrl = NULL; + } + IExec->ReleaseSemaphore( &CardBase->semaphore ); + + AudioCtrl->ahiac_DriverData = NULL; + } +} + + +/****************************************************************************** +** AHIsub_Disable ************************************************************* +******************************************************************************/ + +void +_AHIsub_Disable( struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + + // V6 drivers do not have to preserve all registers + + IExec->Disable(); +} + + +/****************************************************************************** +** AHIsub_Enable ************************************************************** +******************************************************************************/ + +void +_AHIsub_Enable( struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + + // V6 drivers do not have to preserve all registers + + IExec->Enable(); +} + + +/****************************************************************************** +** AHIsub_Start *************************************************************** +******************************************************************************/ + +ULONG +_AHIsub_Start( ULONG flags, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + struct CardData* card = (struct CardData*) AudioCtrl->ahiac_DriverData; + struct PCIDevice *dev = card->pci_dev; + UWORD PlayCtrlFlags = 0, RecCtrlFlags = 0; + ULONG dma_buffer_size = 0; + int i, freqbit = 6; + unsigned long phys_addr; + APTR stack; + + /* Stop playback/recording, free old buffers (if any) */ + //IAHIsub->AHIsub_Stop( flags, AudioCtrl ); + + for( i = 0; i < FREQUENCIES; ++i ) + { + if( AudioCtrl->ahiac_MixFreq == Frequencies[ i ] ) + { + freqbit = i; + break; + } + } + + card->mixerstate = cmimix_rd(dev, card, CMPCI_SB16_MIXER_OUTMIX); + + if( flags & AHISF_PLAY ) + { + ULONG dma_sample_frame_size; + int i; + short *a; + unsigned short cod, ChannelsFlag = CMPCI_REG_FORMAT_16BIT; + + //WriteMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET | CMPCI_REG_CH1_RESET); + //ClearMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET | CMPCI_REG_CH1_RESET); + + /* Allocate a new mixing buffer. Note: The buffer must be cleared, since + it might not be filled by the mixer software interrupt because of + pretimer/posttimer! */ + + card->mix_buffer = IExec->AllocVec( AudioCtrl->ahiac_BuffSize, MEMF_PUBLIC | MEMF_CLEAR ); + + if( card->mix_buffer == NULL ) + { + Req( "Unable to allocate %ld bytes for mixing buffer.", AudioCtrl->ahiac_BuffSize ); + return AHIE_NOMEM; + } + + /* Allocate a buffer large enough for 16-bit double-buffered + playback (mono or stereo) */ + + + if( AudioCtrl->ahiac_Flags & AHIACF_STEREO ) + { + dma_sample_frame_size = 4; + dma_buffer_size = AudioCtrl->ahiac_MaxBuffSamples * dma_sample_frame_size; + ChannelsFlag |= CMPCI_REG_FORMAT_STEREO; + } + else + { + dma_sample_frame_size = 2; + dma_buffer_size = AudioCtrl->ahiac_MaxBuffSamples * dma_sample_frame_size; + } + + //IExec->DebugPrintF("dma_buffer_size = %ld, AudioCtrl->ahiac_BuffSize = %ld, AudioCtrl->ahiac_MaxBuffSamples = %ld\nAudioCtrl->ahiac_BuffSamples = %ld", dma_buffer_size, AudioCtrl->ahiac_BuffSize, AudioCtrl->ahiac_MaxBuffSamples, AudioCtrl->ahiac_BuffSamples); + + card->playback_buffer = pci_alloc_consistent(dma_buffer_size * 2, &card->playback_buffer_nonaligned); + + if (!card->playback_buffer) + { + Req( "Unable to allocate playback buffer." ); + return AHIE_NOMEM; + } + + card->current_bytesize = dma_buffer_size; + card->current_frames = AudioCtrl->ahiac_MaxBuffSamples; + card->current_buffer = card->playback_buffer + card->current_bytesize; + card->playback_interrupt_enabled = TRUE; + + card->flip = 0; + card->oldflip = 0; + + WritePartialMask(dev, card, CMPCI_REG_FUNC_1, CMPCI_REG_DAC_FS_SHIFT, CMPCI_REG_DAC_FS_MASK, FrequencyBits[freqbit]); + WritePartialMask(dev, card, CMPCI_REG_CHANNEL_FORMAT, CMPCI_REG_CH0_FORMAT_SHIFT, CMPCI_REG_CH0_FORMAT_MASK, ChannelsFlag); + WriteMask(dev, card, CMPCI_REG_CHANNEL_FORMAT, (13 << 1)); + + if (IFakeDMA == NULL) + { + stack = IExec->SuperState(); + card->playback_buffer_phys = IMMU->GetPhysicalAddress(card->playback_buffer); + IExec->UserState(stack); + } + else + card->playback_buffer_phys = card->playback_buffer; + //IExec->DebugPrintF("ADDR = %lx\n", card->playback_buffer_phys); + + dev->OutLong(card->iobase + CMPCI_REG_DMA0_BASE, card->playback_buffer_phys); + dev->OutWord(card->iobase + CMPCI_REG_DMA0_LENGTH, (dma_buffer_size / dma_sample_frame_size) * 2 - 1); + dev->OutWord(card->iobase + CMPCI_REG_DMA0_INTLEN, (dma_buffer_size / dma_sample_frame_size) - 1); + + card->is_playing = TRUE; + } + + if( flags & AHISF_RECORD ) + { + UWORD mask; + ULONG ChannelsFlag = CMPCI_REG_FORMAT_16BIT; + unsigned char byte; + + card->current_record_bytesize = RECORD_BUFFER_SAMPLES * 4; + + /* Allocate a new recording buffer (page aligned!) */ + card->record_buffer = pci_alloc_consistent(card->current_record_bytesize * 2, &card->record_buffer_nonaligned); + + if( card->record_buffer == NULL ) + { + Req( "Unable to allocate %ld bytes for the recording buffer.", card->current_record_bytesize); + return AHIE_NOMEM; + } + + SaveMixerState( card ); + UpdateMonitorMixer( card ); + + + + switch (card->input) + { + case 0: // line + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, card->mixerstate | CMPCI_SB16_SW_LINE); + break; + + case 1: // mic + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, card->mixerstate | CMPCI_SB16_SW_MIC); + break; + + case 2: // CD + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, card->mixerstate | CMPCI_SB16_SW_CD); + break; + + case 3: // Aux + byte = dev->InByte(card->iobase + CMPCI_REG_MIXER25); + byte |= 0x30; + dev->OutByte(card->iobase + CMPCI_REG_MIXER25, byte); // unmute Aux + break; + + default: + break; + } + + card->record_interrupt_enabled = TRUE; + + card->recflip = 0; + + WritePartialMask(dev, card, CMPCI_REG_FUNC_1, CMPCI_REG_ADC_FS_SHIFT, CMPCI_REG_ADC_FS_MASK, FrequencyBits[freqbit]); + WritePartialMask(dev, card, CMPCI_REG_CHANNEL_FORMAT, CMPCI_REG_CH1_FORMAT_SHIFT, CMPCI_REG_CH1_FORMAT_MASK, CMPCI_REG_FORMAT_16BIT | CMPCI_REG_FORMAT_STEREO); + + if (IFakeDMA == NULL) + { + stack = IExec->SuperState(); + card->record_buffer_phys = IMMU->GetPhysicalAddress(card->record_buffer); + IExec->UserState(stack); + } + else + card->record_buffer_phys = card->record_buffer; + + + dev->OutLong(card->iobase + CMPCI_REG_DMA1_BASE, card->record_buffer_phys); + //IDOS->Delay(1); + dev->OutWord(card->iobase + CMPCI_REG_DMA1_LENGTH, (card->current_record_bytesize / 4) * 2 - 1); + //IDOS->Delay(1); + dev->OutWord(card->iobase + CMPCI_REG_DMA1_INTLEN, (card->current_record_bytesize / 4) - 1); + //IDOS->Delay(1); + card->current_record_buffer = card->record_buffer + card->current_record_bytesize; + card->is_recording = TRUE; + } + + if( flags & AHISF_PLAY ) + { + z = 0; + WriteMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); + WriteMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + } + + if( flags & AHISF_RECORD ) + { + WriteMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE); + WriteMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + } + + return AHIE_OK; +} + + +/****************************************************************************** +** AHIsub_Update ************************************************************** +******************************************************************************/ + +void +_AHIsub_Update( ULONG flags, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + struct CardData* card = (struct CardData*) AudioCtrl->ahiac_DriverData; +#if 0 + card->current_frames = AudioCtrl->ahiac_BuffSamples; + + if( AudioCtrl->ahiac_Flags & AHIACF_STEREO ) + { + card->current_bytesize = card->current_frames * 4; + } + else + { + card->current_bytesize = card->current_frames * 2; + } +#endif +} + + +/****************************************************************************** +** AHIsub_Stop **************************************************************** +******************************************************************************/ + +void +_AHIsub_Stop( ULONG flags, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + struct CardData* card = (struct CardData*) AudioCtrl->ahiac_DriverData; + struct PCIDevice *dev = card->pci_dev; + + + if( flags & AHISF_PLAY ) + { + unsigned short play_ctl; + + card->is_playing= FALSE; + + ClearMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + ClearMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); + + if (card->current_bytesize > 0) + pci_free_consistent(card->playback_buffer_nonaligned); + + card->current_bytesize = 0; + card->current_frames = 0; + card->current_buffer = NULL; + + if ( card->mix_buffer) + IExec->FreeVec( card->mix_buffer ); + card->mix_buffer = NULL; + card->playback_interrupt_enabled = FALSE; + card->current_bytesize = 0; + //IExec->DebugPrintF("#IRQ's = %ld\n", z); + } + + if( flags & AHISF_RECORD && card->is_recording) + { + unsigned short rec_ctl, val; + unsigned char byte; + + ClearMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + ClearMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE); + + switch (card->input) + { + case 0: // line + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, card->mixerstate); + break; + + case 1: // mic + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, card->mixerstate); + break; + + case 2: // CD + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, card->mixerstate); + break; + + case 3: // Aux + byte = dev->InByte(card->iobase + CMPCI_REG_MIXER25); + dev->OutByte(card->iobase + CMPCI_REG_MIXER25, byte & ~0x30); // mute Aux + break; + + default: + break; + } + + if( card->record_buffer != NULL ) + { + pci_free_consistent( card->record_buffer_nonaligned); + } + + card->record_buffer = NULL; + card->current_record_bytesize = 0; + + card->is_recording = FALSE; + card->record_interrupt_enabled = FALSE; + } + + +} + + +/****************************************************************************** +** AHIsub_GetAttr ************************************************************* +******************************************************************************/ + +LONG +_AHIsub_GetAttr( ULONG attribute, + LONG argument, + LONG def, + struct TagItem* taglist, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + int i; + + + switch( attribute ) + { + case AHIDB_Bits: + return 16; + + case AHIDB_Frequencies: + return FREQUENCIES; + + case AHIDB_Frequency: // Index->Frequency + return (LONG) Frequencies[ argument ]; + + case AHIDB_Index: // Frequency->Index + if( argument <= (LONG) Frequencies[ 0 ] ) + { + return 0; + } + + if( argument >= (LONG) Frequencies[ FREQUENCIES - 1 ] ) + { + return FREQUENCIES-1; + } + + for( i = 1; i < FREQUENCIES; i++ ) + { + if( (LONG) Frequencies[ i ] > argument ) + { + if( ( argument - (LONG) Frequencies[ i - 1 ] ) < ( (LONG) Frequencies[ i ] - argument ) ) + { + return i-1; + } + else + { + return i; + } + } + } + + return 0; // Will not happen + + case AHIDB_Author: + return (LONG) "Davy Wentzler"; + + case AHIDB_Copyright: + return (LONG) "(C) Davy Wentzler"; + + case AHIDB_Version: + return (LONG) LibIDString; + + case AHIDB_Annotation: + return (LONG) + "OS4 PPC native driver"; + + case AHIDB_Record: + return TRUE; + + case AHIDB_FullDuplex: + return TRUE; + + case AHIDB_Realtime: + return TRUE; + + case AHIDB_MaxRecordSamples: + return RECORD_BUFFER_SAMPLES; + + /* formula's: + #include + + unsigned long res = (unsigned long) (0x10000 * pow (10.0, dB / 20.0)); + double dB = 20.0 * log10(0xVALUE / 65536.0); + + printf("dB = %f, res = %lx\n", dB, res);*/ + + case AHIDB_MinMonitorVolume: + return 0x00000; + + case AHIDB_MaxMonitorVolume: + return 0x00000; + + case AHIDB_MinInputGain: + return 0x10000; // 0.0 dB gain + + case AHIDB_MaxInputGain: + return 0x10000; // 0 dB gain + + case AHIDB_MinOutputVolume: + return 0x34; // -62 dB + + case AHIDB_MaxOutputVolume: + return 0x10000; // 0 dB + + case AHIDB_Inputs: + return INPUTS; + + case AHIDB_Input: + return (LONG) Inputs[ argument ]; + + case AHIDB_Outputs: + return OUTPUTS; + + case AHIDB_Output: + return (LONG) Outputs[ argument ]; + + default: + return def; + } +} + + +/****************************************************************************** +** AHIsub_HardwareControl ***************************************************** +******************************************************************************/ + +ULONG +_AHIsub_HardwareControl( ULONG attribute, + LONG argument, + struct AHIAudioCtrlDrv* AudioCtrl, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + struct CardData* card = (struct CardData*) AudioCtrl->ahiac_DriverData; + struct PCIDevice *dev = card->pci_dev; + unsigned char byte; + + switch( attribute ) + { + case AHIC_MonitorVolume: + card->monitor_volume = Linear2MixerGain( (Fixed) argument, &card->monitor_volume_bits ); + //IExec->DebugPrintF("card->monitor_volume = %lu, %lx\n", card->monitor_volume, card->monitor_volume); + if( card->is_recording ) + { + UpdateMonitorMixer( card ); + } + return TRUE; + + case AHIC_MonitorVolume_Query: + return card->monitor_volume; + + case AHIC_InputGain: + card->input_gain = Linear2RecordGain( (Fixed) argument, &card->input_gain_bits ); + //codec_write(card, AC97_RECORD_GAIN, card->input_gain_bits ); + return TRUE; + + case AHIC_InputGain_Query: + return card->input_gain; + + case AHIC_OutputVolume: + { + double dB = 20.0 * log10((Fixed) argument / 65536.0); + unsigned int val = 0xFF - ( ((unsigned int)(-dB/2)) << 3); + cmimix_wr(dev, card, 0x30, val); + cmimix_wr(dev, card, 0x31, val); + return TRUE; + } + case AHIC_OutputVolume_Query: + return card->output_volume; + + case AHIC_Input: + card->input = argument; + + switch (card->input) + { + case 0: // line + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_L, (CMPCI_SB16_MIXER_LINE_SRC_R << 1) ); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_R, CMPCI_SB16_MIXER_LINE_SRC_R ); + break; + + case 1: // mic + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_L, CMPCI_SB16_MIXER_MIC_SRC); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_R, CMPCI_SB16_MIXER_MIC_SRC); + break; + + case 2: // CD + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_L, (CMPCI_SB16_MIXER_CD_SRC_R << 1) ); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_R, CMPCI_SB16_MIXER_CD_SRC_R ); + break; + + case 3: // Aux + byte = dev->InByte(card->iobase + CMPCI_REG_MIXER25); + dev->OutByte(card->iobase + CMPCI_REG_MIXER25, byte | 0xC0); // rec source Aux + break; + + case 4: // SPDIF + + break; + + default: + break; + } + + if( card->is_recording ) + { + UpdateMonitorMixer( card ); + } + + return TRUE; + + case AHIC_Input_Query: + return card->input; + + case AHIC_Output: + card->output = argument; + + if( card->output == 0 ) + { + ClearMask(dev, card, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIFOUT_DAC | CMPCI_REG_SPDIF0_ENABLE); + ClearMask(dev, card, CMPCI_REG_LEGACY_CTRL, CMPCI_REG_XSPDIF_ENABLE); + } + else + { + WriteMask(dev, card, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIFOUT_DAC | CMPCI_REG_SPDIF0_ENABLE); + WriteMask(dev, card, CMPCI_REG_LEGACY_CTRL, CMPCI_REG_XSPDIF_ENABLE); + } + return TRUE; + + case AHIC_Output_Query: + return card->output; + + default: + return FALSE; + } +} diff --git a/workbench/devs/AHI/Drivers/CMI8738/driver-init.c b/workbench/devs/AHI/Drivers/CMI8738/driver-init.c new file mode 100755 index 0000000000..c2c3dad7eb --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/driver-init.c @@ -0,0 +1,276 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#include + +#include + +#undef __USE_INLINE__ +#include + +#include +#include +#include + +#include "library_card.h" +#include "version.h" +#include "misc.h" +#include "regs.h" +#include "DriverData.h" + +struct DriverBase* AHIsubBase; + +struct Library* ExpansionBase = NULL; +struct ExpansionIFace* IExpansion = NULL; +//struct DOSIFace* IDOS = NULL; +struct UtilityIFace* IUtility = NULL; +struct AHIsubIFace* IAHIsub = NULL; +struct PCIIFace* IPCI = NULL; +struct MMUIFace* IMMU = NULL; +struct Library *FakeDMABase = NULL; +struct FakeDMAIFace *IFakeDMA = NULL; + + +#define VENDOR_ID 0x13F6 +#define DEVICE_ID 0x0111 +#define CARD_STRING "CMI8738" + + +/****************************************************************************** +** Custom driver init ********************************************************* +******************************************************************************/ + +BOOL +DriverInit( struct DriverBase* ahisubbase ) +{ + struct CardBase* CardBase = (struct CardBase*) ahisubbase; + struct PCIDevice *dev; + int card_no; + + //IExec->DebugPrintF("DRIVERINIT\n"); + + CardBase->driverdatas = 0; + CardBase->cards_found = 0; + AHIsubBase = ahisubbase; + + DOSBase = IExec->OpenLibrary( DOSNAME, 37 ); + + if( DOSBase == NULL ) + { + Req( "Unable to open 'dos.library' version 37.\n" ); + return FALSE; + } + + if ((IDOS = (struct DOSIFace *) IExec->GetInterface((struct Library *) DOSBase, "main", 1, NULL)) == NULL) + { + Req("Couldn't open IDOS interface!\n"); + return FALSE; + } + + ExpansionBase = IExec->OpenLibrary( "expansion.library", 1 ); + if( ExpansionBase == NULL ) + { + Req( "Unable to open 'expansion.library' version 1.\n" ); + return FALSE; + } + if ((IExpansion = (struct ExpansionIFace *) IExec->GetInterface((struct Library *) ExpansionBase, "main", 1, NULL)) == NULL) + { + Req("Couldn't open IExpansion interface!\n"); + return FALSE; + } + + if ((IPCI = (struct PCIIFace *) IExec->GetInterface((struct Library *) ExpansionBase, "pci", 1, NULL)) == NULL) + { + Req("Couldn't open IPCI interface!\n"); + return FALSE; + } + + if ((IAHIsub = (struct AHIsubIFace *) IExec->GetInterface((struct Library *) AHIsubBase, "main", 1, NULL)) == NULL) + { + Req("Couldn't open IAHIsub interface!\n"); + return FALSE; + } + + if ((IUtility = (struct UtilityIFace *) IExec->GetInterface((struct Library *) UtilityBase, "main", 1, NULL)) == NULL) + { + Req("Couldn't open IUtility interface!\n"); + return FALSE; + } + + if ((IMMU = (struct MMUIFace *) IExec->GetInterface((struct Library *) SysBase, "mmu", 1, NULL)) == NULL) + { + Req("Couldn't open IMMU interface!\n"); + return FALSE; + } + + if(!( FakeDMABase = (struct Library *)IExec->OpenLibrary( "fakedma.library", 52L ))) + { + IExec->DebugPrintF("No fakemda.library found. Using PIO\n"); + } + else + { + if(!( IFakeDMA = (struct FakeDMAIFace *)IExec->GetInterface( FakeDMABase, "main", 1L, NULL ))) + { + IExec->CloseLibrary( FakeDMABase ); + } + } + + IExec->InitSemaphore( &CardBase->semaphore ); + + + /*** Count cards ***********************************************************/ + + CardBase->cards_found = 0; + dev = NULL; + + if ( (dev = IPCI->FindDeviceTags( FDT_VendorID, VENDOR_ID, // while + FDT_DeviceID, DEVICE_ID, //FDT_INDEX, CardBase->cards_found, + TAG_DONE ) ) != NULL ) + { + ++CardBase->cards_found; +// IExec->DebugPrintF("%s found! :-)\n", CARD_STRING); + } + + // Fail if no hardware is present (prevents the audio modes from being added to + // the database if the driver cannot be used). + + if(CardBase->cards_found == 0 ) + { + IExec->DebugPrintF("No %s found! :-(\n", CARD_STRING); + Req( "No card present.\n" ); + return FALSE; + } + + if (dev->Lock(EXCLUSIVE_LOCK) == FALSE) + { + IExec->DebugPrintF("CMI8738: Couldn't lock the device\n"); + return FALSE; + } + + /*** CAMD ******************************************************************/ +#if 0 + IExec->InitSemaphore( &CardBase->camd.Semaphore ); + CardBase->camd.Semaphore.ss_Link.ln_Pri = 0; + + CardBase->camd.Semaphore.ss_Link.ln_Name = Card_CAMD_SEMAPHORE; + IExec->AddSemaphore( &CardBase->camd.Semaphore ); + + CardBase->camd.Cards = CardBase->cards_found; + CardBase->camd.Version = VERSION; + CardBase->camd.Revision = REVISION; + + + CardBase->camd.OpenPortFunc.h_Entry = OpenCAMDPort; + CardBase->camd.OpenPortFunc.h_SubEntry = NULL; + CardBase->camd.OpenPortFunc.h_Data = NULL; + + CardBase->camd.ClosePortFunc.h_Entry = (HOOKFUNC) CloseCAMDPort; + CardBase->camd.ClosePortFunc.h_SubEntry = NULL; + CardBase->camd.ClosePortFunc.h_Data = NULL; + + CardBase->camd.ActivateXmitFunc.h_Entry = (HOOKFUNC) ActivateCAMDXmit; + CardBase->camd.ActivateXmitFunc.h_SubEntry = NULL; + CardBase->camd.ActivateXmitFunc.h_Data = NULL; +#endif + + + /*** Allocate and init all cards *******************************************/ + + CardBase->driverdatas = IExec->AllocVec( sizeof( *CardBase->driverdatas ) * + CardBase->cards_found, + MEMF_PUBLIC ); + + if( CardBase->driverdatas == NULL ) + { + Req( "Out of memory." ); + return FALSE; + } + + card_no = 0; + + if( ( dev = IPCI->FindDeviceTags( FDT_VendorID, VENDOR_ID, // while + FDT_DeviceID, DEVICE_ID, //FDT_INDEX, CardBase->cards_found, + TAG_DONE ) ) != NULL ) + { + CardBase->driverdatas[ card_no ] = AllocDriverData( dev, AHIsubBase ); + ++card_no; + } + + //IExec->DebugPrintF("exit init\n"); + return TRUE; +} + + +/****************************************************************************** +** Custom driver clean-up ***************************************************** +******************************************************************************/ + +VOID +DriverCleanup( struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + int i; + + +#if 0 + if( CardBase->camd.Semaphore.ss_Link.ln_Name != NULL ) + { + IExec->ObtainSemaphore( &CardBase->camd.Semaphore ); + IExec->RemSemaphore( &CardBase->camd.Semaphore ); + IExec->ReleaseSemaphore( &CardBase->camd.Semaphore ); + } +#endif + for( i = 0; i < CardBase->cards_found; ++i ) + { + if (CardBase->driverdatas) + { + if (CardBase->driverdatas) + { + CardBase->driverdatas[i]->pci_dev->Unlock(); + FreeDriverData( CardBase->driverdatas[ i ], AHIsubBase ); + } + } + } + + if (CardBase->driverdatas) + IExec->FreeVec( CardBase->driverdatas ); + + if (IFakeDMA) + IExec->DropInterface( (struct Interface *)IFakeDMA ); + + if (FakeDMABase) + IExec->CloseLibrary( FakeDMABase ); + + if (IUtility) + IExec->DropInterface( (struct Interface *) IUtility); + + if (IExpansion) + IExec->DropInterface( (struct Interface *) IExpansion); + + if (IPCI) + IExec->DropInterface( (struct Interface *) IPCI); + + if (IAHIsub) + IExec->DropInterface( (struct Interface *) IAHIsub); + + if (IDOS) + IExec->DropInterface( (struct Interface *) IDOS); + + if (ExpansionBase) + IExec->CloseLibrary( (struct Library*) ExpansionBase); + + if (UtilityBase) + IExec->CloseLibrary( (struct Library*) UtilityBase); + + if (DOSBase) + IExec->CloseLibrary( (struct Library*) DOSBase); +} + diff --git a/workbench/devs/AHI/Drivers/CMI8738/interrupt.c b/workbench/devs/AHI/Drivers/CMI8738/interrupt.c new file mode 100755 index 0000000000..0547fa1c28 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/interrupt.c @@ -0,0 +1,236 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#include + +#undef __USE_INLINE__ +#include +#include +#include +#include +#include "library.h" +#include "regs.h" +#include "interrupt.h" +#include "misc.h" + +#define min(a,b) ((a)<(b)?(a):(b)) + +int z = 0; + + +//struct tester t[10]; + + +/****************************************************************************** +** Hardware interrupt handler ************************************************* +******************************************************************************/ + + +LONG +CardInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct CardData* card ) +{ + struct AHIAudioCtrlDrv* AudioCtrl = card->audioctrl; + struct DriverBase* AHIsubBase = (struct DriverBase*) card->ahisubbase; + struct PCIDevice *dev = (struct PCIDevice * ) card->pci_dev; + + ULONG intreq; + LONG handled = 0; + + while ( (( intreq = ( dev->InLong(card->iobase + CMPCI_REG_INTR_STATUS ) ) ) & CMPCI_REG_ANY_INTR )!= 0 ) + { + //IExec->DebugPrintF("INT %lx\n", intreq); + if( intreq & CMPCI_REG_CH0_INTR && AudioCtrl != NULL ) + { + unsigned long diff = dev->InLong(card->iobase + CMPCI_REG_DMA0_BASE) - (unsigned long) card->playback_buffer_phys; + + ClearMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + + /* + z++; + if (z < 10) + { + t[z].diff = diff; + t[z].flip = card->flip; + t[z].oldflip = card->oldflip; + }*/ + + /*if ((diff > 50 && diff < card->current_bytesize) || + (diff > card->current_bytesize + 50 && diff < 2 * card->current_bytesize)) + IExec->DebugPrintF("Delayed IRQ %lu %lu\n", diff % card->current_bytesize, card->current_bytesize);*/ + + + if (diff >= card->current_bytesize) //card->flip == 0) // just played buf 1 + { + if (card->flip == 1) + IExec->DebugPrintF("A:Missed IRQ! diff = %lu\n", diff); + + card->flip = 1; + card->current_buffer = card->playback_buffer; + } + else // just played buf 2 + { + if (card->flip == 0) + IExec->DebugPrintF("B:Missed IRQ! diff = %lu\n", diff); + + card->flip = 0; + card->current_buffer = (APTR) ((long) card->playback_buffer + card->current_bytesize); + } + + /*z++; + if (z == 25) + z = 0;*/ + + card->playback_interrupt_enabled = FALSE; + IExec->Cause( &card->playback_interrupt ); + } + + if( intreq & CMPCI_REG_CH1_INTR && AudioCtrl != NULL ) + { + ClearMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + + /*if (z == 0) + IExec->DebugPrintF("rec\n");*/ + z++; + /*if (z == 30) + z = 0;*/ + + if( card->record_interrupt_enabled ) + { + /* Invoke softint to convert and feed AHI with the new sample data */ + + if (card->recflip == 0) // just played buf 1 + { + card->recflip = 1; + card->current_record_buffer = card->record_buffer; + } + else // just played buf 2 + { + card->recflip = 0; + card->current_record_buffer = (APTR) ((long) card->record_buffer + card->current_record_bytesize); + } + + card->record_interrupt_enabled = FALSE; + IExec->Cause( &card->record_interrupt ); + } + } + + handled = 1; + + } + + return handled; +} + + +/****************************************************************************** +** Playback interrupt handler ************************************************* +******************************************************************************/ + +void +PlaybackInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct CardData* card ) +{ + struct AHIAudioCtrlDrv* AudioCtrl = card->audioctrl; + struct DriverBase* AHIsubBase = (struct DriverBase*) card->ahisubbase; + struct PCIDevice *dev = (struct PCIDevice * ) card->pci_dev; + + if( card->mix_buffer != NULL && card->current_buffer != NULL ) + { + BOOL skip_mix; + + WORD* src; + WORD* dst; + size_t skip; + size_t samples; + int i; + + skip_mix = IUtility->CallHookPkt( AudioCtrl->ahiac_PreTimerFunc, (Object*) AudioCtrl, 0 ); + IUtility->CallHookPkt( AudioCtrl->ahiac_PlayerFunc, (Object*) AudioCtrl, NULL ); + + //IExec->DebugPrintF("skip_mix = %d\n", skip_mix); + + if( ! skip_mix ) + { + IUtility->CallHookPkt( AudioCtrl->ahiac_MixerFunc, (Object*) AudioCtrl, card->mix_buffer ); + } + + /* Now translate and transfer to the DMA buffer */ + + + skip = ( AudioCtrl->ahiac_Flags & AHIACF_HIFI ) ? 2 : 1; + samples = card->current_bytesize >> 1; + + src = card->mix_buffer; + dst = card->current_buffer; + + i = samples; + + while( i > 0 ) + { + *dst = ( ( *src & 0xff ) << 8 ) | ( ( *src & 0xff00 ) >> 8 ); + + src += skip; + dst += 1; + + --i; + } + + IExec->CacheClearE( card->current_buffer, (ULONG) dst - (ULONG) card->current_buffer, CACRF_ClearD ); + + IUtility->CallHookPkt( AudioCtrl->ahiac_PostTimerFunc, (Object*) AudioCtrl, 0 ); + } + + WriteMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + card->playback_interrupt_enabled = TRUE; +} + + +/****************************************************************************** +** Record interrupt handler *************************************************** +******************************************************************************/ + +void +RecordInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct CardData* card ) +{ + struct AHIAudioCtrlDrv* AudioCtrl = card->audioctrl; + struct DriverBase* AHIsubBase = (struct DriverBase*) card->ahisubbase; + struct PCIDevice *dev = (struct PCIDevice * ) card->pci_dev; + + struct AHIRecordMessage rm = + { + AHIST_S16S, + card->current_record_buffer, + RECORD_BUFFER_SAMPLES + }; + + int i = 0, shorts = card->current_record_bytesize / 2; + WORD* ptr = (WORD *) card->current_record_buffer; + + + IExec->CacheClearE( card->current_record_buffer, card->current_record_bytesize, CACRF_ClearD); + + while( i < shorts ) + { + *ptr = ( ( *ptr & 0xff ) << 8 ) | ( ( *ptr & 0xff00 ) >> 8 ); + + ++i; + ++ptr; + } + + IUtility->CallHookPkt( AudioCtrl->ahiac_SamplerFunc, (Object*) AudioCtrl, &rm ); + + IExec->CacheClearE( card->current_record_buffer, card->current_record_bytesize, CACRF_ClearD); + + + WriteMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + card->record_interrupt_enabled = TRUE; +} + + diff --git a/workbench/devs/AHI/Drivers/CMI8738/interrupt.h b/workbench/devs/AHI/Drivers/CMI8738/interrupt.h new file mode 100755 index 0000000000..a1cb3aaf69 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/interrupt.h @@ -0,0 +1,28 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#ifndef AHI_Drivers_interrupt_h +#define AHI_Drivers_interrupt_h + +#include + +#include "DriverData.h" + +LONG +CardInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct CardData* dd ); + +void +PlaybackInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct CardData* dd ); + +void +RecordInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct CardData* dd ); + +#endif /* AHI_Drivers_interrupt_h */ diff --git a/workbench/devs/AHI/Drivers/CMI8738/misc.c b/workbench/devs/AHI/Drivers/CMI8738/misc.c new file mode 100755 index 0000000000..e4831e6089 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/misc.c @@ -0,0 +1,583 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#include + +#include +#ifdef __amigaos4__ +#undef __USE_INLINE__ +#include +#else +#include +#include +#endif + +#include +#include +#include +#include + +#include "library_card.h" +#include "regs.h" +#include "interrupt.h" +#include "misc.h" +#include "DriverData.h" + +#define CACHELINE_SIZE 32 + +/* Global in Card.c */ +extern const UWORD InputBits[]; +extern struct DOSIFace *IDOS; +extern struct FakeDMAIFace *IFakeDMA; + +/* Public functions in main.c */ +int card_init(struct CardData *card); +void card_cleanup(struct CardData *card); +void AddResetHandler(struct CardData *card); + + +void WritePartialMask(struct PCIDevice *dev, struct CardData* card, unsigned long reg, unsigned long shift, unsigned long mask, unsigned long val) +{ + ULONG tmp; + + tmp = dev->InLong(card->iobase + reg); + tmp &= ~(mask << shift); + tmp |= val << shift; + dev->OutLong(card->iobase + reg, tmp); +} + + +void ClearMask(struct PCIDevice *dev, struct CardData* card, unsigned long reg, unsigned long mask) +{ + ULONG tmp; + + tmp = dev->InLong(card->iobase + reg); + tmp &= ~mask; + dev->OutLong(card->iobase + reg, tmp); +} + + +void WriteMask(struct PCIDevice *dev, struct CardData* card, unsigned long reg, unsigned long mask) +{ + ULONG tmp; + + tmp = dev->InLong(card->iobase + reg); + tmp |= mask; + dev->OutLong(card->iobase + reg, tmp); +} + +void cmimix_wr(struct PCIDevice *dev, struct CardData* card, unsigned char port, unsigned char val) +{ + dev->OutByte(card->iobase + CMPCI_REG_SBADDR, port); + dev->OutByte(card->iobase + CMPCI_REG_SBDATA, val); +} + +unsigned char cmimix_rd(struct PCIDevice *dev, struct CardData* card, unsigned char port) +{ + dev->OutByte(card->iobase + CMPCI_REG_SBADDR, port); + return (unsigned char) dev->InByte(card->iobase + CMPCI_REG_SBDATA); +} + + +/****************************************************************************** +** DriverData allocation ****************************************************** +******************************************************************************/ + +// This code used to be in _AHIsub_AllocAudio(), but since we're now +// handling CAMD support too, it needs to be done at driver loading +// time. + +struct CardData* +AllocDriverData( struct PCIDevice *dev, + struct DriverBase* AHIsubBase ) +{ + struct CardBase* CardBase = (struct CardBase*) AHIsubBase; + struct CardData* card; + UWORD command_word; + int i, v; + unsigned char byte; + + // FIXME: This should be non-cachable, DMA-able memory + card = IExec->AllocVec( sizeof( *card ), MEMF_PUBLIC | MEMF_CLEAR ); + + if( card == NULL ) + { + Req( "Unable to allocate driver structure." ); + return NULL; + } + + card->ahisubbase = AHIsubBase; + + card->interrupt.is_Node.ln_Type = NT_EXTINTERRUPT; + card->interrupt.is_Node.ln_Pri = 0; + card->interrupt.is_Node.ln_Name = (STRPTR) LibName; + card->interrupt.is_Code = (void(*)(void)) CardInterrupt; + card->interrupt.is_Data = (APTR) card; + + card->playback_interrupt.is_Node.ln_Type = NT_EXTINTERRUPT; + card->playback_interrupt.is_Node.ln_Pri = 0; + card->playback_interrupt.is_Node.ln_Name = (STRPTR) LibName; + card->playback_interrupt.is_Code = PlaybackInterrupt; + card->playback_interrupt.is_Data = (APTR) card; + + card->record_interrupt.is_Node.ln_Type = NT_EXTINTERRUPT; + card->record_interrupt.is_Node.ln_Pri = 0; + card->record_interrupt.is_Node.ln_Name = (STRPTR) LibName; + card->record_interrupt.is_Code = RecordInterrupt; + card->record_interrupt.is_Data = (APTR) card; + + card->pci_dev = dev; + + command_word = dev->ReadConfigWord( PCI_COMMAND ); + command_word |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + dev->WriteConfigWord( PCI_COMMAND, command_word ); + + card->pci_master_enabled = TRUE; + + /*for (i = 0; i < 6; i++) + { + if (dev->GetResourceRange(i)) + IExec->DebugPrintF("BAR[%ld] = %lx\n", i, dev->GetResourceRange(i)->BaseAddress); + }*/ + + card->iobase = dev->GetResourceRange(0)->BaseAddress; + card->length = ~( dev->GetResourceRange(0)->Size & PCI_BASE_ADDRESS_IO_MASK ); + card->irq = dev->MapInterrupt(); + card->chiprev = dev->ReadConfigByte( PCI_REVISION_ID); + card->model = dev->ReadConfigWord( PCI_SUBSYSTEM_ID); + + /*IExec->DebugPrintF("---> chiprev = %u, model = %x, Vendor = %x\n", dev->ReadConfigByte( PCI_REVISION_ID), dev->ReadConfigWord( PCI_SUBSYSTEM_ID), + dev->ReadConfigWord( PCI_SUBSYSTEM_VENDOR_ID));*/ + + + /* Initialize chip */ + if( card_init( card ) < 0 ) + { + IExec->DebugPrintF("Unable to initialize Card subsystem."); + return NULL; + } + + + //IExec->DebugPrintF("INTERRUPT %lu\n", dev->MapInterrupt()); + IExec->AddIntServer(dev->MapInterrupt(), &card->interrupt ); + card->interrupt_added = TRUE; + + + card->card_initialized = TRUE; + card->input = 0; + card->output = 0; + card->monitor_volume = Linear2MixerGain( 0, &card->monitor_volume_bits ); + card->input_gain = Linear2RecordGain( 0x10000, &card->input_gain_bits ); + card->output_volume = Linear2MixerGain( 0x10000, &card->output_volume_bits ); + SaveMixerState(card); + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_RESET, 0); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_L, 0); //(CMPCI_SB16_MIXER_LINE_SRC_R << 1) ); // set input to line + cmimix_wr(dev, card, CMPCI_SB16_MIXER_ADCMIX_R, 0); //CMPCI_SB16_MIXER_LINE_SRC_R); + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_OUTMIX, 0); // set output mute off for line and CD + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_VOICE_L, 0xFF); // PCM + cmimix_wr(dev, card, CMPCI_SB16_MIXER_VOICE_R, 0xFF); + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_CDDA_L, 0x00); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_CDDA_R, 0x00); + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_LINE_L, 0x00); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_LINE_R, 0x00); + + byte = dev->InByte(card->iobase + CMPCI_REG_MIXER25); + dev->OutByte(card->iobase + CMPCI_REG_MIXER25, byte & ~0x30); // mute Aux + dev->OutByte(card->iobase + CMPCI_REG_MIXER25, byte & ~0x01); // turn on mic 20dB boost + dev->OutByte(card->iobase + CMPCI_REG_MIXER_AUX, 0x00); + + byte = dev->InByte(card->iobase + CMPCI_REG_MIXER24); + dev->OutByte(card->iobase + CMPCI_REG_MIXER24, byte | CMPCI_REG_FMMUTE); + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_MIC, 0x00); + + cmimix_wr(dev, card, CMPCI_SB16_MIXER_MASTER_L, 0xFF); + cmimix_wr(dev, card, CMPCI_SB16_MIXER_MASTER_R, 0xFF); + + card->mixerstate = cmimix_rd(dev, card, CMPCI_SB16_MIXER_OUTMIX); + + AddResetHandler(card); + + return card; +} + + +/****************************************************************************** +** DriverData deallocation **************************************************** +******************************************************************************/ + +// And this code used to be in _AHIsub_FreeAudio(). + +void +FreeDriverData( struct CardData* card, + struct DriverBase* AHIsubBase ) +{ + if( card != NULL ) + { + if( card->pci_dev != NULL ) + { + if( card->card_initialized ) + { + card_cleanup( card ); + } + + if( card->pci_master_enabled ) + { + UWORD cmd; + + cmd = ((struct PCIDevice * ) card->pci_dev)->ReadConfigWord( PCI_COMMAND ); + cmd &= ~( PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER ); + ((struct PCIDevice * ) card->pci_dev)->WriteConfigWord( PCI_COMMAND, cmd ); + } + } + + if( card->interrupt_added ) + { + IExec->RemIntServer(((struct PCIDevice * ) card->pci_dev)->MapInterrupt(), &card->interrupt ); + } + + IExec->FreeVec( card ); + } +} + + +int card_init(struct CardData *card) +{ + struct PCIDevice *dev = (struct PCIDevice *) card->pci_dev; + unsigned short cod; + unsigned long val; + + ClearMask(dev, card, CMPCI_REG_MISC, CMPCI_REG_POWER_DOWN); // power up + + WriteMask(dev, card, CMPCI_REG_MISC, CMPCI_REG_BUS_AND_DSP_RESET); + IDOS->Delay(1); + ClearMask(dev, card, CMPCI_REG_MISC, CMPCI_REG_BUS_AND_DSP_RESET); + + /* reset channels */ + WriteMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET | CMPCI_REG_CH1_RESET); + IDOS->Delay(1); + ClearMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET | CMPCI_REG_CH1_RESET); + + /* Disable interrupts and channels */ + ClearMask(dev, card, CMPCI_REG_FUNC_0,CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE); + ClearMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE | CMPCI_REG_CH1_INTR_ENABLE); + + /* Configure DMA channels, ch0 = play, ch1 = capture */ + ClearMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_DIR); + WriteMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_DIR); + + // digital I/O + ClearMask(dev, card, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIFOUT_DAC | CMPCI_REG_SPDIF0_ENABLE); + ClearMask(dev, card, CMPCI_REG_LEGACY_CTRL, CMPCI_REG_LEGACY_SPDIF_ENABLE); + + ClearMask(dev, card, CMPCI_REG_LEGACY_CTRL, 0x80000000); + ClearMask(dev, card, CMPCI_REG_CHANNEL_FORMAT, CM_CHB3D); + ClearMask(dev, card, CMPCI_REG_CHANNEL_FORMAT, CM_CHB3D5C); + ClearMask(dev, card, CMPCI_REG_LEGACY_CTRL, CMPCI_REG_ENABLE_5_1); + ClearMask(dev, card, CMPCI_REG_MISC, 0x100); + ClearMask(dev, card, CMPCI_REG_MISC, CMPCI_REG_N4SPK3D); + WriteMask(dev, card, CMPCI_REG_INTR_STATUS, CMPCI_REG_LEGACY_STEREO | CMPCI_REG_LEGACY_HDMA); + + return 0; +} + + +void card_cleanup(struct CardData *card) +{ +} + + + +/****************************************************************************** +** Misc. ********************************************************************** +******************************************************************************/ + +void +SaveMixerState( struct CardData* card ) +{ +#if 0 + card->ac97_mic = codec_read( card, AC97_MIC_VOL ); + card->ac97_cd = codec_read( card, AC97_CD_VOL ); + card->ac97_video = codec_read( card, AC97_VIDEO_VOL ); + card->ac97_aux = codec_read( card, AC97_AUX_VOL ); + card->ac97_linein = codec_read( card, AC97_LINEIN_VOL ); + card->ac97_phone = codec_read( card, AC97_PHONE_VOL ); +#endif +} + + +void +RestoreMixerState( struct CardData* card ) +{ +#if 0 + codec_write(card, AC97_MIC_VOL, card->ac97_mic ); + codec_write(card, AC97_CD_VOL, card->ac97_cd ); + codec_write(card, AC97_VIDEO_VOL, card->ac97_video ); + codec_write(card, AC97_AUX_VOL, card->ac97_aux ); + codec_write(card, AC97_LINEIN_VOL, card->ac97_linein ); + codec_write(card, AC97_PHONE_VOL, card->ac97_phone ); +#endif +} + +void +UpdateMonitorMixer( struct CardData* card ) +{ +#if 0 + int i = InputBits[ card->input ]; + UWORD m = card->monitor_volume_bits & 0x801f; + UWORD s = card->monitor_volume_bits; + UWORD mm = AC97_MUTE | 0x0008; + UWORD sm = AC97_MUTE | 0x0808; + + if( i == AC97_RECMUX_STEREO_MIX || + i == AC97_RECMUX_MONO_MIX ) + { + // Use the original mixer settings + RestoreMixerState( card ); + } + else + { + codec_write(card, AC97_MIC_VOL, + i == AC97_RECMUX_MIC ? m : mm ); + + codec_write(card, AC97_CD_VOL, + i == AC97_RECMUX_CD ? s : sm ); + + codec_write(card, AC97_VIDEO_VOL, + i == AC97_RECMUX_VIDEO ? s : sm ); + + codec_write(card, AC97_AUX_VOL, + i == AC97_RECMUX_AUX ? s : sm ); + + codec_write(card, AC97_LINEIN_VOL, + i == AC97_RECMUX_LINE ? s : sm ); + + codec_write(card, AC97_PHONE_VOL, + i == AC97_RECMUX_PHONE ? m : mm ); + } +#endif +} + + +Fixed +Linear2MixerGain( Fixed linear, + UWORD* bits ) +{ + static const Fixed gain[ 33 ] = + { + 260904, // +12.0 dB + 219523, // +10.5 dB + 184706, // +9.0 dB + 155410, // +7.5 dB + 130762, // +6.0 dB + 110022, // +4.5 dB + 92572, // +3.0 dB + 77890, // +1.5 dB + 65536, // ±0.0 dB + 55142, // -1.5 dB + 46396, // -3.0 dB + 39037, // -4.5 dB + 32846, // -6.0 dB + 27636, // -7.5 dB + 23253, // -9.0 dB + 19565, // -10.5 dB + 16462, // -12.0 dB + 13851, // -13.5 dB + 11654, // -15.0 dB + 9806, // -16.5 dB + 8250, // -18.0 dB + 6942, // -19.5 dB + 5841, // -21.0 dB + 4915, // -22.5 dB + 4135, // -24.0 dB + 3479, // -25.5 dB + 2927, // -27.0 dB + 2463, // -28.5 dB + 2072, // -30.0 dB + 1744, // -31.5 dB + 1467, // -33.0 dB + 1234, // -34.5 dB + 0 // -oo dB + }; + + int v = 0; + + while( linear < gain[ v ] ) + { + ++v; + } + + if( v == 32 ) + { + *bits = 0x8000; // Mute + } + else + { + *bits = ( v << 8 ) | v; + } + +// KPrintF( "l2mg %08lx -> %08lx (%04lx)\n", linear, gain[ v ], *bits ); + return gain[ v ]; +} + +Fixed +Linear2RecordGain( Fixed linear, + UWORD* bits ) +{ + static const Fixed gain[ 17 ] = + { + 873937, // +22.5 dB + 735326, // +21.0 dB + 618700, // +19.5 dB + 520571, // +18.0 dB + 438006, // +16.5 dB + 368536, // +15.0 dB + 310084, // +13.5 dB + 260904, // +12.0 dB + 219523, // +10.5 dB + 184706, // +9.0 dB + 155410, // +7.5 dB + 130762, // +6.0 dB + 110022, // +4.5 dB + 92572, // +3.0 dB + 77890, // +1.5 dB + 65536, // ±0.0 dB + 0 // -oo dB + }; + + int v = 0; + + while( linear < gain[ v ] ) + { + ++v; + } + + if( v == 16 ) + { + *bits = 0x8000; // Mute + } + else + { + *bits = ( ( 15 - v ) << 8 ) | ( 15 - v ); + } + + return gain[ v ]; +} + + +ULONG +SamplerateToLinearPitch( ULONG samplingrate ) +{ + samplingrate = (samplingrate << 8) / 375; + return (samplingrate >> 1) + (samplingrate & 1); +} + + +APTR DMAheader = 0; +#define GFXMEM_BUFFER (64 * 1024) // 64KB should be enough for everyone... + +void *pci_alloc_consistent(size_t size, APTR *NonAlignedAddress) +{ + void* address; + unsigned long a; + + if (IFakeDMA) + { + if (size > GFXMEM_BUFFER) + { + IExec->DebugPrintF("Error allocating memory! Asking %ld bytes\n", size); + } + + if (DMAheader == 0) + { + DMAheader = IFakeDMA->AllocDMABuffer(GFXMEM_BUFFER); // alloc once + } + + if (DMAheader) + { + APTR bufStart = IFakeDMA->GetDMABufferAttr(DMAheader, DMAB_PUDDLE ); + ULONG bufSize = (ULONG)IFakeDMA->GetDMABufferAttr(DMAheader, DMAB_SIZE ); + + address = bufStart; + *NonAlignedAddress = address; + + a = (unsigned long) address; + a = (a + CACHELINE_SIZE - 1) & ~(CACHELINE_SIZE - 1); + address = (void *) a; + + return address; + } + } + + IExec->DebugPrintF("No gfx mem\n"); + if (IExec->OpenResource("newmemory.resource")) + { + address = IExec->AllocVecTags(size, AVT_Type, MEMF_SHARED, AVT_Contiguous, TRUE, AVT_Lock, TRUE, + AVT_PhysicalAlignment, 32, AVT_Clear, 0, TAG_DONE); + } + else + { + address = IExec->AllocVec( size + CACHELINE_SIZE, MEMF_PUBLIC | MEMF_CLEAR); + + if( address != NULL ) + { + a = (unsigned long) address; + a = (a + CACHELINE_SIZE - 1) & ~(CACHELINE_SIZE - 1); + address = (void *) a; + } + } + + *NonAlignedAddress = address; + + return address; +} + + +void pci_free_consistent(void* addr) +{ + if (IFakeDMA) + { + } + else + { + IExec->FreeVec( addr ); + } +} + +static ULONG ResetHandler(struct ExceptionContext *ctx, struct ExecBase *pExecBase, struct CardData *card) +{ + struct PCIDevice *dev = card->pci_dev; + + ClearMask(dev, card, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + ClearMask(dev, card, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); + + return 0UL; +} + + +void AddResetHandler(struct CardData *card) +{ + static struct Interrupt interrupt; + + interrupt.is_Code = (void (*)())ResetHandler; + interrupt.is_Data = (APTR) card; + interrupt.is_Node.ln_Pri = 0; + interrupt.is_Node.ln_Type = NT_EXTINTERRUPT; + interrupt.is_Node.ln_Name = "reset handler"; + + IExec->AddResetCallback( &interrupt ); +} + diff --git a/workbench/devs/AHI/Drivers/CMI8738/misc.h b/workbench/devs/AHI/Drivers/CMI8738/misc.h new file mode 100755 index 0000000000..0e3cb2c5c9 --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/misc.h @@ -0,0 +1,60 @@ +/* +The contents of this file are subject to the AROS Public License Version 1.1 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.aros.org/license.html +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +ANY KIND, either express or implied. See the License for the specific language governing rights and +limitations under the License. + +The Original Code is written by Davy Wentzler. +*/ + +#ifndef AHI_Drivers_misc_h +#define AHI_Drivers_misc_h + +#include + +#include +#include +#include + +void WritePartialMask(struct PCIDevice *dev, struct CardData* card, unsigned long reg, unsigned long shift, unsigned long mask, unsigned long val); +void ClearMask(struct PCIDevice *dev, struct CardData* card, unsigned long reg, unsigned long mask); +void WriteMask(struct PCIDevice *dev, struct CardData* card, unsigned long reg, unsigned long mask); +void cmimix_wr(struct PCIDevice *dev, struct CardData* card, unsigned char port, unsigned char val); +unsigned char cmimix_rd(struct PCIDevice *dev, struct CardData* card, unsigned char port); + + +struct CardData* +AllocDriverData( struct PCIDevice* dev, + struct DriverBase* AHIsubBase ); + +void +FreeDriverData( struct CardData* card, + struct DriverBase* AHIsubBase ); + +void +SaveMixerState( struct CardData* card ); + +void +RestoreMixerState( struct CardData* card ); + +void +UpdateMonitorMixer( struct CardData* card ); + +Fixed +Linear2MixerGain( Fixed linear, + UWORD* bits ); + +Fixed +Linear2RecordGain( Fixed linear, + UWORD* bits ); + +ULONG +SamplerateToLinearPitch( ULONG samplingrate ); + +void *pci_alloc_consistent(size_t size, APTR *NonAlignedAddress); + +void pci_free_consistent(void* addr); + +#endif /* AHI_Drivers_misc_h */ diff --git a/workbench/devs/AHI/Drivers/CMI8738/regs.h b/workbench/devs/AHI/Drivers/CMI8738/regs.h new file mode 100755 index 0000000000..72a0620c5e --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/regs.h @@ -0,0 +1,292 @@ +/* $NetBSD: cmpcireg.h,v 1.6 2003/12/04 13:57:31 keihan Exp $ */ + +/* + * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI . + * + * This code is derived from software contributed to The NetBSD Foundation + * by ITOH Yasufumi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* C-Media CMI8x38 Audio Chip Support */ + +#ifndef _DEV_PCI_CMPCIREG_H_ +#define _DEV_PCI_CMPCIREG_H_ (1) + +/* + * PCI Configuration Registers + */ + +#define CMPCI_PCI_IOBASEREG (PCI_MAPREG_START) + + +/* + * I/O Space + */ + +#define CMPCI_REG_FUNC_0 0x00 +# define CMPCI_REG_CH0_DIR 0x00000001 +# define CMPCI_REG_CH1_DIR 0x00000002 +# define CMPCI_REG_CH0_PAUSE 0x00000004 +# define CMPCI_REG_CH1_PAUSE 0x00000008 +# define CMPCI_REG_CH0_ENABLE 0x00010000 +# define CMPCI_REG_CH1_ENABLE 0x00020000 +# define CMPCI_REG_CH0_RESET 0x00040000 +# define CMPCI_REG_CH1_RESET 0x00080000 + +#define CMPCI_REG_FUNC_1 0x04 +# define CMPCI_REG_JOY_ENABLE 0x00000002 +# define CMPCI_REG_UART_ENABLE 0x00000004 +# define CMPCI_REG_LEGACY_ENABLE 0x00000008 +# define CMPCI_REG_BREQ 0x00000010 +# define CMPCI_REG_MCBINTR_ENABLE 0x00000020 +# define CMPCI_REG_SPDIFOUT_DAC 0x00000040 +# define CMPCI_REG_SPDIF_LOOP 0x00000080 +# define CMPCI_REG_SPDIF0_ENABLE 0x00000100 +# define CMPCI_REG_SPDIF1_ENABLE 0x00000200 +# define CMPCI_REG_DAC_FS_SHIFT 10 +# define CMPCI_REG_DAC_FS_MASK 0x00000007 +# define CMPCI_REG_ADC_FS_SHIFT 13 +# define CMPCI_REG_ADC_FS_MASK 0x00000007 + +#define CMPCI_REG_CHANNEL_FORMAT 0x08 +# define CMPCI_REG_SPDIN_PHASE 0x80 +# define CMPCI_REG_CH0_FORMAT_SHIFT 0 +# define CMPCI_REG_CH0_FORMAT_MASK 0x00000003 +# define CMPCI_REG_CH1_FORMAT_SHIFT 2 +# define CMPCI_REG_CH1_FORMAT_MASK 0x00000003 +# define CMPCI_REG_FORMAT_MONO 0x00000000 +# define CMPCI_REG_FORMAT_STEREO 0x00000001 +# define CMPCI_REG_FORMAT_8BIT 0x00000000 +# define CMPCI_REG_FORMAT_16BIT 0x00000002 + +#define CMPCI_REG_INTR_CTRL 0x0c +# define CMPCI_REG_CH0_INTR_ENABLE 0x00010000 +# define CMPCI_REG_CH1_INTR_ENABLE 0x00020000 +# define CMPCI_REG_TDMA_INTR_ENABLE 0x00040000 + +#define CMPCI_REG_INTR_STATUS 0x10 +# define CMPCI_REG_CH0_INTR 0x00000001 +# define CMPCI_REG_CH1_INTR 0x00000002 +# define CMPCI_REG_CH0_BUSY 0x00000004 +# define CMPCI_REG_CH1_BUSY 0x00000008 +# define CMPCI_REG_LEGACY_STEREO 0x00000010 +# define CMPCI_REG_LEGACY_HDMA 0x00000020 +# define CMPCI_REG_DMASTAT 0x00000040 +# define CMPCI_REG_XDO46 0x00000080 +# define CMPCI_REG_HTDMA_INTR 0x00004000 +# define CMPCI_REG_LTDMA_INTR 0x00008000 +# define CMPCI_REG_UART_INTR 0x00010000 +# define CMPCI_REG_MCB_INTR 0x04000000 +# define CMPCI_REG_VCO 0x08000000 +# define CMPCI_REG_ANY_INTR 0x80000000 + +#define CMPCI_REG_LEGACY_CTRL 0x14 +# define CMPCI_REG_LEGACY_SPDIF_ENABLE 0x00200000 // wave/pcm to /spdif out: causes distortion +# define CMPCI_REG_SPDIF_COPYRIGHT 0x00400000 +# define CMPCI_REG_XSPDIF_ENABLE 0x00800000 // turns on the red light +# define CMPCI_REG_FMSEL_SHIFT 24 +# define CMPCI_REG_FMSEL_MASK 0x00000003 +# define CMPCI_REG_VSBSEL_SHIFT 26 +# define CMPCI_REG_VSBSEL_MASK 0x00000003 +# define CMPCI_REG_VMPUSEL_SHIFT 29 +# define CMPCI_REG_VMPUSEL_MASK 0x00000003 +# define CMPCI_REG_ENABLE_5_1 0x00008000 + +#define CMPCI_REG_MISC 0x18 +# define CMPCI_REG_2ND_SPDIFIN 0x00000100 +# define CMPCI_REG_SPDIFOUT_48K 0x00008000 +# define CMPCI_REG_FM_ENABLE 0x00080000 +# define CMPCI_REG_SPDFLOOPI 0x00100000 +# define CMPCI_REG_SPDIF48K 0x01000000 +# define CMPCI_REG_5V 0x02000000 +# define CMPCI_REG_N4SPK3D 0x04000000 +# define CMPCI_REG_BUS_AND_DSP_RESET 0x40000000 +# define CMPCI_REG_POWER_DOWN 0x80000000 + +#define CMPCI_REG_SBDATA 0x22 +#define CMPCI_REG_SBADDR 0x23 +# define CMPCI_SB16_MIXER_RESET 0x00 + +# define CMPCI_SB16_MIXER_MASTER_L 0x30 // mixer levels for output +# define CMPCI_SB16_MIXER_MASTER_R 0x31 +# define CMPCI_SB16_MIXER_VOICE_L 0x32 +# define CMPCI_SB16_MIXER_VOICE_R 0x33 +# define CMPCI_SB16_MIXER_FM_L 0x34 +# define CMPCI_SB16_MIXER_FM_R 0x35 +# define CMPCI_SB16_MIXER_CDDA_L 0x36 +# define CMPCI_SB16_MIXER_CDDA_R 0x37 +# define CMPCI_SB16_MIXER_LINE_L 0x38 +# define CMPCI_SB16_MIXER_LINE_R 0x39 +# define CMPCI_SB16_MIXER_MIC 0x3A +# define CMPCI_SB16_MIXER_VALBITS 5 +# define CMPCI_SB16_MIXER_SPEAKER 0x3B +# define CMPCI_SB16_MIXER_SPEAKER_VALBITS 2 + +# define CMPCI_SB16_MIXER_OUTMIX 0x3C // mutes for output +# define CMPCI_SB16_SW_MIC 0x01 +# define CMPCI_SB16_SW_CD_R 0x02 +# define CMPCI_SB16_SW_CD_L 0x04 +# define CMPCI_SB16_SW_CD (CMPCI_SB16_SW_CD_L|CMPCI_SB16_SW_CD_R) +# define CMPCI_SB16_SW_LINE_R 0x08 +# define CMPCI_SB16_SW_LINE_L 0x10 +# define CMPCI_SB16_SW_LINE (CMPCI_SB16_SW_LINE_L|CMPCI_SB16_SW_LINE_R) +# define CMPCI_SB16_SW_FM_R 0x20 +# define CMPCI_SB16_SW_FM_L 0x40 +# define CMPCI_SB16_SW_FM (CMPCI_SB16_SW_FM_L|CMPCI_SB16_SW_FM_R) + +# define CMPCI_SB16_MIXER_ADCMIX_L 0x3D // select/mute recording inputs +# define CMPCI_SB16_MIXER_ADCMIX_R 0x3E +# define CMPCI_SB16_MIXER_FM_SRC_R 0x20 +# define CMPCI_SB16_MIXER_LINE_SRC_R 0x08 +# define CMPCI_SB16_MIXER_CD_SRC_R 0x02 +# define CMPCI_SB16_MIXER_MIC_SRC 0x01 +# define CMPCI_SB16_MIXER_SRC_R_TO_L(v) ((v) << 1) + +# define CMPCI_SB16_MIXER_L_TO_R(addr) ((addr)+1) + +# define CMPCI_ADJUST_MIC_GAIN(sc, x) cmpci_adjust((x), 0xf8) +# define CMPCI_ADJUST_GAIN(sc, x) cmpci_adjust((x), 0xf8) +# define CMPCI_ADJUST_2_GAIN(sc, x) cmpci_adjust((x), 0xc0) + +#define CMPCI_REG_MIXER24 0x24 +# define CMPCI_REG_SPDIN_MONITOR 0x01 +# define CMPCI_REG_SURROUND 0x02 +# define CMPCI_REG_INDIVIDUAL 0x20 +# define CMPCI_REG_REVERSE_FR 0x10 +# define CMPCI_REG_FMMUTE 0x80 +# define CMPCI_REG_WSMUTE 0x40 +# define CMPCI_REG_WAVEINL 0x08 +# define CMPCI_REG_WAVEINR 0x04 + +#define CMPCI_REG_MIXER25 0x25 +# define CMPCI_REG_RAUXREN 0x80 +# define CMPCI_REG_RAUXLEN 0x40 +# define CMPCI_REG_VAUXRM 0x20 /* 0: mute, 1: unmute */ +# define CMPCI_REG_VAUXLM 0x10 +# define CMPCI_REG_VADMIC 0x0E +# define CMPCI_REG_MICGAINZ 0x01 /* 1: disable preamp */ + +# define CMPCI_ADJUST_ADMIC_GAIN(sc, x) (cmpci_adjust((x), 0xe0) >> 5) +# define CMPCI_REG_ADMIC_VALBITS 3 +# define CMPCI_REG_ADMIC_MASK 0x07 +# define CMPCI_REG_ADMIC_SHIFT 0x01 + +/* Note that the doc tells a lie */ +#define CMPCI_REG_MIXER_AUX 0x26 +# define CMPCI_ADJUST_AUX_GAIN(sc, l, r) \ + (cmpci_adjust((l), 0xc0) >> 4 | cmpci_adjust((r), 0xc0)) +# define CMPCI_REG_AUX_VALBITS 4 + +#define CMPCI_REG_MPU_BASE 0x40 +#define CMPCI_REG_MPU_SIZE 0x10 +#define CMPCI_REG_FM_BASE 0x50 +#define CMPCI_REG_FM_SIZE 0x10 + +#define CMPCI_REG_DMA0_BASE 0x80 +#define CMPCI_REG_DMA0_LENGTH 0x84 // total sample frames - 1 allocated +#define CMPCI_REG_DMA0_INTLEN 0x86 // nr of sample frames - 1 before an interrupt occurs +#define CMPCI_REG_DMA1_BASE 0x88 +#define CMPCI_REG_DMA1_LENGTH 0x8C +#define CMPCI_REG_DMA1_INTLEN 0x8E + + +/* sample rate */ +#define CMPCI_REG_RATE_5512 0 +#define CMPCI_REG_RATE_11025 1 +#define CMPCI_REG_RATE_22050 2 +#define CMPCI_REG_RATE_44100 3 +#define CMPCI_REG_RATE_8000 4 +#define CMPCI_REG_RATE_16000 5 +#define CMPCI_REG_RATE_32000 6 +#define CMPCI_REG_RATE_48000 7 +#define CMPCI_REG_NUMRATE 8 + + +/* + * Mixer device + * + * Note that cmpci_query_devinfo() is optimized depending on + * the order of this. Be careful if you change the values. + */ +#define CMPCI_DAC_VOL 0 /* inputs.dac */ +#define CMPCI_FM_VOL 1 /* inputs.fmsynth */ +#define CMPCI_CD_VOL 2 /* inputs.cd */ +#define CMPCI_LINE_IN_VOL 3 /* inputs.line */ +#define CMPCI_AUX_IN_VOL 4 /* inputs.aux */ +#define CMPCI_MIC_VOL 5 /* inputs.mic */ + +#define CMPCI_DAC_MUTE 6 /* inputs.dac.mute */ +#define CMPCI_FM_MUTE 7 /* inputs.fmsynth.mute */ +#define CMPCI_CD_MUTE 8 /* inputs.cd.mute */ +#define CMPCI_LINE_IN_MUTE 9 /* inputs.line.mute */ +#define CMPCI_AUX_IN_MUTE 10 /* inputs.aux.mute */ +#define CMPCI_MIC_MUTE 11 /* inputs.mic.mute */ + +#define CMPCI_MIC_PREAMP 12 /* inputs.mic.preamp */ +#define CMPCI_PCSPEAKER 13 /* inputs.speaker */ + +#define CMPCI_RECORD_SOURCE 14 /* record.source */ +#define CMPCI_MIC_RECVOL 15 /* record.mic */ + +#define CMPCI_PLAYBACK_MODE 16 /* playback.mode */ +#define CMPCI_SPDIF_IN_SELECT 17 /* spdif.input */ +#define CMPCI_SPDIF_IN_PHASE 18 /* spdif.input.phase */ +#define CMPCI_SPDIF_LOOP 19 /* spdif.output */ +#define CMPCI_SPDIF_OUT_PLAYBACK 20 /* spdif.output.playback */ +#define CMPCI_SPDIF_OUT_VOLTAGE 21 /* spdif.output.voltage */ +#define CMPCI_MONITOR_DAC 22 /* spdif.monitor */ + +#define CMPCI_MASTER_VOL 23 /* outputs.master */ +#define CMPCI_REAR 24 /* outputs.rear */ +#define CMPCI_INDIVIDUAL 25 /* outputs.rear.individual */ +#define CMPCI_REVERSE 26 /* outputs.rear.reverse */ +#define CMPCI_SURROUND 27 /* outputs.surround */ + +#define CMPCI_NDEVS 28 + +#define CMPCI_INPUT_CLASS 28 +#define CMPCI_OUTPUT_CLASS 29 +#define CMPCI_RECORD_CLASS 30 +#define CMPCI_PLAYBACK_CLASS 31 +#define CMPCI_SPDIF_CLASS 32 + + +#define CMPCI_LEFT 0 +#define CMPCI_RIGHT 1 +#define CMPCI_LR 0 + +#define CM_CHB3D5C 0x80000000 /* 5,6 channels */ +#define CM_CHB3D 0x20000000 /* 4 channels */ + + + +#endif /* _DEV_PCI_CMPCIREG_H_ */ + +/* end of file */ + diff --git a/workbench/devs/AHI/Drivers/CMI8738/version.h b/workbench/devs/AHI/Drivers/CMI8738/version.h new file mode 100755 index 0000000000..07beb0a0ba --- /dev/null +++ b/workbench/devs/AHI/Drivers/CMI8738/version.h @@ -0,0 +1,3 @@ +#define VERSION 5 +#define REVISION 17 +#define VERS "5.17 (04.11.2006)" -- 2.11.4.GIT