* Rewrote controller reset - it now performs a full reset, and does no longer

assume the driver to be in reset when started.
* Major cleanup of the register access: now hda_controller and hda_stream both
  have member functions for this, the OREGx() and REGx() macros are gone.
* Made the register names and definitions more descriptive - the short names
  of the specs are still mentioned in the comments.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25352 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-05-07 16:30:11 +00:00
parent 1a662751d5
commit a941060027
3 changed files with 405 additions and 291 deletions

View File

@ -51,17 +51,85 @@ enum {
};
struct hda_codec;
struct hda_stream;
/*! This structure describes a single HDA compliant
controller. It contains a list of available streams
for use by the codecs contained, and the messaging queue
(verb/response) buffers for communication.
*/
struct hda_controller {
struct pci_info pci_info;
vint32 opened;
const char* devfs_path;
area_id regs_area;
vuint8* regs;
uint32 irq;
uint16 codec_status;
uint32 num_input_streams;
uint32 num_output_streams;
uint32 num_bidir_streams;
uint32 corb_length;
uint32 rirb_length;
uint32 rirb_read_pos;
uint32 corb_write_pos;
area_id corb_rirb_pos_area;
corb_t* corb;
rirb_t* rirb;
uint32* stream_positions;
hda_codec* codecs[HDA_MAX_CODECS + 1];
hda_codec* active_codec;
uint32 num_codecs;
hda_stream* streams[HDA_MAX_STREAMS];
uint8 Read8(uint32 reg)
{
return *(regs + reg);
}
uint16 Read16(uint32 reg)
{
return *(vuint16*)(regs + reg);
}
uint32 Read32(uint32 reg)
{
return *(vuint32*)(regs + reg);
}
void Write8(uint32 reg, uint8 value)
{
*(regs + reg) = value;
}
void Write16(uint32 reg, uint16 value)
{
*(vuint16*)(regs + reg) = value;
}
void Write32(uint32 reg, uint32 value)
{
*(vuint32*)(regs + reg) = value;
}
};
/*! This structure describes a single stream of audio data,
which is can have multiple channels (for stereo or better).
*/
struct hda_stream {
uint32 id; /* HDA controller stream # */
uint32 off; /* HDA I/O/B descriptor offset */
bool running; /* Is this stream active? */
uint32 offset; /* HDA I/O/B descriptor offset */
bool running;
spinlock lock; /* Write lock */
uint32 type;
hda_controller* controller;
uint32 pin_widget; /* PIN Widget ID */
uint32 io_widgets[MAX_IO_WIDGETS]; /* Input/Output Converter Widget ID */
uint32 num_io_widgets;
@ -87,6 +155,36 @@ struct hda_stream {
area_id buffer_area;
area_id buffer_descriptors_area;
uint32 physical_buffer_descriptors; /* BDL physical address */
uint8 Read8(uint32 reg)
{
return controller->Read8(HDAC_STREAM_BASE + offset + reg);
}
uint16 Read16(uint32 reg)
{
return controller->Read16(HDAC_STREAM_BASE + offset + reg);
}
uint8 Read32(uint32 reg)
{
return controller->Read32(HDAC_STREAM_BASE + offset + reg);
}
void Write8(uint32 reg, uint8 value)
{
*(controller->regs + HDAC_STREAM_BASE + offset + reg) = value;
}
void Write16(uint32 reg, uint8 value)
{
*(vuint16*)(controller->regs + HDAC_STREAM_BASE + offset + reg) = value;
}
void Write32(uint32 reg, uint8 value)
{
*(vuint32*)(controller->regs + HDAC_STREAM_BASE + offset + reg) = value;
}
};
struct hda_widget {
@ -174,40 +272,6 @@ struct hda_codec {
struct hda_controller* controller;
};
/*! This structure describes a single HDA compliant
controller. It contains a list of available streams
for use by the codecs contained, and the messaging queue
(verb/response) buffers for communication.
*/
struct hda_controller {
struct pci_info pci_info;
vint32 opened;
const char* devfs_path;
area_id regs_area;
vuint8* regs;
uint32 irq;
uint16 codec_status;
uint32 num_input_streams;
uint32 num_output_streams;
uint32 num_bidir_streams;
uint32 corb_length;
uint32 rirb_length;
uint32 rirb_read_pos;
uint32 corb_write_pos;
area_id corb_rirb_pos_area;
corb_t* corb;
rirb_t* rirb;
uint32* stream_positions;
hda_codec* codecs[HDA_MAX_CODECS + 1];
hda_codec* active_codec;
uint32 num_codecs;
hda_stream* streams[HDA_MAX_STREAMS];
};
/* driver.c */
extern device_hooks gDriverHooks;

View File

@ -14,9 +14,17 @@
#define MAKE_RATE(base, multiply, divide) \
((base == 44100 ? FMT_44_1_BASE_RATE : 0) \
| ((multiply - 1) << FMT_MULTIPLY_RATE_SHIFT) \
| ((divide - 1) << FMT_DIVIDE_RATE_SHIFT))
((base == 44100 ? FORMAT_44_1_BASE_RATE : 0) \
| ((multiply - 1) << FORMAT_MULTIPLY_RATE_SHIFT) \
| ((divide - 1) << FORMAT_DIVIDE_RATE_SHIFT))
#define HDAC_INPUT_STREAM_OFFSET(controller, index) \
((index) * HDAC_STREAM_SIZE)
#define HDAC_OUTPUT_STREAM_OFFSET(controller, index) \
(((controller)->num_input_streams + (index)) * HDAC_STREAM_SIZE)
#define HDAC_BIDIR_STREAM_OFFSET(controller, index) \
(((controller)->num_input_streams + (controller)->num_output_streams \
+ (index)) * HDAC_STREAM_SIZE)
static const struct {
uint32 multi_rate;
@ -67,13 +75,13 @@ stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
if (!stream->running)
return;
status = OREG8(controller, stream->off, STS);
status = stream->Read8(HDAC_STREAM_STATUS);
if (status == 0)
return;
OREG8(controller, stream->off, STS) = status;
stream->Write8(HDAC_STREAM_STATUS, status);
if ((status & STS_BCIS) != 0) {
if ((status & STATUS_BUFFER_COMPLETED) != 0) {
// Buffer Completed Interrupt
acquire_spinlock(&stream->lock);
@ -86,38 +94,38 @@ stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
} else
dprintf("HDA: stream status %x\n", status);
dprintf("hda: stream status %x\n", status);
}
static int32
hda_interrupt_handler(hda_controller* controller)
{
int32 rc = B_HANDLED_INTERRUPT;
int32 handled = B_HANDLED_INTERRUPT;
/* Check if this interrupt is ours */
uint32 intsts = REG32(controller, INTSTS);
if ((intsts & INTSTS_GIS) == 0)
uint32 intrStatus = controller->Read32(HDAC_INTR_STATUS);
if ((intrStatus & INTR_STATUS_GLOBAL) == 0)
return B_UNHANDLED_INTERRUPT;
/* Controller or stream related? */
if (intsts & INTSTS_CIS) {
uint32 stateStatus = REG16(controller, STATESTS);
uint8 rirbStatus = REG8(controller, RIRBSTS);
uint8 corbStatus = REG8(controller, CORBSTS);
if (intrStatus & INTR_STATUS_CONTROLLER) {
uint32 stateStatus = controller->Read16(HDAC_STATE_STATUS);
uint8 rirbStatus = controller->Read8(HDAC_RIRB_STATUS);
uint8 corbStatus = controller->Read8(HDAC_CORB_STATUS);
if (stateStatus) {
/* Detected Codec state change */
REG16(controller, STATESTS) = stateStatus;
controller->Write16(HDAC_STATE_STATUS, stateStatus);
controller->codec_status = stateStatus;
}
/* Check for incoming responses */
if (rirbStatus) {
REG8(controller, RIRBSTS) = rirbStatus;
controller->Write8(HDAC_RIRB_STATUS, rirbStatus);
if (rirbStatus & RIRBSTS_RINTFL) {
uint16 writePos = (REG16(controller, RIRBWP) + 1)
if ((rirbStatus & RIRB_STATUS_RESPONSE) != 0) {
uint16 writePos = (controller->Read16(HDAC_RIRB_WRITE_POS) + 1)
% controller->rirb_length;
for (; controller->rirb_read_pos != writePos;
@ -146,26 +154,26 @@ hda_interrupt_handler(hda_controller* controller)
/* Store response in codec */
codec->responses[codec->response_count++] = response;
release_sem_etc(codec->response_sem, 1, B_DO_NOT_RESCHEDULE);
rc = B_INVOKE_SCHEDULER;
handled = B_INVOKE_SCHEDULER;
}
}
if (rirbStatus & RIRBSTS_OIS)
if ((rirbStatus & RIRB_STATUS_OVERRUN) != 0)
dprintf("hda: RIRB Overflow\n");
}
/* Check for sending errors */
if (corbStatus) {
REG8(controller, CORBSTS) = corbStatus;
controller->Write8(HDAC_CORB_STATUS, corbStatus);
if (corbStatus & CORBSTS_MEI)
if ((corbStatus & CORB_STATUS_MEMORY_ERROR) != 0)
dprintf("hda: CORB Memory Error!\n");
}
}
if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) {
if ((intrStatus & INTR_STATUS_STREAM_MASK) != 0) {
for (uint32 index = 0; index < HDA_MAX_STREAMS; index++) {
if ((intsts & (1 << index)) != 0) {
if ((intrStatus & (1 << index)) != 0) {
if (controller->streams[index]) {
stream_handle_interrupt(controller,
controller->streams[index]);
@ -179,25 +187,80 @@ hda_interrupt_handler(hda_controller* controller)
/* NOTE: See HDA001 => CIS/GIS cannot be cleared! */
return rc;
return handled;
}
static status_t
hda_hw_start(hda_controller* controller)
reset_controller(hda_controller* controller)
{
int timeout = 10;
// stop streams
// TODO: reset controller first? (we currently reset on uninit only)
for (uint32 i = 0; i < controller->num_input_streams; i++) {
controller->Write8(HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
controller->Write8(HDAC_STREAM_STATUS + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
}
for (uint32 i = 0; i < controller->num_output_streams; i++) {
controller->Write8(HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
controller->Write8(HDAC_STREAM_STATUS + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
}
for (uint32 i = 0; i < controller->num_bidir_streams; i++) {
controller->Write8(HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
controller->Write8(HDAC_STREAM_STATUS + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
}
/* Put controller out of reset mode */
REG32(controller, GCTL) |= GCTL_CRST;
// stop DMA
controller->Write8(HDAC_CORB_CONTROL, 0);
controller->Write8(HDAC_RIRB_CONTROL, 0);
do {
// reset DMA position buffer
controller->Write32(HDAC_DMA_POSITION_BASE_LOWER, 0);
controller->Write32(HDAC_DMA_POSITION_BASE_UPPER, 0);
// Set reset bit - it must be asserted for at least 100us
uint32 control = controller->Read32(HDAC_GLOBAL_CONTROL);
controller->Write32(HDAC_GLOBAL_CONTROL, control & ~GLOBAL_CONTROL_RESET);
for (int timeout = 0; timeout < 10; timeout++) {
snooze(100);
} while (--timeout && !(REG32(controller, GCTL) & GCTL_CRST));
return timeout ? B_OK : B_TIMED_OUT;
control = controller->Read32(HDAC_GLOBAL_CONTROL);
if ((control & GLOBAL_CONTROL_RESET) == 0)
break;
}
if ((control & GLOBAL_CONTROL_RESET) != 0) {
dprintf("hda: unable to reset controller\n");
return B_BUSY;
}
// Unset reset bit
control = controller->Read32(HDAC_GLOBAL_CONTROL);
controller->Write32(HDAC_GLOBAL_CONTROL, control | GLOBAL_CONTROL_RESET);
for (int timeout = 0; timeout < 10; timeout++) {
snooze(100);
control = controller->Read32(HDAC_GLOBAL_CONTROL);
if ((control & GLOBAL_CONTROL_RESET) != 0)
break;
}
if ((control & GLOBAL_CONTROL_RESET) == 0) {
dprintf("hda: unable to exit reset\n");
return B_BUSY;
}
// Wait for codecs to finish their own reset (apparently needs more
// time than documented in the specs)
snooze(1000);
return B_OK;
}
@ -217,29 +280,29 @@ init_corb_rirb_pos(hda_controller* controller)
physical_entry pe;
/* Determine and set size of CORB */
corbSize = REG8(controller, CORBSIZE);
if (corbSize & CORBSIZE_CAP_256E) {
corbSize = controller->Read8(HDAC_CORB_SIZE);
if ((corbSize & CORB_SIZE_CAP_256_ENTRIES) != 0) {
controller->corb_length = 256;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbSize & CORBSIZE_CAP_16E) {
controller->Write8(HDAC_CORB_SIZE, CORB_SIZE_256_ENTRIES);
} else if (corbSize & CORB_SIZE_CAP_16_ENTRIES) {
controller->corb_length = 16;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbSize & CORBSIZE_CAP_2E) {
controller->Write8(HDAC_CORB_SIZE, CORB_SIZE_16_ENTRIES);
} else if (corbSize & CORB_SIZE_CAP_2_ENTRIES) {
controller->corb_length = 2;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
controller->Write8(HDAC_CORB_SIZE, CORB_SIZE_2_ENTRIES);
}
/* Determine and set size of RIRB */
rirbSize = REG8(controller, RIRBSIZE);
if (rirbSize & RIRBSIZE_CAP_256E) {
rirbSize = controller->Read8(HDAC_RIRB_SIZE);
if (rirbSize & RIRB_SIZE_CAP_256_ENTRIES) {
controller->rirb_length = 256;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbSize & RIRBSIZE_CAP_16E) {
controller->Write8(HDAC_RIRB_SIZE, RIRB_SIZE_256_ENTRIES);
} else if (rirbSize & RIRB_SIZE_CAP_16_ENTRIES) {
controller->rirb_length = 16;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbSize & RIRBSIZE_CAP_2E) {
controller->Write8(HDAC_RIRB_SIZE, RIRB_SIZE_16_ENTRIES);
} else if (rirbSize & RIRB_SIZE_CAP_2_ENTRIES) {
controller->rirb_length = 2;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_2E;
controller->Write8(HDAC_RIRB_SIZE, RIRB_SIZE_2_ENTRIES);
}
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
@ -267,39 +330,43 @@ init_corb_rirb_pos(hda_controller* controller)
}
/* Program CORB/RIRB for these locations */
REG32(controller, CORBLBASE) = (uint32)pe.address;
REG32(controller, CORBUBASE) = 0;
REG32(controller, RIRBLBASE) = (uint32)pe.address + rirbOffset;
REG32(controller, RIRBUBASE) = 0;
controller->Write32(HDAC_CORB_BASE_LOWER, (uint32)pe.address);
controller->Write32(HDAC_CORB_BASE_UPPER, 0);
controller->Write32(HDAC_RIRB_BASE_LOWER, (uint32)pe.address + rirbOffset);
controller->Write32(HDAC_RIRB_BASE_UPPER, 0);
/* Program DMA position update */
REG32(controller, DMA_POSITION_BASE_LOWER) = (uint32)pe.address + posOffset;
REG32(controller, DMA_POSITION_BASE_UPPER) = 0;
controller->Write32(HDAC_DMA_POSITION_BASE_LOWER,
(uint32)pe.address + posOffset);
controller->Write32(HDAC_DMA_POSITION_BASE_UPPER, 0);
controller->stream_positions = (uint32*)
((uint8*)controller->corb + posOffset);
/* Reset CORB read pointer */
/* NOTE: See HDA011 for corrected procedure! */
REG16(controller, CORBRP) = CORBRP_RST;
controller->Write16(HDAC_CORB_READ_POS, CORB_READ_POS_RESET);
do {
spin(10);
} while ( !(REG16(controller, CORBRP) & CORBRP_RST) );
REG16(controller, CORBRP) = 0;
} while ((controller->Read16(HDAC_CORB_READ_POS)
& CORB_READ_POS_RESET) == 0);
controller->Write16(HDAC_CORB_READ_POS, 0);
/* Reset RIRB write pointer */
REG16(controller, RIRBWP) = RIRBWP_RST;
controller->Write16(HDAC_RIRB_WRITE_POS, RIRB_WRITE_POS_RESET);
/* Generate interrupt for every response */
REG16(controller, RINTCNT) = 1;
controller->Write16(HDAC_RESPONSE_INTR_COUNT, 1);
/* Setup cached read/write indices */
controller->rirb_read_pos = 1;
controller->corb_write_pos = 0;
/* Gentlemen, start your engines... */
REG8(controller, CORBCTL) = CORBCTL_RUN | CORBCTL_MEIE;
REG8(controller, RIRBCTL) = RIRBCTL_DMAEN | RIRBCTL_OIC | RIRBCTL_RINTCTL;
controller->Write8(HDAC_CORB_CONTROL,
CORB_CONTROL_RUN | CORB_CONTROL_MEMORY_ERROR_INTR);
controller->Write8(HDAC_RIRB_CONTROL, RIRB_CONTROL_DMA_ENABLE
| RIRB_CONTROL_OVERRUN_INTR | RIRB_CONTROL_RESPONSE_INTR);
return B_OK;
}
@ -337,17 +404,18 @@ hda_stream_new(hda_audio_group* audioGroup, int type)
stream->buffer_area = B_ERROR;
stream->buffer_descriptors_area = B_ERROR;
stream->type = type;
stream->controller = controller;
switch (type) {
case STREAM_PLAYBACK:
stream->id = 1;
stream->off = controller->num_input_streams * HDAC_SDSIZE;
stream->offset = HDAC_OUTPUT_STREAM_OFFSET(controller, 0);
controller->streams[controller->num_input_streams] = stream;
break;
case STREAM_RECORD:
stream->id = 2;
stream->off = 0;
stream->offset = HDAC_INPUT_STREAM_OFFSET(controller, 0);
controller->streams[0] = stream;
break;
@ -374,22 +442,18 @@ hda_stream_new(hda_audio_group* audioGroup, int type)
status_t
hda_stream_start(hda_controller* controller, hda_stream* stream)
{
stream->buffer_ready_sem = create_sem(0,
stream->type == STREAM_PLAYBACK ? "hda_playback_sem" : "hda_record_sem");
stream->buffer_ready_sem = create_sem(0, stream->type == STREAM_PLAYBACK
? "hda_playback_sem" : "hda_record_sem");
if (stream->buffer_ready_sem < B_OK)
return stream->buffer_ready_sem;
REG32(controller, INTCTL) |= 1 << (stream->off / HDAC_SDSIZE);
OREG8(controller, stream->off, CTL0) |= CTL0_IOCE | CTL0_FEIE | CTL0_DEIE
| CTL0_RUN;
#if 0
while (!(OREG8(controller, stream->off, CTL0) & CTL0_RUN))
snooze(1);
#endif
controller->Write32(HDAC_INTR_CONTROL, controller->Read32(HDAC_INTR_CONTROL)
| (1 << (stream->offset / HDAC_STREAM_SIZE)));
stream->Write8(HDAC_STREAM_CONTROL0, stream->Read8(HDAC_STREAM_CONTROL0)
| CONTROL0_BUFFER_COMPLETED_INTR | CONTROL0_FIFO_ERROR_INTR
| CONTROL0_DESCRIPTOR_ERROR_INTR | CONTROL0_RUN);
stream->running = true;
return B_OK;
}
@ -400,14 +464,11 @@ hda_stream_start(hda_controller* controller, hda_stream* stream)
status_t
hda_stream_stop(hda_controller* controller, hda_stream* stream)
{
OREG8(controller, stream->off, CTL0) &= ~(CTL0_IOCE | CTL0_FEIE | CTL0_DEIE
| CTL0_RUN);
REG32(controller, INTCTL) &= ~(1 << (stream->off / HDAC_SDSIZE));
#if 0
while ((OREG8(controller, stream->off, CTL0) & CTL0_RUN) != 0)
snooze(1);
#endif
stream->Write8(HDAC_STREAM_CONTROL0, stream->Read8(HDAC_STREAM_CONTROL0)
& ~(CONTROL0_BUFFER_COMPLETED_INTR | CONTROL0_FIFO_ERROR_INTR
| CONTROL0_DESCRIPTOR_ERROR_INTR | CONTROL0_RUN));
controller->Write32(HDAC_INTR_CONTROL, controller->Read32(HDAC_INTR_CONTROL)
& ~(1 << (stream->offset / HDAC_STREAM_SIZE)));
stream->running = false;
delete_sem(stream->buffer_ready_sem);
@ -513,14 +574,14 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
/* Configure stream registers */
format = stream->num_channels - 1;
switch (stream->sample_format) {
case B_FMT_8BIT_S: format |= FMT_8BIT; stream->bps = 8; break;
case B_FMT_16BIT: format |= FMT_16BIT; stream->bps = 16; break;
case B_FMT_20BIT: format |= FMT_20BIT; stream->bps = 20; break;
case B_FMT_24BIT: format |= FMT_24BIT; stream->bps = 24; break;
case B_FMT_32BIT: format |= FMT_32BIT; stream->bps = 32; break;
case B_FMT_8BIT_S: format |= FORMAT_8BIT; stream->bps = 8; break;
case B_FMT_16BIT: format |= FORMAT_16BIT; stream->bps = 16; break;
case B_FMT_20BIT: format |= FORMAT_20BIT; stream->bps = 20; break;
case B_FMT_24BIT: format |= FORMAT_24BIT; stream->bps = 24; break;
case B_FMT_32BIT: format |= FORMAT_32BIT; stream->bps = 32; break;
default:
dprintf("%s: Invalid sample format: 0x%lx\n", __func__,
dprintf("hda: Invalid sample format: 0x%lx\n",
stream->sample_format);
break;
}
@ -536,27 +597,27 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, stream->id,
stream->rate, stream->bps);
OREG16(audioGroup->codec->controller, stream->off, FMT) = format;
OREG32(audioGroup->codec->controller, stream->off, BDPL)
= stream->physical_buffer_descriptors;
OREG32(audioGroup->codec->controller, stream->off, BDPU) = 0;
OREG16(audioGroup->codec->controller, stream->off, LVI)
= stream->num_buffers - 1;
stream->Write16(HDAC_STREAM_FORMAT, format);
stream->Write32(HDAC_STREAM_BUFFERS_BASE_LOWER,
stream->physical_buffer_descriptors);
stream->Write32(HDAC_STREAM_BUFFERS_BASE_UPPER, 0);
stream->Write16(HDAC_STREAM_LAST_VALID, stream->num_buffers - 1);
/* total cyclic buffer size in _bytes_ */
OREG32(audioGroup->codec->controller, stream->off, CBL)
= stream->sample_size * stream->num_channels * stream->num_buffers
* stream->buffer_length;
OREG8(audioGroup->codec->controller, stream->off, CTL2) = stream->id << 4;
stream->Write32(HDAC_STREAM_BUFFER_SIZE, stream->sample_size
* stream->num_channels * stream->num_buffers * stream->buffer_length);
stream->Write8(HDAC_STREAM_CONTROL2, stream->id << 4);
REG32(audioGroup->codec->controller, DMA_POSITION_BASE_LOWER)
|= DMA_POSITION_ENABLED;
stream->controller->Write32(HDAC_DMA_POSITION_BASE_LOWER,
stream->controller->Read32(HDAC_DMA_POSITION_BASE_LOWER)
| DMA_POSITION_ENABLED);
hda_codec* codec = audioGroup->codec;
for (uint32 i = 0; i < stream->num_io_widgets; i++) {
verb[0] = MAKE_VERB(audioGroup->codec->addr, stream->io_widgets[i],
verb[0] = MAKE_VERB(codec->addr, stream->io_widgets[i],
VID_SET_CONVERTER_FORMAT, format);
verb[1] = MAKE_VERB(audioGroup->codec->addr, stream->io_widgets[i],
verb[1] = MAKE_VERB(codec->addr, stream->io_widgets[i],
VID_SET_CONVERTER_STREAM_CHANNEL, stream->id << 4);
hda_send_verbs(audioGroup->codec, verb, response, 2);
hda_send_verbs(codec, verb, response, 2);
}
snooze(1000);
@ -572,12 +633,11 @@ hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count)
{
hda_controller *controller = codec->controller;
uint32 sent = 0;
status_t rc;
codec->response_count = 0;
while (sent < count) {
uint32 readPos = REG16(controller, CORBRP);
uint32 readPos = controller->Read16(HDAC_CORB_READ_POS);
uint32 queued = 0;
while (sent < count) {
@ -594,11 +654,11 @@ hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count)
queued++;
}
REG16(controller, CORBWP) = controller->corb_write_pos;
rc = acquire_sem_etc(codec->response_sem, queued, B_RELATIVE_TIMEOUT,
1000ULL * 50);
if (rc < B_OK)
return rc;
controller->Write16(HDAC_CORB_WRITE_POS, controller->corb_write_pos);
status_t status = acquire_sem_etc(codec->response_sem, queued,
B_RELATIVE_TIMEOUT, 50000ULL);
if (status < B_OK)
return status;
}
if (responses != NULL)
@ -612,9 +672,8 @@ hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count)
status_t
hda_hw_init(hda_controller* controller)
{
status_t rc;
uint16 gcap;
uint32 index;
uint16 capabilities;
status_t status;
/* Map MMIO registers */
controller->regs_area = map_physical_memory("hda_hw_regs",
@ -622,58 +681,58 @@ hda_hw_init(hda_controller* controller)
controller->pci_info.u.h0.base_register_sizes[0], B_ANY_KERNEL_ADDRESS,
0, (void**)&controller->regs);
if (controller->regs_area < B_OK) {
rc = controller->regs_area;
status = controller->regs_area;
goto error;
}
/* Absolute minimum hw is online; we can now install interrupt handler */
controller->irq = controller->pci_info.u.h0.interrupt_line;
rc = install_io_interrupt_handler(controller->irq,
status = install_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller, 0);
if (rc != B_OK)
if (status != B_OK)
goto no_irq;
/* show some hw features */
gcap = REG16(controller, GCAP);
dprintf("HDA: HDA v%d.%d, O:%d/I:%d/B:%d, #SDO:%d, 64bit:%s\n",
REG8(controller, VMAJ), REG8(controller, VMIN),
GCAP_OSS(gcap), GCAP_ISS(gcap), GCAP_BSS(gcap),
GCAP_NSDO(gcap) ? GCAP_NSDO(gcap) *2 : 1,
gcap & GCAP_64OK ? "yes" : "no" );
capabilities = controller->Read16(HDAC_GLOBAL_CAP);
controller->num_input_streams = GLOBAL_CAP_INPUT_STREAMS(capabilities);
controller->num_output_streams = GLOBAL_CAP_OUTPUT_STREAMS(capabilities);
controller->num_bidir_streams = GLOBAL_CAP_BIDIR_STREAMS(capabilities);
controller->num_input_streams = GCAP_OSS(gcap);
controller->num_output_streams = GCAP_ISS(gcap);
controller->num_bidir_streams = GCAP_BSS(gcap);
/* show some hw features */
dprintf("hda: HDA v%d.%d, O:%ld/I:%ld/B:%ld, #SDO:%d, 64bit:%s\n",
controller->Read8(HDAC_VERSION_MAJOR),
controller->Read8(HDAC_VERSION_MINOR),
controller->num_output_streams, controller->num_input_streams,
controller->num_bidir_streams,
GLOBAL_CAP_NUM_SDO(capabilities),
GLOBAL_CAP_64BIT(capabilities) ? "yes" : "no");
/* Get controller into valid state */
rc = hda_hw_start(controller);
if (rc != B_OK)
status = reset_controller(controller);
if (status != B_OK)
goto reset_failed;
/* Setup CORB/RIRB/DMA POS */
rc = init_corb_rirb_pos(controller);
if (rc != B_OK)
status = init_corb_rirb_pos(controller);
if (status != B_OK)
goto corb_rirb_failed;
REG16(controller, WAKEEN) = 0x7fff;
controller->Write16(HDAC_WAKE_ENABLE, 0x7fff);
/* Enable controller interrupts */
REG32(controller, INTCTL) = INTCTL_GIE | INTCTL_CIE;
/* Wait for codecs to warm up */
snooze(1000);
controller->Write32(HDAC_INTR_CONTROL, INTR_CONTROL_GLOBAL_ENABLE
| INTR_CONTROL_CONTROLLER_ENABLE);
if (!controller->codec_status) {
rc = ENODEV;
status = ENODEV;
goto corb_rirb_failed;
}
for (index = 0; index < HDA_MAX_CODECS; index++) {
// Create codecs
for (uint32 index = 0; index < HDA_MAX_CODECS; index++) {
if ((controller->codec_status & (1 << index)) != 0)
hda_codec_new(controller, index);
}
for (index = 0; index < HDA_MAX_CODECS; index++) {
for (uint32 index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index]
&& controller->codecs[index]->num_audio_groups > 0) {
controller->active_codec = controller->codecs[index];
@ -684,10 +743,10 @@ hda_hw_init(hda_controller* controller)
if (controller->active_codec != NULL)
return B_OK;
rc = ENODEV;
status = ENODEV;
corb_rirb_failed:
REG32(controller, INTCTL) = 0;
controller->Write32(HDAC_INTR_CONTROL, 0);
reset_failed:
remove_io_interrupt_handler(controller->irq,
@ -699,9 +758,9 @@ no_irq:
controller->regs = NULL;
error:
dprintf("ERROR: %s(%ld)\n", strerror(rc), rc);
dprintf("hda: ERROR: %s(%ld)\n", strerror(status), status);
return rc;
return status;
}
@ -731,17 +790,11 @@ hda_hw_uninit(hda_controller* controller)
/* Stop all audio streams */
hda_hw_stop(controller);
/* Stop CORB/RIRB */
REG8(controller, CORBCTL) = 0;
REG8(controller, RIRBCTL) = 0;
reset_controller(controller);
REG32(controller, CORBLBASE) = 0;
REG32(controller, RIRBLBASE) = 0;
REG32(controller, DMA_POSITION_BASE_LOWER) = 0;
/* Disable interrupts, and remove interrupt handler */
controller->Write32(HDAC_INTR_CONTROL, 0);
/* Disable interrupts, reset controller, and remove interrupt handler */
REG32(controller, INTCTL) = 0;
REG32(controller, GCTL) &= ~GCTL_CRST;
remove_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller);

View File

@ -4,135 +4,132 @@
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
* Axel Dörfler, axeld@pinc-software.de
*/
#ifndef HDAC_REGS_H
#define HDAC_REGS_H
#include <SupportDefs.h>
/* Accessors for HDA controller registers */
#define REG32(controller, reg) (*(vuint32*)((controller)->regs + HDAC_##reg))
#define REG16(controller, reg) (*(vuint16*)((controller)->regs + HDAC_##reg))
#define REG8(controller, reg) (*((controller)->regs + HDAC_##reg))
#define OREG32(controller, stream, reg) \
(*(vuint32*)((controller)->regs + HDAC_SDBASE + (stream) + HDAC_SD_##reg))
#define OREG16(controller, stream, reg) \
(*(vuint16*)((controller)->regs + HDAC_SDBASE + (stream) + HDAC_SD_##reg))
#define OREG8(controller, stream, reg) \
(*((controller)->regs + HDAC_SDBASE + (stream) + HDAC_SD_##reg))
/* Controller register definitions */
#define HDAC_GLOBAL_CAP 0x00 // 16bits, GCAP
#define GLOBAL_CAP_OUTPUT_STREAMS(cap) (((cap) >> 12) & 15)
#define GLOBAL_CAP_INPUT_STREAMS(cap) (((cap) >> 8) & 15)
#define GLOBAL_CAP_BIDIR_STREAMS(cap) (((cap) >> 3) & 15)
#define GLOBAL_CAP_NUM_SDO(cap) ((((cap) >> 1) & 3) ? (cap & 6) : 1)
#define GLOBAL_CAP_64BIT(cap) (((cap) & 1) != 0)
/* Register definitions */
#define HDAC_GCAP 0x00 /* 16bits */
#define GCAP_OSS(gcap) (((gcap) >> 12) & 15)
#define GCAP_ISS(gcap) (((gcap) >> 8) & 15)
#define GCAP_BSS(gcap) (((gcap) >> 3) & 15)
#define GCAP_NSDO(gcap) (((gcap) >> 1) & 3)
#define GCAP_64OK ((gcap) & 1)
#define HDAC_VERSION_MINOR 0x02 // 8bits, VMIN
#define HDAC_VERSION_MAJOR 0x03 // 8bits, VMAJ
#define HDAC_VMIN 0x02 /* 8bits */
#define HDAC_VMAJ 0x03 /* 8bits */
#define HDAC_GLOBAL_CONTROL 0x08 // 32bits, GCTL
#define GLOBAL_CONTROL_UNSOLICITED (1 << 8)
// accept unsolicited responses
#define GLOBAL_CONTROL_FLUSH (1 << 1)
#define GLOBAL_CONTROL_RESET (1 << 0)
#define HDAC_GCTL 0x08 /* 32bits */
#define GCTL_UNSOL (1 << 8) /* Accept Unsolicited responses */
#define GCTL_FCNTRL (1 << 1) /* Flush Control */
#define GCTL_CRST (1 << 0) /* Controller Reset */
#define HDAC_WAKE_ENABLE 0x0c // 16bits, WAKEEN
#define HDAC_STATE_STATUS 0x0e // 16bits, STATESTS
#define HDAC_WAKEEN 0x0c /* 16bits */
#define HDAC_STATESTS 0x0e /* 16bits */
#define HDAC_INTR_CONTROL 0x20 // 32bits, INTCTL
#define INTR_CONTROL_GLOBAL_ENABLE (1 << 31)
#define INTR_CONTROL_CONTROLLER_ENABLE (1 << 30)
#define HDAC_INTCTL 0x20 /* 32bits */
#define INTCTL_GIE (1 << 31) /* Global Interrupt Enable */
#define INTCTL_CIE (1 << 30) /* Controller Interrupt Enable */
#define HDAC_INTR_STATUS 0x24 // 32bits, INTSTS
#define INTR_STATUS_GLOBAL (1 << 31)
#define INTR_STATUS_CONTROLLER (1 << 30)
#define INTR_STATUS_STREAM_MASK 0x3fffffff
#define HDAC_INTSTS 0x24 /* 32bits */
#define INTSTS_GIS (1 << 31) /* Global Interrupt Status */
#define INTSTS_CIS (1 << 30) /* Controller Interrupt Status */
#define HDAC_CORB_BASE_LOWER 0x40 // 32bits, CORBLBASE
#define HDAC_CORB_BASE_UPPER 0x44 // 32bits, CORBUBASE
#define HDAC_CORB_WRITE_POS 0x48 // 16bits, CORBWP
#define HDAC_CORBLBASE 0x40 /* 32bits */
#define HDAC_CORBUBASE 0x44 /* 32bits */
#define HDAC_CORBWP 0x48 /* 16bits */
#define HDAC_CORB_READ_POS 0x4a // 16bits, CORBRP
#define CORB_READ_POS_RESET (1 << 15)
#define HDAC_CORBRP 0x4a /* 16bits */
#define CORBRP_RST (1 << 15)
#define HDAC_CORB_CONTROL 0x4c // 8bits, CORBCTL
#define CORB_CONTROL_RUN (1 << 1)
#define CORB_CONTROL_MEMORY_ERROR_INTR (1 << 0)
#define HDAC_CORBCTL 0x4c /* 8bits */
#define CORBCTL_RUN (1 << 1)
#define CORBCTL_MEIE (1 << 0)
#define HDAC_CORB_STATUS 0x4d // 8bits, CORBSTS
#define CORB_STATUS_MEMORY_ERROR (1 << 0)
#define HDAC_CORBSTS 0x4d /* 8bits */
#define CORBSTS_MEI (1 << 0)
#define HDAC_CORB_SIZE 0x4e // 8bits, CORBSIZE
#define CORB_SIZE_CAP_2_ENTRIES (1 << 4)
#define CORB_SIZE_CAP_16_ENTRIES (1 << 5)
#define CORB_SIZE_CAP_256_ENTRIES (1 << 6)
#define CORB_SIZE_2_ENTRIES 0x00 // 8 byte
#define CORB_SIZE_16_ENTRIES 0x01 // 64 byte
#define CORB_SIZE_256_ENTRIES 0x02 // 1024 byte
#define HDAC_CORBSIZE 0x4e /* 8bits */
#define CORBSIZE_CAP_2E (1 << 4)
#define CORBSIZE_CAP_16E (1 << 5)
#define CORBSIZE_CAP_256E (1 << 6)
#define CORBSIZE_SZ_2E 0
#define CORBSIZE_SZ_16E (1 << 0)
#define CORBSIZE_SZ_256E (1 << 1)
#define HDAC_RIRB_BASE_LOWER 0x50 // 32bits, RIRBLBASE
#define HDAC_RIRB_BASE_UPPER 0x54 // 32bits, RIRBUBASE
#define HDAC_RIRBLBASE 0x50 /* 32bits */
#define HDAC_RIRBUBASE 0x54 /* 32bits */
#define HDAC_RIRB_WRITE_POS 0x58 // 16bits, RIRBWP
#define RIRB_WRITE_POS_RESET (1 << 15)
#define HDAC_RIRBWP 0x58 /* 16bits */
#define RIRBWP_RST (1 << 15)
#define HDAC_RESPONSE_INTR_COUNT 0x5a // 16bits, RINTCNT
#define HDAC_RINTCNT 0x5a /* 16bits */
#define HDAC_RIRB_CONTROL 0x5c // 8bits, RIRBCTL
#define RIRB_CONTROL_OVERRUN_INTR (1 << 2)
#define RIRB_CONTROL_DMA_ENABLE (1 << 1)
#define RIRB_CONTROL_RESPONSE_INTR (1 << 0)
#define HDAC_RIRBCTL 0x5c /* 8bits */
#define RIRBCTL_OIC (1 << 2)
#define RIRBCTL_DMAEN (1 << 1)
#define RIRBCTL_RINTCTL (1 << 0)
#define HDAC_RIRB_STATUS 0x5d // 8bits, RIRBSTS
#define RIRB_STATUS_OVERRUN (1 << 2)
#define RIRB_STATUS_RESPONSE (1 << 0)
#define HDAC_RIRBSTS 0x5d
#define RIRBSTS_OIS (1 << 2)
#define RIRBSTS_RINTFL (1 << 0)
#define HDAC_RIRB_SIZE 0x5e // 8bits, RIRBSIZE
#define RIRB_SIZE_CAP_2_ENTRIES (1 << 4)
#define RIRB_SIZE_CAP_16_ENTRIES (1 << 5)
#define RIRB_SIZE_CAP_256_ENTRIES (1 << 6)
#define RIRB_SIZE_2_ENTRIES 0x00
#define RIRB_SIZE_16_ENTRIES 0x01
#define RIRB_SIZE_256_ENTRIES 0x02
#define HDAC_RIRBSIZE 0x5e /* 8bits */
#define RIRBSIZE_CAP_2E (1 << 4)
#define RIRBSIZE_CAP_16E (1 << 5)
#define RIRBSIZE_CAP_256E (1 << 6)
#define RIRBSIZE_SZ_2E 0
#define RIRBSIZE_SZ_16E (1 << 0)
#define RIRBSIZE_SZ_256E (1 << 1)
#define HDAC_DMA_POSITION_BASE_LOWER 0x70 /* 32bits */
#define HDAC_DMA_POSITION_BASE_UPPER 0x74 /* 32bits */
#define HDAC_DMA_POSITION_BASE_LOWER 0x70 // 32bits, DPLBASE
#define HDAC_DMA_POSITION_BASE_UPPER 0x74 // 32bits, DPUBASE
#define DMA_POSITION_ENABLED 1
#define HDAC_SDBASE 0x80
#define HDAC_SDSIZE 0x20
/* Stream Descriptor Registers */
#define HDAC_STREAM_BASE 0x80
#define HDAC_STREAM_SIZE 0x20
#define HDAC_SD_CTL0 0x00 /* 8bits */
#define CTL0_SRST (1 << 0)
#define CTL0_RUN (1 << 1)
#define CTL0_IOCE (1 << 2)
#define CTL0_FEIE (1 << 3)
#define CTL0_DEIE (1 << 4)
#define HDAC_SD_CTL1 0x01 /* 8bits */
#define HDAC_SD_CTL2 0x02 /* 8bits */
#define CTL2_DIR (1 << 3)
#define CTL2_TP (1 << 2)
#define HDAC_SD_STS 0x03 /* 8bits */
#define STS_BCIS (1 << 2)
#define STS_FIFOE (1 << 3)
#define STS_DESE (1 << 4)
#define STS_FIFORDY (1 << 5)
#define HDAC_SD_LPIB 0x04 /* 32bits */
#define HDAC_SD_CBL 0x08 /* 32bits */
#define HDAC_SD_LVI 0x0C /* 16bits */
#define HDAC_SD_FIFOS 0x10 /* 16bits */
#define HDAC_SD_FMT 0x12 /* 16bits */
#define FMT_8BIT (0 << 4)
#define FMT_16BIT (1 << 4)
#define FMT_20BIT (2 << 4)
#define FMT_24BIT (3 << 4)
#define FMT_32BIT (4 << 4)
#define FMT_44_1_BASE_RATE (1 << 14)
#define FMT_MULTIPLY_RATE_SHIFT 11
#define FMT_DIVIDE_RATE_SHIFT 8
#define HDAC_SD_BDPL 0x18 /* 32bits */
#define HDAC_SD_BDPU 0x1C /* 32bits */
#define HDAC_STREAM_CONTROL0 0x00 // 8bits, CTL0
#define CONTROL0_RESET (1 << 0)
#define CONTROL0_RUN (1 << 1)
#define CONTROL0_BUFFER_COMPLETED_INTR (1 << 2)
#define CONTROL0_FIFO_ERROR_INTR (1 << 3)
#define CONTROL0_DESCRIPTOR_ERROR_INTR (1 << 4)
#define HDAC_STREAM_CONTROL1 0x01 // 8bits, CTL1
#define HDAC_STREAM_CONTROL2 0x02 // 8bits, CTL2
#define CONTROL2_STREAM_MASK 0xf0
#define CONTROL2_STREAM_SHIFT 4
#define CONTROL2_BIDIR (1 << 3)
#define CONTROL2_TRAFFIC_PRIORITY (1 << 2)
#define CONTROL2_STRIPE_SDO_MASK 0x03
#define HDAC_STREAM_STATUS 0x03 // 8bits, STS
#define STATUS_BUFFER_COMPLETED (1 << 2)
#define STATUS_FIFO_ERROR (1 << 3)
#define STATUS_DESCRIPTOR_ERROR (1 << 4)
#define STATUS_FIFO_READY (1 << 5)
#define HDAC_STREAM_POSITION 0x04 // 32bits, LPIB
#define HDAC_STREAM_BUFFER_SIZE 0x08 // 32bits, CBL
#define HDAC_STREAM_LAST_VALID 0x0c // 16bits, LVI
#define HDAC_STREAM_FIFO_SIZE 0x10 // 16bits, FIFOS
#define HDAC_STREAM_FORMAT 0x12 // 16bits, FMT
#define FORMAT_8BIT (0 << 4)
#define FORMAT_16BIT (1 << 4)
#define FORMAT_20BIT (2 << 4)
#define FORMAT_24BIT (3 << 4)
#define FORMAT_32BIT (4 << 4)
#define FORMAT_44_1_BASE_RATE (1 << 14)
#define FORMAT_MULTIPLY_RATE_SHIFT 11
#define FORMAT_DIVIDE_RATE_SHIFT 8
#define HDAC_STREAM_BUFFERS_BASE_LOWER 0x18 // 32bits, BDPL
#define HDAC_STREAM_BUFFERS_BASE_UPPER 0x1c // 32bits, BDPU
typedef uint32 corb_t;
typedef struct {
@ -140,8 +137,8 @@ typedef struct {
uint32 flags;
} rirb_t;
#define RESPONSE_FLAGS_CODEC_MASK 0x0000000f
#define RESPONSE_FLAGS_UNSOLICITED (1 << 4)
#define RESPONSE_FLAGS_CODEC_MASK 0x0000000f
#define RESPONSE_FLAGS_UNSOLICITED (1 << 4)
typedef struct {
uint64 address;