diff options
-rw-r--r-- | AC97.HC | 228 | ||||
-rw-r--r-- | README.md | 7 |
2 files changed, 235 insertions, 0 deletions
@@ -0,0 +1,228 @@ +#define INT_LAST_VALID_ENTRY 1 << 2 +#define INT_IOC 1 << 3 +#define INT_FIFO_ERR 1 << 4 + +#define BDL_BUF_SIZE 2044 +#define PCM_BUF_SIZE 2048 +#define MAX_BDLS 32 + +#define PCM_IN 0 +#define PCM_OUT 1 +#define MIC_IN 2 + +// Native Audio Mixer registers (all U16) + +#define RESET 0x00 // Reset Register +#define MASTER_VOL 0x02 // Set Master Output Volume +#define MIC_VOL 0x0E // Set Microphone Volume +#define PCM_VOL 0x18 // Set Output Volume of PCM patterns +#define REC_SLC 0x1A // Select Input Device +#define REC_GAIN 0x1C // Set Input Gain +#define MIC_GAIN 0x1E // Set Gain of Microphone +#define EXT_ID 0x28 // Supported extended functions +#define EXT_CTRL 0x2A // Enabling extended functions +#define EXT_FRONT_RATE 0x2C // Sample rate of front speaker + +// Native Audio Bus Master registers + +#define PCM_INPUT_REG_BOX \ + 0x00 // NABM register box for PCM IN (sizeof NABM register box) +#define PCM_OUTPUT_REG_BOX \ + 0x10 // NABM register box for PCM OUT (sizeof NABM register box) +#define MIC_INPUT_REG_BOX \ + 0x20 // NABM register box for Microphone (sizeof NABM register box) +#define GLOBAL_CTL 0x2C // Global Control Register (U32) +#define GLOBAL_STS 0x30 // Global Status Register (U32) + +// NABM register box registers + +#define BUFFER_DSC_ADDR 0x00 // Physical Address of Buffer Descriptor List (U32) +#define CUR_ENTRY_VAL \ + 0x04 // Number of Actual Processed Buffer Descriptor Entry (U8) +#define LAST_VALID_ENTRY 0x05 // Number of all Descriptor Entries (U8) +#define TRANSFER_STS 0x06 // Status of Transferring Data (U16) +#define CUR_IDX_PROC_SAMPLES \ + 0x08 // Number of Transferred Samples in Actual Processed Entry (U16) +#define PRCSD_ENTRY 0x0A // Number of Actual Processed Buffer Entry (U8) +#define BUFFER_CNT \ + 0x0B // Most Important Register for controlling Transfers (U8) + +class @ac97_bdl_entry { + U32 addr; + U16 length; // length - 1 + U16 flags; +}; + +class @ac97_bdl { + @ac97_bdl_entry entries[32]; +}; + +class @ac97 { + @pci_info pci; + @ac97_bdl *bdl[3]; + U16 nam; + U16 nabm; +}; + +@ac97 AC97; + +#define AUDIO_MAX_STREAMS 16 +#define AUDIO_OUTPUT_BUFFER_SIZE 1024 +#define AUDIO_STREAM_FIFO_SIZE 1048576 * 16 +#define AUDIO_STREAM_TYPE_INPUT 0 +#define AUDIO_STREAM_TYPE_OUTPUT 1 + +class @audio_stream { + CFifoI64 *data; +}; + +class @audio { + @audio_stream output[AUDIO_MAX_STREAMS]; + I64 output_frames[AUDIO_MAX_STREAMS]; +}; + +@audio Audio; + +U0 @audio_init() { + I64 i = 0; + for (i = 0; i < AUDIO_MAX_STREAMS; i++) + Audio.output[i].data = FifoI64New(AUDIO_STREAM_FIFO_SIZE); +} + +@audio_init; + +I64 @audio_get_available_output_stream() { + I64 stream = 0; + while (FifoI64Cnt(Audio.output[stream].data)) + stream++; + if (stream > AUDIO_MAX_STREAMS - 1) + return -1; + return stream; +} + +I64 @audio_play_sfx(U32 *data, I64 length) { + I64 i; + I64 stream = @audio_get_available_output_stream; + if (stream < 0) + return stream; + for (i = 0; i < length; i++) + FifoI64Ins(Audio.output[stream].data, data[i]); + return stream; +} + +U0 @ac97_mix_output(U32 *buf, I64 length = NULL) { + I64 i; + I64 j; + I64 acc_sample_L = 0; + I64 acc_sample_R = 0; + I64 acc_streams = 0; + U32 sample; + + if (!length) + length = AUDIO_OUTPUT_BUFFER_SIZE; + for (i = 0; i < length / 4; i++) { + acc_sample_L = 0; + acc_sample_R = 0; + acc_streams = 0; + + for (j = 0; j < AUDIO_MAX_STREAMS; j++) { + if (FifoI64Cnt(Audio.output[j].data)) { + FifoI64Rem(Audio.output[j].data, &sample); + Audio.output_frames[j]++; + acc_streams++; + acc_sample_L += sample.u16[0]; + acc_sample_R += sample.u16[1]; + } + } + + buf[i].i16[0] = ToI64(acc_sample_L / Sqrt(acc_streams)); + buf[i].i16[1] = ToI64(acc_sample_R / Sqrt(acc_streams)); + } +} + +U0 @ac97_fill_buffer() { + I64 idx = InU8(AC97.nabm + PCM_OUTPUT_REG_BOX + LAST_VALID_ENTRY); + U32 *buf = AC97.bdl[PCM_OUT]->entries[idx].addr; + @ac97_mix_output(buf, BDL_BUF_SIZE); + OutU8(AC97.nabm + PCM_OUTPUT_REG_BOX + LAST_VALID_ENTRY, ++idx); +} + +U0 @ac97_process_audio() { + U16 status = InU16(AC97.nabm + PCM_OUTPUT_REG_BOX + TRANSFER_STS); + if (status & INT_IOC) { + @ac97_fill_buffer; + OutU16(AC97.nabm + PCM_OUTPUT_REG_BOX + TRANSFER_STS, 0x1C); + } +} + +I64 @ac97_init() { + I64 i; + I64 j; + // Scan for device + j = PCIClassFind(0x040100, 0); + if (j < 0) { + device_not_found: + AdamLog("\n[AC'97] Device not found\n"); + return -1; + } + @get_pci_info(j, &AC97.pci); + + if (AC97.pci.vendor_id != 0x8086 || AC97.pci.device_id != 0x2415) + goto device_not_found; + + AC97.nam = AC97.pci.bar[0] & 0xFFFFFF00; + AC97.nabm = AC97.pci.bar[1] & 0xFFFFFF00; + + // Enable port IO, disable MMIO + PCIWriteU8(j.u8[2], j.u8[1], j.u8[0], 0x4, 5); + + OutU32(AC97.nabm + GLOBAL_CTL, 0x03); + OutU16(AC97.nam + RESET, 0xFFFF); + + // Set PCM Output to Max volume + OutU16(AC97.nam + PCM_VOL, 0x0000); + + // Allocate Buffer Descriptor Lists + AC97.bdl[PCM_IN] = CAllocAligned(sizeof(@ac97_bdl), 4096, Fs->code_heap); + AC97.bdl[PCM_OUT] = CAllocAligned(sizeof(@ac97_bdl), 4096, Fs->code_heap); + AC97.bdl[MIC_IN] = CAllocAligned(sizeof(@ac97_bdl), 4096, Fs->code_heap); + + for (i = 0; i < MAX_BDLS; i++) { + AC97.bdl[PCM_OUT]->entries[i].addr = + CAllocAligned(PCM_BUF_SIZE, 4096, Fs->code_heap); + AC97.bdl[PCM_OUT]->entries[i].length = BDL_BUF_SIZE / 2; + AC97.bdl[PCM_OUT]->entries[i].flags = 1 << 15; + } + + // Set addresses of Buffer Descriptor Lists + // OutU32(AC97.nabm + PCM_INPUT_REG_BOX + BUFFER_DSC_ADDR, AC97.bdl[PCM_IN]); + OutU32(AC97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_DSC_ADDR, AC97.bdl[PCM_OUT]); + // OutU32(AC97.nabm + MIC_INPUT_REG_BOX + BUFFER_DSC_ADDR, AC97.bdl[MIC_IN]); + + // Set Master Volume + OutU16(AC97.nam + MASTER_VOL, 0x0F0F); + + // Stop playing sound + OutU8(AC97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_CNT, 0); + + // Fill one buffers + @ac97_fill_buffer; + + // Enable interrupt handler + //@pci_register_int_handler(&@ac97_int_handler); + + // Start playing sound + OutU8(AC97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_CNT, 1); + + return 0; +} + +U0 @ac97_task() { + while (1) { + @ac97_process_audio; + Sleep(1); + } +} + +@ac97_init; +Spawn(&@ac97_task, , "AC97 Task", 1);
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..84e1777 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +To include: + +```toml +[Dependencies] +ac97 = "https://git.xslendi.xyz/slendi/ac97.git" +``` + |