grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / devs / AHI / Drivers / EMU10kx / emu10kx-interrupt.c
bloba7ccf70fa1f2306378425da22cc07474be7eaefc
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 <libraries/ahi_sub.h>
23 #include <exec/execbase.h>
24 #include <clib/alib_protos.h>
25 #include <proto/exec.h>
27 #include "library.h"
28 #include "8010.h"
29 #include "pci_wrapper.h"
31 #define min(a,b) ((a)<(b)?(a):(b))
33 #ifdef __AMIGAOS4__
34 # define CallHookA CallHookPkt
35 #endif
38 static WORD*
39 copy_mono( WORD* src, WORD* dst, int count, int stride, BOOL src32, BOOL flush_caches );
41 static WORD*
42 copy_stereo( WORD* lsrc, WORD* rsrc, WORD* dst, int count, int stride, BOOL src32, BOOL flush_caches );
45 /******************************************************************************
46 ** Hardware interrupt handler *************************************************
47 ******************************************************************************/
49 #ifdef __AMIGAOS4__
50 LONG
51 EMU10kxInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct EMU10kxData* dd )
52 #else
53 ULONG
54 EMU10kxInterrupt( struct EMU10kxData* dd )
55 #endif
57 struct AHIAudioCtrlDrv* AudioCtrl = dd->audioctrl;
58 struct DriverBase* AHIsubBase = (struct DriverBase*) dd->ahisubbase;
59 struct EMU10kxBase* EMU10kxBase = (struct EMU10kxBase*) AHIsubBase;
61 ULONG intreq;
62 BOOL handled = FALSE;
64 while( ( intreq = ahi_pci_inl( dd->card.iobase + IPR, dd->card.pci_dev ) ) != 0 )
66 // KPrintF("IRQ: %08lx\n", intreq );
67 if( intreq & IPR_INTERVALTIMER &&
68 AudioCtrl != NULL )
70 int hw = sblive_readptr( &dd->card, CCCA_CURRADDR, dd->voices[0].num );
71 int diff = dd->current_position - ( hw - dd->voices[0].start );
73 if( diff < 0 )
75 diff += AudioCtrl->ahiac_MaxBuffSamples * 2;
78 // KPrintF( ">>> hw_pos = %08lx; current_pos = %08lx; diff=%ld <<<\n",
79 // hw, dd->current_position, diff );
81 if( (ULONG) diff < dd->current_length )
83 if( dd->playback_interrupt_enabled )
85 /* Invoke softint to fetch new sample data */
87 dd->playback_interrupt_enabled = FALSE;
88 Cause( &dd->playback_interrupt );
89 /* KPrintF("hw[0]=%08lx, hw[1]=%08lx, hw[2]=%08lx, hw[3]=%08lx\n", */
90 /* sblive_readptr( &dd->card, CCCA_CURRADDR, dd->voices[0].num ), */
91 /* sblive_readptr( &dd->card, CCCA_CURRADDR, dd->voices[1].num ), */
92 /* sblive_readptr( &dd->card, CCCA_CURRADDR, dd->voices[2].num ), */
93 /* sblive_readptr( &dd->card, CCCA_CURRADDR, dd->voices[3].num ) ); */
98 if( intreq & ( IPR_ADCBUFHALFFULL | IPR_ADCBUFFULL ) )
100 if( intreq & IPR_ADCBUFHALFFULL )
102 dd->current_record_buffer = dd->record_buffer;
104 else
106 dd->current_record_buffer = ( dd->record_buffer +
107 RECORD_BUFFER_SAMPLES * 4 / 2 );
110 if( dd->record_interrupt_enabled )
112 /* Invoke softint to convert and feed AHI with the new sample data */
114 dd->record_interrupt_enabled = FALSE;
115 Cause( &dd->record_interrupt );
119 if( intreq & IPR_MIDIRECVBUFEMPTY )
121 unsigned char b;
123 while( emu10k1_mpu_read_data( &dd->card, &b ) >= 0 )
125 struct ReceiveMessage msg = { b };
127 if( dd->camd_receivefunc == NULL )
129 KPrintF( "emu10kx.audio got unexpected IPR_MIDIRECVBUFEMPTY\n" );
132 // KPrintF( "\t%lx\n", (int) b );
133 CallHookA( dd->camd_receivefunc, (Object*) EMU10kxBase, &msg );
137 if( intreq & IPR_MIDITRANSBUFEMPTY )
139 ULONG b;
141 if( dd->camd_transmitfunc == NULL )
143 KPrintF( "emu10kx.audio got unexpected IPR_MIDITRANSBUFEMPTY\n" );
146 b = CallHookA( dd->camd_transmitfunc, (Object*) EMU10kxBase, NULL );
148 // KPrintF( "%08lx\n", b );
150 // Check if d0.w is negative (as the V37 did once?) or if bit 8
151 // is set (as the V40 example does)
153 if( ( b & 0x00008100 ) == 0x0000 )
155 emu10k1_mpu_write_data( &dd->card, b );
158 if( ( !dd->camd_v40 && ( b & 0x00ff0000 ) != 0 ) ||
159 ( b & 0x00008100 ) != 0 )
161 // KPrintF( "Disabling interrupts\n" );
162 emu10k1_irq_disable( &dd->card, INTE_MIDITXENABLE );
166 /* Clear interrupt pending bit(s) */
167 ahi_pci_outl( intreq, dd->card.iobase + IPR, dd->card.pci_dev );
169 handled = TRUE;
172 return handled;
176 /******************************************************************************
177 ** Playback interrupt handler *************************************************
178 ******************************************************************************/
180 #ifdef __AMIGAOS4__
181 void
182 PlaybackInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct EMU10kxData* dd )
183 #else
184 void
185 PlaybackInterrupt( struct EMU10kxData* dd )
186 #endif
188 struct AHIAudioCtrlDrv* AudioCtrl = dd->audioctrl;
189 struct DriverBase* AHIsubBase = (struct DriverBase*) dd->ahisubbase;
190 struct EMU10kxBase* EMU10kxBase = (struct EMU10kxBase*) AHIsubBase;
192 if( dd->mix_buffer != NULL && dd->current_buffers[0] != NULL )
194 BOOL skip_mix;
196 WORD* src;
197 size_t samples;
198 int s;
200 skip_mix = CallHookA( AudioCtrl->ahiac_PreTimerFunc, (Object*) AudioCtrl, 0 );
202 CallHookA( AudioCtrl->ahiac_PlayerFunc, (Object*) AudioCtrl, NULL );
204 if( ! skip_mix )
206 CallHookA( AudioCtrl->ahiac_MixerFunc, (Object*) AudioCtrl, dd->mix_buffer );
209 /* Now translate and transfer to the DMA buffer */
210 samples = dd->current_length;
212 s = min( samples,
213 AudioCtrl->ahiac_MaxBuffSamples * 2 - dd->current_position );
215 src = dd->mix_buffer;
217 switch( AudioCtrl->ahiac_BuffType)
219 case AHIST_M16S:
220 dd->current_buffers[0] = copy_mono( src, dd->current_buffers[0],
221 s, 1, FALSE,
222 EMU10kxBase->flush_caches );
223 src += s;
224 break;
226 case AHIST_S16S:
227 dd->current_buffers[0] = copy_stereo( src, src + 1, dd->current_buffers[0],
228 s, 2, FALSE,
229 EMU10kxBase->flush_caches );
230 src += s * 2;
231 break;
233 case AHIST_M32S:
234 dd->current_buffers[0] = copy_mono( src, dd->current_buffers[0],
235 s, 2, TRUE,
236 EMU10kxBase->flush_caches );
237 src += s * 2;
238 break;
240 case AHIST_S32S:
241 dd->current_buffers[0] = copy_stereo( src, src + 2, dd->current_buffers[0],
242 s, 4, TRUE,
243 EMU10kxBase->flush_caches );
244 src += s * 4;
245 break;
247 case AHIST_L7_1:
248 dd->current_buffers[0] = copy_stereo( src, src + 2, dd->current_buffers[0],
249 s, 16, TRUE,
250 EMU10kxBase->flush_caches );
251 dd->current_buffers[1] = copy_stereo( src + 4, src + 6, dd->current_buffers[1],
252 s, 16, TRUE,
253 EMU10kxBase->flush_caches );
254 dd->current_buffers[2] = copy_stereo( src + 8, src + 10, dd->current_buffers[2],
255 s, 16, TRUE,
256 EMU10kxBase->flush_caches );
257 dd->current_buffers[3] = copy_stereo( src + 12, src + 14, dd->current_buffers[3],
258 s, 16, TRUE,
259 EMU10kxBase->flush_caches );
260 src += s * 16;
261 break;
264 dd->current_position += s;
265 samples -= s;
267 if( dd->current_position == AudioCtrl->ahiac_MaxBuffSamples * 2 )
269 dd->current_buffers[0] = dd->voices[0].mem.addr;
270 dd->current_buffers[1] = dd->voices[1].mem.addr;
271 dd->current_buffers[2] = dd->voices[2].mem.addr;
272 dd->current_buffers[3] = dd->voices[3].mem.addr;
273 dd->current_position = 0;
276 if( samples > 0 )
278 s = samples;
280 switch( AudioCtrl->ahiac_BuffType)
282 case AHIST_M16S:
283 dd->current_buffers[0] = copy_mono( src, dd->current_buffers[0],
284 s, 1, FALSE,
285 EMU10kxBase->flush_caches );
286 break;
288 case AHIST_S16S:
289 dd->current_buffers[0] = copy_stereo( src, src + 1, dd->current_buffers[0],
290 s, 2, FALSE,
291 EMU10kxBase->flush_caches );
292 break;
294 case AHIST_M32S:
295 dd->current_buffers[0] = copy_mono( src, dd->current_buffers[0],
296 s, 2, TRUE,
297 EMU10kxBase->flush_caches );
298 break;
300 case AHIST_S32S:
301 dd->current_buffers[0] = copy_stereo( src, src + 2, dd->current_buffers[0],
302 s, 4, TRUE,
303 EMU10kxBase->flush_caches );
304 break;
306 case AHIST_L7_1:
307 dd->current_buffers[0] = copy_stereo( src, src + 2, dd->current_buffers[0],
308 s, 16, TRUE,
309 EMU10kxBase->flush_caches );
310 dd->current_buffers[1] = copy_stereo( src + 4, src + 6, dd->current_buffers[1],
311 s, 16, TRUE,
312 EMU10kxBase->flush_caches );
313 dd->current_buffers[2] = copy_stereo( src + 8, src + 10, dd->current_buffers[2],
314 s, 16, TRUE,
315 EMU10kxBase->flush_caches );
316 dd->current_buffers[3] = copy_stereo( src + 12, src + 14, dd->current_buffers[3],
317 s, 16, TRUE,
318 EMU10kxBase->flush_caches );
319 break;
322 dd->current_position += s;
325 CallHookA( AudioCtrl->ahiac_PostTimerFunc, (Object*) AudioCtrl, 0 );
328 dd->playback_interrupt_enabled = TRUE;
332 static WORD*
333 copy_mono( WORD* src, WORD* dst, int count, int stride, BOOL src32, BOOL flush_caches )
335 WORD* first = dst;
336 WORD* last = dst + count;
337 int x, y;
339 #ifndef WORDS_BIGENDIAN
340 if( src32 )
342 // Move to high 16 bits
343 ++src;
345 #endif
347 for( x = 0, y = 0; y < count; x += stride, ++y )
349 #ifndef WORDS_BIGENDIAN
350 dst[y] = src[x];
351 #else
352 dst[y] = ( ( src[x] & 0xff ) << 8 ) | ( ( src[x] & 0xff00 ) >> 8 );
353 #endif
356 if( flush_caches )
358 CacheClearE( first, (ULONG) last - (ULONG) first, CACRF_ClearD );
361 return last;
365 static WORD*
366 copy_stereo( WORD* lsrc, WORD* rsrc, WORD* dst, int count, int stride, BOOL src32, BOOL flush_caches )
368 WORD* first = dst;
369 WORD* last = dst + count * 2;
370 int x, y;
372 #ifndef WORDS_BIGENDIAN
373 if( src32 )
375 // Move to high 16 bits
376 ++lsrc;
377 ++rsrc;
379 #endif
381 for( x = 0, y = 0; y < count * 2; x += stride, y += 2 )
383 #ifndef WORDS_BIGENDIAN
384 dst[y+0] = lsrc[x];
385 dst[y+1] = rsrc[x];
386 #else
387 dst[y+0] = ( ( lsrc[x] & 0xff ) << 8 ) | ( ( lsrc[x] & 0xff00 ) >> 8 );
388 dst[y+1] = ( ( rsrc[x] & 0xff ) << 8 ) | ( ( rsrc[x] & 0xff00 ) >> 8 );
389 #endif
392 if( flush_caches )
394 CacheClearE( first, (ULONG) last - (ULONG) first, CACRF_ClearD );
397 return last;
401 /******************************************************************************
402 ** Record interrupt handler ***************************************************
403 ******************************************************************************/
405 #ifdef __AMIGAOS4__
406 void
407 RecordInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct EMU10kxData* dd )
408 #else
409 void
410 RecordInterrupt( struct EMU10kxData* dd )
411 #endif
413 struct AHIAudioCtrlDrv* AudioCtrl = dd->audioctrl;
414 struct DriverBase* AHIsubBase = (struct DriverBase*) dd->ahisubbase;
415 struct EMU10kxBase* EMU10kxBase = (struct EMU10kxBase*) AHIsubBase;
416 #ifdef __AMIGAOS4__
417 ULONG CacheCommand = CACRF_InvalidateD;
418 #else
419 ULONG CacheCommand = CACRF_ClearD;
420 #endif
423 struct AHIRecordMessage rm =
425 AHIST_S16S,
426 dd->current_record_buffer,
427 RECORD_BUFFER_SAMPLES / 2
430 #ifdef WORDS_BIGENDIAN
431 int i = 0;
432 WORD* ptr = dd->current_record_buffer;
433 #endif
435 #ifndef __AMIGAOS4__
436 // As OS4 can do invalidate only, we don't need to do flushing here.
437 // Between the invalidate at the end, DMA and entering this interrupt code,
438 // nobody should have touched this half of the record buffer.
440 if( EMU10kxBase->flush_caches )
442 // This is used to invalidate the cache
444 CacheClearE( dd->current_record_buffer,
445 RECORD_BUFFER_SAMPLES / 2 * 4,
446 CacheCommand );
448 #endif
450 #ifdef WORDS_BIGENDIAN
451 while( i < RECORD_BUFFER_SAMPLES / 2 * 2 )
453 *ptr = ( ( *ptr & 0xff ) << 8 ) | ( ( *ptr & 0xff00 ) >> 8 );
455 ++i;
456 ++ptr;
458 #endif
460 CallHookA( AudioCtrl->ahiac_SamplerFunc, (Object*) AudioCtrl, &rm );
462 if( EMU10kxBase->flush_caches )
464 // This is used to make sure the call above doesn't push dirty data
465 // the next time it's called. God help us if dd->current_record_buffer
466 // is not a the beginning of a cache line and there are dirty data
467 // in the DMA buffer before or after the current buffer.
469 CacheClearE( dd->current_record_buffer,
470 RECORD_BUFFER_SAMPLES / 2 * 4,
471 CacheCommand );
474 dd->record_interrupt_enabled = TRUE;