src/audio/mint/SDL_mintaudio_xbios.c
author Sam Lantinga
Sat, 19 Sep 2009 13:29:40 +0000
changeset 3280 00cace2d9080
parent 2942 1e431c2631ee
permissions -rw-r--r--
Merged a cleaned up version of Jiang's code changes from Google Summer of Code 2009
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library 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 GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /*
    25 	MiNT audio driver
    26 	using XBIOS functions (Falcon)
    27 
    28 	Patrice Mandin, Didier Méquignon
    29 */
    30 
    31 #include <unistd.h>
    32 #include <support.h>
    33 
    34 /* Mint includes */
    35 #include <mint/osbind.h>
    36 #include <mint/falcon.h>
    37 #include <mint/cookie.h>
    38 
    39 #include "SDL_audio.h"
    40 #include "../SDL_audio_c.h"
    41 #include "../SDL_sysaudio.h"
    42 
    43 #include "../../video/ataricommon/SDL_atarimxalloc_c.h"
    44 
    45 #include "SDL_mintaudio.h"
    46 #include "SDL_mintaudio_dma8.h"
    47 
    48 /*--- Defines ---*/
    49 
    50 #define MINT_AUDIO_DRIVER_NAME "mint_xbios"
    51 
    52 /* Debug print info */
    53 #define DEBUG_NAME "audio:xbios: "
    54 #if 0
    55 #define DEBUG_PRINT(what) \
    56 	{ \
    57 		printf what; \
    58 	}
    59 #else
    60 #define DEBUG_PRINT(what)
    61 #endif
    62 
    63 static unsigned long cookie_snd = 0;
    64 
    65 static void
    66 MINTXBIOS_LockDevice(_THIS)
    67 {
    68     /* Stop replay */
    69     Buffoper(0);
    70 }
    71 
    72 static void
    73 MINTXBIOS_UnlockDevice(_THIS)
    74 {
    75     /* Restart replay */
    76     Buffoper(SB_PLA_ENA | SB_PLA_RPT);
    77 }
    78 
    79 static void
    80 MINTXBIOS_CloseDevice(_THIS)
    81 {
    82     if (this->hidden != NULL) {
    83         /* Stop replay */
    84         SDL_MintAudio_WaitThread();
    85         Buffoper(0);
    86 
    87         if (!SDL_MintAudio_mint_present) {
    88             /* Uninstall interrupt */
    89             Jdisint(MFP_DMASOUND);
    90         }
    91 
    92         /* Wait if currently playing sound */
    93         while (SDL_MintAudio_mutex != 0) {
    94         }
    95 
    96         /* Clear buffers */
    97         if (SDL_MintAudio_audiobuf[0]) {
    98             Mfree(SDL_MintAudio_audiobuf[0]);
    99             SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
   100         }
   101 
   102         /* Unlock sound system */
   103         Unlocksnd();
   104 
   105         SDL_free(this->hidden);
   106         this->hidden = NULL;
   107     }
   108 }
   109 
   110 /* Falcon XBIOS implementation of Devconnect() is buggy with external clock */
   111 static void
   112 Devconnect2(int src, int dst, int sclk, int pre)
   113 {
   114     static const unsigned short MASK1[3] = { 0, 0x6000, 0 };
   115     static const unsigned short MASK2[4] = { 0xFFF0, 0xFF8F, 0xF0FF, 0x0FFF };
   116     static const unsigned short INDEX1[4] = { 1, 3, 5, 7 };
   117     static const unsigned short INDEX2[4] = { 0, 2, 4, 6 };
   118     unsigned short sync_div, dev_ctrl, dest_ctrl;
   119     void *oldstack;
   120 
   121     if (dst == 0) {
   122         return;
   123     }
   124 
   125     oldstack = (void *) Super(0);
   126 
   127     dev_ctrl = DMAAUDIO_IO.dev_ctrl;
   128     dest_ctrl = DMAAUDIO_IO.dest_ctrl;
   129     dev_ctrl &= MASK2[src];
   130 
   131     if (src == ADC) {
   132         dev_ctrl |= MASK1[sclk];
   133     } else {
   134         dev_ctrl |= (INDEX1[sclk] << (src << 4));
   135     }
   136 
   137     if (dst & DMAREC) {
   138         dest_ctrl &= 0xFFF0;
   139         dest_ctrl |= INDEX1[src];
   140     }
   141 
   142     if (dst & DSPRECV) {
   143         dest_ctrl &= 0xFF8F;
   144         dest_ctrl |= (INDEX1[src] << 4);
   145     }
   146 
   147     if (dst & EXTOUT) {
   148         dest_ctrl &= 0xF0FF;
   149         dest_ctrl |= (INDEX1[src] << 8);
   150     }
   151 
   152     if (dst & DAC) {
   153         dev_ctrl &= 0x0FFF;
   154         dev_ctrl |= MASK1[sclk];
   155         dest_ctrl &= 0x0FFF;
   156         dest_ctrl |= (INDEX2[src] << 12);
   157     }
   158 
   159     sync_div = DMAAUDIO_IO.sync_div;
   160     if (sclk == CLKEXT) {
   161         pre <<= 8;
   162         sync_div &= 0xF0FF;
   163     } else {
   164         sync_div &= 0xFFF0;
   165     }
   166     sync_div |= pre;
   167 
   168     DMAAUDIO_IO.dev_ctrl = dev_ctrl;
   169     DMAAUDIO_IO.dest_ctrl = dest_ctrl;
   170     DMAAUDIO_IO.sync_div = sync_div;
   171 
   172     Super(oldstack);
   173 }
   174 
   175 static void
   176 MINTXBIOS_CheckExternalClock(_THIS)
   177 {
   178 #define SIZE_BUF_CLOCK_MEASURE (44100/10)
   179 
   180     unsigned long cookie_snd;
   181     char *buffer;
   182     int i, j;
   183 
   184     /* DSP present with its GPIO port ? */
   185     if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
   186         return;
   187     }
   188     if ((cookie_snd & SND_DSP) == 0) {
   189         return;
   190     }
   191 
   192     buffer = Atari_SysMalloc(SIZE_BUF_CLOCK_MEASURE, MX_STRAM);
   193     if (buffer == NULL) {
   194         DEBUG_PRINT((DEBUG_NAME "Not enough memory for the measure\n"));
   195         return;
   196     }
   197     SDL_memset(buffer, 0, SIZE_BUF_CLOCK_MEASURE);
   198 
   199     Buffoper(0);
   200     Settracks(0, 0);
   201     Setmontracks(0);
   202     Setmode(MONO8);
   203     Jdisint(MFP_TIMERA);
   204 
   205     for (i = 0; i < 2; i++) {
   206         Gpio(GPIO_SET, 7);      /* DSP port gpio outputs */
   207         Gpio(GPIO_WRITE, 2 + i);        /* 22.5792/24.576 MHz for 44.1/48KHz */
   208         Devconnect2(DMAPLAY, DAC, CLKEXT, CLK50K);      /* Matrix and clock source */
   209         Setbuffer(0, buffer, buffer + SIZE_BUF_CLOCK_MEASURE);  /* Set buffer */
   210         Xbtimer(XB_TIMERA, 5, 38, SDL_MintAudio_XbiosInterruptMeasureClock);    /* delay mode timer A, prediv /64, 1KHz */
   211         Jenabint(MFP_TIMERA);
   212         SDL_MintAudio_clocktics = 0;
   213         Buffoper(SB_PLA_ENA);
   214         usleep(110000);
   215 
   216         if ((Buffoper(-1) & 1) == 0) {
   217             if (SDL_MintAudio_clocktics) {
   218                 unsigned long khz;
   219 
   220                 khz =
   221                     ((SIZE_BUF_CLOCK_MEASURE /
   222                       SDL_MintAudio_clocktics) + 1) & 0xFFFFFFFE;
   223                 DEBUG_PRINT((DEBUG_NAME "measure %d: freq=%lu KHz\n",
   224                              i + 1, khz));
   225 
   226                 if (khz == 44) {
   227                     for (j = 1; j < 4; j++) {
   228                         SDL_MintAudio_AddFrequency(this,
   229                                                    MASTERCLOCK_44K
   230                                                    /
   231                                                    (MASTERPREDIV_FALCON
   232                                                     * (1 << j)),
   233                                                    MASTERCLOCK_44K,
   234                                                    (1 << j) - 1, 2 + i);
   235                     }
   236                 } else if (khz == 48) {
   237                     for (j = 1; j < 4; j++) {
   238                         SDL_MintAudio_AddFrequency(this,
   239                                                    MASTERCLOCK_48K
   240                                                    /
   241                                                    (MASTERPREDIV_FALCON
   242                                                     * (1 << j)),
   243                                                    MASTERCLOCK_48K,
   244                                                    (1 << j) - 1, 2 + i);
   245                     }
   246                 }
   247             } else {
   248                 DEBUG_PRINT((DEBUG_NAME "No measure\n"));
   249             }
   250         } else {
   251             DEBUG_PRINT((DEBUG_NAME "No SDMA clock\n"));
   252         }
   253 
   254         Buffoper(0);            /* stop */
   255         Jdisint(MFP_TIMERA);    /* Uninstall interrupt */
   256     }
   257 
   258     Mfree(buffer);
   259 }
   260 
   261 static int
   262 MINTXBIOS_CheckAudio(_THIS)
   263 {
   264     int i;
   265     Uint32 extclock;
   266 
   267     DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",
   268                  SDL_AUDIO_BITSIZE(this->spec.format)));
   269     DEBUG_PRINT(("float=%d, ", SDL_AUDIO_ISFLOAT(this->spec.format)));
   270     DEBUG_PRINT(("signed=%d, ", SDL_AUDIO_ISSIGNED(this->spec.format)));
   271     DEBUG_PRINT(("big endian=%d, ",
   272                  SDL_AUDIO_ISBIGENDIAN(this->spec.format)));
   273     DEBUG_PRINT(("channels=%d, ", this->spec.channels));
   274     DEBUG_PRINT(("freq=%d\n", this->spec.freq));
   275 
   276     this->spec.format |= SDL_AUDIO_MASK_SIGNED; /* Audio is always signed */
   277 
   278     /* clamp out int32/float32 */
   279     if (SDL_AUDIO_BITSIZE(this->spec.format) >= 16) {
   280         this->spec.format = AUDIO_S16MSB;       /* Audio is always big endian */
   281         this->spec.channels = 2;        /* 16 bits always stereo */
   282     } else if (this->spec.channels > 2) {
   283         this->spec.channels = 2;        /* no more than stereo! */
   284     }
   285 
   286     MINTAUDIO_freqcount = 0;
   287 
   288     /* Add external clocks if present */
   289     MINTXBIOS_CheckExternalClock(this);
   290 
   291     /* Standard clocks */
   292     for (i = 1; i < 12; i++) {
   293         /* Remove unusable Falcon codec predivisors */
   294         if ((i == 6) || (i == 8) || (i == 10)) {
   295             continue;
   296         }
   297         SDL_MintAudio_AddFrequency(this,
   298                                    MASTERCLOCK_FALCON1 /
   299                                    (MASTERPREDIV_FALCON * (i + 1)),
   300                                    MASTERCLOCK_FALCON1, i, -1);
   301     }
   302 
   303 #if 1
   304     for (i = 0; i < MINTAUDIO_freqcount; i++) {
   305         DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
   306                      i, MINTAUDIO_frequencies[i].frequency,
   307                      MINTAUDIO_frequencies[i].masterclock,
   308                      MINTAUDIO_frequencies[i].predivisor));
   309     }
   310 #endif
   311 
   312     MINTAUDIO_numfreq = SDL_MintAudio_SearchFrequency(this, this->spec.freq);
   313     this->spec.freq = MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
   314 
   315     DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",
   316                  SDL_AUDIO_BITSIZE(this->spec.format)));
   317     DEBUG_PRINT(("float=%d, ", SDL_AUDIO_ISFLOAT(this->spec.format)));
   318     DEBUG_PRINT(("signed=%d, ", SDL_AUDIO_ISSIGNED(this->spec.format)));
   319     DEBUG_PRINT(("big endian=%d, ",
   320                  SDL_AUDIO_ISBIGENDIAN(this->spec.format)));
   321     DEBUG_PRINT(("channels=%d, ", this->spec.channels));
   322     DEBUG_PRINT(("freq=%d\n", this->spec.freq));
   323 
   324     return 0;
   325 }
   326 
   327 static void
   328 MINTXBIOS_InitAudio(_THIS)
   329 {
   330     int channels_mode, dmaclock, prediv;
   331     void *buffer;
   332 
   333     /* Stop currently playing sound */
   334     SDL_MintAudio_quit_thread = SDL_FALSE;
   335     SDL_MintAudio_thread_finished = SDL_TRUE;
   336     SDL_MintAudio_WaitThread();
   337     Buffoper(0);
   338 
   339     /* Set replay tracks */
   340     Settracks(0, 0);
   341     Setmontracks(0);
   342 
   343     /* Select replay format */
   344     channels_mode = STEREO16;
   345     switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
   346     case 8:
   347         if (this->spec.channels == 2) {
   348             channels_mode = STEREO8;
   349         } else {
   350             channels_mode = MONO8;
   351         }
   352         break;
   353     }
   354     if (Setmode(channels_mode) < 0) {
   355         DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n"));
   356     }
   357 
   358     dmaclock = MINTAUDIO_frequencies[MINTAUDIO_numfreq].masterclock;
   359     prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
   360     if (MINTAUDIO_frequencies[MINTAUDIO_numfreq].gpio_bits != -1) {
   361         Gpio(GPIO_SET, 7);      /* DSP port gpio outputs */
   362         Gpio(GPIO_WRITE, MINTAUDIO_frequencies[MINTAUDIO_numfreq].gpio_bits);
   363         Devconnect2(DMAPLAY, DAC | EXTOUT, CLKEXT, prediv);
   364     } else {
   365         Devconnect2(DMAPLAY, DAC, CLK25M, prediv);
   366     }
   367 
   368     /* Set buffer */
   369     buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
   370     if (Setbuffer(0, buffer, buffer + this->spec.size) < 0) {
   371         DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n"));
   372     }
   373 
   374     if (SDL_MintAudio_mint_present) {
   375         SDL_MintAudio_thread_pid = tfork(SDL_MintAudio_Thread, 0);
   376     } else {
   377         /* Install interrupt */
   378         Jdisint(MFP_DMASOUND);
   379         /*Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_XbiosInterrupt); */
   380         Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_Dma8Interrupt);
   381         Jenabint(MFP_DMASOUND);
   382 
   383         if (Setinterrupt(SI_TIMERA, SI_PLAY) < 0) {
   384             DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n"));
   385         }
   386     }
   387 
   388     /* Go */
   389     Buffoper(SB_PLA_ENA | SB_PLA_RPT);
   390     DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
   391 }
   392 
   393 static int
   394 MINTXBIOS_OpenDevice(_THIS, const char *devname, int iscapture)
   395 {
   396     /* Lock sound system */
   397     if (Locksnd() != 1) {
   398         SDL_SetError("MINTXBIOS_OpenAudio: Audio system already in use");
   399         return 0;
   400     }
   401 
   402     SDL_MintAudio_device = this;
   403 
   404     /* Check audio capabilities */
   405     if (MINTXBIOS_CheckAudio(this) == -1) {
   406         return 0;
   407     }
   408 
   409     /* Initialize all variables that we clean on shutdown */
   410     this->hidden = (struct SDL_PrivateAudioData *)
   411         SDL_malloc((sizeof *this->hidden));
   412     if (this->hidden == NULL) {
   413         SDL_OutOfMemory();
   414         return 0;
   415     }
   416     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   417 
   418     SDL_CalculateAudioSpec(&this->spec);
   419 
   420     /* Allocate memory for audio buffers in DMA-able RAM */
   421     DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", this->spec.size));
   422 
   423     SDL_MintAudio_audiobuf[0] =
   424         Atari_SysMalloc(this->spec.size * 2, MX_STRAM);
   425     if (SDL_MintAudio_audiobuf[0] == NULL) {
   426         SDL_free(this->hidden);
   427         this->hidden = NULL;
   428         SDL_OutOfMemory();
   429         return 0;
   430     }
   431     SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + this->spec.size;
   432     SDL_MintAudio_numbuf = 0;
   433     SDL_memset(SDL_MintAudio_audiobuf[0], this->spec.silence,
   434                this->spec.size * 2);
   435     SDL_MintAudio_audiosize = this->spec.size;
   436     SDL_MintAudio_mutex = 0;
   437 
   438     DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n",
   439                  SDL_MintAudio_audiobuf[0]));
   440     DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n",
   441                  SDL_MintAudio_audiobuf[1]));
   442 
   443     SDL_MintAudio_CheckFpu();
   444 
   445     /* Setup audio hardware */
   446     MINTXBIOS_InitAudio(this);
   447 
   448     return 1;                   /* good to go. */
   449 }
   450 
   451 static int
   452 MINTXBIOS_Init(SDL_AudioDriverImpl * impl)
   453 {
   454     unsigned long dummy = 0;
   455     /*SDL_MintAudio_mint_present = (Getcookie(C_MiNT, &dummy) == C_FOUND); */
   456     SDL_MintAudio_mint_present = SDL_FALSE;
   457 
   458     /* We can't use XBIOS in interrupt with Magic, don't know about thread */
   459     if (Getcookie(C_MagX, &dummy) == C_FOUND) {
   460         return (0);
   461     }
   462 
   463     /* Cookie _SND present ? if not, assume ST machine */
   464     if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
   465         cookie_snd = SND_PSG;
   466     }
   467 
   468     /* Check if we have 16 bits audio */
   469     if ((cookie_snd & SND_16BIT) == 0) {
   470         SDL_SetError(DEBUG_NAME "no 16-bit sound");
   471         return (0);
   472     }
   473 
   474     /* Check if audio is lockable */
   475     if (Locksnd() != 1) {
   476         SDL_SetError(DEBUG_NAME "audio locked by other application");
   477         return (0);
   478     }
   479 
   480     Unlocksnd();
   481 
   482     DEBUG_PRINT((DEBUG_NAME "XBIOS audio available!\n"));
   483 
   484     /* Set the function pointers */
   485     impl->OpenDevice = MINTXBIOS_OpenDevice;
   486     impl->CloseDevice = MINTXBIOS_CloseDevice;
   487     impl->LockDevice = MINTXBIOS_LockDevice;
   488     impl->UnlockDevice = MINTXBIOS_UnlockDevice;
   489     impl->OnlyHasDefaultOutputDevice = 1;
   490     impl->ProvidesOwnCallbackThread = 1;
   491     impl->SkipMixerLock = 1;
   492 
   493     return 2;                   /* 2 == definitely has an audio device. */
   494 }
   495 
   496 AudioBootStrap MINTAUDIO_XBIOS_bootstrap = {
   497     MINT_AUDIO_DRIVER_NAME, "MiNT XBIOS audio driver", MINTXBIOS_Init, 0
   498 };
   499 
   500 /* vi: set ts=4 sw=4 expandtab: */