revert between 56095 -> 55830 in arch
[AROS.git] / workbench / devs / AHI / Drivers / ac97 / ac97-main.c
blobe064fe613a049b08403dbc90144bcb5d332b350d
2 #define DEBUG 1
3 #include <aros/debug.h>
5 #include <devices/ahi.h>
6 #include <dos/dostags.h>
7 #include <exec/memory.h>
8 #include <libraries/ahi_sub.h>
9 #include <utility/tagitem.h>
11 #include <proto/ahi_sub.h>
12 #include <proto/exec.h>
13 #include <proto/dos.h>
14 #include <proto/utility.h>
15 #include <stddef.h>
17 #include <asm/io.h>
19 #include "library.h"
20 #include "DriverData.h"
22 void
23 SlaveEntry( void );
25 PROCGW( static, void, slaveentry, SlaveEntry );
28 /* There is probably no reason to support all these frequencies. If,
29 * for example, your hardware is locked at 48 kHz, it's ok to only
30 * present one single mixing/recording frequency to the user. If your
31 * hardware has internal resamples and accepts any frequency, select a
32 * few common ones.
35 static const LONG frequencies[] =
37 48000, // DAT
40 #define FREQUENCIES (sizeof frequencies / sizeof frequencies[ 0 ])
42 static const ULONG table_5bit[] = {
43 0xb53c,
44 0x804e,
45 0x5ad5,
46 0x404e,
47 0x2d86,
48 0x203a,
49 0x16d1,
50 0x1027,
51 0x0b6f,
52 0x0818,
53 0x05bb,
54 0x040f,
55 0x02df,
56 0x0209,
57 0x0171,
58 0x0105,
59 0x00b9,
60 0x0083,
61 0x005d,
62 0x0042,
63 0x002e,
64 0x0021,
65 0x0017,
66 0x0010,
67 0x000c,
68 0x0008,
69 0x0006,
70 0x0004,
71 0x0003,
72 0x0002,
73 0x0001,
74 0x0000
77 static UWORD LinToLog(ULONG vol)
79 int i;
81 if (!vol) return 0x20;
83 for (i=0; i < 32; i++)
85 if (vol > table_5bit[i])
87 return i;
90 return 0x1f;
93 static AROS_INTP(play_int);
96 /******************************************************************************
97 ** AHIsub_AllocAudio **********************************************************
98 ******************************************************************************/
100 ULONG
101 _AHIsub_AllocAudio( struct TagItem* taglist,
102 struct AHIAudioCtrlDrv* AudioCtrl,
103 struct DriverBase* AHIsubBase )
105 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
107 AudioCtrl->ahiac_DriverData = AllocVec( sizeof( struct AC97Data ),
108 MEMF_CLEAR | MEMF_PUBLIC );
110 #define dd ((struct AC97Data *) AudioCtrl->ahiac_DriverData)
112 D(bug("AHI: AllocAudio: dd=%08x\n", dd));
114 if( dd != NULL )
116 dd->slavesignal = -1;
117 dd->mastersignal = AllocSignal( -1 );
118 dd->mastertask = (struct Process*) FindTask( NULL );
119 dd->ahisubbase = ac97Base;
120 dd->out_volume = 0x10000;
122 else
124 return AHISF_ERROR;
128 dd->irq.is_Node.ln_Type = NT_INTERRUPT;
129 dd->irq.is_Node.ln_Pri = 0;
130 dd->irq.is_Node.ln_Name = "AHI Int";
131 dd->irq.is_Code = (APTR)play_int;
132 dd->irq.is_Data = AudioCtrl;
134 AddIntServer(INTB_KERNEL + ac97Base->irq_num, &dd->irq);
137 D(bug("AHI: AllocAudio: Everything OK\n"));
139 if( dd->mastersignal == -1 )
141 return AHISF_ERROR;
144 /* Setting the only working frequency for AC97 */
145 AudioCtrl->ahiac_MixFreq = 48000;
147 return ( AHISF_KNOWSTEREO |
148 AHISF_MIXING |
149 AHISF_TIMING );
153 /******************************************************************************
154 ** AHIsub_FreeAudio ***********************************************************
155 ******************************************************************************/
157 void
158 _AHIsub_FreeAudio( struct AHIAudioCtrlDrv* AudioCtrl,
159 struct DriverBase* AHIsubBase )
161 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
163 D(bug("AHI: FreeAudio\n"));
165 RemIntServer(INTB_KERNEL + ac97Base->irq_num, &dd->irq);
167 D(bug("AHI: FreeAudio: IRQ removed\n"));
169 if( AudioCtrl->ahiac_DriverData != NULL )
171 FreeSignal( dd->mastersignal );
173 D(bug("AHI: FreeAudio: Signal freed\n"));
175 FreeVec( AudioCtrl->ahiac_DriverData );
177 D(bug("AHI: FreeAudio: DriverData freed\n"));
179 AudioCtrl->ahiac_DriverData = NULL;
184 /******************************************************************************
185 ** AHIsub_Disable *************************************************************
186 ******************************************************************************/
188 void
189 _AHIsub_Disable( struct AHIAudioCtrlDrv* AudioCtrl,
190 struct DriverBase* AHIsubBase )
192 // V6 drivers do not have to preserve all registers
194 Disable();
198 /******************************************************************************
199 ** AHIsub_Enable **************************************************************
200 ******************************************************************************/
202 void
203 _AHIsub_Enable( struct AHIAudioCtrlDrv* AudioCtrl,
204 struct DriverBase* AHIsubBase )
206 // V6 drivers do not have to preserve all registers
208 Enable();
212 /******************************************************************************
213 ** AHIsub_Start ***************************************************************
214 ******************************************************************************/
216 ULONG
217 _AHIsub_Start( ULONG flags,
218 struct AHIAudioCtrlDrv* AudioCtrl,
219 struct DriverBase* AHIsubBase )
221 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
223 D(bug("AHI: Start\n"));
225 AHIsub_Stop( flags, AudioCtrl );
227 D(bug("AHI: Start: Stop called\n"));
229 if(flags & AHISF_PLAY)
231 struct TagItem proctags[] =
233 { NP_Entry, (IPTR) &slaveentry },
234 { NP_Name, (IPTR) LibName },
235 { NP_Priority, -1 },
236 { TAG_DONE, 0 }
239 dd->mixbuffer = AllocVec( AudioCtrl->ahiac_BuffSize,
240 MEMF_ANY | MEMF_PUBLIC );
242 D(bug("AHI: Start: Mixing buffer = %08x\n",dd->mixbuffer));
244 if( dd->mixbuffer == NULL ) return AHIE_NOMEM;
246 Forbid();
248 dd->slavetask = CreateNewProc( proctags );
250 D(bug("AHI: Start: Slave task = %08x\n",dd->slavetask));
252 if( dd->slavetask != NULL )
254 dd->slavetask->pr_Task.tc_UserData = AudioCtrl;
257 D(bug("AHI: Start: Slave task UserData set\n"));
259 Permit();
261 if( dd->slavetask != NULL )
263 Wait( 1L << dd->mastersignal ); // Wait for slave to come alive
265 D(bug("AHI: Start: Slave task UP and running\n"));
267 if( dd->slavetask == NULL ) // Is slave alive or dead?
269 return AHIE_UNKNOWN;
272 else
274 return AHIE_NOMEM; // Well, out of memory or whatever...
278 if( flags & AHISF_RECORD )
280 return AHIE_UNKNOWN;
283 D(bug("AHI: Start: Everything OK\n"));
285 return AHIE_OK;
289 /******************************************************************************
290 ** AHIsub_Update **************************************************************
291 ******************************************************************************/
293 void
294 _AHIsub_Update( ULONG flags,
295 struct AHIAudioCtrlDrv* AudioCtrl,
296 struct DriverBase* AHIsubBase )
298 // Empty function
302 /******************************************************************************
303 ** AHIsub_Stop ****************************************************************
304 ******************************************************************************/
306 void
307 _AHIsub_Stop( ULONG flags,
308 struct AHIAudioCtrlDrv* AudioCtrl,
309 struct DriverBase* AHIsubBase )
311 if( flags & AHISF_PLAY )
313 if( dd->slavetask != NULL )
315 if( dd->slavesignal != -1 )
317 Signal( (struct Task*) dd->slavetask,
318 1L << dd->slavesignal ); // Kill him!
321 Wait( 1L << dd->mastersignal ); // Wait for slave to die
324 FreeVec( dd->mixbuffer );
325 dd->mixbuffer = NULL;
328 if(flags & AHISF_RECORD)
330 // Do nothing
335 /******************************************************************************
336 ** AHIsub_GetAttr *************************************************************
337 ******************************************************************************/
339 IPTR
340 _AHIsub_GetAttr( ULONG attribute,
341 LONG argument,
342 IPTR def,
343 struct TagItem* taglist,
344 struct AHIAudioCtrlDrv* AudioCtrl,
345 struct DriverBase* AHIsubBase )
347 size_t i;
349 switch( attribute )
351 case AHIDB_Bits:
352 return 16;
354 case AHIDB_Frequencies:
355 return FREQUENCIES;
357 case AHIDB_Frequency: // Index->Frequency
358 return (LONG) frequencies[ argument ];
360 case AHIDB_Index: // Frequency->Index
361 if( argument <= frequencies[ 0 ] )
363 return 0;
366 if( argument >= frequencies[ FREQUENCIES - 1 ] )
368 return FREQUENCIES - 1;
371 for( i = 1; i < FREQUENCIES; i++ )
373 if( frequencies[ i ] > argument )
375 if( ( argument - frequencies[ i - 1 ] ) <
376 ( frequencies[ i ] - argument ) )
378 return i-1;
380 else
382 return i;
387 return 0; // Will not happen
389 case AHIDB_Author:
390 return (IPTR) "Michal Schulz";
392 case AHIDB_Copyright:
393 return (IPTR) "APL";
395 case AHIDB_Version:
396 return (IPTR) LibIDString;
398 case AHIDB_Record:
399 return FALSE;
401 case AHIDB_Realtime:
402 return TRUE;
404 case AHIDB_Outputs:
405 return 1;
408 case AHIDB_MinMonitorVolume:
409 return 0x00000;
411 case AHIDB_MaxMonitorVolume:
412 return 0x10000;
414 case AHIDB_MinOutputVolume:
415 return 0x00000;
417 case AHIDB_MaxOutputVolume:
418 return 0x10000;
420 case AHIDB_Output:
421 return (IPTR) "Default"; // We have only one "output"!
423 default:
424 return def;
429 /******************************************************************************
430 ** AHIsub_HardwareControl *****************************************************
431 ******************************************************************************/
433 ULONG
434 _AHIsub_HardwareControl( ULONG attribute,
435 LONG argument,
436 struct AHIAudioCtrlDrv* AudioCtrl,
437 struct DriverBase* AHIsubBase )
439 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
440 UWORD vol;
442 switch(attribute)
444 case AHIC_OutputVolume:
445 vol = LinToLog(argument);
447 if (vol == 0x20) vol = 0x8000;
448 else vol = vol | vol << 8;
450 D(bug("SetVol %05x translated to %04x\n", argument, vol));
451 dd->out_volume = argument;
452 if (ac97Base->mixer_set_reg)
453 ac97Base->mixer_set_reg(ac97Base, AC97_PCM_VOL, vol);
454 return TRUE;
456 case AHIC_OutputVolume_Query:
457 return dd->out_volume;
460 return 0;
463 #undef SysBase
465 static AROS_INTH1(play_int, struct AHIAudioCtrlDrv *, AudioCtrl)
467 AROS_INTFUNC_INIT
469 struct DriverBase* AHIsubBase;
470 struct ac97Base* ac97Base;
472 AHIsubBase = (struct DriverBase*) dd->ahisubbase;
473 ac97Base = (struct ac97Base*) AHIsubBase;
475 dd->old_SR = inw((IPTR)ac97Base->dmabase + ac97Base->off_po_sr);
476 outw(dd->old_SR & 0x1c, (IPTR)ac97Base->dmabase + ac97Base->off_po_sr);
478 if ((dd->old_SR & 0x1c) && dd->slavetask)
480 /* Signaling the slave task */
481 Signal((struct Task *)dd->slavetask, SIGBREAKF_CTRL_E);
484 return FALSE;
486 AROS_INTFUNC_EXIT