diff --git a/src/add-ons/kernel/drivers/audio/Jamfile b/src/add-ons/kernel/drivers/audio/Jamfile index ace84cfcd2..7498609d1e 100644 --- a/src/add-ons/kernel/drivers/audio/Jamfile +++ b/src/add-ons/kernel/drivers/audio/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers audio ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ; +SubInclude HAIKU_TOP src add-ons kernel drivers audio cmedia ; SubInclude HAIKU_TOP src add-ons kernel drivers audio echo ; SubInclude HAIKU_TOP src add-ons kernel drivers audio emuxki ; SubInclude HAIKU_TOP src add-ons kernel drivers audio hda ; diff --git a/src/add-ons/kernel/drivers/audio/cmedia/Jamfile b/src/add-ons/kernel/drivers/audio/cmedia/Jamfile new file mode 100644 index 0000000000..51e173ede9 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src add-ons kernel drivers audio cmedia ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders audio ; + +KernelAddon cmedia : + cm.c + joy.c + midi.c + mixer.c + mux.c + pcm.c +; + +Package haiku-cmedia-cvs + : + cmedia + : + boot home config add-ons kernel drivers bin ; + +PackageDriverSymLink haiku-cmedia-cvs : audio cmedia ; diff --git a/src/add-ons/kernel/drivers/audio/cmedia/cm.c b/src/add-ons/kernel/drivers/audio/cmedia/cm.c new file mode 100644 index 0000000000..6d26beba58 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/cm.c @@ -0,0 +1,784 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include "cmedia_pci.h" +#include "cm_private.h" + +#include +#include + +#if !defined(_KERNEL_EXPORT_H) +#include +#endif + + +#if DEBUG +#define KPRINTF(x) kprintf x +#else +#define KPRINTF(x) +#endif + +EXPORT status_t init_hardware(void); +EXPORT status_t init_driver(void); +EXPORT void uninit_driver(void); +EXPORT const char ** publish_devices(void); +EXPORT device_hooks * find_device(const char *); + + +static char pci_name[] = B_PCI_MODULE_NAME; +static pci_module_info *pci; +static char gameport_name[] = "generic/gameport/v1"; +generic_gameport_module * gameport; +static char mpu401_name[] = B_MPU_401_MODULE_NAME; +generic_mpu401_module * mpu401; + +#define DO_JOY 1 +#define DO_MIDI 1 +#define DO_PCM 1 +#define DO_MUX 0 +#define DO_MIXER 0 + +#if DO_MIDI +extern device_hooks midi_hooks; +#endif /* DO_MIDI */ +#if DO_JOY +extern device_hooks joy_hooks; +#endif /* DO_JOY */ +#if DO_PCM +extern device_hooks pcm_hooks; +#endif /* DO_PCM */ +#if DO_MUX +extern device_hooks mux_hooks; +#endif /* DO_MUX */ +#if DO_MIXER +extern device_hooks mixer_hooks; +#endif /* DO_MIXER */ + + +int32 num_cards; +cmedia_pci_dev cards[NUM_CARDS]; +int num_names; +char * names[NUM_CARDS*7+1]; +/* vuchar *io_base; */ + + +/* ---------- + PCI_IO_RD - read a byte from pci i/o space +----- */ + +uint8 +PCI_IO_RD (int offset) +{ + return (*pci->read_io_8) (offset); +} + + +/* ---------- + PCI_IO_RD_32 - read a 32 bit value from pci i/o space +----- */ + +uint32 +PCI_IO_RD_32 (int offset) +{ + return (*pci->read_io_32) (offset); +} +/* ---------- + PCI_IO_WR - write a byte to pci i/o space +----- */ + +void +PCI_IO_WR (int offset, uint8 val) +{ + (*pci->write_io_8) (offset, val); +} + + +/* detect presence of our hardware */ +status_t +init_hardware(void) +{ + int ix=0; + pci_info info; + status_t err = ENODEV; + + ddprintf(("cmedia_pci: init_hardware()\n")); + + if (get_module(pci_name, (module_info **)&pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == CMEDIA_PCI_VENDOR_ID && + (info.device_id == CMEDIA_8338A_DEVICE_ID || + info.device_id == CMEDIA_8338B_DEVICE_ID || + info.device_id == CMEDIA_8738A_DEVICE_ID || + info.device_id == CMEDIA_8738B_DEVICE_ID )) { + err = B_OK; + } + ix++; + } +#if defined(__POWERPC__) && 0 + { + char area_name [32]; + area_info area; + area_id id; + + sprintf (area_name, "pci_bus%d_isa_io", info.bus); + id = find_area (area_name); + if (id < 0) + err = id; + else if ((err = get_area_info (id, &area)) == B_OK) + io_base = area.address; + } +#endif + + put_module(pci_name); + + return err; +} + + +void set_direct( cmedia_pci_dev * card, int regno, uchar value, uchar mask) +{ + if (mask == 0) + { + return; + } + if (mask != 0xff) + { + uchar old = PCI_IO_RD(card->enhanced+regno); + value = (value&mask)|(old&~mask); + } + PCI_IO_WR(card->enhanced+regno, value); + ddprintf(("cmedia_pci: CM%02x = %02x\n", regno, value)); +} + + +uchar get_direct(cmedia_pci_dev * card, int regno) +{ + uchar ret = PCI_IO_RD(card->enhanced+regno); + return ret; +} + + + +void set_indirect(cmedia_pci_dev * card, int regno, uchar value, uchar mask) +{ + PCI_IO_WR(card->enhanced+0x23, regno); + EIEIO(); + if (mask == 0) + { + return; + } + if (mask != 0xff) + { + uchar old = PCI_IO_RD(card->enhanced+0x22); + value = (value&mask)|(old&~mask); + } + PCI_IO_WR(card->enhanced+0x22, value); + EIEIO(); + ddprintf(("cmedia_pci: CMX%02x = %02x\n", regno, value)); +} + + + +uchar get_indirect(cmedia_pci_dev * card,int regno) +{ + uchar ret; + PCI_IO_WR(card->enhanced+0x23, regno); + EIEIO(); + ret = PCI_IO_RD(card->enhanced+0x22); + return ret; +} + + +#if 0 +void dump_card(cmedia_pci_dev * card) +{ + int ix; + dprintf("\n"); + dprintf("CM: "); + for (ix=0; ix<6; ix++) { + if (ix == 2 || ix == 3) dprintf(" "); + else dprintf(" %02x", get_direct(card, ix)); + } + for (ix=0; ix<0x32; ix++) { + if (!(ix & 7)) { + dprintf("\nCMX%02x:", ix); + } + dprintf(" %02x", get_indirect(card, ix)); + } + dprintf("\n"); + dprintf("\n"); +} +#else +void dump_card(cmedia_pci_dev * card) +{ +} +#endif + + +static void +disable_card_interrupts(cmedia_pci_dev * card) +{ + set_direct(card, 0x0e, 0x00, 0x03); +} + + +static status_t +setup_dma(cmedia_pci_dev * card) +{ + /* we're appropriating some ISA space here... */ + /* need kernel support to do it right */ + const uint16 base = card->enhanced+0x80; + ddprintf(("cmedia_pci: dma base is 0x%04x\n", base)); + if(0 == base) + return B_DEV_RESOURCE_CONFLICT; + card->dma_base = base; + return B_OK; +} + + +static void +set_default_registers( + cmedia_pci_dev * card) +{ + static uchar values[] = { +#ifdef DO_JOY + 0x04, 0x02, 0x02, /* enable joystick */ +#endif + + 0x0a, 0x01, 0x01, /* enable SPDIF inverse before SPDIF_LOOP */ + 0x04, 0x80, 0x80, /* enable SPDIF_LOOP */ + + 0x1a, 0x00, 0x20, /* SPD32SEL disable */ + 0x1a, 0x00, 0x10, /* SPDFLOOPI disable */ + + 0x1b, 0x04, 0x04, /* dual channel mode enable */ + 0x1a, 0x00, 0x80, /* Double DAC structure disable */ + + 0x24, 0x00, 0x02, /* 3D surround disable */ + + 0x24, 0x00, 0x01, /* disable SPDIF_IN PCM to DAC */ +#ifdef DO_MIDI + 0x04, 0x04, 0x04, /* enable MPU-401 */ + 0x17, 0x00, 0x60, /* default at 0x330 */ +#endif + }; + uchar * ptr = values; + + while (ptr < values+sizeof(values)) { + set_direct(card, ptr[0], ptr[1], ptr[2]); + ptr += 3; + } +} + + +static void +make_device_names( + cmedia_pci_dev * card) +{ + char * name = card->name; + sprintf(name, "cmedia_pci/%ld", card-cards+1); + +#if DO_MIDI + sprintf(card->midi.name, "midi/%s", name); + names[num_names++] = card->midi.name; +#endif /* DO_MIDI */ +#if DO_JOY + sprintf(card->joy.name1, "joystick/%s", name); + names[num_names++] = card->joy.name1; +#endif /* DO_JOY */ +#if DO_PCM + /* cmedia_pci DMA doesn't work when physical NULL isn't NULL from PCI */ + /* this is a hack to not export bad devices on BeBox hardware */ + if ((*pci->ram_address)(NULL) == NULL) { + sprintf(card->pcm.name, "audio/raw/%s", name); + names[num_names++] = card->pcm.name; + sprintf(card->pcm.oldname, "audio/old/%s", name); + names[num_names++] = card->pcm.oldname; + } +#endif /* DO_PCM */ +#if DO_MUX + sprintf(card->mux.name, "audio/mux/%s", name); + names[num_names++] = card->mux.name; +#endif /* DO_MUX */ +#if DO_MIXER + sprintf(card->mixer.name, "audio/mix/%s", name); + names[num_names++] = card->mixer.name; +#endif /* DO_MIXER */ + names[num_names] = NULL; +} + + +/* We use the SV chip in ISA DMA addressing mode, which is 24 bits */ +/* so we need to find suitable, locked, contiguous memory in that */ +/* physical address range. */ + +static status_t +find_low_memory( + cmedia_pci_dev * card) +{ + size_t low_size = (MIN_MEMORY_SIZE+(B_PAGE_SIZE-1))&~(B_PAGE_SIZE-1); + physical_entry where; + size_t trysize; + area_id curarea; + void * addr; + char name[DEVNAME]; + + sprintf(name, "%s_low", card->name); + if (low_size < MIN_MEMORY_SIZE) { + low_size = MIN_MEMORY_SIZE; + } + trysize = low_size; + + curarea = find_area(name); + if (curarea >= 0) { /* area there from previous run */ + area_info ainfo; + ddprintf(("cmedia_pci: testing likely candidate...\n")); + if (get_area_info(curarea, &ainfo)) { + ddprintf(("cmedia_pci: no info\n")); + goto allocate; + } + /* test area we found */ + trysize = ainfo.size; + addr = ainfo.address; + if (trysize < low_size) { + ddprintf(("cmedia_pci: too small (%lx)\n", trysize)); + goto allocate; + } + if (get_memory_map(addr, trysize, &where, 1) < B_OK) { + ddprintf(("cmedia_pci: no memory map\n")); + goto allocate; + } + if ((uint32)where.address & 0xff000000) { + ddprintf(("cmedia_pci: bad physical address\n")); + goto allocate; + } + if (ainfo.lock < B_FULL_LOCK || where.size < low_size) { + ddprintf(("cmedia_pci: lock not contiguous\n")); + goto allocate; + } +dprintf("cmedia_pci: physical %p logical %p\n", where.address, ainfo.address); + goto a_o_k; + } + +allocate: + if (curarea >= 0) { + delete_area(curarea); /* area didn't work */ + curarea = -1; + } + ddprintf(("cmedia_pci: allocating new low area\n")); + + curarea = create_area(name, &addr, B_ANY_KERNEL_ADDRESS, + trysize, B_LOMEM, B_READ_AREA | B_WRITE_AREA); + ddprintf(("cmedia_pci: create_area(%lx) returned %lx logical %p\n", + trysize, curarea, addr)); + if (curarea < 0) { + goto oops; + } + if (get_memory_map(addr, low_size, &where, 1) < 0) { + delete_area(curarea); + curarea = B_ERROR; + goto oops; + } + ddprintf(("cmedia_pci: physical %p\n", where.address)); + if ((uint32)where.address & 0xff000000) { + delete_area(curarea); + curarea = B_ERROR; + goto oops; + } + if ((((uint32)where.address)+low_size) & 0xff000000) { + delete_area(curarea); + curarea = B_ERROR; + goto oops; + } + /* hey, it worked! */ + if (trysize > low_size) { /* don't waste */ + resize_area(curarea, low_size); + } + +oops: + if (curarea < 0) { + dprintf("cmedia_pci: failed to create low_mem area\n"); + return curarea; + } +a_o_k: + ddprintf(("cmedia_pci: successfully found or created low area!\n")); + card->low_size = low_size; + card->low_mem = addr; + card->low_phys = (vuchar *)where.address; + card->map_low = curarea; + return B_OK; +} + + +static status_t +setup_cmedia_pci( + cmedia_pci_dev * card) +{ + status_t err = B_OK; +/* cpu_status cp; */ + + ddprintf(("cmedia_pci: setup_cmedia_pci(%p)\n", card)); + + if ((card->pcm.init_sem = create_sem(1, "cm pcm init")) < B_OK) + goto bail; +#if 1 + if ((*mpu401->create_device)(0x330, &card->midi.driver, +#else + if ((*mpu401->create_device)(card->info.u.h0.base_registers[3], &card->midi.driver, +#endif + 0, midi_interrupt_op, &card->midi) < B_OK) + goto bail3; +#if 1 + if ((*gameport->create_device)(0x201, &card->joy.driver) < B_OK) +#else + if ((*gameport->create_device)(card->info.u.h0.base_registers[4], &card->joy.driver) < B_OK) +#endif + goto bail4; + ddprintf(("midi %p gameport %p\n", card->midi.driver, card->joy.driver)); + card->midi.card = card; + + err = find_low_memory(card); + if (err < B_OK) { + goto bail5; + } + + //cp = disable_interrupts(); + //acquire_spinlock(&card->hardware); + + make_device_names(card); + card->enhanced = card->info.u.h0.base_registers[0]; + ddprintf(("cmedia_pci: %s enhanced at %x\n", card->name, card->enhanced)); + + ddprintf(("cmedia_pci: revision %x\n", get_indirect(card, 0x15))); + + disable_card_interrupts(card); + if (setup_dma(card) != B_OK) + { + dprintf("cmedia pci: can't setup DMA\n"); + goto bail6; + } + + set_default_registers(card); + + //release_spinlock(&card->hardware); + //restore_interrupts(cp); + + return B_OK; + +bail6: + // deallocate low memory +bail5: + (*gameport->delete_device)(card->joy.driver); +bail4: + (*mpu401->delete_device)(card->midi.driver); +bail3: + delete_sem(card->pcm.init_sem); +bail: + return err < B_OK ? err : B_ERROR; +} + + +static int +debug_cmedia( + int argc, + char * argv[]) +{ + int ix = 0; + if (argc == 2) { + ix = parse_expression(argv[1])-1; + } + if (argc > 2 || ix < 0 || ix >= num_cards) { + dprintf("cmedia_pci: dude, you gotta watch your syntax!\n"); + return -1; + } + dprintf("%s: enhanced registers at 0x%x\n", cards[ix].name, + cards[ix].enhanced); + dprintf("%s: open %ld dma_a at 0x%x dma_c 0x%x\n", cards[ix].pcm.name, + cards[ix].pcm.open_count, cards[ix].pcm.dma_a, cards[ix].pcm.dma_c); + if (cards[ix].pcm.open_count) { + dprintf(" dma_a: 0x%lx+0x%lx dma_c: 0x%lx+0x%lx\n", + PCI_IO_RD_32((int)cards[ix].pcm.dma_a), PCI_IO_RD_32((int)cards[ix].pcm.dma_a+4), + PCI_IO_RD_32((int)cards[ix].pcm.dma_c), PCI_IO_RD_32((int)cards[ix].pcm.dma_c+4)); + } + return 0; +} + + +status_t +init_driver(void) +{ + int ix=0; + pci_info info; + num_cards = 0; + + ddprintf(("cmedia_pci: init_driver()\n")); + load_driver_symbols("cmedia_pci"); + + if (get_module(pci_name, (module_info **) &pci)) + return ENOSYS; + + if (get_module(gameport_name, (module_info **) &gameport)) { + put_module(pci_name); + return ENOSYS; + } + ddprintf(("MPU\n")); + if (get_module(mpu401_name, (module_info **) &mpu401)) { + put_module(gameport_name); + put_module(pci_name); + return ENOSYS; + } + + ddprintf(("MPU: %p\n", mpu401)); + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == CMEDIA_PCI_VENDOR_ID && + (info.device_id == CMEDIA_8338A_DEVICE_ID || + info.device_id == CMEDIA_8338B_DEVICE_ID || + info.device_id == CMEDIA_8738A_DEVICE_ID || + info.device_id == CMEDIA_8738B_DEVICE_ID )) { + if (num_cards == NUM_CARDS) { + dprintf("Too many C-Media cards installed!\n"); + break; + } + memset(&cards[num_cards], 0, sizeof(cmedia_pci_dev)); + cards[num_cards].info = info; + if (setup_cmedia_pci(&cards[num_cards])) { + dprintf("Setup of C-Media %ld failed\n", num_cards+1); + } + else { + num_cards++; + } + } + ix++; + } + if (!num_cards) { + KPRINTF(("no cards\n")); + put_module(mpu401_name); + put_module(gameport_name); + put_module(pci_name); + ddprintf(("cmedia_pci: no suitable cards found\n")); + return ENODEV; + } + +#if DEBUG + add_debugger_command("cmedia", debug_cmedia, "cmedia [card# (1-n)]"); +#endif + return B_OK; +} + + +static void +teardown_cmedia_pci( + cmedia_pci_dev * card) +{ + static uchar regs[] = { +#ifdef DO_JOY + 0x04, 0x00, 0x02, /* enable joystick */ +#endif +#ifdef DO_MIDI + 0x04, 0x00, 0x04, /* enable MPU-401 */ +#endif + }; + uchar * ptr = regs; + cpu_status cp; + + /* remove created devices */ + (*gameport->delete_device)(card->joy.driver); + (*mpu401->delete_device)(card->midi.driver); + + cp = disable_interrupts(); + acquire_spinlock(&card->hardware); + + while (ptr < regs+sizeof(regs)) { + set_direct(card, ptr[0], ptr[1], ptr[2]); + ptr += 3; + } + disable_card_interrupts(card); + + release_spinlock(&card->hardware); + restore_interrupts(cp); + + delete_sem(card->pcm.init_sem); +} + + +void +uninit_driver(void) +{ + int ix, cnt = num_cards; + num_cards = 0; + + ddprintf(("cmedia_pci: uninit_driver()\n")); + remove_debugger_command("cmedia", debug_cmedia); + + for (ix=0; ixhardware); + + status = get_direct(card, 0x10); + +#if DEBUG +/* kprintf("%x\n", status); / * */ +#endif +#if DO_PCM + if (status & 0x02) { + if (dma_c_interrupt(card)) { + handled = B_INVOKE_SCHEDULER; + } + else { + handled = B_HANDLED_INTERRUPT; + } + /* acknowledge interrupt */ + set_direct(card, 0x0e, 0x00, 0x02); + set_direct(card, 0x0e, 0x02, 0x02); + } + if (status & 0x01) { + if (dma_a_interrupt(card)) { + handled = B_INVOKE_SCHEDULER; + } + else { + handled = B_HANDLED_INTERRUPT; + } + /* acknowledge interrupt */ + set_direct(card, 0x0e, 0x00, 0x01); + set_direct(card, 0x0e, 0x01, 0x01); + } +#endif +#if DO_MIDI + status = get_direct(card, 0x12); + if (status & 0x01) { + if (midi_interrupt(card)) { + handled = B_INVOKE_SCHEDULER; + } else { + handled = B_HANDLED_INTERRUPT; + } + } +#endif + + /* Sometimes, the Sonic Vibes will receive a byte of Midi data... + ** And duly note it in the MPU401 status register... + ** And generate an interrupt... + ** But not bother setting the midi interrupt bit in the ISR. + ** Thanks a lot, S3. + */ + if(handled == B_UNHANDLED_INTERRUPT){ + if (midi_interrupt(card)) { + handled = B_INVOKE_SCHEDULER; + } + } + +/* KTRACE(); / * */ + release_spinlock(&card->hardware); + restore_interrupts(cp); + + return handled; +// return (handled == B_INVOKE_SCHEDULER) ? B_HANDLED_INTERRUPT : handled; +} + + +void +increment_interrupt_handler( + cmedia_pci_dev * card) +{ + KPRINTF(("cmedia_pci: increment_interrupt_handler()\n")); + if (atomic_add(&card->inth_count, 1) == 0) { + // !!! + KPRINTF(("cmedia_pci: intline %d int %p\n", card->info.u.h0.interrupt_line, cmedia_pci_interrupt)); + install_io_interrupt_handler(card->info.u.h0.interrupt_line, + cmedia_pci_interrupt, card, 0); + } +} + + +void +decrement_interrupt_handler( + cmedia_pci_dev * card) +{ + KPRINTF(("cmedia_pci: decrement_interrupt_handler()\n")); + if (atomic_add(&card->inth_count, -1) == 1) { + KPRINTF(("cmedia_pci: remove_io_interrupt_handler()\n")); + remove_io_interrupt_handler(card->info.u.h0.interrupt_line, cmedia_pci_interrupt, card); + } +} + + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/cm_private.h b/src/add-ons/kernel/drivers/audio/cmedia/cm_private.h new file mode 100644 index 0000000000..6d4c37ded4 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/cm_private.h @@ -0,0 +1,194 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(_CM_PRIVATE_H) +#define _CM_PRIVATE_H + +#if !defined(_CMEDIA_PCI_H) +#include "cmedia_pci.h" +#endif + +#if !defined(_PCI_H) +#include +#endif + +#if !defined(DEBUG) +#define DEBUG 0 +#endif + + +#define NUM_CARDS 3 +#define DEVNAME 32 + + +#define EXPORT __declspec(dllexport) + +#if defined(__INTEL__) +#define EIEIO() +#elif defined(__POWERPC__) +#define EIEIO() __eieio() +#endif + +#if DEBUG +#define ddprintf(x) dprintf x +#define KTRACE() kprintf("%s %d\n", __FILE__, __LINE__) +#else +#define ddprintf(x) +#define KTRACE() +#endif + +/* neither value may be larger than 65536 */ +#define PLAYBACK_BUF_SIZE 2048 +#define RECORD_BUF_SIZE PLAYBACK_BUF_SIZE +/* always create this much, so larger buffers can be requested */ +#define MIN_MEMORY_SIZE 32768 + +/* clock crystal frequency */ +#define F_REF 24576000 +/* data book says 80 MHz ... */ +#define MIN_FREQ 80000000 +/* tolerance for sample rate clock derivation - book has this at 0.005 */ +#define TOLERANCE 0.001 + +/* there are five logical devices: midi, joystick, pcm, mux and mixer */ + +typedef struct _midi_dev +{ + struct _cmedia_pci_dev *card; + void * driver; + void * cookie; + int32 count; + char name[64]; +} midi_dev; + +typedef struct _joy_dev +{ + void * driver; + char name1[64]; +} joy_dev; + +typedef cmedia_pci_audio_format pcm_cfg; +typedef cmedia_pci_audio_buf_header pcm_buf_hdr; + +enum +{ /* these map to the mode enable bits in the CMX13 register */ + kPlayback = 1, + kRecord = 2 +}; + +typedef struct +{ + struct _cmedia_pci_dev * card; + char name[DEVNAME]; + char oldname[DEVNAME]; + sem_id init_sem; + int32 open_count; + int32 open_mode; + +/* playback from a cyclic, small-ish buffer */ + + int32 wr_lock; + int dma_a; + vuchar * wr_1; + vuchar * wr_2; + vuchar * wr_cur; + size_t wr_size; + int wr_silence; + int32 write_waiting; + sem_id write_sem; + size_t was_written; + uint32 wr_skipped; + sem_id wr_entry; + bigtime_t wr_time; + uint64 wr_total; + sem_id wr_time_sem; + int32 wr_time_wait; + +/* recording into a cyclic, somewhat larger buffer */ + + int32 rd_lock; + int dma_c; + vuchar * rd_1; + vuchar * rd_2; + vuchar * rd_cur; + size_t rd_size; + int32 read_waiting; + sem_id read_sem; + size_t was_read; + bigtime_t next_rd_time; + bigtime_t rd_time; + uint32 rd_skipped; /* count of misses */ + sem_id rd_entry; + uint64 rd_total; + sem_id rd_time_sem; + int32 rd_time_wait; + +/* buffers are owned by the device record (because of allocation) */ + + pcm_cfg config; + + sem_id old_cap_sem; + sem_id old_play_sem; +} pcm_dev; + +typedef struct +{ + struct _cmedia_pci_dev * card; + char name[DEVNAME]; + int32 open_count; +} mux_dev; + +typedef struct +{ + struct _cmedia_pci_dev * card; + char name[DEVNAME]; + int32 open_count; +} mixer_dev; + +typedef struct _cmedia_pci_dev +{ + char name[DEVNAME]; /* used for resources */ + int32 hardware; /* spinlock */ + int enhanced; /* offset to port */ + int32 inth_count; + int dma_base; + size_t low_size; /* size of low memory */ + vuchar * low_mem; + vuchar * low_phys; /* physical address */ + area_id map_low; /* area pointing to low memory */ + pci_info info; + midi_dev midi; + joy_dev joy; + pcm_dev pcm; + mux_dev mux; + mixer_dev mixer; +} cmedia_pci_dev; + + +extern int32 num_cards; +extern cmedia_pci_dev cards[NUM_CARDS]; + + +extern void set_direct(cmedia_pci_dev *, int, uchar, uchar); +extern uchar get_direct(cmedia_pci_dev *, int); +extern void set_indirect(cmedia_pci_dev *, int, uchar, uchar); +extern uchar get_indirect(cmedia_pci_dev *, int); +extern void increment_interrupt_handler(cmedia_pci_dev *); +extern void decrement_interrupt_handler(cmedia_pci_dev *); + + +extern bool midi_interrupt(cmedia_pci_dev *); +extern void midi_interrupt_op(int32 op, void * data); +extern bool dma_a_interrupt(cmedia_pci_dev *); +extern bool dma_c_interrupt(cmedia_pci_dev *); + +extern void PCI_IO_WR(int offset, uint8 val); +extern uint8 PCI_IO_RD(int offset); +extern uint32 PCI_IO_RD_32(int offset); + +extern generic_gameport_module * gameport; + +#endif /* _CM_PRIVATE_H */ + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/cmedia_pci.h b/src/add-ons/kernel/drivers/audio/cmedia/cmedia_pci.h new file mode 100644 index 0000000000..233d105c74 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/cmedia_pci.h @@ -0,0 +1,112 @@ +/* cmedia_pci.h -- specifics for S3-based PCI audio cards */ +/* $Id: cmedia_pci.h,v 1.3 1999/10/13 02:29:19 cltien Exp $ */ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(_CMEDIA_PCI_H) +#define _CMEDIA_PCI_H + +#include +#include +#include +#include "audio_driver.h" +#include "midi_driver.h" +#include "joystick_driver.h" + + +#define CMEDIA_PCI_VENDOR_ID 0x13F6 /* C-Media Inc */ +#define CMEDIA_8338A_DEVICE_ID 0x0100 /* CM8338A */ +#define CMEDIA_8338B_DEVICE_ID 0x0101 /* CM8338B */ +#define CMEDIA_8738A_DEVICE_ID 0x0111 /* CM8738A */ +#define CMEDIA_8738B_DEVICE_ID 0x0112 /* CM8738B */ + +#define CMEDIA_PCI_JOYSTICK_MIN_LATENCY 5000 /* 200 times a second! */ +#define CMEDIA_PCI_JOYSTICK_MAX_LATENCY 100000 /* 10 times a second */ + +typedef struct joystick cmedia_pci_joystick; + + +typedef struct audio_format cmedia_pci_audio_format; +typedef struct audio_buf_header cmedia_pci_audio_buf_header; + + +/* the mux devices use these records */ +typedef audio_routing cmedia_pci_routing; + +/* this is the argument for ioctl() */ +typedef audio_routing_cmd cmedia_pci_routing_cmd; + + +/* selectors for routing */ +#define CMEDIA_PCI_INPUT_MUX B_AUDIO_INPUT_SELECT +#define CMEDIA_PCI_MIC_BOOST B_AUDIO_MIC_BOOST +#define CMEDIA_PCI_MIDI_OUTPUT_TO_SYNTH B_AUDIO_MIDI_OUTPUT_TO_SYNTH +#define CMEDIA_PCI_MIDI_INPUT_TO_SYNTH B_AUDIO_MIDI_INPUT_TO_SYNTH +#define CMEDIA_PCI_MIDI_OUTPUT_TO_PORT B_AUDIO_MIDI_OUTPUT_TO_PORT + +/* input MUX source values */ +#define CMEDIA_PCI_INPUT_CD B_AUDIO_INPUT_CD +#define CMEDIA_PCI_INPUT_DAC B_AUDIO_INPUT_DAC +#define CMEDIA_PCI_INPUT_AUX2 B_AUDIO_INPUT_AUX2 +#define CMEDIA_PCI_INPUT_LINE B_AUDIO_INPUT_LINE_IN +#define CMEDIA_PCI_INPUT_AUX1 B_AUDIO_INPUT_AUX1 +#define CMEDIA_PCI_INPUT_MIC B_AUDIO_INPUT_MIC +#define CMEDIA_PCI_INPUT_MIX_OUT B_AUDIO_INPUT_MIX_OUT + + +/* the mixer devices use these records */ +typedef audio_level cmedia_pci_level; + +/* this is the arg to ioctl() */ +typedef audio_level_cmd cmedia_pci_level_cmd; + +/* bitmask for the flags */ +#define CMEDIA_PCI_LEVEL_MUTED B_AUDIO_LEVEL_MUTED + +/* selectors for levels */ +#define CMEDIA_PCI_LEFT_ADC_INPUT_G B_AUDIO_MIX_ADC_LEFT +#define CMEDIA_PCI_RIGHT_ADC_INPUT_G B_AUDIO_MIX_ADC_RIGHT +#define CMEDIA_PCI_LEFT_AUX1_LOOPBACK_GAM B_AUDIO_MIX_VIDEO_LEFT +#define CMEDIA_PCI_RIGHT_AUX1_LOOPBACK_GAM B_AUDIO_MIX_VIDEO_RIGHT +#define CMEDIA_PCI_LEFT_CD_LOOPBACK_GAM B_AUDIO_MIX_CD_LEFT +#define CMEDIA_PCI_RIGHT_CD_LOOPBACK_GAM B_AUDIO_MIX_CD_RIGHT +#define CMEDIA_PCI_LEFT_LINE_LOOPBACK_GAM B_AUDIO_MIX_LINE_IN_LEFT +#define CMEDIA_PCI_RIGHT_LINE_LOOPBACK_GAM B_AUDIO_MIX_LINE_IN_RIGHT +#define CMEDIA_PCI_MIC_LOOPBACK_GAM B_AUDIO_MIX_MIC +#define CMEDIA_PCI_LEFT_SYNTH_OUTPUT_GAM B_AUDIO_MIX_SYNTH_LEFT +#define CMEDIA_PCI_RIGHT_SYNTH_OUTPUT_GAM B_AUDIO_MIX_SYNTH_RIGHT +#define CMEDIA_PCI_LEFT_AUX2_LOOPBACK_GAM B_AUDIO_MIX_AUX_LEFT +#define CMEDIA_PCI_RIGHT_AUX2_LOOPBACK_GAM B_AUDIO_MIX_AUX_RIGHT +#define CMEDIA_PCI_LEFT_MASTER_VOLUME_AM B_AUDIO_MIX_LINE_OUT_LEFT +#define CMEDIA_PCI_RIGHT_MASTER_VOLUME_AM B_AUDIO_MIX_LINE_OUT_RIGHT +#define CMEDIA_PCI_LEFT_PCM_OUTPUT_GAM B_AUDIO_MIX_DAC_LEFT +#define CMEDIA_PCI_RIGHT_PCM_OUTPUT_GAM B_AUDIO_MIX_DAC_RIGHT +#define CMEDIA_PCI_DIGITAL_LOOPBACK_AM B_AUDIO_MIX_LOOPBACK_LEVEL + + +/* secret handshake ioctl()s */ +#define SV_SECRET_HANDSHAKE 10100 +typedef struct { + bigtime_t wr_time; + bigtime_t rd_time; + uint32 wr_skipped; + uint32 rd_skipped; + uint64 wr_total; + uint64 rd_total; + uint32 _reserved_[6]; +} sv_handshake; +#define SV_RD_TIME_WAIT 10101 +#define SV_WR_TIME_WAIT 10102 +typedef struct { + bigtime_t time; + bigtime_t bytes; + uint32 skipped; + uint32 _reserved_[3]; +} sv_timing; + + + +#endif /* _CMEDIA_PCI_H */ + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/joy.c b/src/add-ons/kernel/drivers/audio/cmedia/joy.c new file mode 100644 index 0000000000..d5967241f5 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/joy.c @@ -0,0 +1,112 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include "cm_private.h" +#include + +#if !defined(_KERNEL_EXPORT_H) +#include +#endif /* _KERNEL_EXPORT_H */ + + +static status_t joy_open(const char *name, uint32 flags, void **cookie); +static status_t joy_close(void *cookie); +static status_t joy_free(void *cookie); +static status_t joy_control(void *cookie, uint32 op, void *data, size_t len); +static status_t joy_read(void *cookie, off_t pos, void *data, size_t *len); +static status_t joy_write(void *cookie, off_t pos, const void *data, size_t *len); + + +#define MIN_COMP -7 +#define MAX_COMP 8 + + +device_hooks joy_hooks = { + &joy_open, + &joy_close, + &joy_free, + &joy_control, + &joy_read, + &joy_write, + NULL, /* select */ + NULL, /* deselect */ + NULL, /* readv */ + NULL /* writev */ +}; + + +static status_t +joy_open( + const char * name, + uint32 flags, + void ** cookie) +{ + int ix; + int offset = -1; + + ddprintf(("cmedia_pci: joy_open()\n")); + + *cookie = NULL; + for (ix=0; ixopen_hook)(cards[ix].joy.driver, flags, cookie); +} + + +static status_t +joy_close( + void * cookie) +{ + return (*gameport->close_hook)(cookie); +} + + +static status_t +joy_free( + void * cookie) +{ + return (*gameport->free_hook)(cookie); +} + + +static status_t +joy_control( + void * cookie, + uint32 iop, + void * data, + size_t len) +{ + return (*gameport->control_hook)(cookie, iop, data, len); +} + + +static status_t +joy_read( + void * cookie, + off_t pos, + void * data, + size_t * nread) +{ + return (*gameport->read_hook)(cookie, pos, data, nread); +} + + +static status_t +joy_write( + void * cookie, + off_t pos, + const void * data, + size_t * nwritten) +{ + return (*gameport->write_hook)(cookie, pos, data, nwritten); +} + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/joystick_driver.h b/src/add-ons/kernel/drivers/audio/cmedia/joystick_driver.h new file mode 100644 index 0000000000..5c88981f6f --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/joystick_driver.h @@ -0,0 +1,113 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _JOYSTICK_DRIVER_H +#define _JOYSTICK_DRIVER_H + +#include +#include +#include + +typedef struct _joystick { + bigtime_t timestamp; + uint32 horizontal; + uint32 vertical; + bool button1; + bool button2; +} joystick; + +/* maximum number of axes on one controller (pads count as 2 axes each) */ +#define MAX_AXES 12 +/* maximum number of hats on one controller -- PADS SHOULD BE RETURNED AS AXES! */ +#define MAX_HATS 8 +/* maximum number of buttons on one controller */ +#define MAX_BUTTONS 32 +/* maximum number of controllers on one port */ +#define MAX_STICKS 4 + +typedef struct _extended_joystick { + bigtime_t timestamp; /* system_time when it was read */ + uint32 buttons; /* lsb to msb, 1 == on */ + int16 axes[MAX_AXES]; /* -32768 to 32767, X, Y, Z, U, V, W */ + uint8 hats[MAX_HATS]; /* 0 through 8 (1 == N, 3 == E, 5 == S, 7 == W) */ +} extended_joystick; + +#define MAX_CONFIG_SIZE 100 + +enum { /* flags for joystick module info */ + js_flag_force_feedback = 0x1, + js_flag_force_feedback_directional = 0x2 +}; + +typedef struct _joystick_module_info { + char module_name[64]; + char device_name[64]; + int16 num_axes; + int16 num_buttons; + int16 num_hats; + uint16 _reserved[7]; + uint32 flags; + uint16 num_sticks; + int16 config_size; + char device_config[MAX_CONFIG_SIZE]; /* device specific */ +} joystick_module_info; + +/* Note that joystick_module is something used by the game port driver */ +/* to talk to digital joysticks; if you're writing a sound card driver */ +/* and want to add support for a /dev/joystick device, use the generic_gameport */ +/* module. */ + +typedef struct _joystick_module { + module_info minfo; + /** "configure" might change the "info" if it auto-detects a device */ + int (*configure)(int port, joystick_module_info * info, size_t size, void ** out_cookie); + /** "read" actual data from device into "data" */ + int (*read)(void * cookie, int port, extended_joystick * data, size_t size); + /** "crumble" the cookie (deallocate) when done */ + int (*crumble)(void * cookie, int port); + /** "force" tells the joystick to exert force on the same axes as input for the specified duration */ + int (*force)(void * cookie, int port, bigtime_t duration, extended_joystick * force, size_t size); + int _reserved_; +} joystick_module; + +/** Doing force feedback means writing an extended_joystick to the device with force values. + The "timestamp" should be the duration of the feedback. Successive writes will be queued + by the device module. */ +enum { /* Joystick driver ioctl() opcodes */ + B_JOYSTICK_GET_SPEED_COMPENSATION = B_JOYSTICK_DRIVER_BASE, + /* arg -> ptr to int32 */ + B_JOYSTICK_SET_SPEED_COMPENSATION, /* arg -> ptr to int32 */ + B_JOYSTICK_GET_MAX_LATENCY, /* arg -> ptr to long long */ + B_JOYSTICK_SET_MAX_LATENCY, /* arg -> ptr to long long */ + B_JOYSTICK_SET_DEVICE_MODULE, /* arg -> ptr to joystick_module; also enters enhanced mode */ + B_JOYSTICK_GET_DEVICE_MODULE, /* arg -> ptr to joystick_module */ + B_JOYSTICK_SET_RAW_MODE /* arg -> ptr to bool (true or false) */ +}; + +/* Speed compensation is not generally necessary, because the joystick */ +/* driver is measuring using real time, not just # cycles. "0" means the */ +/* default, center value. + typically returns higher values; - returns lower */ +/* A typical range might be from -10 to +10, but it varies by driver */ + +/* Lower latency will make for more overhead in reading the joystick */ +/* ideally, you set this value to just short of how long it takes you */ +/* to calculate and render a frame. 30 fps -> latency 33000 */ + + +typedef struct _generic_gameport_module { + module_info minfo; + status_t (*create_device)(int port, void ** out_storage); + status_t (*delete_device)(void * storage); + status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie); + status_t (*close_hook)(void * cookie); + status_t (*free_hook)(void * cookie); + status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len); + status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len); + status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len); + int _reserved_; +} generic_gameport_module; + + +#endif diff --git a/src/add-ons/kernel/drivers/audio/cmedia/midi.c b/src/add-ons/kernel/drivers/audio/cmedia/midi.c new file mode 100644 index 0000000000..63aa7e68a7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/midi.c @@ -0,0 +1,186 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include +#include +#include + +#include "cm_private.h" + + +extern void dump_card(cmedia_pci_dev * card); + +#if !defined(_KERNEL_EXPORT_H) +#include +#endif /* _KERNEL_EXPORT_H */ + +#define MIDI_ACTIVE_SENSE 0xfe +#define SNOOZE_GRANULARITY 500 + +extern generic_mpu401_module * mpu401; + +void +midi_interrupt_op( + int32 op, + void * data) +{ + midi_dev * port = (midi_dev *)data; + ddprintf(("port = %p\n", port)); + if (op == B_MPU_401_ENABLE_CARD_INT) { + cpu_status cp; + ddprintf(("cmedia_pci: B_MPU_401_ENABLE_CARD_INT\n")); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + increment_interrupt_handler(port->card); + set_direct(port->card, 0x01, 0x00, 0x80); + set_indirect(port->card, 0x2A, 0x04, 0xff); + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + } + else if (op == B_MPU_401_DISABLE_CARD_INT) { + /* turn off MPU interrupts */ + cpu_status cp; + ddprintf(("cmedia_pci: B_MPU_401_DISABLE_CARD_INT\n")); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + set_direct(port->card, 0x01, 0x80, 0x80); + /* remove interrupt handler if necessary */ + decrement_interrupt_handler(port->card); + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + } + ddprintf(("cmedia_pci: midi_interrupt_op() done\n")); +} + +static status_t midi_open(const char *name, uint32 flags, void **cookie); +static status_t midi_close(void *cookie); +static status_t midi_free(void *cookie); +static status_t midi_control(void *cookie, uint32 op, void *data, size_t len); +static status_t midi_read(void *cookie, off_t pos, void *data, size_t *len); +static status_t midi_write(void *cookie, off_t pos, const void *data, size_t *len); + + +device_hooks midi_hooks = { + &midi_open, + &midi_close, + &midi_free, + &midi_control, + &midi_read, + &midi_write, + NULL, /* select */ + NULL, /* deselect */ + NULL, /* readv */ + NULL /* writev */ +}; + +static status_t +midi_open( + const char * name, + uint32 flags, + void ** cookie) +{ + int ix; + int ret; + + ddprintf(("cmedia_pci: midi_open()\n")); + + *cookie = NULL; + for (ix=0; ix= num_cards) { + ddprintf(("bad device\n")); + return ENODEV; + } + + ddprintf(("cmedia_pci: mpu401: %p open(): %p driver: %p\n", mpu401, mpu401->open_hook, cards[ix].midi.driver)); + ret = (*mpu401->open_hook)(cards[ix].midi.driver, flags, cookie); + if (ret >= B_OK) { + cards[ix].midi.cookie = *cookie; + atomic_add(&cards[ix].midi.count, 1); + } + ddprintf(("cmedia_pci: mpu401: open returns %x / %p\n", ret, *cookie)); + return ret; +} + + +static status_t +midi_close( + void * cookie) +{ + ddprintf(("cmedia_pci: midi_close()\n")); + return (*mpu401->close_hook)(cookie); +} + + +static status_t +midi_free( + void * cookie) +{ + int ix; + status_t f; + ddprintf(("cmedia_pci: midi_free()\n")); + f = (*mpu401->free_hook)(cookie); + for (ix=0; ixcontrol_hook)(cookie, iop, data, len); +} + + +static status_t +midi_read( + void * cookie, + off_t pos, + void * ptr, + size_t * nread) +{ + return (*mpu401->read_hook)(cookie, pos, ptr, nread); +} + + +static status_t +midi_write( + void * cookie, + off_t pos, + const void * ptr, + size_t * nwritten) +{ + return (*mpu401->write_hook)(cookie, pos, ptr, nwritten); +} + + +bool +midi_interrupt( + cmedia_pci_dev * dev) +{ + if (!dev->midi.driver) { +// kprintf("aiigh\n"); + return false; + } + + return (*mpu401->interrupt_hook)(dev->midi.driver); +} + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/midi_driver.h b/src/add-ons/kernel/drivers/audio/cmedia/midi_driver.h new file mode 100644 index 0000000000..d641b8bb1a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/midi_driver.h @@ -0,0 +1,110 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _MIDI_DRIVER_H +#define _MIDI_DRIVER_H + +#include +#include + +/* ----- + ioctl codes +----- */ + +/* the old opcodes are deprecated, and may or may not work + in newer drivers */ +enum { + B_MIDI_GET_READ_TIMEOUT = B_MIDI_DRIVER_BASE, + B_MIDI_SET_READ_TIMEOUT, + B_MIDI_TIMED_READ, + B_MIDI_TIMED_WRITE, + B_MIDI_WRITE_SYNC, + B_MIDI_WRITE_CLEAR, + B_MIDI_GET_READ_TIMEOUT_OLD = B_DEVICE_OP_CODES_END + 1, + B_MIDI_SET_READ_TIMEOUT_OLD +}; + +/* the default timeout when you open a midi driver */ +#define B_MIDI_DEFAULT_TIMEOUT 1000000000000000LL + +/* Usage: + To read, set "data" to a pointer to your buffer, and "size" to the + maximum size of this buffer. On return, "when" will contain the time + at which the data was received (first byte) and "size" will contain + the actual amount of data read. + Call ioctl(fd, B_MIDI_TIMED_READ, &midi_timed_data, sizeof(midi_timed_data)); + To write, set "when" to when you want the first byte to go out the + wire, set "data" to point to your data, and set "size" to the size + of your data. + Call ioctl(fd, B_MIDI_TIMED_WRITE, &midi_timed_data, sizeof(midi_timed_data)); +*/ +typedef struct { + bigtime_t when; + size_t size; + unsigned char * data; +} midi_timed_data; + + +/* The MIDI parser returns the number of bytes that a message contains, given the */ +/* initial byte. For some messages, this is not known until the second byte is seen. */ +/* For such messages, a state > 0 is returned as well as some count > 0. When state */ +/* is > 0, you should call (*parse) for the next byte as well, which might modify */ +/* the returned message size. Message size will always be returned with the current */ +/* byte being counted as byte 1. A return of 0 means that the byte initiates a new */ +/* message. SysX is handled by returning max_size until the end or next initial message */ +/* is seen. So your loop looks something like: */ +/* + uint32 state = 0; // Only set this to 0 the first time you call the parser. + // preserve the 'state' between invocations of your read() hook. + int todo = 0; + unsigned char * end = in_buf+buf_size + unsigned char * out_buf = in_buf; + while (true) { + uchar byte = read_midi(); + if (!todo || state) { + todo = (*parser->parse)(&state, byte, end-out_buf); + } + if (todo < 1) { + unput_midi(byte); + } else { + *(out_buf++) = byte; + todo--; + } + if (todo < 1 || out_buf >= end) { + received_midi_message(in_buf, out_buf-in_buf); + todo = 0; + } + } + */ + +#define B_MIDI_PARSER_MODULE_NAME "media/midiparser/v1" + +typedef struct _midi_parser_module_info { + module_info minfo; + int (*parse)(uint32 * state, uchar byte, size_t max_size); + int _reserved_; +} midi_parser_module_info; + +#define B_MPU_401_MODULE_NAME "generic/mpu401/v1" + +enum { + B_MPU_401_ENABLE_CARD_INT = 1, + B_MPU_401_DISABLE_CARD_INT +}; +typedef struct _generic_mpu401_module { + module_info minfo; + status_t (*create_device)(int port, void ** out_storage, uint32 workarounds, void (*interrupt_op)(int32 op, void * card), void * card); + status_t (*delete_device)(void * storage); + status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie); + status_t (*close_hook)(void * cookie); + status_t (*free_hook)(void * cookie); + status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len); + status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len); + status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len); + bool (*interrupt_hook)(void * cookie); + int _reserved_; +} generic_mpu401_module; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/cmedia/mixer.c b/src/add-ons/kernel/drivers/audio/cmedia/mixer.c new file mode 100644 index 0000000000..1285997cd7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/mixer.c @@ -0,0 +1,304 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include "cm_private.h" +#include + +#if !defined(_KERNEL_EXPORT_H) +#include +#endif /* _KERNEL_EXPORT_H */ + + +static status_t mixer_open(const char *name, uint32 flags, void **cookie); +static status_t mixer_close(void *cookie); +static status_t mixer_free(void *cookie); +static status_t mixer_control(void *cookie, uint32 op, void *data, size_t len); +static status_t mixer_read(void *cookie, off_t pos, void *data, size_t *len); +static status_t mixer_write(void *cookie, off_t pos, const void *data, size_t *len); + +device_hooks mixer_hooks = { + &mixer_open, + &mixer_close, + &mixer_free, + &mixer_control, + &mixer_read, + &mixer_write, + NULL, /* select */ + NULL, /* deselect */ + NULL, /* readv */ + NULL /* writev */ +}; + + +typedef struct { + int selector; + int port; + float div; + float sub; + int minval; + int maxval; + int leftshift; + int mask; + int mutemask; +} mixer_info; + +/* mute is special -- when it's 0x01, it means enable... */ +mixer_info the_mixers[] = { + {CMEDIA_PCI_LEFT_ADC_INPUT_G, 0, 1.5, 0.0, 0, 15, 0, 0x0f, 0x00}, + {CMEDIA_PCI_RIGHT_ADC_INPUT_G, 1, 1.5, 0.0, 0, 15, 0, 0x0f, 0x00}, + {CMEDIA_PCI_LEFT_AUX1_LOOPBACK_GAM, 2, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_RIGHT_AUX1_LOOPBACK_GAM, 3, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_LEFT_CD_LOOPBACK_GAM, 4, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_RIGHT_CD_LOOPBACK_GAM, 5, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_LEFT_LINE_LOOPBACK_GAM, 6, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_RIGHT_LINE_LOOPBACK_GAM, 7, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_MIC_LOOPBACK_GAM, 8, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_LEFT_SYNTH_OUTPUT_GAM, 0xa, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_RIGHT_SYNTH_OUTPUT_GAM, 0xb, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_LEFT_AUX2_LOOPBACK_GAM, 0xc, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_RIGHT_AUX2_LOOPBACK_GAM, 0xd, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_LEFT_MASTER_VOLUME_AM, 0xe, -1.5, 0.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_RIGHT_MASTER_VOLUME_AM, 0xf, -1.5, 0.0, 0, 31, 0, 0x1f, 0x80}, + {CMEDIA_PCI_LEFT_PCM_OUTPUT_GAM, 0x10, -1.5, 0.0, 0, 63, 0, 0x3f, 0x80}, + {CMEDIA_PCI_RIGHT_PCM_OUTPUT_GAM, 0x11, -1.5, 0.0, 0, 63, 0, 0x3f, 0x80}, + {CMEDIA_PCI_DIGITAL_LOOPBACK_AM, 0x16, -1.5, 0.0, 0, 63, 2, 0xfc, 0x01}, +}; +#define N_MIXERS (sizeof(the_mixers) / sizeof(the_mixers[0])) + +static int +map_mixer(int selector) { + int i; + for (i = 0; i < N_MIXERS; i++) + if (the_mixers[i].selector == selector) + return i; + return -1; +} + + +static status_t +mixer_open( + const char * name, + uint32 flags, + void ** cookie) +{ + int ix; + /* mixer_dev * it = NULL; */ + + ddprintf(("cmedia_pci: mixer_open()\n")); + + *cookie = NULL; + for (ix=0; ixopen_count, -1); + + return B_OK; +} + + +static status_t +mixer_free( + void * cookie) +{ + ddprintf(("cmedia_pci: mixer_free()\n")); + + if (((mixer_dev *)cookie)->open_count != 0) { + dprintf("cmedia_pci: mixer open_count is bad in mixer_free()!\n"); + } + return B_OK; /* already done in close */ +} + + +static int +get_mixer_value( + cmedia_pci_dev * card, + cmedia_pci_level * lev) +{ + int ix = map_mixer(lev->selector); + uchar val; + if (ix < 0) { + return B_BAD_VALUE; + } + val = get_indirect(card, the_mixers[ix].port); + lev->flags = 0; + if (!the_mixers[ix].mutemask) { + /* no change */ + } + else if (the_mixers[ix].mutemask == 0x01) { + if (!(val & 0x01)) { + lev->flags |= CMEDIA_PCI_LEVEL_MUTED; + } + } + else if (val & the_mixers[ix].mutemask) { + lev->flags |= CMEDIA_PCI_LEVEL_MUTED; + } + val &= the_mixers[ix].mask; + val >>= the_mixers[ix].leftshift; + lev->value = ((float)val)*the_mixers[ix].div+the_mixers[ix].sub; + + return B_OK; +} + + +static int +gather_info( + mixer_dev * mixer, + cmedia_pci_level * data, + int count) +{ + int ix; + cpu_status cp; + + cp = disable_interrupts(); + acquire_spinlock(&mixer->card->hardware); + + for (ix=0; ixcard, &data[ix]) < B_OK) + break; + } + + release_spinlock(&mixer->card->hardware); + restore_interrupts(cp); + + return ix; +} + + +static status_t +set_mixer_value( + cmedia_pci_dev * card, + cmedia_pci_level * lev) +{ + int selector = map_mixer(lev->selector); + int value; + int mask; + if (selector < 0) { + return EINVAL; + } + value = (lev->value-the_mixers[selector].sub)/the_mixers[selector].div; + if (value < the_mixers[selector].minval) { + value = the_mixers[selector].minval; + } + if (value > the_mixers[selector].maxval) { + value = the_mixers[selector].maxval; + } + value <<= the_mixers[selector].leftshift; + if (the_mixers[selector].mutemask) { + if (the_mixers[selector].mutemask == 0x01) { + if (!(lev->flags & CMEDIA_PCI_LEVEL_MUTED)) { + value |= the_mixers[selector].mutemask; + } + } else { + if (lev->flags & CMEDIA_PCI_LEVEL_MUTED) { + value |= the_mixers[selector].mutemask; + } + } + } + mask = the_mixers[selector].mutemask | the_mixers[selector].mask; + set_indirect(card, the_mixers[selector].port, value, mask); + return B_OK; +} + + +static int +disperse_info( + mixer_dev * mixer, + cmedia_pci_level * data, + int count) +{ + int ix; + cpu_status cp; + + cp = disable_interrupts(); + acquire_spinlock(&mixer->card->hardware); + + for (ix=0; ixcard, &data[ix]) < B_OK) + break; + } + + release_spinlock(&mixer->card->hardware); + restore_interrupts(cp); + + return ix; +} + + +static status_t +mixer_control( + void * cookie, + uint32 iop, + void * data, + size_t len) +{ + mixer_dev * it = (mixer_dev *)cookie; + status_t err = B_OK; + + if (!data) { + return B_BAD_VALUE; + } + + ddprintf(("cmedia_pci: mixer_control()\n")); /* slow printing */ + + switch (iop) { + case B_MIXER_GET_VALUES: + ((cmedia_pci_level_cmd *)data)->count = + gather_info(it, ((cmedia_pci_level_cmd *)data)->data, + ((cmedia_pci_level_cmd *)data)->count); + break; + case B_MIXER_SET_VALUES: + ((cmedia_pci_level_cmd *)data)->count = + disperse_info(it, ((cmedia_pci_level_cmd *)data)->data, + ((cmedia_pci_level_cmd *)data)->count); + break; + default: + err = B_BAD_VALUE; + break; + } + return err; +} + + +static status_t +mixer_read( + void * cookie, + off_t pos, + void * data, + size_t * nread) +{ + return EPERM; +} + + +static status_t +mixer_write( + void * cookie, + off_t pos, + const void * data, + size_t * nwritten) +{ + return EPERM; +} + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/mux.c b/src/add-ons/kernel/drivers/audio/cmedia/mux.c new file mode 100644 index 0000000000..aecd7ba6c7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/mux.c @@ -0,0 +1,281 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include "cm_private.h" +#include + +#if !defined(_KERNEL_EXPORT_H) +#include +#endif /* _KERNEL_EXPORT_H */ + + +static status_t mux_open(const char *name, uint32 flags, void **cookie); +static status_t mux_close(void *cookie); +static status_t mux_free(void *cookie); +static status_t mux_control(void *cookie, uint32 op, void *data, size_t len); +static status_t mux_read(void *cookie, off_t pos, void *data, size_t *len); +static status_t mux_write(void *cookie, off_t pos, const void *data, size_t *len); + +device_hooks mux_hooks = { + &mux_open, + &mux_close, + &mux_free, + &mux_control, + &mux_read, + &mux_write, + NULL, /* select */ + NULL, /* deselect */ + NULL, /* readv */ + NULL /* writev */ +}; + + +typedef struct { + int port_l; + int port_r; + int minval; + int maxval; + int leftshift; + int mask; + char name[B_OS_NAME_LENGTH]; +} mux_info; + +const mux_info the_muxes[] = { + { 0, 1, 1, 7, 5, 0xe0, "Sampling Input" }, + { 0, -1, 0, 1, 4, 0x10, "Mic +20dB Selection" }, + { 0x2a, -1, 0, 1, 0, 0x01, "MIDI Output To Synth" }, + { 0x2a, -1, 0, 1, 1, 0x02, "MIDI Input To Synth" }, + { 0x2a, -1, 0, 1, 2, 0x04, "MIDI Output To Port" }, +}; + +static const uchar unmap_input[] = { + 0, + CMEDIA_PCI_INPUT_CD, + CMEDIA_PCI_INPUT_DAC, + CMEDIA_PCI_INPUT_AUX2, + CMEDIA_PCI_INPUT_LINE, + CMEDIA_PCI_INPUT_AUX1, + CMEDIA_PCI_INPUT_MIC, + CMEDIA_PCI_INPUT_MIX_OUT +}; + +static uchar +map_input(uchar val) { + int i; + for (i = 0; i < 8; i++) + if (unmap_input[i] == val) + return i; + return 0; +} + + +static status_t +mux_open( + const char * name, + uint32 flags, + void ** cookie) +{ + int ix; + /* mux_dev * plex = NULL; */ + + ddprintf(("cmedia_pci: mux_open()\n")); + + *cookie = NULL; + for (ix=0; ixopen_count, -1); + + return B_OK; +} + + +static status_t +mux_free( + void * cookie) +{ + ddprintf(("cmedia_pci: mux_free()\n")); + + if (((mux_dev *)cookie)->open_count != 0) { + dprintf("cmedia_pci: mux open_count is bad in mux_free()!\n"); + } + return B_OK; /* already done in close */ +} + + +static int +get_mux_value( + cmedia_pci_dev * card, + int ix) +{ + uchar val; + if (ix < 0) { + return -1; + } + if (ix > 4) { + return -1; + } + val = get_indirect(card, the_muxes[ix].port_l); + val &= the_muxes[ix].mask; + val >>= the_muxes[ix].leftshift; + if (ix == CMEDIA_PCI_INPUT_MUX) + return unmap_input[val]; + return val; +} + + +static int +gather_info( + mux_dev * mux, + cmedia_pci_routing * data, + int count) +{ + int ix; + cpu_status cp; + + cp = disable_interrupts(); + acquire_spinlock(&mux->card->hardware); + + for (ix=0; ixcard, data[ix].selector); + if (data[ix].value < 0) { + break; + } + } + + release_spinlock(&mux->card->hardware); + restore_interrupts(cp); + + return ix; +} + + +static status_t +set_mux_value( + cmedia_pci_dev * card, + int selector, + int value) +{ + ddprintf(("set_mux_value(%d,%d)\n", selector, value)); + if (selector < 0 || selector > 4) { + ddprintf(("selector EINVAL\n")); + return EINVAL; + } + if (selector == CMEDIA_PCI_INPUT_MUX) + value = map_input(value); + if (value < the_muxes[selector].minval || + value > the_muxes[selector].maxval) { + ddprintf(("value EINVAL\n")); + return EINVAL; + } + set_indirect(card, the_muxes[selector].port_l, + (value << the_muxes[selector].leftshift), + the_muxes[selector].mask); + if (the_muxes[selector].port_r > -1) { + set_indirect(card, the_muxes[selector].port_r, + (value << the_muxes[selector].leftshift), + the_muxes[selector].mask); + } + return B_OK; +} + + +static int +disperse_info( + mux_dev * mux, + cmedia_pci_routing * data, + int count) +{ + int ix; + cpu_status cp; + + cp = disable_interrupts(); + acquire_spinlock(&mux->card->hardware); + + for (ix=0; ixcard, data[ix].selector, data[ix].value) < B_OK) { + break; + } + } + + release_spinlock(&mux->card->hardware); + restore_interrupts(cp); + + return ix; +} + + +static status_t +mux_control( + void * cookie, + uint32 iop, + void * data, + size_t len) +{ + mux_dev * plex = (mux_dev *)cookie; + status_t err = B_OK; + + ddprintf(("cmedia_pci: mux_control()\n")); /* slow printing */ + + switch (iop) { + case B_ROUTING_GET_VALUES: + ((cmedia_pci_routing_cmd *)data)->count = + gather_info(plex, ((cmedia_pci_routing_cmd *)data)->data, + ((cmedia_pci_routing_cmd *)data)->count); + break; + case B_ROUTING_SET_VALUES: + ((cmedia_pci_routing_cmd *)data)->count = + disperse_info(plex, ((cmedia_pci_routing_cmd *)data)->data, + ((cmedia_pci_routing_cmd *)data)->count); + break; + default: + err = B_BAD_VALUE; + break; + } + return err; +} + + +static status_t +mux_read( + void * cookie, + off_t pos, + void * data, + size_t * nread) +{ + return EPERM; +} + + +static status_t +mux_write( + void * cookie, + off_t pos, + const void * data, + size_t * nwritten) +{ + return EPERM; +} + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/pcm.c b/src/add-ons/kernel/drivers/audio/cmedia/pcm.c new file mode 100644 index 0000000000..e04ccf255a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/pcm.c @@ -0,0 +1,1710 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include + +#include +#include "R3MediaDefs.h" + +#include "cm_private.h" +#include "sound.h" + +#if !defined(_KERNEL_EXPORT_H) +#include +#endif /* _KERNEL_EXPORT_H */ + +extern int sprintf(char *, const char *, ...); + + +extern void dump_card(cmedia_pci_dev * card); + +#if !defined(OLDAPI) + #if DEBUG + #define OLDAPI(x) dprintf x + #else + #define OLDAPI(x) + #endif +#endif + +#if DEBUG +int32 int_cnt; +int32 put_cnt; +bigtime_t the_time; +#endif + +#if 0 +/* early Intel kernels forgot to export these functions */ +#undef B_HOST_TO_LENDIAN_FLOAT +#undef B_HOST_TO_BENDIAN_FLOAT +#undef B_LENDIAN_TO_HOST_FLOAT +#undef B_BENDIAN_TO_HOST_FLOAT + +static float +_swap_float_(float x) +{ + uint32 temp1 = *(uint32*)&x; + uint32 temp2 = ((temp1>>24)|((temp1>>8)&0xff00)|((temp1<<8)&0xff0000)| + (temp1<<24)); + return *(float *)&temp2; +} + +#if B_HOST_IS_BENDIAN +#define B_HOST_TO_LENDIAN_FLOAT(x) _swap_float_(x) +#define B_HOST_TO_BENDIAN_FLOAT(x) ((float)(x)) +#define B_LENDIAN_TO_HOST_FLOAT(x) _swap_float_(x) +#define B_BENDIAN_TO_HOST_FLOAT(x) ((float)(x)) +#else +#define B_HOST_TO_LENDIAN_FLOAT(x) ((float)(x)) +#define B_HOST_TO_BENDIAN_FLOAT(x) _swap_float_(x) +#define B_LENDIAN_TO_HOST_FLOAT(x) ((float)(x)) +#define B_BENDIAN_TO_HOST_FLOAT(x) _swap_float_(x) +#endif + +#endif + + +static status_t pcm_open(const char *name, uint32 flags, void **cookie); +static status_t pcm_close(void *cookie); +static status_t pcm_free(void *cookie); +static status_t pcm_control(void *cookie, uint32 op, void *data, size_t len); +static status_t pcm_read(void *cookie, off_t pos, void *data, size_t *len); +static status_t pcm_write(void *cookie, off_t pos, const void *data, size_t *len); +//static status_t pcm_writev(void *cookie, off_t pos, const iovec *vec, size_t count, size_t *len); /* */ + +device_hooks pcm_hooks = { + &pcm_open, + &pcm_close, + &pcm_free, + &pcm_control, + &pcm_read, + &pcm_write, + NULL, /* select */ + NULL, /* deselect */ + NULL, /* readv */ + NULL // &pcm_writev /* writev */ +}; + +static pcm_cfg default_pcm = { + 44100.0, /* sample rate */ + 2, /* channels */ + 0x2, /* format */ +#if B_HOST_IS_BENDIAN + 1, /* endian (big) */ +#else + 0, /* endian (little) */ +#endif + 0, /* header size */ + PLAYBACK_BUF_SIZE, /* these are currently hard-coded */ + RECORD_BUF_SIZE /* and cannot be changed without re-compile */ +}; + + +#if 0 + +typedef struct { + uint8 control; + uint8 imask; + uint8 regs[0x2e]; +} chip_state; + +static void +save_state( + pcm_dev * port, + chip_state * state) +{ + int ix; + state->control = get_direct(port->card, 0); + state->imask = get_direct(port->card, 1); + for (ix=0; ix<0x0e; ix++) { + if (ix != 0x28 && ix != 0x29 && ix != 0x1a && ix != 0x1b) { + state->regs[ix] = get_indirect(port->card, ix+0x30); + } + } +} + + +static void +restore_state( + pcm_dev * port, + const chip_state * state) +{ + int ix; + set_direct(port->card, 0, state->control, 0xff); + for (ix=0; ix<0x0e; ix++) { + if (ix != 0x28 && ix != 0x29 && ix != 0x1a && ix != 0x1b) { + set_indirect(port->card, ix, state->regs[ix]+0x30, 0xff); + } + } + set_direct(port->card, 1, state->imask, 0xff); +} + + +static void +reset_chip( + pcm_dev * port) +{ + set_direct(port->card, 0x1b, 0x40, 0x40); + snooze(2); + set_direct(port->card, 0x1b, 0x00, 0x40); +} + +#endif + + +static void +stop_dma( + pcm_dev * port) +{ + set_direct(port->card, 0x24, 0x40, 0x40); // mute wave stream + set_direct(port->card, 0x02, 0, 0x03); // stop both ch0 and ch1 + + set_direct(port->card, 0x02, 0x0c, 0x0c); // reset both ch0 and ch1 + set_direct(port->card, 0x02, 0, 0x0c); + ddprintf(("cmedia_pci: DMA stopped\n")); +} + + +static void +start_dma( + pcm_dev * port) +{ + int sample_size = 1; + + /* start out with a clean slate */ + + KTRACE(); + ddprintf(("cmedia_pci: start_dma()\n")); + if (port->config.format == 0x11) { + memset((void*)port->card->low_mem, 0x80, port->config.play_buf_size + + port->config.rec_buf_size); + } + else { + memset((void *)port->card->low_mem, 0, port->config.play_buf_size + + port->config.rec_buf_size); + } + + port->wr_cur = port->wr_2; + port->wr_silence = port->config.play_buf_size; + port->was_written = 0; + port->rd_cur = port->rd_2; + port->was_read = port->config.rec_buf_size/2; /* how much has been read */ + port->wr_total = 0; + port->rd_total = 0; + + /* we split the available low memory buffer in a small chunk */ + /* for playback, and a large chunk for recording. Then we split */ + /* each of those in half for double-buffering. While the DMA */ + /* just runs the entire range of the buffer, wrapping around when */ + /* done, the count register is set up to generate interrupt after */ + /* each half of the buffer. Because of latency requirements, we */ + /* will get 187 interrupts a second from playback, and 94 interrupts */ + /* a second from recording, at 48 kHz sampling rate, when buffers */ + /* are 2048 for playback and 4096 for record. */ + + ddprintf(("play_buf_size %lx rec_buf_size %lx\n", + port->config.play_buf_size/2, port->config.rec_buf_size/2)); + + PCI_IO_WR(port->dma_c, ((uint32)port->card->low_phys+ + port->config.play_buf_size)&0xff); + PCI_IO_WR(port->dma_c+1, (((uint32)port->card->low_phys+ + port->config.play_buf_size)>>8)&0xff); + PCI_IO_WR(port->dma_c+2, (((uint32)port->card->low_phys+ + port->config.play_buf_size)>>16)&0xff); + PCI_IO_WR(port->dma_c+3, 0); + /* if this is a 16 bit channel, divide transfer count in 2 */ + if (port->config.format != 0x11) + sample_size *= 2; + /* if this is a stereo channel, divide transfer count in 2 */ + if (port->config.channels == 2) + sample_size *= 2; + PCI_IO_WR(port->dma_c+4, (port->config.rec_buf_size/sample_size-1)&0xff); + PCI_IO_WR(port->dma_c+5, ((port->config.rec_buf_size/sample_size-1)>>8)&0xff); + PCI_IO_WR(port->dma_c+6, (port->rd_size/sample_size-1)&0xff); + PCI_IO_WR(port->dma_c+7, ((port->rd_size/sample_size-1)>>8)&0xff); + + PCI_IO_WR(port->dma_a, ((uint32)port->card->low_phys)&0xff); + PCI_IO_WR(port->dma_a+1, ((uint32)port->card->low_phys>>8)&0xff); + PCI_IO_WR(port->dma_a+2, ((uint32)port->card->low_phys>>16)&0xff); + PCI_IO_WR(port->dma_a+3, 0); + PCI_IO_WR(port->dma_a+4, (port->config.play_buf_size/sample_size-1)&0xff); + PCI_IO_WR(port->dma_a+5, ((port->config.play_buf_size/sample_size-1)>>8)&0xff); + PCI_IO_WR(port->dma_a+6, (port->wr_size/sample_size-1)&0xff); + PCI_IO_WR(port->dma_a+7, ((port->wr_size/sample_size-1)>>8)&0xff); + +/* here, we should mute the PCM output to avoid clicking */ + + ddprintf(("cmedia_pci: DMA starts as %lx/%lx\n", port->config.format, port->open_mode)); + set_direct(port->card, 0x24, 0x00, 0x40); + +/* enable ch0 as play, and ch1 as record */ + set_direct(port->card, 0, 0x02, 0x03); + set_direct(port->card, 0x02, 0x03, 0x03); + +/* here, we should snooze for 16 samples' time, then un-mute the PCM output */ + KTRACE(); +} + + +static status_t +configure_pcm( + pcm_dev * port, + pcm_cfg * config, + bool force) +{ + status_t err = B_OK; + int m = 0, n = 0, r = 0; /* parameters for the PLL sample rate synthesizer */ + int asr = -1; /* alternate sample rate divisor */ + uint32 s; /* size of buffer */ + + ddprintf(("cmedia_pci: configure_pcm()\n")); + + /* check args */ + if (config->sample_rate < 4000.0) { + config->sample_rate = default_pcm.sample_rate; + } + if (config->sample_rate > 48000.0) { + config->sample_rate = 48000.0; + } + if (config->channels < 1) { + config->channels = default_pcm.channels; + } + if (config->channels > 2) { + config->channels = default_pcm.channels; + } + /* secret format of format: upper nybble = signed, unsigned, float */ + /* lower nybble = bytes per sample */ + if ((config->format != 0x11) && (config->format != 0x2) && + (config->format != 0x24) && (config->format != 0x4)) { + config->format = default_pcm.format; + } + if (config->buf_header < 0) { + config->buf_header = 0; + } + + /* figure out buffer size that's a power of two and within size limits */ + if (!config->play_buf_size) { + /* default is 256 samples for a comfy 6 ms latency */ + s = 256*config->channels*(config->format&0xf); + } /* minimum is 32 samples for a more extreme 0.75ms latency */ + else for (s = 32*config->channels*(config->format&0xf); s < MIN_MEMORY_SIZE/2; s = (s<<1)) { + if (s >= config->play_buf_size) { + break; + } + } + config->play_buf_size = s; + if (!config->rec_buf_size) { + s = 256*config->channels*(config->format&0xf); + } + else for (s = 32*config->channels*(config->format&0xf); s < MIN_MEMORY_SIZE/2; s = (s<<1)) { + if (s >= config->rec_buf_size) { + break; + } + } + config->rec_buf_size = s; + + /* calculate m, n and r (and asr) */ + + if (!force && abs(config->sample_rate - port->config.sample_rate) < config->sample_rate/250) { + n = -1; + } + else if (config->sample_rate == 48000.0) { + asr = 7; + } + else if (config->sample_rate == 32000.0) { + asr = 6; + } + else if (config->sample_rate == 16000.0) { + asr = 5; + } + else if (config->sample_rate == 8000.0) { + asr = 4; + } + else if (config->sample_rate == 44100.0) { + asr = 3; + } + else if (config->sample_rate == 22050.0) { + asr = 2; + } + else if (config->sample_rate == 11025.0) { + asr = 1; + } + else { + float freq; + float delta = 1000000.0; + int sr = -1; + int sn = -1; + int sm = -1; + float diff; + + for (r=0; r<8; r++) { + if ((1<sample_rate*512 < MIN_FREQ) { + continue; + } + break; + } + if (r == 8) { + OLDAPI(("cmedia_pci: r value is 8!\n")); + r = 7; + } + n = 0; + do { + n++; + m = config->sample_rate*512/1000*(n+2)*(1< 255) { + ddprintf(("cmedia_pci: m > 255; outahere\n")); + break; + } + freq = (m+2)*(F_REF/1000)/(512*(n+2)*(1<sample_rate; + if (diff < 0) { + diff = -diff; + } + if (diff < delta) { + sr = r; + sn = n; + sm = m; + } + } while (n < 31); + r = sr; + n = sn; + m = sm; + ddprintf(("cmedia_pci: m = %d r = %d n = %d\n", m, r, n)); + } + + /* configure device */ + + if (!force) { + stop_dma(port); + /* should mute PCM out, too */ + } + if (asr > -1 || n > -1) { /* new sampling rate */ + if (asr > -1) { + port->config.sample_rate = config->sample_rate; + set_direct(port->card, 0x05, (asr<<5)|(asr<<2), 0xfc); + } + else { + port->config.sample_rate = ((float)m+2.0)*(F_REF/1000.0)/ + (0.512*(n+2.0)*(1<sample_rate = port->config.sample_rate; +#if 1 + /* not exact the frequency supported */ +#else + set_indirect(port->card, 0x24, m, 0xff); + set_indirect(port->card, 0x25, (r<<5)|n, 0xff); + set_indirect(port->card, 0x22, 0x00, 0xff); +#endif + } + } + if (force || config->channels != port->config.channels || + config->format != port->config.format) { + uchar val = 0; + if (config->channels == 2) { + val |= 0x01; /* stereo */ + } + if (config->format != 0x11) { + val |= 0x02; /* 16 bits */ + } + set_direct(port->card, 0x08, (val<<2)|val, 0x0f); /* MCE -- may take time to take effect */ + port->config.channels = config->channels; + port->config.format = config->format; + } + if (force || config->big_endian != port->config.big_endian) { + port->config.big_endian = config->big_endian; + } + if (force || config->buf_header != port->config.buf_header) { + port->config.buf_header = config->buf_header; + } + if (force || config->play_buf_size != port->config.play_buf_size*2) { + port->config.play_buf_size = config->play_buf_size*2; /* because we break it in two */ + } + if (force || config->rec_buf_size != port->config.rec_buf_size*2) { + port->config.rec_buf_size = config->rec_buf_size*2; /* because we break it in two */ + } + +/* here is where we should care about record and playback buffer sizes */ + + ddprintf(("cmedia_pci: play %04lx rec %04lx\n", port->config.play_buf_size/2, + port->config.rec_buf_size/2)); + + port->wr_1 = port->card->low_mem; + port->wr_2 = port->wr_1+port->config.play_buf_size/2; + port->wr_size = port->config.play_buf_size/2; + + port->rd_1 = port->card->low_mem+port->config.play_buf_size; + port->rd_2 = port->rd_1+port->config.rec_buf_size/2; + port->rd_size = port->config.rec_buf_size/2; + + if (!force) { + /* should un-mute PCM out, if we muted it */ + start_dma(port); + } + return err; +} + + +static status_t +pcm_open( + const char * name, + uint32 flags, + void ** cookie) +{ + int ix; + pcm_dev * port = NULL; + char name_buf[256]; + int32 prev_mode; + + ddprintf(("cmedia_pci: pcm_open()\n")); + + *cookie = NULL; + for (ix=0; ixinit_sem); + + prev_mode = port->open_mode; + if ((flags & 3) == O_RDONLY) { + atomic_or(&port->open_mode, kRecord); + } + else if ((flags & 3) == O_WRONLY) { + atomic_or(&port->open_mode, kPlayback); + } + else { + atomic_or(&port->open_mode, kPlayback|kRecord); + } + + if (atomic_add(&port->open_count, 1) == 0) { + + /* initialize device first time */ + + port->card = &cards[ix]; + port->config = default_pcm; + port->config.play_buf_size *= 2; + port->config.rec_buf_size *= 2; + + /* playback */ + + port->wr_lock = 0; + port->dma_a = cards[ix].dma_base; + port->wr_1 = cards[ix].low_mem; + port->wr_2 = cards[ix].low_mem+port->config.play_buf_size/2; + port->wr_size = port->config.play_buf_size/2; + port->write_waiting = 0; + sprintf(name_buf, "WS:%s", port->name); + name_buf[B_OS_NAME_LENGTH-1] = 0; + port->write_sem = create_sem(0, name_buf); + if (port->write_sem < B_OK) { + port->open_count = 0; + return port->write_sem; + } + set_sem_owner(port->write_sem, B_SYSTEM_TEAM); + name_buf[0] = 'W'; name_buf[1] = 'E'; + port->wr_entry = create_sem(1, name_buf); + if (port->wr_entry < B_OK) { + delete_sem(port->write_sem); + port->open_count = 0; + return port->wr_entry; + } + set_sem_owner(port->wr_entry, B_SYSTEM_TEAM); + name_buf[1] = 'T'; + port->wr_time_wait = 0; + port->wr_time_sem = create_sem(0, name_buf); + if (port->wr_time_sem < B_OK) { + delete_sem(port->write_sem); + delete_sem(port->wr_entry); + port->open_count = 0; + return port->wr_time_sem; + } + set_sem_owner(port->wr_time_sem, B_SYSTEM_TEAM); + + /* recording */ + + port->rd_lock = 0; + port->dma_c = cards[ix].dma_base+0x08; + port->rd_1 = cards[ix].low_mem+port->config.play_buf_size; + port->rd_2 = cards[ix].low_mem+port->config.play_buf_size+port->config.rec_buf_size/2; + port->rd_size = port->config.rec_buf_size/2; + port->read_waiting = 0; + name_buf[0] = 'R'; name_buf[1] = 'S'; + port->read_sem = create_sem(0, name_buf); + if (port->read_sem < B_OK) { + delete_sem(port->write_sem); + delete_sem(port->wr_entry); + delete_sem(port->wr_time_sem); + port->open_count = 0; + return port->read_sem; + } + set_sem_owner(port->read_sem, B_SYSTEM_TEAM); + name_buf[0] = 'R'; name_buf[1] = 'E'; + port->rd_entry = create_sem(1, name_buf); + if (port->rd_entry < B_OK) { + delete_sem(port->write_sem); + delete_sem(port->wr_entry); + delete_sem(port->read_sem); + delete_sem(port->wr_time_sem); + port->open_count = 0; + return port->rd_entry; + } + set_sem_owner(port->rd_entry, B_SYSTEM_TEAM); + name_buf[1] = 'T'; + port->rd_time_wait = 0; + port->rd_time_sem = create_sem(0, name_buf); + if (port->rd_time_sem < B_OK) { + delete_sem(port->write_sem); + delete_sem(port->wr_entry); + delete_sem(port->read_sem); + delete_sem(port->wr_time_sem); + delete_sem(port->rd_entry); + port->open_count = 0; + return port->rd_time_sem; + } + set_sem_owner(port->rd_time_sem, B_SYSTEM_TEAM); + + port->rd_time = 0; + port->next_rd_time = 0; + port->wr_time = 0; + + /* old API */ + + port->old_cap_sem = -1; + port->old_play_sem = -1; + + /* configuration */ + + configure_pcm(port, &default_pcm, true); + + /* interrupts */ + KTRACE(); + increment_interrupt_handler(port->card); + + set_direct(port->card, 0x0e, 0x03, 0x03); /* */ + start_dma(port); + + /* initialization is done, let other clients of the driver go */ + } else { + if (prev_mode != port->open_mode) { + pcm_cfg temp = port->config; + temp.play_buf_size /= 2; + temp.rec_buf_size /= 2; + configure_pcm(port, &temp, false); /* change rec/play if needed */ + } + } + release_sem(port->init_sem); + +#if DEBUG + dump_card(&cards[ix]); +#endif + + return B_OK; +} + + +static status_t +pcm_close( + void * cookie) +{ + pcm_dev * port = (pcm_dev *)cookie; + cpu_status cp; + int spin = 0; + + ddprintf(("cmedia_pci: pcm_close()\n")); + + acquire_sem(port->init_sem); + + if (atomic_add(&port->open_count, -1) == 1) { + + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + + /* turn off interrupts */ + stop_dma(port); + set_direct(port->card, 0x0e, 0x00, 0x03); /* */ + + if (port->config.format == 0x11) { + memset((void *)port->wr_1, 0x80, port->config.play_buf_size); /* play silence */ + } + else { + memset((void *)port->wr_1, 0, port->config.play_buf_size); /* play silence */ + } + spin = 1; + + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + + delete_sem(port->write_sem); + delete_sem(port->read_sem); + delete_sem(port->wr_entry); + delete_sem(port->rd_entry); + delete_sem(port->rd_time_sem); + delete_sem(port->wr_time_sem); + port->write_sem = -1; + port->read_sem = -1; + port->wr_entry = -1; + port->rd_entry = -1; + port->rd_time_sem = -1; + port->wr_time_sem = -1; + } + release_sem(port->init_sem); + + if (spin) { + /* wait so we know FIFO gets filled with silence */ + snooze(port->config.play_buf_size*1000/(port->config.sample_rate* + (port->config.format&0xf)*port->config.channels/1000)); + } + return B_OK; +} + + +static status_t +pcm_free( + void * cookie) +{ + cpu_status cp; + pcm_dev * port = (pcm_dev *)cookie; + + ddprintf(("cmedia_pci: pcm_free()\n")); + + acquire_sem(port->init_sem); + + if (((pcm_dev *)cookie)->open_count == 0) { + + /* the last free will actually stop everything */ + + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + + decrement_interrupt_handler(port->card); + + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + } + release_sem(port->init_sem); + + return B_OK; +} + + +static status_t +pcm_control( + void * cookie, + uint32 iop, + void * data, + size_t len) +{ + // declarations for SPDIF settings I/O + static int32 chipinfo[] = { 0,0 }; + uchar reg_value; + char DriverVersion[] = "1.3.2 (Jul 17, 2001)"; + + pcm_dev * port = (pcm_dev *)cookie; + status_t err = B_BAD_VALUE; + pcm_cfg config = port->config; + static float rates[7] = { 48000.0, 44100.0, 32000.0, 22050.0, 16000.0, 11025.0, 8000.0 }; + bool configure = false; + config.play_buf_size /= 2; + config.rec_buf_size /= 2; + + ddprintf(("cmedia_pci: pcm_control()\n")); + + switch (iop) { + case B_AUDIO_GET_AUDIO_FORMAT: + memcpy(data, &config, sizeof(port->config)); + err = B_OK; + break; + case B_AUDIO_GET_PREFERRED_SAMPLE_RATES: + memcpy(data, rates, sizeof(rates)); + err = B_OK; + break; + case B_AUDIO_SET_AUDIO_FORMAT: + memcpy(&config, data, sizeof(config)); + configure = true; + err = B_OK; + break; + case SV_RD_TIME_WAIT: + atomic_add(&port->rd_time_wait, 1); + err = acquire_sem(port->rd_time_sem); + if (err >= B_OK) { + cpu_status cp; + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->rd_lock); + ((sv_timing *)data)->time = port->rd_time; + ((sv_timing *)data)->bytes = port->rd_total; + ((sv_timing *)data)->skipped = port->rd_skipped; + ((sv_timing *)data)->_reserved_[0] = 0xffffffffUL; + release_spinlock(&port->rd_lock); + restore_interrupts(cp); + } + break; + case SV_WR_TIME_WAIT: + atomic_add(&port->wr_time_wait, 1); + err = acquire_sem(port->wr_time_sem); + if (err >= B_OK) { + cpu_status cp; + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->wr_lock); + ((sv_timing *)data)->time = port->wr_time; + ((sv_timing *)data)->bytes = port->wr_total; + ((sv_timing *)data)->skipped = port->wr_skipped; + ((sv_timing *)data)->_reserved_[0] = 0xffffffffUL; + release_spinlock(&port->wr_lock); + restore_interrupts(cp); + } + break; + case SV_SECRET_HANDSHAKE: { + cpu_status cp; + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->wr_lock); + acquire_spinlock(&port->rd_lock); + ((sv_handshake *)data)->wr_time = port->wr_time; + ((sv_handshake *)data)->wr_skipped = port->wr_skipped; + ((sv_handshake *)data)->rd_time = port->rd_time; + ((sv_handshake *)data)->rd_skipped = port->rd_skipped; + ((sv_handshake *)data)->wr_total = port->wr_total; + ((sv_handshake *)data)->rd_total = port->rd_total; + ((sv_handshake *)data)->_reserved_[0] = 0xffffffffUL; + err = B_OK; + release_spinlock(&port->rd_lock); + release_spinlock(&port->wr_lock); + restore_interrupts(cp); + } break; + case SOUND_GET_PARAMS: { + cpu_status cp; + uchar u; + sound_setup * sound = (sound_setup *)data; + err = B_OK; + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + /* Here we get to hard-code the mix/mux values. */ + /* Huh-huh; he said "hard-code"! */ + sound->sample_rate = kHz_44_1; + if (!port->config.big_endian == !B_HOST_IS_BENDIAN) { + sound->playback_format = linear_16bit_big_endian_stereo; + sound->capture_format = linear_16bit_big_endian_stereo; + } + else { + sound->playback_format = linear_16bit_little_endian_stereo; + sound->capture_format = linear_16bit_little_endian_stereo; + } + sound->dither_enable = false; + sound->loop_attn = 0; + sound->loop_enable = 0; + sound->output_boost = 0; + sound->highpass_enable = 0; + /* this is a master control on C-Media... */ + u = get_indirect(port->card, 0x30)>>2; + sound->mono_gain = u&63; + sound->mono_mute = 0; + + /* left channel */ + u = get_indirect(port->card, 0x3d); // Legacy SB compatible Mixer + switch (u) + { + case 0x10: + sound->left.adc_source = line; // record line left + break; + + case 4: + sound->left.adc_source = aux1; // record CD left ?? + break; + + case 1: + sound->left.adc_source = mic; // record mic left + break; + + default: + sound->left.adc_source = loopback; + break; + } + u = get_indirect(port->card, 0x3f)>>4; + sound->left.adc_gain = u&15; + + u = get_direct(port->card, 0x25)<<4; + sound->left.mic_gain_enable = u&16; + + u = get_indirect(port->card, 0x36)>>3; + sound->left.aux1_mix_gain = 31-(u&31); + + u = get_indirect(port->card, 0x3c)<<5; + sound->left.aux1_mix_mute = ~u&128; + + u = get_indirect(port->card, 0x34)>>3; + sound->left.aux2_mix_gain = 31-(u&31); + + u = get_direct(port->card, 0x24); + sound->left.aux2_mix_mute = u&128; + + u = get_indirect(port->card, 0x38)>>3; + sound->left.line_mix_gain = 31-(u&31); + + u = get_indirect(port->card, 0x3c)<<3; + sound->left.line_mix_mute = ~u&128; + + u = get_indirect(port->card, 0x32)>>2; + sound->left.dac_attn = 63-(u&63); + + u = get_direct(port->card, 0x24)<<1; + sound->left.dac_mute = u&128; + + /* right channel */ + u = get_indirect(port->card, 0x3e); + switch (u) + { + case 8: + sound->right.adc_source = line; //record line right + break; + + case 2: + sound->right.adc_source = aux1; // record CD right? + break; + + case 1: + sound->right.adc_source = mic; // record mic right + break; + + default: + sound->right.adc_source = loopback; + break; + } + u = get_indirect(port->card, 0x40)>>4; + sound->right.adc_gain = u&15; + sound->right.mic_gain_enable = sound->left.mic_gain_enable; + u = get_indirect(port->card, 0x37)>>3; + sound->right.aux1_mix_gain = 31-(u&31); + u = get_indirect(port->card, 0x3c)<<6; + sound->right.aux1_mix_mute = ~u&128; + u = get_indirect(port->card, 0x35)>>3; + sound->right.aux2_mix_gain = 31-(u&31); + u = get_direct(port->card, 0x24); + sound->right.aux2_mix_mute = u&128; + u = get_indirect(port->card, 0x39)>>3; + sound->right.line_mix_gain = 31-(u&31); + u = get_indirect(port->card, 0x3c)<<4; + sound->right.line_mix_mute = ~u&128; + u = get_indirect(port->card, 0x33)>>2; + sound->right.dac_attn = 63-(u&63); + u = get_direct(port->card, 0x24)<<1; + sound->right.dac_mute = u&128; + /* done */ + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + } break; + case SOUND_SET_PARAMS: { + cpu_status cp; + uchar u; + sound_setup * sound = (sound_setup *)data; + err = B_OK; + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + /* Here we get to hard-code the mix/mux values. */ + /* Huh-huh; he said "hard-code"! */ + + /* ignore sample rate */ + sound->sample_rate = kHz_44_1; + if (config.sample_rate < 43999 || config.sample_rate > 44201) { + config.sample_rate = 44100.0; + configure = true; + } + /* we only support 16-bit formats */ + if (sound->playback_format == linear_16bit_big_endian_stereo && + sound->capture_format == linear_16bit_big_endian_stereo) { + if (!config.big_endian != !B_HOST_IS_BENDIAN || config.format != 0x2) { + config.big_endian = B_HOST_IS_BENDIAN; + config.format = 0x2; + configure = true; + } + OLDAPI(("same_endian\n")); + } + else if (sound->playback_format == linear_16bit_little_endian_stereo && + sound->capture_format == linear_16bit_little_endian_stereo) { + if (!config.big_endian != !!B_HOST_IS_BENDIAN || config.format != 0x2) { + config.big_endian = !B_HOST_IS_BENDIAN; + config.format = 0x2; + configure = true; + } + OLDAPI(("other_endian\n")); + } + else { + config.big_endian = !!B_HOST_IS_BENDIAN; + configure = true; + OLDAPI(("other format!!!\n")); + } + /* ignore these values */ + sound->dither_enable = false; + sound->loop_attn = 0; + sound->loop_enable = 0; + sound->output_boost = 0; + sound->highpass_enable = 0; + /* this is a stereo control on C-Media... */ + u = (sound->mono_gain>>1)&0x1f; + OLDAPI(("output: %x\n", u)); + set_indirect(port->card, 0x30, u<<3, 0xff); + set_indirect(port->card, 0x31, u<<3, 0xff); + /* left channel */ + switch (sound->left.adc_source) + { + case line: + u = 1<<4; + break; + case aux1: + u = 1<<2; + break; + case mic: + u = 1<<0; + break; + default: + u = 0x15; + break; + } + OLDAPI(("input: %x\n", u)); + set_indirect(port->card, 0x3d, u, 0xff); + u = (sound->left.adc_gain&15); + set_indirect(port->card, 0x3f, u<<4, 0xff); + u = sound->left.mic_gain_enable ? 0 : 0x01; + set_direct(port->card, 0x25, u, 0x01); + u = 31-(sound->left.aux1_mix_gain&31); + OLDAPI(("cd: %x\n", u)); + set_indirect(port->card, 0x36, u<<3, 0xff); + u = sound->left.aux1_mix_mute ? 0 : 0x04; + set_indirect(port->card, 0x3c, u, 0x04); + u = 31-(sound->left.aux2_mix_gain&31); + OLDAPI(("aux2: %x\n", u)); + set_indirect(port->card, 0x34, u<<3, 0xff); + u = sound->left.aux2_mix_mute ? 0x80 : 0; + set_direct(port->card, 0x24, u, 0x80); + u = 31-(sound->left.line_mix_gain&31); + OLDAPI(("line: %x\n", u)); + set_indirect(port->card, 0x38, u<<3, 0xff); + u = sound->left.line_mix_mute ? 0 : 0x10; + set_indirect(port->card, 0x3c, u, 0x10); + u = 63-(sound->left.dac_attn & 63); + OLDAPI(("PCM: %x\n", u)); + set_indirect(port->card, 0x32, u<<2, 0xff); + u = sound->left.dac_mute ? 0x40 : 0; + set_direct(port->card, 0x24, u, 0x40); + /* right channel */ + switch (sound->right.adc_source) { + case line: + u = 1<<3; + break; + case aux1: + u = 1<<1; + break; + case mic: + u = 1<<0; + break; + default: + u = 0x0a; + break; + } + sound->right.mic_gain_enable = sound->left.mic_gain_enable; + set_indirect(port->card, 0x3e, u, 0xff); + u = (sound->right.adc_gain&15); + set_indirect(port->card, 0x40, u<<4, 0xff); + u = sound->right.mic_gain_enable ? 0 : 0x01; + set_direct(port->card, 0x25, u, 0x01); + u = 31-(sound->right.aux1_mix_gain&31); + set_indirect(port->card, 0x37, u<<3, 0xff); + u = sound->right.aux1_mix_mute ? 0 : 0x02; + set_indirect(port->card, 0x3c, u, 0x02); + u = 31-(sound->right.aux2_mix_gain&31); + set_indirect(port->card, 0x35, u<<3, 0xff); + u = sound->right.aux2_mix_mute ? 0x80 : 0; + set_direct(port->card, 0x24, u, 0x80); + u = 31-(sound->right.line_mix_gain&31); + set_indirect(port->card, 0x39, u<<3, 0xff); + u = sound->right.line_mix_mute ? 0 : 0x08; + set_indirect(port->card, 0x3c, u, 0x08); + u = 63-(sound->right.dac_attn & 63); + set_indirect(port->card, 0x33, u<<2, 0xff); + u = sound->right.dac_mute ? 0x40 : 0; + set_direct(port->card, 0x24, u, 0x40); + /* done */ + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + } break; + case SOUND_SET_PLAYBACK_COMPLETION_SEM: + port->old_play_sem = *(sem_id *)data; + err = B_OK; + break; + case SOUND_SET_CAPTURE_COMPLETION_SEM: + port->old_cap_sem = *(sem_id *)data; + err = B_OK; + break; +// case SOUND_GET_PLAYBACK_TIMESTAMP: +// break; +// case SOUND_GET_CAPTURE_TIMESTAMP: +// break; +// case SOUND_DEBUG_ON: +// break; +// case SOUND_DEBUG_OFF: +// break; + case SOUND_UNSAFE_WRITE: { + audio_buffer_header * buf = (audio_buffer_header *)data; + size_t n = buf->reserved_1-sizeof(*buf); + pcm_write(cookie, 0, buf+1, &n); + buf->time = port->wr_time; + buf->sample_clock = port->wr_total/4 * 10000 / 441; + err = release_sem(port->old_play_sem); + } break; + case SOUND_UNSAFE_READ: { + audio_buffer_header * buf = (audio_buffer_header *)data; + size_t n = buf->reserved_1-sizeof(*buf); + pcm_read(cookie, 0, buf+1, &n); + buf->time = port->rd_time; + buf->sample_clock = port->rd_total/4 * 10000 / 441; + err = release_sem(port->old_cap_sem); + } break; + case SOUND_LOCK_FOR_DMA: + err = B_OK; + break; + case SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE: + config.play_buf_size = (int32)data; + configure = true; + err = B_OK; + break; + case SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE: + config.rec_buf_size = (int32)data; + configure = true; + err = B_OK; + break; + case SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE: + *(int32*)data = config.play_buf_size; + err = B_OK; + break; + case SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE: + *(int32*)data = config.rec_buf_size; + err = B_OK; + break; + + +// control ports for SPDIF settings + case SOUND_GET_SPDIF_IN_OUT_LOOPBACK: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x04 ); + if( reg_value && 0x80 ) *(int8 *)data = 1; + err = B_OK; + break; + + case SOUND_SET_SPDIF_IN_OUT_LOOPBACK: + if( *(int8 *)data == 0 ) // disable SPDIF-IN loopback to SPDIF (bypass) + set_direct( port->card, 0x04, 0x00, 0x80 ); + else // enable SPDIF-IN loopback to SPDIF (bypass) + set_direct( port->card, 0x04, 0x80, 0x80 ); + err = B_OK; + break; + + + + + case SOUND_GET_SPDIF_OUT: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x16 ); // Adresse 0x16 + if( reg_value && 0x80 ) *(int8 *)data = 1; + err = B_OK; + break; + + case SOUND_SET_SPDIF_OUT: + if( *(int8 *)data == 0 ) // disable SPDIF-OUT + set_direct( port->card, 0x16, 0x00, 0x80); + else // enable SPDIF-OUT + set_direct( port->card, 0x16, 0x80, 0x80 ); + err = B_OK; + break; + + + + case SOUND_GET_SPDIF_MONITOR: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x24 ); + if( reg_value && 0x01 ) *(int8 *)data = 1; + err = B_OK; + break; + + + case SOUND_SET_SPDIF_MONITOR: + if( *(int8 *)data == 0 ) // disable SPDIF_IN PCM to DAC (CDPlay) + set_direct( port->card, 0x24, 0x00, 0x01 ); + else // enable SPDIF_IN PCM to DAC (CDPlay) + set_direct( port->card, 0x24, 0x01, 0x01 ); + err = B_OK; + break; + + case SOUND_GET_SPDIF_OUT_LEVEL: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x1b ); + if( reg_value && 0x02 ) *(int8 *)data = 1; + err = B_OK; + break; + + case SOUND_SET_SPDIF_OUT_LEVEL: + if( *(int8 *)data == 0 ) // enable SPDIF-OUT optical + set_direct( port->card, 0x1b, 0x00, 0x02 ); + else // enable SPDIF-OUT coaxial + set_direct( port->card, 0x1b, 0x02, 0x02 ); + break; + + case SOUND_GET_SPDIF_IN_FORMAT: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x08 ); // Adresse 0x08 + if( reg_value && 0x80 ) *(int8 *)data = 1; + err = B_OK; + break; + + + case SOUND_SET_SPDIF_IN_FORMAT: + if( *(int8 *)data == 0 ) // disable SPDIF inverse (SPDIF normal) + set_direct( port->card, 0x08, 0x00, 0x80 ); + else // enable SPDIF inverse + set_direct( port->card, 0x08, 0x80, 0x80 ); // Adresse 0x08, Daten 0x80 + err = B_OK; + break; + + + case SOUND_GET_SPDIF_IN_OUT_COPYRIGHT: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x16 ); + if( reg_value && 0x40 ) *(int8 *)data = 1; + err = B_OK; + break; + + case SOUND_SET_SPDIF_IN_OUT_COPYRIGHT: + if( *(int8 *)data == 0 ) // disable SPDIF-IN/OUT copyright protection + set_direct( port->card, 0x16, 0x00, 0x40 ); + else // enable SPDIF-IN/OUT copyright protection + set_direct( port->card, 0x16, 0x40, 0x40 ); + err = B_OK; + break; + + case SOUND_GET_SPDIF_IN_VALIDITY: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x27 ); + if( reg_value && 0x02 ) *(int8 *)data = 1; + err = B_OK; + break; + + case SOUND_SET_SPDIF_IN_VALIDITY: + if( *(int8 *)data == 0 ) // disable SPDIF-IN validity detection + set_direct( port->card, 0x27, 0x00, 0x02 ); + else // enable SPDIF-IN validity detection + set_direct( port->card, 0x27, 0x02, 0x02 ); + err = B_OK; + break; +// control ports for analog settings + + case SOUND_GET_4_CHANNEL_DUPLICATE: + *(int8 *)data = 0; + reg_value = get_direct( port->card, 0x1b ); + if( reg_value && 0x04 ) *(int8 *)data = 1; + +// 0x1b, 0x04, 0x04, /* dual channel mode enable */ +// 0x1a, 0x00, 0x80, /* Double DAC structure disable */ + + err = B_OK; + break; + + case SOUND_SET_4_CHANNEL_DUPLICATE: + if( *(int8 *)data == 0 ) // disable 4 channel analog duplicate mode + set_direct( port->card, 0x1b, 0x00, 0x04 ); + else // enable 4 channel analog duplicate mode + set_direct( port->card, 0x1b, 0x04, 0x04 ); + err = B_OK; + break; +// control ports for additional info + + case SOUND_GET_DEVICE_ID: +// *(int32*)data.vendor_id = cards[0].info.vendor_id; +// *(int32*)data.device_id = cards[0].info.device_id; + +// chipinfo[0] = cards[0].info.vendor_id; + *(int32 *)data = cards[0].info.device_id; + +// memcpy(data, &chipinfo, sizeof(chipinfo)); + err = B_OK; + break; + + case SOUND_GET_INTERNAL_CHIP_ID: + // XXX + break; + + case SOUND_GET_DRIVER_VERSION: + memcpy(data, &DriverVersion, sizeof(DriverVersion)); + break; + + default: + OLDAPI(("cmedia_pci: unknown code %ld\n", iop)); + err = B_BAD_VALUE; + break; + } + if ((err == B_OK) && configure) { + cpu_status cp; + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + err = configure_pcm(port, &config, false); + release_spinlock(&port->card->hardware); + restore_interrupts(cp); + } + return err; +} + + +static void +copy_short_to_float( + float * f, + const short * s, + int c, + int endian) /* endian means float data in big-endian */ +{ + if (endian) { + while (c > 1) { + short sh = B_LENDIAN_TO_HOST_FLOAT(*s); + *(f++) = B_HOST_TO_BENDIAN_FLOAT((float)B_LENDIAN_TO_HOST_INT16(sh)); + s++; + c -= 2; + } + } + else { + while (c > 1) { + short sh = B_LENDIAN_TO_HOST_FLOAT(*s); + *(f++) = B_HOST_TO_LENDIAN_FLOAT((float)B_LENDIAN_TO_HOST_INT16(sh)); + s++; + c -= 2; + } + } +} + + +static void +copy_float_to_short( + short * s, + const float * f, + int c, + int endian) /* endian means float data in big-endian */ +{ + if (endian) { + while (c > 1) { + float fl = *(f++); + *(s++) = B_HOST_TO_LENDIAN_INT16((float)B_BENDIAN_TO_HOST_FLOAT(fl)); + c -= 2; + } + } + else { + while (c > 1) { + float fl = *(f++); + *(s++) = B_HOST_TO_LENDIAN_INT16((float)B_LENDIAN_TO_HOST_FLOAT(fl)); + c -= 2; + } + } +} + + +static void +swap_copy( + short * dest, + const short * src, + int c) +{ + while (c > 1) { + unsigned short sh = *(src++); + *(dest++) = ((sh << 8) | (sh >> 8)); + c -= 2; + } +} + + +static status_t +pcm_read( + void * cookie, + off_t pos, + void * data, + size_t * nread) +{ + pcm_dev * port = (pcm_dev *)cookie; + size_t to_read = *nread; + status_t err; + int block; + cpu_status cp; + int bytes_xferred; + void * hdrptr = data; + int hdrsize = port->config.buf_header; + cmedia_pci_audio_buf_header hdr; + +// ddprintf(("cmedia_pci: pcm_read()\n")); /* we're here */ + + *nread = 0; + data = ((char *)data)+hdrsize; + to_read -= hdrsize; + + err = acquire_sem_etc(port->rd_entry, 1, B_CAN_INTERRUPT, 0); + if (err < B_OK) { + return err; + } + + hdr.capture_time = port->rd_time; + + goto first_time; + + while (to_read > 0) { + /* wait for more data */ + atomic_add(&port->read_waiting, 1); + err = acquire_sem_etc(port->read_sem, 1, B_CAN_INTERRUPT, 0); + if (err < B_OK) { + release_sem(port->rd_entry); + return err; + } + +first_time: /* we need to check whether anything's available first */ + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->rd_lock); + + block = port->rd_size-port->was_read; + + if (port->config.format == 0x24) { + if (block > (to_read>>1)) { /* floats expand by factor 2 */ + block = to_read>>1; + } + } + else if (block > to_read) { + block = to_read; + } + switch (port->config.format) { + case 0x24: /* floats */ + copy_short_to_float((float *)data, (const short *)(port->rd_cur+port->was_read), + block, !B_HOST_IS_LENDIAN == !port->config.big_endian); + bytes_xferred = block * 2; + break; + case 0x02: /* shorts */ + if (!B_HOST_IS_LENDIAN == !port->config.big_endian) { + /* we need to swap */ + swap_copy((short *)data, (const short *)(port->rd_cur+port->was_read), block); + bytes_xferred = block; + break; + } + /* else fall through to default case */ + case 0x11: /* bytes */ + default: + memcpy(data, (void *)(port->rd_cur+port->was_read), block); + bytes_xferred = block; + break; + } + port->was_read += block; + + release_spinlock(&port->rd_lock); + restore_interrupts(cp); + + to_read -= bytes_xferred; + data = ((char *)data)+bytes_xferred; + *nread += bytes_xferred; + } + + /* provide header if requested */ + if (hdrsize > 0) { + ddprintf(("header %d\n", hdrsize)); + *nread += hdrsize; + hdr.capture_size = *nread; + hdr.sample_rate = port->config.sample_rate; + if (hdrsize > sizeof(hdr)) { + hdrsize = sizeof(hdr); + } + memcpy(hdrptr, &hdr, hdrsize); + } + + release_sem(port->rd_entry); + + return B_OK; +} + + +static status_t +pcm_write( + void * cookie, + off_t pos, + const void * data, + size_t * nwritten) +{ + pcm_dev * port = (pcm_dev *)cookie; + status_t err; + cpu_status cp; + int written = 0; + int to_write = *nwritten; /* in play bytes, not input bytes! */ + int block; + int bytes_xferred; + +// ddprintf(("cmedia_pci: pcm_write()\n")); /* we're here */ + + *nwritten = 0; + + err = acquire_sem_etc(port->wr_entry, 1, B_CAN_INTERRUPT, 0); + if (err < B_OK) { + return err; + } + + atomic_add(&port->write_waiting, 1); + if (port->config.format == 0x24) { + to_write >>= 1; /* floats collapse by 2 */ + } + while (to_write > 0) { + + /* wait to write */ + + err = acquire_sem_etc(port->write_sem, 1, B_CAN_INTERRUPT, 0); + if (err < B_OK) { + release_sem(port->wr_entry); + return err; + } + +#if DEBUG + put_cnt++; + { + bigtime_t delta = system_time() - the_time; + if (delta < 1) { + ddprintf(("cmedia_pci: delta %Ld (low!) #%ld\n", delta, put_cnt)); + } + else if (delta > 2000) { + ddprintf(("cmedia_pci: delta %Ld (high!) #%ld\n", delta, put_cnt)); + } + } + if (put_cnt != int_cnt) { + static int last; + if (last != int_cnt-put_cnt) + OLDAPI(("cmedia_pci: %ld mismatch\n", int_cnt-put_cnt)); + last = int_cnt-put_cnt; + } +#endif /* DEBUG */ + + KTRACE(); + cp = disable_interrupts(); + acquire_spinlock(&port->wr_lock); + + block = port->wr_size-port->was_written; + if (block > to_write) { + /* must let next guy in */ + if (atomic_add(&port->write_waiting, -1) > 0) { + release_sem_etc(port->write_sem, 1, B_DO_NOT_RESCHEDULE); + } + else { + atomic_add(&port->write_waiting, 1); /* undo damage */ + } + block = to_write; + } + else if (block < to_write) { + atomic_add(&port->write_waiting, 1); /* we will loop back */ + } + switch (port->config.format) { + case 0x24: /* floats */ + copy_float_to_short((short *)(port->wr_cur+port->was_written), (const float *)data, + block, !B_HOST_IS_LENDIAN == !port->config.big_endian); + bytes_xferred = block * 2; + break; + case 0x02: /* shorts */ + if (!B_HOST_IS_LENDIAN == !port->config.big_endian) { + /* we need to swap */ + swap_copy((short *)(port->wr_cur+port->was_written), (const short *)data, block); + bytes_xferred = block; + break; + } + /* else fall through to default case */ + case 0x11: /* bytes */ + default: + memcpy((void *)(port->wr_cur+port->was_written), data, block); + bytes_xferred = block; + break; + } + port->was_written += block; + port->wr_silence = 0; + + release_spinlock(&port->wr_lock); + restore_interrupts(cp); + + data = ((char *)data)+bytes_xferred; + written += bytes_xferred; + to_write -= block; + } + + *nwritten = written; + release_sem(port->wr_entry); + + return B_OK; +} + + +bool +dma_a_interrupt( + cmedia_pci_dev * dev) +{ + bool ret = false; + pcm_dev * port = &dev->pcm; + volatile uchar * ptr; + uint32 addr; + uint32 offs; + bigtime_t st = system_time(); + int32 ww; + +#if 0 +ddprintf(("cmedia_pci: dma_a 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_a), PCI_IO_RD_32((int)port->dma_a+4))); +#endif +// KTRACE(); /* */ + acquire_spinlock(&port->wr_lock); + + if (port->write_sem < 0) { + kprintf("cmedia_pci: spurious DMA A interrupt!\n"); + release_spinlock(&port->wr_lock); + return false; + } + /* copy possible silence into playback buffer */ + + if (port->was_written > 0 && port->was_written < port->wr_size) { + if (port->config.format == 0x11) { + memset((void *)(port->wr_cur+port->was_written), 0x80, port->wr_size-port->was_written); + } + else { + memset((void *)(port->wr_cur+port->was_written), 0, port->wr_size-port->was_written); + } + } + + /* because the system may be lacking and not hand us the */ + /* interrupt in time, we check which half is currently being */ + /* played, and set the pointer to the other half */ + + addr = PCI_IO_RD_32((uint32)port->dma_a); + if ((offs = addr-(uint32)port->card->low_phys) < port->wr_size) { + ptr = port->wr_2; + } + else { + ptr = port->wr_1; + } + port->wr_total += port->config.play_buf_size/2; + /* compensate for interrupt latency */ + /* assuming 4 byte frames */ + port->wr_time = st-(offs&(port->config.play_buf_size/2-1))*250000LL/(int64)port->config.sample_rate; + if ((ww = atomic_add(&port->wr_time_wait, -1)) > 0) { + release_sem_etc(port->wr_time_sem, 1, B_DO_NOT_RESCHEDULE); + ret = true; + } + else { + atomic_add(&port->wr_time_wait, 1); /* re-set to 0 */ + } + + if (port->wr_cur == ptr) { + port->wr_skipped++; + OLDAPI(("cmedia_pci: write skipped %ld\n", port->wr_skipped)); + } + port->wr_cur = ptr; + port->was_written = 0; + + /* check for client there to write into buffer */ + + if (atomic_add(&port->write_waiting, -1) > 0) { +#if DEBUG + int_cnt++; + the_time = st; +#endif + release_sem_etc(port->write_sem, 1, B_DO_NOT_RESCHEDULE); + ret = true; + } + else { + atomic_add(&port->write_waiting, 1); + /* if none there, fill with silence */ + if (port->wr_silence < port->config.play_buf_size*2) { + if (port->config.format == 0x11) { + memset((void *)ptr, 0x80, port->wr_size); + } + else { + memset((void *)ptr, 0, port->wr_size); + } + port->wr_silence += port->wr_size; + } + } + /* copying will be done in user thread */ + + release_spinlock(&port->wr_lock); + return ret; +} + + +bool +dma_c_interrupt( + cmedia_pci_dev * dev) +{ + bool ret = false; + pcm_dev * port = &dev->pcm; + volatile uchar * ptr; + uint32 addr; + uint32 offs; + int32 rr; + bigtime_t st = system_time(); + + /* mark data as readable in record buffer */ +#if 0 +ddprintf(("cmedia_pci: dma_c 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_c), PCI_IO_RD_32((int)port->dma_c+4))); +#endif +// KTRACE(); /* */ + acquire_spinlock(&port->rd_lock); + + if (port->read_sem < 0) { + kprintf("cmedia_pci: spurious DMA C interrupt!\n"); + release_spinlock(&port->rd_lock); + return false; + } + /* if we lose an interrupt, we automatically avoid constant glitching by setting */ + /* the write pointer based on where the DMA counter is everytime */ + addr = PCI_IO_RD_32((uint32)port->dma_c); + if ((offs = addr-port->config.play_buf_size-(uint32)port->card->low_phys) < port->rd_size) { + ptr = port->rd_2; + } + else { + ptr = port->rd_1; + } + if (port->rd_cur == ptr) { + port->rd_skipped++; + OLDAPI(("cmedia_pci: read skipped %ld\n", port->rd_skipped)); + } + port->rd_total += port->rd_size; + + port->rd_cur = ptr; + port->was_read = 0; + port->rd_time = port->next_rd_time; + /* time stamp when this buffer became available -- compensate for interrupt latency */ + port->next_rd_time = st-(offs&(port->config.rec_buf_size/2-1))*1000000LL/(int64)port->config.sample_rate; + if ((rr = atomic_add(&port->rd_time_wait, -1)) > 0) { + release_sem_etc(port->rd_time_sem, 1, B_DO_NOT_RESCHEDULE); + ret = true; + } + else { + atomic_add(&port->rd_time_wait, 1); /* re-set to 0 */ + } + + if (atomic_add(&port->read_waiting, -1) > 0) { + release_sem_etc(port->read_sem, 1, B_DO_NOT_RESCHEDULE); + ret = true; + } + else { + atomic_add(&port->read_waiting, 1); + } + /* copying will be done in the user thread */ + release_spinlock(&port->rd_lock); + return ret; +} + diff --git a/src/add-ons/kernel/drivers/audio/cmedia/sound.h b/src/add-ons/kernel/drivers/audio/cmedia/sound.h new file mode 100644 index 0000000000..764d42711c --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/cmedia/sound.h @@ -0,0 +1,118 @@ +/* ++++++++++ + $Source: /home/cltien/cvs/BeOS/inc/sound.h,v $ + $Revision: 1.1.1.1 $ + $Author: cltien $ + $Date: 1999/10/12 18:38:08 $ + + Data structures and control calls for using the sound driver ++++++ */ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _SOUND_H +#define _SOUND_H + +#ifndef _DRIVERS_H +#include +#endif + +enum adc_source { + line=0, aux1, mic, loopback +}; + +enum sample_rate { + kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, kHz_18_9, + kHz_32_0, kHz_22_05, kHz_37_8 = 9, kHz_44_1 = 11, kHz_48_0, kHz_33_075, + kHz_9_6, kHz_6_62 +}; + +enum sample_format { + linear_8bit_unsigned_mono = 0, linear_8bit_unsigned_stereo, + ulaw_8bit_companded_mono, ulaw_8bit_companded_stereo, + linear_16bit_little_endian_mono, linear_16bit_little_endian_stereo, + alaw_8bit_companded_mono, alaw_8bit_companded_stereo, + sample_format_reserved_1, sample_format_reserved_2, + adpcm_4bit_mono, adpcm_4bit_stereo, + linear_16bit_big_endian_mono, linear_16bit_big_endian_stereo, + sample_format_reserved_3, sample_format_reserved_4 +}; + +struct channel { + enum adc_source adc_source; /* adc input source */ + char adc_gain; /* 0..15 adc gain, in 1.5 dB steps */ + char mic_gain_enable; /* non-zero enables 20 dB MIC input gain */ + char aux1_mix_gain; /* 0..31 aux1 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */ + char aux1_mix_mute; /* non-zero mutes aux1 mix */ + char aux2_mix_gain; /* 0..31 aux2 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */ + char aux2_mix_mute; /* non-zero mutes aux2 mix */ + char line_mix_gain; /* 0..31 line mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */ + char line_mix_mute; /* non-zero mutes line mix */ + char dac_attn; /* 0..61 dac attenuation, in -1.5 dB steps */ + char dac_mute; /* non-zero mutes dac output */ +}; + +typedef struct sound_setup { + struct channel left; /* left channel setup */ + struct channel right; /* right channel setup */ + enum sample_rate sample_rate; /* sample rate */ + enum sample_format playback_format;/* sample format for playback */ + enum sample_format capture_format; /* sample format for capture */ + char dither_enable; /* non-zero enables dither on 16 => 8 bit */ + char loop_attn; /* 0..64 adc to dac loopback attenuation, in -1.5 dB steps */ + char loop_enable; /* non-zero enables loopback */ + char output_boost; /* zero (2.0 Vpp) non-zero (2.8 Vpp) output level boost */ + char highpass_enable;/* non-zero enables highpass filter in adc */ + char mono_gain; /* 0..64 mono speaker gain */ + char mono_mute; /* non-zero mutes speaker */ +// char spdif_mute; /* non-zero mutes spdif out */ +} sound_setup; + + +/* ----- + control opcodes for sound driver +----- */ + +enum { + SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END, + SOUND_SET_PARAMS, /* 10000 */ + SOUND_SET_PLAYBACK_COMPLETION_SEM, + SOUND_SET_CAPTURE_COMPLETION_SEM, + SOUND_GET_PLAYBACK_TIMESTAMP, /* 10003 */ + SOUND_GET_CAPTURE_TIMESTAMP, + SOUND_DEBUG_ON, + SOUND_DEBUG_OFF, /* 10006 */ + SOUND_UNSAFE_WRITE, + SOUND_UNSAFE_READ, + SOUND_LOCK_FOR_DMA, /* 10009 */ + SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE, + SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE, + SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE, /* 10012 */ + SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE, + +// control ports for SPDIF settings + SOUND_GET_SPDIF_IN_OUT_LOOPBACK, + SOUND_SET_SPDIF_IN_OUT_LOOPBACK, + SOUND_GET_SPDIF_OUT, + SOUND_SET_SPDIF_OUT, + SOUND_GET_SPDIF_MONITOR, + SOUND_SET_SPDIF_MONITOR, + SOUND_GET_SPDIF_OUT_LEVEL, + SOUND_SET_SPDIF_OUT_LEVEL, + SOUND_GET_SPDIF_IN_FORMAT, + SOUND_SET_SPDIF_IN_FORMAT, + SOUND_GET_SPDIF_IN_OUT_COPYRIGHT, + SOUND_SET_SPDIF_IN_OUT_COPYRIGHT, + SOUND_GET_SPDIF_IN_VALIDITY, + SOUND_SET_SPDIF_IN_VALIDITY, +// control ports for analog settings + SOUND_GET_4_CHANNEL_DUPLICATE, + SOUND_SET_4_CHANNEL_DUPLICATE, +// control ports for additional info + SOUND_GET_DEVICE_ID, + SOUND_GET_INTERNAL_CHIP_ID, + SOUND_GET_DRIVER_VERSION +}; + +#endif