- Set a default PCM volume so that something can be heard when driver is
[AROS.git] / workbench / devs / AHI / Drivers / ac97 / ac97-main.c
blob077ad3fdfac9104f046735211a62c57ce15b3310
2 #define DEBUG 0
3 #include <aros/debug.h>
5 #include <config.h>
7 #include <devices/ahi.h>
8 #include <dos/dostags.h>
9 #include <exec/memory.h>
10 #include <libraries/ahi_sub.h>
11 #include <utility/tagitem.h>
13 #include <proto/ahi_sub.h>
14 #include <proto/exec.h>
15 #include <proto/dos.h>
16 #include <proto/utility.h>
17 #include <stddef.h>
19 #include <hidd/irq.h>
20 #include <asm/io.h>
22 #include "library.h"
23 #include "DriverData.h"
25 void
26 SlaveEntry( void );
28 PROCGW( static, void, slaveentry, SlaveEntry );
31 /* There is probably no reason to support all these frequencies. If,
32 * for example, your hardware is locked at 48 kHz, it's ok to only
33 * present one single mixing/recording frequency to the user. If your
34 * hardware has internal resamples and accepts any frequency, select a
35 * few common ones.
38 static const LONG frequencies[] =
40 48000, // DAT
43 #define FREQUENCIES (sizeof frequencies / sizeof frequencies[ 0 ])
45 static const ULONG table_5bit[] = {
46 0xb53c,
47 0x804e,
48 0x5ad5,
49 0x404e,
50 0x2d86,
51 0x203a,
52 0x16d1,
53 0x1027,
54 0x0b6f,
55 0x0818,
56 0x05bb,
57 0x040f,
58 0x02df,
59 0x0209,
60 0x0171,
61 0x0105,
62 0x00b9,
63 0x0083,
64 0x005d,
65 0x0042,
66 0x002e,
67 0x0021,
68 0x0017,
69 0x0010,
70 0x000c,
71 0x0008,
72 0x0006,
73 0x0004,
74 0x0003,
75 0x0002,
76 0x0001,
77 0x0000
80 static UWORD LinToLog(ULONG vol)
82 int i;
84 if (!vol) return 0x20;
86 for (i=0; i < 32; i++)
88 if (vol > table_5bit[i])
90 return i;
93 return 0x1f;
96 static void play_int(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw);
99 /******************************************************************************
100 ** AHIsub_AllocAudio **********************************************************
101 ******************************************************************************/
103 ULONG
104 _AHIsub_AllocAudio( struct TagItem* taglist,
105 struct AHIAudioCtrlDrv* AudioCtrl,
106 struct DriverBase* AHIsubBase )
108 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
109 OOP_Object *irq = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL);
112 AudioCtrl->ahiac_DriverData = AllocVec( sizeof( struct AC97Data ),
113 MEMF_CLEAR | MEMF_PUBLIC );
115 #define dd ((struct AC97Data *) AudioCtrl->ahiac_DriverData)
117 D(bug("AHI: AllocAudio: dd=%08x\n", dd));
119 if( dd != NULL )
121 dd->slavesignal = -1;
122 dd->mastersignal = AllocSignal( -1 );
123 dd->mastertask = (struct Process*) FindTask( NULL );
124 dd->ahisubbase = ac97Base;
125 dd->out_volume = 0x10000;
127 else
129 return AHISF_ERROR;
132 dd->irq = AllocVec(sizeof (HIDDT_IRQ_Handler), MEMF_CLEAR | MEMF_PUBLIC);
134 if (dd->irq)
136 struct pHidd_IRQ_AddHandler __msg__ = {
137 mID: OOP_GetMethodID(CLID_Hidd_IRQ, moHidd_IRQ_AddHandler),
138 handlerinfo: dd->irq,
139 id: ac97Base->irq_num,
140 }, *msg = &__msg__;
142 dd->irq->h_Node.ln_Pri = 0;
143 dd->irq->h_Node.ln_Name = "AHI Int";
144 dd->irq->h_Code = play_int;
145 dd->irq->h_Data = AudioCtrl;
147 OOP_DoMethod(irq, (OOP_Msg)msg);
149 OOP_DisposeObject(irq);
152 D(bug("AHI: AllocAudio: Everything OK\n"));
154 if( dd->mastersignal == -1 )
156 return AHISF_ERROR;
159 /* Setting the only working frequency for AC97 */
160 AudioCtrl->ahiac_MixFreq = 48000;
162 return ( AHISF_KNOWSTEREO |
163 AHISF_MIXING |
164 AHISF_TIMING );
168 /******************************************************************************
169 ** AHIsub_FreeAudio ***********************************************************
170 ******************************************************************************/
172 void
173 _AHIsub_FreeAudio( struct AHIAudioCtrlDrv* AudioCtrl,
174 struct DriverBase* AHIsubBase )
176 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
177 OOP_Object *irq = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL);
179 D(bug("AHI: FreeAudio\n"));
181 if (dd->irq)
183 struct pHidd_IRQ_RemHandler __msg__ = {
184 mID: OOP_GetMethodID(CLID_Hidd_IRQ, moHidd_IRQ_RemHandler),
185 handlerinfo: dd->irq,
186 }, *msg = &__msg__;
187 OOP_DoMethod(irq, (OOP_Msg)msg);
188 FreeVec(dd->irq);
191 D(bug("AHI: FreeAudio: IRQ removed\n"));
193 if( AudioCtrl->ahiac_DriverData != NULL )
195 FreeSignal( dd->mastersignal );
197 D(bug("AHI: FreeAudio: Signal freed\n"));
199 FreeVec( AudioCtrl->ahiac_DriverData );
201 D(bug("AHI: FreeAudio: DriverData freed\n"));
203 AudioCtrl->ahiac_DriverData = NULL;
206 OOP_DisposeObject(irq);
208 D(bug("AHI: FreeAudio: IRQ object freed\n"));
213 /******************************************************************************
214 ** AHIsub_Disable *************************************************************
215 ******************************************************************************/
217 void
218 _AHIsub_Disable( struct AHIAudioCtrlDrv* AudioCtrl,
219 struct DriverBase* AHIsubBase )
221 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
223 // V6 drivers do not have to preserve all registers
225 Disable();
229 /******************************************************************************
230 ** AHIsub_Enable **************************************************************
231 ******************************************************************************/
233 void
234 _AHIsub_Enable( struct AHIAudioCtrlDrv* AudioCtrl,
235 struct DriverBase* AHIsubBase )
237 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
239 // V6 drivers do not have to preserve all registers
241 Enable();
245 /******************************************************************************
246 ** AHIsub_Start ***************************************************************
247 ******************************************************************************/
249 ULONG
250 _AHIsub_Start( ULONG flags,
251 struct AHIAudioCtrlDrv* AudioCtrl,
252 struct DriverBase* AHIsubBase )
254 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
256 D(bug("AHI: Start\n"));
258 AHIsub_Stop( flags, AudioCtrl );
260 D(bug("AHI: Start: Stop called\n"));
262 if(flags & AHISF_PLAY)
264 struct TagItem proctags[] =
266 { NP_Entry, (IPTR) &slaveentry },
267 { NP_Name, (IPTR) LibName },
268 { NP_Priority, -1 },
269 { TAG_DONE, 0 }
272 dd->mixbuffer = AllocVec( AudioCtrl->ahiac_BuffSize,
273 MEMF_ANY | MEMF_PUBLIC );
275 D(bug("AHI: Start: Mixing buffer = %08x\n",dd->mixbuffer));
277 if( dd->mixbuffer == NULL ) return AHIE_NOMEM;
279 Forbid();
281 dd->slavetask = CreateNewProc( proctags );
283 D(bug("AHI: Start: Slave task = %08x\n",dd->slavetask));
285 if( dd->slavetask != NULL )
287 dd->slavetask->pr_Task.tc_UserData = AudioCtrl;
290 D(bug("AHI: Start: Slave task UserData set\n"));
292 Permit();
294 if( dd->slavetask != NULL )
296 Wait( 1L << dd->mastersignal ); // Wait for slave to come alive
298 D(bug("AHI: Start: Slave task UP and running\n"));
300 if( dd->slavetask == NULL ) // Is slave alive or dead?
302 return AHIE_UNKNOWN;
305 else
307 return AHIE_NOMEM; // Well, out of memory or whatever...
311 if( flags & AHISF_RECORD )
313 return AHIE_UNKNOWN;
316 D(bug("AHI: Start: Everything OK\n"));
318 return AHIE_OK;
322 /******************************************************************************
323 ** AHIsub_Update **************************************************************
324 ******************************************************************************/
326 void
327 _AHIsub_Update( ULONG flags,
328 struct AHIAudioCtrlDrv* AudioCtrl,
329 struct DriverBase* AHIsubBase )
331 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
333 // Empty function
337 /******************************************************************************
338 ** AHIsub_Stop ****************************************************************
339 ******************************************************************************/
341 void
342 _AHIsub_Stop( ULONG flags,
343 struct AHIAudioCtrlDrv* AudioCtrl,
344 struct DriverBase* AHIsubBase )
346 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
348 if( flags & AHISF_PLAY )
350 if( dd->slavetask != NULL )
352 if( dd->slavesignal != -1 )
354 Signal( (struct Task*) dd->slavetask,
355 1L << dd->slavesignal ); // Kill him!
358 Wait( 1L << dd->mastersignal ); // Wait for slave to die
361 FreeVec( dd->mixbuffer );
362 dd->mixbuffer = NULL;
365 if(flags & AHISF_RECORD)
367 // Do nothing
372 /******************************************************************************
373 ** AHIsub_GetAttr *************************************************************
374 ******************************************************************************/
376 IPTR
377 _AHIsub_GetAttr( ULONG attribute,
378 LONG argument,
379 IPTR def,
380 struct TagItem* taglist,
381 struct AHIAudioCtrlDrv* AudioCtrl,
382 struct DriverBase* AHIsubBase )
384 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
385 size_t i;
387 switch( attribute )
389 case AHIDB_Bits:
390 return 16;
392 case AHIDB_Frequencies:
393 return FREQUENCIES;
395 case AHIDB_Frequency: // Index->Frequency
396 return (LONG) frequencies[ argument ];
398 case AHIDB_Index: // Frequency->Index
399 if( argument <= frequencies[ 0 ] )
401 return 0;
404 if( argument >= frequencies[ FREQUENCIES - 1 ] )
406 return FREQUENCIES - 1;
409 for( i = 1; i < FREQUENCIES; i++ )
411 if( frequencies[ i ] > argument )
413 if( ( argument - frequencies[ i - 1 ] ) <
414 ( frequencies[ i ] - argument ) )
416 return i-1;
418 else
420 return i;
425 return 0; // Will not happen
427 case AHIDB_Author:
428 return (IPTR) "Michal Schulz";
430 case AHIDB_Copyright:
431 return (IPTR) "APL";
433 case AHIDB_Version:
434 return (IPTR) LibIDString;
436 case AHIDB_Record:
437 return FALSE;
439 case AHIDB_Realtime:
440 return TRUE;
442 case AHIDB_Outputs:
443 return 1;
446 case AHIDB_MinMonitorVolume:
447 return 0x00000;
449 case AHIDB_MaxMonitorVolume:
450 return 0x10000;
452 case AHIDB_MinOutputVolume:
453 return 0x00000;
455 case AHIDB_MaxOutputVolume:
456 return 0x10000;
458 case AHIDB_Output:
459 return (IPTR) "Default"; // We have only one "output"!
461 default:
462 return def;
467 /******************************************************************************
468 ** AHIsub_HardwareControl *****************************************************
469 ******************************************************************************/
471 ULONG
472 _AHIsub_HardwareControl( ULONG attribute,
473 LONG argument,
474 struct AHIAudioCtrlDrv* AudioCtrl,
475 struct DriverBase* AHIsubBase )
477 struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
478 UWORD vol;
480 switch(attribute)
482 case AHIC_OutputVolume:
483 vol = LinToLog(argument);
485 if (vol == 0x20) vol = 0x8000;
486 else vol = vol | vol << 8;
488 D(bug("SetVol %05x translated to %04x\n", argument, vol));
489 dd->out_volume = argument;
490 if (ac97Base->mixer_set_reg)
491 ac97Base->mixer_set_reg(ac97Base, AC97_PCM_VOL, vol);
492 return TRUE;
494 case AHIC_OutputVolume_Query:
495 return dd->out_volume;
498 return 0;
501 #undef SysBase
502 static void play_int(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
504 struct AHIAudioCtrlDrv* AudioCtrl;
505 struct DriverBase* AHIsubBase;
506 struct ac97Base* ac97Base;
507 struct ExecBase *SysBase;
509 AudioCtrl = (struct AHIAudioCtrlDrv*) irq->h_Data;
510 AHIsubBase = (struct DriverBase*) dd->ahisubbase;
511 ac97Base = (struct ac97Base*) AHIsubBase;
512 SysBase = (struct SysBase*) ac97Base->sysbase;
514 dd->old_SR = inw(ac97Base->dmabase + ac97Base->off_po_sr);
515 outw(dd->old_SR & 0x1c, ac97Base->dmabase + ac97Base->off_po_sr);
517 if ((dd->old_SR & 0x1c) && dd->slavetask)
519 /* Signaling the slave task */
520 Signal((struct Task *)dd->slavetask, SIGBREAKF_CTRL_E);