#include "driver.h"
#include "hardware.h"
#include <drivers/ISA.h>
static isa_module_info* gISA;
static void
hw_codec_write_byte(sb16_dev_t* dev, uint8 value)
{
int i;
for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
if (!(gISA->read_io_8(dev->port + SB16_CODEC_WRITE_STATUS) & 0x80))
break;
}
gISA->write_io_8(dev->port + SB16_CODEC_WRITE_DATA, value);
}
static int
hw_codec_read_byte(sb16_dev_t* dev)
{
int i;
for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
if (gISA->read_io_8(dev->port + SB16_CODEC_READ_STATUS) & 0x80)
break;
}
return gISA->read_io_8(dev->port + SB16_CODEC_READ_DATA);
}
static void
hw_codec_reg_write(sb16_dev_t* dev, uint8 index, uint8 value)
{
gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
gISA->write_io_8(dev->port + SB16_MIXER_DATA, value);
}
static int
hw_codec_reg_read(sb16_dev_t* dev, uint8 index)
{
gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
return gISA->read_io_8(dev->port + SB16_MIXER_DATA);
}
static int
hw_codec_read_version(sb16_dev_t* dev)
{
int minor, major;
hw_codec_write_byte(dev, SB16_CODEC_VERSION);
major = hw_codec_read_byte(dev);
minor = hw_codec_read_byte(dev);
dprintf("%s: SB16 version %d.%d\n", __func__, major, minor);
return (major << 8) + minor;
}
#if 0
static void
hw_codec_read_irq_setup(sb16_dev_t* dev)
{
int mask = hw_codec_reg_read(dev, SB16_IRQ_SETUP);
dev->irq = 5;
if (mask & 0x01)
dev->irq = 2;
if (mask & 0x02)
dev->irq = 5;
if (mask & 0x04)
dev->irq = 7;
if (mask & 0x08)
dev->irq = 10;
}
static void
hw_codec_read_dma_setup(sb16_dev_t* dev)
{
int mask = hw_codec_reg_read(dev, SB16_DMA_SETUP);
dev->dma8 = 1;
if (mask & 0x01)
dev->dma8 = 0;
if (mask & 0x02)
dev->dma8 = 1;
if (mask & 0x08)
dev->dma8 = 3;
dev->dma16 = dev->dma8;
if (mask & 0x20)
dev->dma16 = 5;
if (mask & 0x40)
dev->dma16 = 6;
if (mask & 0x80)
dev->dma16 = 7;
}
#endif
static void
hw_codec_write_irq_setup(sb16_dev_t* dev)
{
int mask = 0x02;
if (dev->irq == 2)
mask = 0x01;
if (dev->irq == 5)
mask = 0x02;
if (dev->irq == 7)
mask = 0x04;
if (dev->irq == 10)
mask = 0x08;
hw_codec_reg_write(dev, SB16_IRQ_SETUP, mask);
}
static void
hw_codec_write_dma_setup(sb16_dev_t* dev)
{
hw_codec_reg_write(dev, SB16_DMA_SETUP, (1 << dev->dma8) | (1 << dev->dma16));
}
static int32
hw_codec_inth(void* cookie)
{
sb16_dev_t* dev = (sb16_dev_t*)cookie;
int32 rc = B_UNHANDLED_INTERRUPT;
int status = hw_codec_reg_read(dev, SB16_IRQ_STATUS);
if (status & 0x03) {
rc = B_HANDLED_INTERRUPT;
if (status & 0x01)
gISA->read_io_8(dev->port + SB16_CODEC_ACK_8_BIT);
if (status & 0x02)
gISA->read_io_8(dev->port + SB16_CODEC_ACK_16_BIT);
if (dev->irq >= 8)
gISA->write_io_8(0xa0, 0x20);
gISA->write_io_8(0x20, 0x20);
if (((dev->playback_stream.bits >> 3) & status) != 0) {
sb16_stream_buffer_done(&dev->playback_stream);
rc = B_INVOKE_SCHEDULER;
}
if (((dev->record_stream.bits >> 3) & status) != 0) {
sb16_stream_buffer_done(&dev->record_stream);
rc = B_INVOKE_SCHEDULER;
}
if ((status & 0x04) != 0) {
rc = B_INVOKE_SCHEDULER;
}
}
return rc;
}
static status_t
hw_codec_reset(sb16_dev_t* dev)
{
int times, delay;
for (times = 0; times < 10; times++) {
gISA->write_io_8(dev->port + SB16_CODEC_RESET, 1);
for (delay = 0; delay < SB16_CODEC_RESET_DELAY; delay++)
gISA->read_io_8(dev->port + SB16_CODEC_RESET);
gISA->write_io_8(dev->port + SB16_CODEC_RESET, 0);
if (hw_codec_read_byte(dev) == 0xaa)
return B_OK;
}
return B_IO_ERROR;
}
static status_t
hw_codec_detect(sb16_dev_t* dev)
{
status_t rc;
if ((rc=hw_codec_reset(dev)) == B_OK) {
if (hw_codec_read_version(dev) >= 0x400) {
hw_codec_write_irq_setup(dev);
hw_codec_write_dma_setup(dev);
rc = B_OK;
} else {
rc = B_BAD_VALUE;
}
}
return rc;
}
status_t
sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc)
{
return B_OK;
}
status_t
sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s)
{
return B_OK;
}
status_t
sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s)
{
return B_OK;
}
void
sb16_stream_buffer_done(sb16_stream_t* stream)
{
}
status_t
sb16_hw_init(sb16_dev_t* dev)
{
status_t rc;
if ((rc=get_module(B_ISA_MODULE_NAME, (module_info**)&gISA)) != B_OK)
return rc;
if ((rc=hw_codec_detect(dev)) == B_OK) {
if ((rc=gISA->lock_isa_dma_channel(dev->dma8)) == B_OK &&
(rc=gISA->lock_isa_dma_channel(dev->dma16)) == B_OK) {
rc = install_io_interrupt_handler(dev->irq, hw_codec_inth, dev, 0);
}
}
return rc;
}
void
sb16_hw_stop(sb16_dev_t* dev)
{
}
void
sb16_hw_uninit(sb16_dev_t* dev)
{
remove_io_interrupt_handler(dev->irq, hw_codec_inth, dev);
if (gISA != NULL) {
gISA->unlock_isa_dma_channel(dev->dma8);
if (dev->dma8 != dev->dma16)
gISA->unlock_isa_dma_channel(dev->dma16);
put_module(B_ISA_MODULE_NAME);
}
}