X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=bootcensus;a=blobdiff_plain;f=src%2Fau_sb.c;h=b42a603ad6b471072f312ec08127262608d91c1c;hp=f3184aeda46c1d0460006b9dfdd18febc5c67598;hb=1e2ddab65ead32350d8ff87a6de74168e429666b;hpb=bd35c984636a2d5dfdff7a80f8eaa4e388c6899c diff --git a/src/au_sb.c b/src/au_sb.c index f3184ae..b42a603 100644 --- a/src/au_sb.c +++ b/src/au_sb.c @@ -19,6 +19,8 @@ along with this program. If not, see . #include "audio.h" #include "au_sb.h" #include "asmops.h" +#include "intr.h" +#include "dma.h" #define REG_MIXPORT (base_port + 0x4) #define REG_MIXDATA (base_port + 0x5) @@ -33,20 +35,49 @@ along with this program. If not, see . #define WSTAT_BUSY 0x80 #define RSTAT_RDY 0x80 -#define CMD_SET_RATE 0x41 -#define CMD_PLAY_PCM 0xa6 -#define CMD_STOP_PCM 0xd9 -#define CMD_GET_VER 0xe1 +#define CMD_RATE 0x40 +#define CMD_SB16_OUT_RATE 0x41 +#define CMD_SB16_IN_RATE 0x42 +#define CMD_GET_VER 0xe1 + +/* start DMA playback/recording. combine with fifo/auto/input flags */ +#define CMD_START_DMA8 0xc0 +#define CMD_START_DMA16 0xb0 +#define CMD_FIFO 0x02 +#define CMD_AUTO 0x04 +#define CMD_INPUT 0x08 + +/* immediately pause/continue */ +#define CMD_PAUSE_DMA8 0xd0 +#define CMD_CONT_DMA8 0xd4 +#define CMD_PAUSE_DMA16 0xd5 +#define CMD_CONT_DMA16 0xd6 + +/* end the playback at the end of the current buffer */ +#define CMD_END_DMA16 0xd9 +#define CMD_END_DMA8 0xda + +/* transfer mode commands */ +#define CMD_MODE_SIGNED 0x10 +#define CMD_MODE_STEREO 0x20 #define VER_MAJOR(x) ((x) >> 8) #define VER_MINOR(x) ((x) & 0xff) +static void intr_handler(); +static void start_dma_transfer(uint32_t addr, int size); static void write_dsp(unsigned char val); static unsigned char read_dsp(void); static int get_dsp_version(void); static const char *sbname(int ver); +extern unsigned char low_mem_buffer[]; + static int base_port; +static int irq, dma_chan; +static int sb16; +static void *buffer; +static int xfer_mode; int sb_detect(void) { @@ -56,12 +87,17 @@ int sb_detect(void) base_port = 0x200 + ((i + 1) << 4); if(sb_reset_dsp() == 0) { ver = get_dsp_version(); + sb16 = VER_MAJOR(ver) >= 4; printf("sb_detect: found %s (DSP v%d.%02d) at port %xh\n", sbname(ver), VER_MAJOR(ver), VER_MINOR(ver), base_port); return 1; } } + /* TODO: detect these somehow? */ + irq = 5; + dma_chan = 1; + return 0; } @@ -84,6 +120,101 @@ int sb_reset_dsp(void) return -1; } +void sb_set_output_rate(int rate) +{ + if(sb16) { + write_dsp(CMD_SB16_OUT_RATE); + write_dsp(rate >> 8); + write_dsp(rate & 0xff); + } else { + int tcon = 256 - 1000000 / rate; + write_dsp(CMD_RATE); + write_dsp(tcon); + } +} + +void *sb_buffer(int *size) +{ + *size = 65536; + return buffer; +} + +void sb_start(int rate, int nchan) +{ + uint32_t addr; + int size; + + /* for now just use the are after boot2. it's only used by the second stage + * loader and the VBE init code, none of which should overlap with audio playback. + * It's not necessary to use low memory. We can use up to the first 16mb for this, + * so if this becomes an issue, I'll make a DMA buffer allocator over 1mb. + * start the buffer from the next 64k boundary. + */ + addr = ((uint32_t)low_mem_buffer + 0xffff) & 0xffff0000; + buffer = (void*)addr; + + xfer_mode = CMD_MODE_SIGNED; + if(nchan > 1) { + xfer_mode |= CMD_MODE_STEREO; + } + + if(!(size = audio_callback(buffer, 65536))) { + return; + } + + interrupt(IRQ_TO_INTR(irq), intr_handler); + + start_dma_transfer(addr, size); +} + +void sb_pause(void) +{ + write_dsp(CMD_PAUSE_DMA8); +} + +void sb_continue(void) +{ + write_dsp(CMD_CONT_DMA8); +} + +void sb_stop(void) +{ + write_dsp(CMD_END_DMA8); +} + +void sb_volume(int vol) +{ + /* TODO */ +} + +static void intr_handler() +{ + int size; + + /* ask for more data */ + if(!(size = audio_callback(buffer, 65536))) { + sb_stop(); + return; + } + start_dma_transfer((uint32_t)buffer, size); + + /* acknowledge the interrupt */ + inb(REG_INTACK); +} + +static void start_dma_transfer(uint32_t addr, int size) +{ + /* set up the next DMA transfer */ + dma_out(dma_chan, addr, size, DMA_SINGLE); + + /* program the DSP to accept the DMA transfer */ + write_dsp(CMD_START_DMA8 | CMD_FIFO); + write_dsp(xfer_mode); + size--; + write_dsp(size & 0xff); + write_dsp((size >> 8) & 0xff); +} + static void write_dsp(unsigned char val) { while(inb(REG_WSTAT) & WSTAT_BUSY);