es1371: Audio driver for vmware

This commit is contained in:
K. Lange 2021-10-10 15:13:29 +09:00
parent 72825a7d46
commit 538cc9319e
2 changed files with 337 additions and 0 deletions

View File

@ -9,6 +9,7 @@ if lspci -q 8086:2415 then insmod /mod/ac97.ko
if lspci -q 1234:1111,15ad:07a0 then insmod /mod/vmware.ko
if lspci -q 80EE:CAFE then insmod /mod/vbox.ko
if lspci -q 8086:0046 then insmod /mod/i965.ko
if lspci -q 1274:1371 then insmod /mod/es1371.ko
if lspci -q 8086:100e,8086:1004,8086:100f,8086:10ea,8086:10d3 then insmod /mod/e1000.ko

336
modules/es1371.c Normal file
View File

@ -0,0 +1,336 @@
/**
* @file kernel/audio/es1371.c
* @brief Driver for the Ensoniq ES1371.
*
* @ref http://www.vogons.org/download/file.php?id=13036&sid=30df81e15e2521deb842a79f451b1161
*
* @copyright
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2021 K. Lange
*/
#include <errno.h>
#include <kernel/types.h>
#include <kernel/string.h>
#include <kernel/printf.h>
#include <kernel/pci.h>
#include <kernel/process.h>
#include <kernel/mmu.h>
#include <kernel/list.h>
#include <kernel/module.h>
#include <kernel/time.h>
#include <kernel/mod/snd.h>
#include <kernel/arch/x86_64/ports.h>
#include <kernel/arch/x86_64/regs.h>
#include <kernel/arch/x86_64/irq.h>
#define ES_PORT_CONTROL 0x00
#define ES_PORT_STATUS 0x04
#define ES_PORT_UART_DATA 0x08
#define ES_PORT_UART_STS 0x09
#define ES_PORT_UART_TEST 0x0a
#define ES_PORT_MEMORY_PAGE 0x0c
#define ES_PORT_SRC_RW 0x10
#define ES_PORT_CODEC_RW 0x14
#define ES_PORT_LEGACY 0x18
#define ES_PORT_SERIAL 0x20
#define ES_PORT_P1_FRAME_CNT 0x24
#define ES_PORT_P2_FRAME_CNT 0x28
#define ES_PORT_R_FRAME_CNT 0x2c
#define ES_PORT_P1_BUF_ADDR 0x30
#define ES_PORT_P1_BUF_DEF 0x34
#define ES_PORT_P2_BUF_ADDR 0x38
#define ES_PORT_P2_BUF_DEF 0x3c
/**
* Control bits
*/
#define ES_CTRL_SYNC_RES (1 << 14)
#define ES_CTRL_DAC2_EN (1 << 5)
/**
* Status bits
*/
#define ES_STATUS_INTR (1 << 31)
#define ES_STATUS_DAC2 (1 << 1)
/**
* Serial control bits
*/
#define ES_SERIAL_P2_END_INC_MASK (0x7 << 19)
#define ES_SERIAL_P2_END_INC_TWO (2 << 19)
#define ES_SERIAL_P2_ST_INC_MASK (0x7 << 16)
#define ES_SERIAL_P2_LOOP_MASK (1 << 14)
#define ES_SERIAL_P2_PAUSE (1 << 12)
#define ES_SERIAL_P2_INTR_EN (1 << 9)
#define ES_SERIAL_P2_DAC_SEN (1 << 6)
#define ES_SERIAL_P2_MODE_MASK (0x3 << 2)
#define ES_SERIAL_P2_MODE_16BIT (1 << 3)
#define ES_SERIAL_P2_MODE_STEREO (1 << 2)
/**
* SRC RW register bits
*/
#define ES_SRC_REG_MASK (0xF << 19) /* SRC, DAC1, DAC2, ADC disable... */
#define ES_SRC_REG_WE (1 << 24)
#define ES_SRC_REG_BUSY (1 << 23)
#define ES_SRC_REG(x) ((x & 0x7F) << 25)
/**
* 16-bit registers for the samplerate converter
*/
#define ES_SRC_P2_TRUNCN 0x74
#define ES_SRC_P2_INTREGS 0x75
#define ES_SRC_P2_ACCUMFRAC 0x76
#define ES_SRC_P2_VFREQFRAC 0x77
/**
* Volume is specified with a sign bit, 3 integer bits, and the rest as fractional.
* I am not sure what this actually gets used for - a multiplier?
*/
#define ES_SRC_P2_VOL_L 0x7E
#define ES_SRC_P2_VOL_R 0x7F
struct es1371_device {
uint32_t pci_device;
int portbase;
int irq;
int bits;
int mask;
uint32_t serial;
int16_t * buf;
};
static struct es1371_device _device;
static snd_knob_t _knobs[] = {
{
"Master",
SND_KNOB_MASTER
},
};
static int es1371_mixer_read(uint32_t knob_id, uint32_t *val);
static int es1371_mixer_write(uint32_t knob_id, uint32_t val);
static snd_device_t _snd = {
.name = "Ensoniq ES1371",
.device = &_device,
.playback_speed = 48000,
.playback_format = SND_FORMAT_L16SLE,
.knobs = _knobs,
.num_knobs = 1,
.mixer_read = es1371_mixer_read,
.mixer_write = es1371_mixer_write,
};
static void find_es1371(uint32_t device, uint16_t vendorid, uint16_t deviceid, void * extra) {
struct es1371_device * es1371 = extra;
if ((vendorid == 0x1274) && (deviceid == 0x1371)) {
es1371->pci_device = device;
}
}
static int es1371_irq_handler(struct regs * regs) {
uint32_t status = inportl(_device.portbase + ES_PORT_STATUS);
if (!(status & ES_STATUS_INTR)) return 0;
if (status & ES_STATUS_DAC2) {
/* Reset the interrupt-waiting bit by toggling interrupts off and on for this DAC */
outportl(_device.portbase + ES_PORT_SERIAL, _device.serial & ~ES_SERIAL_P2_INTR_EN);
outportl(_device.portbase + ES_PORT_SERIAL, _device.serial);
outportl(_device.portbase + ES_PORT_MEMORY_PAGE, 0x0c);
uint32_t def = inportl(_device.portbase + ES_PORT_P2_BUF_DEF);
snd_request_buf(&_snd, 0x1000, (uint8_t*)_device.buf + ((def & 0xFFFF0000) ? 0 : 0x1000));
}
irq_ack(_device.irq);
return 1;
}
static int es1371_mixer_read(uint32_t knob_id, uint32_t *val) {
switch (knob_id) {
/* This is essentially the same as how we get the volume from the ICH AC'97, but
* we have to get the AC97 codec through this port access thing... */
case SND_KNOB_MASTER: {
outportl(_device.portbase + ES_PORT_CODEC_RW, (0x02 << 16) | (1 << 23));
uint32_t tmp = inportl(_device.portbase + ES_PORT_CODEC_RW) & 0xFFFF;
if (tmp == 0x8000) {
*val = 0;
} else {
/* 6 bit value */
*val = (tmp & _device.mask) << (sizeof(*val) * 8 - _device.bits);
*val = ~*val;
*val &= (uint32_t)_device.mask << (sizeof(*val) * 8 - _device.bits);
}
break;
}
default:
return -1;
}
return 0;
}
static int es1371_mixer_write(uint32_t knob_id, uint32_t val) {
switch (knob_id) {
case SND_KNOB_MASTER: {
uint16_t encoded;
if (val == 0x0) {
encoded = 0x8000;
} else {
val = ~val;
val >>= (sizeof(val) * 8 - _device.bits);
encoded = (val & 0xFF) | (val << 8);
}
outportl(_device.portbase + ES_PORT_CODEC_RW,
(0x02 << 16) | encoded);
break;
}
default:
return -1;
}
return 0;
}
static void delay_yield(size_t subticks) {
unsigned long s, ss;
relative_time(0, subticks, &s, &ss);
sleep_until((process_t *)this_core->current_process, s, ss);
switch_task(0);
}
/**
* @brief Write to the samplerate converter RAM
*/
static void src_write(int port, uint16_t value) {
uint32_t x;
while ((x = inportl(_device.portbase + ES_PORT_SRC_RW)) & ES_SRC_REG_BUSY);
x &= ES_SRC_REG_MASK;
x |= ES_SRC_REG_WE | ES_SRC_REG(port) | (value & 0xFFFF);
outportl(_device.portbase + ES_PORT_SRC_RW, x);
}
static int es1371_install(int argc, char * argv[]) {
pci_scan(&find_es1371, -1, &_device);
if (!_device.pci_device) {
return -ENODEV;
}
/* Get I/O port from PCI */
_device.portbase = pci_read_field(_device.pci_device, PCI_BAR0, 4) & ((uint32_t)-1) << 1;
/* Enable port-IO, bus mastering, interrupts */
uint16_t command_reg = pci_read_field(_device.pci_device, PCI_COMMAND, 2);
command_reg |= (1 << 2) | (1 << 0);
command_reg &= ~(1 << 10);
pci_write_field(_device.pci_device,
PCI_COMMAND, 2, command_reg);
/* Reset the controller */
uint32_t ctrl = 0;
uint32_t serial = 0;
outportl(_device.portbase + ES_PORT_CONTROL, ctrl);
outportl(_device.portbase + ES_PORT_SERIAL, serial);
outportl(_device.portbase + ES_PORT_LEGACY, 0);
outportl(_device.portbase + ES_PORT_CONTROL, ctrl | ES_CTRL_SYNC_RES);
inportl(_device.portbase + ES_PORT_CONTROL);
delay_yield(2000);
outportl(_device.portbase + ES_PORT_CONTROL, ctrl);
/* Get 8192 of audio buffer space */
uintptr_t addr = mmu_allocate_n_frames(2) << 12;
if (!addr) {
return -ENODEV;
}
if (addr > 0xFFFFffff) {
/* This thing only supports 32-bit physical addresses, so if we got something
* too high (unlikely at early boot) we need to bail. */
printf("es1371: Allocated buffer is beyond the reach of 32-bit DMA engine.\n");
return -ENODEV;
}
/* Map interrupt handler */
_device.irq = pci_get_interrupt(_device.pci_device);
irq_install_handler(_device.irq, es1371_irq_handler, "es1371");
/* Zero out the audio buffer */
_device.buf = mmu_map_from_physical(addr);
memset(_device.buf, 0, 0x2000);
/* Disable sound rate converter while we program it */
outportl(_device.portbase + ES_PORT_SRC_RW,
(1 << 22));
/* Turn everything off? */
for (int i = 0; i < 0x80; ++i) {
src_write(i, 0);
}
/* Set for 48KHz */
src_write(ES_SRC_P2_TRUNCN, 16 << 4);
src_write(ES_SRC_P2_INTREGS, 16 << 10);
/* This seems to be the sample rate converter's left/right audio volume? */
src_write(ES_SRC_P2_VOL_L, 0x1 << 12);
src_write(ES_SRC_P2_VOL_R, 0x1 << 12);
outportl(_device.portbase + ES_PORT_SRC_RW, 0);
outportl(_device.portbase + ES_PORT_CODEC_RW, 0);
delay_yield(2000);
/* Set AC'97 codec volumes, which are inverted (-dB) */
_device.bits = 5;
_device.mask = 0x1f;
outportl(_device.portbase + ES_PORT_CODEC_RW,
(0x02 << 16)); /* Master */
outportl(_device.portbase + ES_PORT_CODEC_RW,
(0x18 << 16)); /* PCM OUT */
outportb(ES_PORT_UART_STS, 0);
outportb(ES_PORT_UART_TEST, 0);
outportb(ES_PORT_STATUS, 0);
/* Set up buffer for two pages we can swap between,
* which gives us good latency for immediate playback */
outportl(_device.portbase + ES_PORT_MEMORY_PAGE, 0x0c);
outportl(_device.portbase + ES_PORT_P2_BUF_ADDR, addr);
outportl(_device.portbase + ES_PORT_P2_BUF_DEF, 0x7FF);
outportl(_device.portbase + ES_PORT_P2_FRAME_CNT, 0x400);
/* Configure playback mode */
serial = inportl(_device.portbase + ES_PORT_SERIAL);
serial &=
~(ES_SERIAL_P2_LOOP_MASK | ES_SERIAL_P2_END_INC_MASK | ES_SERIAL_P2_DAC_SEN |
ES_SERIAL_P2_PAUSE | ES_SERIAL_P2_ST_INC_MASK | ES_SERIAL_P2_MODE_MASK);
serial |=
ES_SERIAL_P2_INTR_EN |
ES_SERIAL_P2_MODE_STEREO | ES_SERIAL_P2_MODE_16BIT | /* 16-bit sterio */
ES_SERIAL_P2_END_INC_TWO;
outportl(_device.portbase + ES_PORT_SERIAL, serial);
_device.serial = serial;
/* Turn it on! */
ctrl = inportl(_device.portbase + ES_PORT_CONTROL);
outportl(_device.portbase + ES_PORT_CONTROL, ES_CTRL_DAC2_EN | ctrl);
snd_register(&_snd);
return 0;
}
static int fini(void) {
snd_unregister(&_snd);
return 0;
}
struct Module metadata = {
.name = "es1371",
.init = es1371_install,
.fini = fini,
};