grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / devs / AHI / Drivers / EMU10kx / emu10kx-misc.c
blob51685bd1d4f109025e4c339a1f412b3c40b8f9b7
1 /*
2 emu10kx.audio - AHI driver for SoundBlaster Live! series
3 Copyright (C) 2002-2005 Martin Blom <martin@blom.org>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include <config.h>
22 #include <exec/memory.h>
23 #include <proto/dos.h>
25 #include "library.h"
26 #include "8010.h"
27 #include "emu10kx-interrupt.h"
28 #include "emu10kx-misc.h"
29 #include "pci_wrapper.h"
31 /* Global in emu10kx.c */
32 extern const UWORD InputBits[];
34 /* Public functions in main.c */
35 int emu10k1_init(struct emu10k1_card *card);
36 void emu10k1_cleanup(struct emu10k1_card *card);
38 #ifdef __AMIGAOS4__
39 static void AddResetHandler(struct EMU10kxData* dd);
40 #endif
42 /******************************************************************************
43 ** DriverData allocation ******************************************************
44 ******************************************************************************/
46 // This code used to be in _AHIsub_AllocAudio(), but since we're now
47 // handling CAMD support too, it needs to be done at driver loading
48 // time.
50 INTGW( static, void, playbackinterrupt, PlaybackInterrupt );
51 INTGW( static, void, recordinterrupt, RecordInterrupt );
52 INTGW( static, ULONG, emu10kxinterrupt, EMU10kxInterrupt );
55 struct EMU10kxData*
56 AllocDriverData( APTR dev,
57 struct DriverBase* AHIsubBase )
59 struct EMU10kxData* dd;
60 UWORD command_word;
62 // FIXME: This should be non-cachable, DMA-able memory
63 dd = AllocVec( sizeof( *dd ), MEMF_PUBLIC | MEMF_CLEAR );
65 if( dd == NULL )
67 Req( "Unable to allocate driver structure." );
68 return NULL;
72 dd->ahisubbase = AHIsubBase;
74 dd->interrupt.is_Node.ln_Type = INTERRUPT_NODE_TYPE;
75 dd->interrupt.is_Node.ln_Pri = 0;
76 dd->interrupt.is_Node.ln_Name = (STRPTR) LibName;
77 dd->interrupt.is_Code = (APTR) emu10kxinterrupt;
78 dd->interrupt.is_Data = (APTR) dd;
80 dd->playback_interrupt.is_Node.ln_Type = INTERRUPT_NODE_TYPE;
81 dd->playback_interrupt.is_Node.ln_Pri = 0;
82 dd->playback_interrupt.is_Node.ln_Name = (STRPTR) LibName;
83 dd->playback_interrupt.is_Code = (APTR) playbackinterrupt;
84 dd->playback_interrupt.is_Data = (APTR) dd;
86 dd->record_interrupt.is_Node.ln_Type = INTERRUPT_NODE_TYPE;
87 dd->record_interrupt.is_Node.ln_Pri = 0;
88 dd->record_interrupt.is_Node.ln_Name = (STRPTR) LibName;
89 dd->record_interrupt.is_Code = (APTR) recordinterrupt;
90 dd->record_interrupt.is_Data = (APTR) dd;
92 dd->card.pci_dev = dev;
94 // if( pci_set_dma_mask(dd->card.pci_dev, EMU10K1_DMA_MASK) )
95 // {
96 // printf( "Unable to set DMA mask for card." );
97 // goto error;
98 // }
100 command_word = ahi_pci_read_config_word( PCI_COMMAND, dd->card.pci_dev );
101 command_word |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
102 ahi_pci_write_config_word( PCI_COMMAND, command_word, dd->card.pci_dev );
104 dd->pci_master_enabled = TRUE;
106 // FIXME: How about latency/pcibios_set_master()??
108 dd->card.iobase = (unsigned long) ahi_pci_get_base_address(0, dev);
109 dd->card.length = ~( ahi_pci_get_base_size(0, dev) & PCI_BASE_ADDRESS_IO_MASK );
110 dd->card.irq = ahi_pci_get_irq(dev);
111 dd->card.chiprev = ahi_pci_read_config_byte( PCI_REVISION_ID, dev );
112 dd->card.model = ahi_pci_read_config_word( PCI_SUBSYSTEM_ID, dev );
113 dd->card.is_audigy = ( ahi_pci_read_config_word(PCI_DEVICE_ID, dev) == PCI_DEVICE_ID_CREATIVE_AUDIGY );
114 dd->card.is_aps = ( ahi_pci_read_config_long( PCI_SUBSYSTEM_VENDOR_ID, dev) == EMU_APS_SUBID );
116 ahi_pci_add_intserver( &dd->interrupt, dev);
118 dd->interrupt_added = TRUE;
120 /* Initialize chip */
121 if( emu10k1_init( &dd->card ) < 0 )
123 Req( "Unable to initialize EMU10kx subsystem.");
124 return NULL;
127 dd->emu10k1_initialized = TRUE;
130 /* Initialize mixer */
132 emu10k1_writeac97( &dd->card, AC97_RESET, 0L);
134 Delay( 1 );
136 if (emu10k1_readac97( &dd->card, AC97_RESET ) & 0x8000) {
137 Req( "ac97 codec not present.");
138 return NULL;
142 dd->input = 0;
143 dd->output = 0;
144 dd->monitor_volume = Linear2MixerGain( 0, &dd->monitor_volume_bits );
145 dd->input_gain = Linear2RecordGain( 0x10000, &dd->input_gain_bits );
146 dd->output_volume = Linear2MixerGain( 0x10000, &dd->output_volume_bits );
148 // No attenuation and natural tone for all outputs
149 emu10k1_writeac97( &dd->card, AC97_MASTER_VOL_STEREO, 0x0000 );
150 emu10k1_writeac97( &dd->card, AC97_MASTER_VOL_MONO, 0x0000 );
151 emu10k1_writeac97( &dd->card, AC97_MASTER_TONE, 0x0f0f );
153 emu10k1_writeac97( &dd->card, AC97_RECORD_GAIN, 0x0000 );
154 emu10k1_writeac97( &dd->card, AC97_RECORD_SELECT, InputBits[ 0 ] );
156 emu10k1_writeac97( &dd->card, AC97_PCMOUT_VOL, 0x0808 );
157 emu10k1_writeac97( &dd->card, AC97_PCBEEP_VOL, 0x0000 );
159 emu10k1_writeac97( &dd->card, AC97_LINEIN_VOL, 0x0808 );
160 emu10k1_writeac97( &dd->card, AC97_MIC_VOL, AC97_MUTE | 0x0008 );
161 emu10k1_writeac97( &dd->card, AC97_CD_VOL, 0x0808 );
162 emu10k1_writeac97( &dd->card, AC97_AUX_VOL, 0x0808 );
163 emu10k1_writeac97( &dd->card, AC97_PHONE_VOL, 0x0008 );
164 emu10k1_writeac97( &dd->card, AC97_VIDEO_VOL, 0x0808 );
167 if (emu10k1_readac97( &dd->card, AC97_EXTENDED_ID ) & 0x0080 )
169 sblive_writeptr( &dd->card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE);
171 // Disable center/LFE to front speakers (Not headphone; it's actially surround mix.)
172 emu10k1_writeac97( &dd->card, AC97_HEADPHONE_VOL, AC97_MUTE | 0x0808 );
174 // No attenuation for center/LFE
175 emu10k1_writeac97( &dd->card, AC97_SURROUND_MASTER, 0x0 );
178 #ifdef __AMIGAOS4__
179 AddResetHandler(dd);
180 #endif
182 return dd;
186 /******************************************************************************
187 ** DriverData deallocation ****************************************************
188 ******************************************************************************/
190 // And this code used to be in _AHIsub_FreeAudio().
192 void
193 FreeDriverData( struct EMU10kxData* dd,
194 struct DriverBase* AHIsubBase )
196 if( dd != NULL )
198 if( dd->card.pci_dev != NULL )
200 if( dd->emu10k1_initialized )
202 emu10k1_cleanup( &dd->card );
205 if( dd->pci_master_enabled )
207 UWORD cmd;
209 cmd = ahi_pci_read_config_word( PCI_COMMAND, dd->card.pci_dev );
210 cmd &= ~( PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER );
211 ahi_pci_write_config_word( PCI_COMMAND, cmd, dd->card.pci_dev );
215 if( dd->interrupt_added )
217 ahi_pci_rem_intserver( &dd->interrupt, dd->card.pci_dev );
220 FreeVec( dd );
224 /******************************************************************************
225 ** Misc. **********************************************************************
226 ******************************************************************************/
228 void
229 SaveMixerState( struct EMU10kxData* dd )
231 dd->ac97_mic = emu10k1_readac97( &dd->card, AC97_MIC_VOL );
232 dd->ac97_cd = emu10k1_readac97( &dd->card, AC97_CD_VOL );
233 dd->ac97_video = emu10k1_readac97( &dd->card, AC97_VIDEO_VOL );
234 dd->ac97_aux = emu10k1_readac97( &dd->card, AC97_AUX_VOL );
235 dd->ac97_linein = emu10k1_readac97( &dd->card, AC97_LINEIN_VOL );
236 dd->ac97_phone = emu10k1_readac97( &dd->card, AC97_PHONE_VOL );
240 void
241 RestoreMixerState( struct EMU10kxData* dd )
243 emu10k1_writeac97( &dd->card, AC97_MIC_VOL, dd->ac97_mic );
244 emu10k1_writeac97( &dd->card, AC97_CD_VOL, dd->ac97_cd );
245 emu10k1_writeac97( &dd->card, AC97_VIDEO_VOL, dd->ac97_video );
246 emu10k1_writeac97( &dd->card, AC97_AUX_VOL, dd->ac97_aux );
247 emu10k1_writeac97( &dd->card, AC97_LINEIN_VOL, dd->ac97_linein );
248 emu10k1_writeac97( &dd->card, AC97_PHONE_VOL, dd->ac97_phone );
251 void
252 UpdateMonitorMixer( struct EMU10kxData* dd )
254 int i = InputBits[ dd->input ];
255 UWORD m = dd->monitor_volume_bits & 0x801f;
256 UWORD s = dd->monitor_volume_bits;
257 UWORD mm = AC97_MUTE | 0x0008;
258 UWORD sm = AC97_MUTE | 0x0808;
260 if( i == AC97_RECMUX_STEREO_MIX ||
261 i == AC97_RECMUX_MONO_MIX )
263 // Use the original mixer settings
264 RestoreMixerState( dd );
266 else
268 emu10k1_writeac97( &dd->card, AC97_MIC_VOL,
269 i == AC97_RECMUX_MIC ? m : mm );
271 emu10k1_writeac97( &dd->card, AC97_CD_VOL,
272 i == AC97_RECMUX_CD ? s : sm );
274 emu10k1_writeac97( &dd->card, AC97_VIDEO_VOL,
275 i == AC97_RECMUX_VIDEO ? s : sm );
277 emu10k1_writeac97( &dd->card, AC97_AUX_VOL,
278 i == AC97_RECMUX_AUX ? s : sm );
280 emu10k1_writeac97( &dd->card, AC97_LINEIN_VOL,
281 i == AC97_RECMUX_LINE ? s : sm );
283 emu10k1_writeac97( &dd->card, AC97_PHONE_VOL,
284 i == AC97_RECMUX_PHONE ? m : mm );
289 Fixed
290 Linear2MixerGain( Fixed linear,
291 UWORD* bits )
293 static const Fixed gain[ 33 ] =
295 260904, // +12.0 dB
296 219523, // +10.5 dB
297 184706, // +9.0 dB
298 155410, // +7.5 dB
299 130762, // +6.0 dB
300 110022, // +4.5 dB
301 92572, // +3.0 dB
302 77890, // +1.5 dB
303 65536, // ±0.0 dB
304 55142, // -1.5 dB
305 46396, // -3.0 dB
306 39037, // -4.5 dB
307 32846, // -6.0 dB
308 27636, // -7.5 dB
309 23253, // -9.0 dB
310 19565, // -10.5 dB
311 16462, // -12.0 dB
312 13851, // -13.5 dB
313 11654, // -15.0 dB
314 9806, // -16.5 dB
315 8250, // -18.0 dB
316 6942, // -19.5 dB
317 5841, // -21.0 dB
318 4915, // -22.5 dB
319 4135, // -24.0 dB
320 3479, // -25.5 dB
321 2927, // -27.0 dB
322 2463, // -28.5 dB
323 2072, // -30.0 dB
324 1744, // -31.5 dB
325 1467, // -33.0 dB
326 1234, // -34.5 dB
327 0 // -oo dB
330 int v = 0;
332 while( linear < gain[ v ] )
334 ++v;
337 if( v == 32 )
339 *bits = 0x8000; // Mute
341 else
343 *bits = ( v << 8 ) | v;
346 // KPrintF( "l2mg %08lx -> %08lx (%04lx)\n", linear, gain[ v ], *bits );
347 return gain[ v ];
350 Fixed
351 Linear2RecordGain( Fixed linear,
352 UWORD* bits )
354 static const Fixed gain[ 17 ] =
356 873937, // +22.5 dB
357 735326, // +21.0 dB
358 618700, // +19.5 dB
359 520571, // +18.0 dB
360 438006, // +16.5 dB
361 368536, // +15.0 dB
362 310084, // +13.5 dB
363 260904, // +12.0 dB
364 219523, // +10.5 dB
365 184706, // +9.0 dB
366 155410, // +7.5 dB
367 130762, // +6.0 dB
368 110022, // +4.5 dB
369 92572, // +3.0 dB
370 77890, // +1.5 dB
371 65536, // ±0.0 dB
372 0 // -oo dB
375 int v = 0;
377 while( linear < gain[ v ] )
379 ++v;
382 if( v == 16 )
384 *bits = 0x8000; // Mute
386 else
388 *bits = ( ( 15 - v ) << 8 ) | ( 15 - v );
391 return gain[ v ];
395 ULONG
396 SamplerateToLinearPitch( ULONG samplingrate )
398 samplingrate = (samplingrate << 8) / 375;
399 return (samplingrate >> 1) + (samplingrate & 1);
403 #ifdef __AMIGAOS4__
404 static ULONG ResetHandler(struct ExceptionContext *ctx, struct ExecBase *pExecBase, struct EMU10kxData* dd)
406 emu10k1_irq_disable( &dd->card, INTE_INTERVALTIMERENB );
407 emu10k1_voices_stop( dd->voices, dd->voices_started );
409 return 0UL;
413 void AddResetHandler(struct EMU10kxData* dd)
415 static struct Interrupt interrupt;
417 interrupt.is_Code = (void (*)())ResetHandler;
418 interrupt.is_Data = (APTR) dd;
419 interrupt.is_Node.ln_Pri = 0;
420 interrupt.is_Node.ln_Type = NT_EXTINTERRUPT;
421 interrupt.is_Node.ln_Name = "reset handler";
423 AddResetCallback( &interrupt );
425 #endif