* Shuffled the functions a bit around to separate private from public functions.

* Renamed some functions, added comments.
* Minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24182 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-02-29 17:04:35 +00:00
parent c607395208
commit abf12dd83f

View File

@ -58,7 +58,254 @@ next_corb(hda_controller *controller)
}
// #pragma mark -
//! Called with interrupts off
static void
stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
{
uint8 status;
if (!stream->running)
return;
status = OREG8(controller, stream->off, STS);
if (status == 0)
return;
OREG8(controller, stream->off, STS) = status;
if ((status & STS_BCIS) != 0) {
// Buffer Completed Interrupt
acquire_spinlock(&stream->lock);
stream->real_time = system_time();
stream->frames_count += stream->buffer_length;
stream->buffer_cycle = (stream->buffer_cycle + 1)
% stream->num_buffers;
release_spinlock(&stream->lock);
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
} else
dprintf("HDA: stream status %x\n", status);
}
static int32
hda_interrupt_handler(hda_controller* controller)
{
int32 rc = B_HANDLED_INTERRUPT;
/* Check if this interrupt is ours */
uint32 intsts = REG32(controller, INTSTS);
if ((intsts & INTSTS_GIS) == 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 (stateStatus) {
/* Detected Codec state change */
REG16(controller, STATESTS) = stateStatus;
controller->codec_status = stateStatus;
}
/* Check for incoming responses */
if (rirbStatus) {
REG8(controller, RIRBSTS) = rirbStatus;
if (rirbStatus & RIRBSTS_RINTFL) {
uint16 writePos = (REG16(controller, RIRBWP) + 1)
% controller->rirb_length;
for (; controller->rirb_read_pos != writePos;
controller->rirb_read_pos = next_rirb(controller)) {
uint32 response = current_rirb(controller).response;
uint32 responseFlags = current_rirb(controller).flags;
uint32 cad = responseFlags & RESPONSE_FLAGS_CODEC_MASK;
hda_codec* codec = controller->codecs[cad];
if ((responseFlags & RESPONSE_FLAGS_UNSOLICITED) != 0) {
dprintf("hda: Unsolicited response: %08lx/%08lx\n",
response, responseFlags);
continue;
}
if (codec == NULL) {
dprintf("hda: Response for unknown codec %ld: "
"%08lx/%08lx\n", cad, response, responseFlags);
continue;
}
if (codec->response_count >= MAX_CODEC_RESPONSES) {
dprintf("hda: too many responses received for codec %ld"
": %08lx/%08lx!\n", cad, response, responseFlags);
continue;
}
/* 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;
}
}
if (rirbStatus & RIRBSTS_OIS)
dprintf("hda: RIRB Overflow\n");
}
/* Check for sending errors */
if (corbStatus) {
REG8(controller, CORBSTS) = corbStatus;
if (corbStatus & CORBSTS_MEI)
dprintf("hda: CORB Memory Error!\n");
}
}
if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) {
for (uint32 index = 0; index < HDA_MAX_STREAMS; index++) {
if ((intsts & (1 << index)) != 0) {
if (controller->streams[index]) {
stream_handle_interrupt(controller,
controller->streams[index]);
} else {
dprintf("hda: Stream interrupt for unconfigured stream "
"%d!\n", index);
}
}
}
}
/* NOTE: See HDA001 => CIS/GIS cannot be cleared! */
return rc;
}
static status_t
hda_hw_start(hda_controller* controller)
{
int timeout = 10;
// TODO: reset controller first? (we currently reset on uninit only)
/* Put controller out of reset mode */
REG32(controller, GCTL) |= GCTL_CRST;
do {
snooze(100);
} while (--timeout && !(REG32(controller, GCTL) & GCTL_CRST));
return timeout ? B_OK : B_TIMED_OUT;
}
/*! Allocates and initializes the Command Output Ring Buffer (CORB), and
Response Input Ring Buffer (RIRB) to the maximum supported size, and also
the DMA position buffer.
Programs the controller hardware to make use of these buffers (the DMA
positioning is actually enabled in hda_stream_setup_buffers()).
*/
static status_t
init_corb_rirb_pos(hda_controller* controller)
{
uint32 memSize, rirbOffset, posOffset;
uint8 corbSize, rirbSize, posSize;
status_t rc = B_OK;
physical_entry pe;
/* Determine and set size of CORB */
corbSize = REG8(controller, CORBSIZE);
if (corbSize & CORBSIZE_CAP_256E) {
controller->corb_length = 256;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbSize & CORBSIZE_CAP_16E) {
controller->corb_length = 16;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbSize & CORBSIZE_CAP_2E) {
controller->corb_length = 2;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
}
/* Determine and set size of RIRB */
rirbSize = REG8(controller, RIRBSIZE);
if (rirbSize & RIRBSIZE_CAP_256E) {
controller->rirb_length = 256;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbSize & RIRBSIZE_CAP_16E) {
controller->rirb_length = 16;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbSize & RIRBSIZE_CAP_2E) {
controller->rirb_length = 2;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_2E;
}
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
rirbOffset = (controller->corb_length * sizeof(corb_t) + 0x7f) & ~0x7f;
posOffset = (rirbOffset + controller->rirb_length * sizeof(rirb_t) + 0x7f)
& 0x7f;
posSize = 8 * (controller->num_input_streams
+ controller->num_output_streams + controller->num_bidir_streams);
memSize = (posOffset + posSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
/* Allocate memory area */
controller->corb_rirb_pos_area = create_area("hda corb/rirb/pos",
(void**)&controller->corb, B_ANY_KERNEL_ADDRESS, memSize,
B_CONTIGUOUS, 0);
if (controller->corb_rirb_pos_area < 0)
return controller->corb_rirb_pos_area;
/* Rirb is after corb+aligment */
controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirbOffset);
if ((rc = get_memory_map(controller->corb, memSize, &pe, 1)) != B_OK) {
delete_area(controller->corb_rirb_pos_area);
return rc;
}
/* 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;
/* Program DMA position update */
REG32(controller, DMA_POSITION_BASE_LOWER) = (uint32)pe.address + posOffset;
REG32(controller, 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;
do {
spin(10);
} while ( !(REG16(controller, CORBRP) & CORBRP_RST) );
REG16(controller, CORBRP) = 0;
/* Reset RIRB write pointer */
REG16(controller, RIRBWP) = RIRBWP_RST;
/* Generate interrupt for every response */
REG16(controller, RINTCNT) = 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;
return B_OK;
}
// #pragma mark - public stream functions
void
@ -113,6 +360,9 @@ hda_stream_new(hda_controller* controller, int type)
}
/*! Starts a stream's DMA engine, and enables generating and receiving
interrupts for this stream.
*/
status_t
hda_stream_start(hda_controller* controller, hda_stream* stream)
{
@ -136,38 +386,9 @@ hda_stream_start(hda_controller* controller, hda_stream* stream)
}
//! Called with interrupts off
static void
hda_stream_check_intr(hda_controller* controller, hda_stream* stream)
{
uint8 status;
if (!stream->running)
return;
status = OREG8(controller, stream->off, STS);
if (status == 0)
return;
OREG8(controller, stream->off, STS) = status;
if ((status & STS_BCIS) != 0) {
// Buffer Completed Interrupt
acquire_spinlock(&stream->lock);
stream->real_time = system_time();
stream->frames_count += stream->buffer_length;
stream->buffer_cycle = (stream->buffer_cycle + 1)
% stream->num_buffers;
release_spinlock(&stream->lock);
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
} else
dprintf("HDA: stream status %x\n", status);
}
/*! Stops the stream's DMA engine, and turns off interrupts for this
stream.
*/
status_t
hda_stream_stop(hda_controller* controller, hda_stream* stream)
{
@ -330,7 +551,7 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
}
// #pragma mark -
// #pragma mark - public controller functions
status_t
@ -374,215 +595,6 @@ hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count)
}
static int32
hda_interrupt_handler(hda_controller* controller)
{
int32 rc = B_HANDLED_INTERRUPT;
/* Check if this interrupt is ours */
uint32 intsts = REG32(controller, INTSTS);
if ((intsts & INTSTS_GIS) == 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 (stateStatus) {
/* Detected Codec state change */
REG16(controller, STATESTS) = stateStatus;
controller->codec_status = stateStatus;
}
/* Check for incoming responses */
if (rirbStatus) {
REG8(controller, RIRBSTS) = rirbStatus;
if (rirbStatus & RIRBSTS_RINTFL) {
uint16 writePos = (REG16(controller, RIRBWP) + 1)
% controller->rirb_length;
for (; controller->rirb_read_pos != writePos;
controller->rirb_read_pos = next_rirb(controller)) {
uint32 response = current_rirb(controller).response;
uint32 responseFlags = current_rirb(controller).flags;
uint32 cad = responseFlags & RESPONSE_FLAGS_CODEC_MASK;
hda_codec* codec = controller->codecs[cad];
if ((responseFlags & RESPONSE_FLAGS_UNSOLICITED) != 0) {
dprintf("hda: Unsolicited response: %08lx/%08lx\n",
response, responseFlags);
continue;
}
if (codec == NULL) {
dprintf("hda: Response for unknown codec %ld: "
"%08lx/%08lx\n", cad, response, responseFlags);
continue;
}
if (codec->response_count >= MAX_CODEC_RESPONSES) {
dprintf("hda: too many responses received for codec %ld"
": %08lx/%08lx!\n", cad, response, responseFlags);
continue;
}
/* 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;
}
}
if (rirbStatus & RIRBSTS_OIS)
dprintf("%s: RIRB Overflow\n", __func__);
}
/* Check for sending errors */
if (corbStatus) {
REG8(controller, CORBSTS) = corbStatus;
if (corbStatus & CORBSTS_MEI)
dprintf("%s: CORB Memory Error!\n", __func__);
}
}
if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) {
int index;
for (index = 0; index < HDA_MAX_STREAMS; index++) {
if ((intsts & (1 << index)) != 0) {
if (controller->streams[index])
hda_stream_check_intr(controller, controller->streams[index]);
else {
dprintf("%s: Stream interrupt for unconfigured stream "
"%d!\n", __func__, index);
}
}
}
}
/* NOTE: See HDA001 => CIS/GIS cannot be cleared! */
return rc;
}
static status_t
hda_hw_start(hda_controller* controller)
{
int timeout = 10;
/* Put controller out of reset mode */
REG32(controller, GCTL) |= GCTL_CRST;
do {
snooze(100);
} while (--timeout && !(REG32(controller, GCTL) & GCTL_CRST));
return timeout ? B_OK : B_TIMED_OUT;
}
static status_t
hda_hw_corb_rirb_pos_init(hda_controller* controller)
{
uint32 memSize, rirbOffset, posOffset;
uint8 corbSize, rirbSize, posSize;
status_t rc = B_OK;
physical_entry pe;
/* Determine and set size of CORB */
corbSize = REG8(controller, CORBSIZE);
if (corbSize & CORBSIZE_CAP_256E) {
controller->corb_length = 256;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbSize & CORBSIZE_CAP_16E) {
controller->corb_length = 16;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbSize & CORBSIZE_CAP_2E) {
controller->corb_length = 2;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
}
/* Determine and set size of RIRB */
rirbSize = REG8(controller, RIRBSIZE);
if (rirbSize & RIRBSIZE_CAP_256E) {
controller->rirb_length = 256;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbSize & RIRBSIZE_CAP_16E) {
controller->rirb_length = 16;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbSize & RIRBSIZE_CAP_2E) {
controller->rirb_length = 2;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_2E;
}
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
rirbOffset = (controller->corb_length * sizeof(corb_t) + 0x7f) & ~0x7f;
posOffset = (rirbOffset + controller->rirb_length * sizeof(rirb_t) + 0x7f)
& 0x7f;
posSize = 8 * (controller->num_input_streams
+ controller->num_output_streams + controller->num_bidir_streams);
memSize = (posOffset + posSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
/* Allocate memory area */
controller->corb_rirb_pos_area = create_area("hda corb/rirb/pos",
(void**)&controller->corb, B_ANY_KERNEL_ADDRESS, memSize,
B_CONTIGUOUS, 0);
if (controller->corb_rirb_pos_area < 0)
return controller->corb_rirb_pos_area;
/* Rirb is after corb+aligment */
controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirbOffset);
if ((rc = get_memory_map(controller->corb, memSize, &pe, 1)) != B_OK) {
delete_area(controller->corb_rirb_pos_area);
return rc;
}
/* 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;
/* Program DMA position update */
REG32(controller, DMA_POSITION_BASE_LOWER) = (uint32)pe.address + posOffset;
REG32(controller, 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;
do {
spin(10);
} while ( !(REG16(controller, CORBRP) & CORBRP_RST) );
REG16(controller, CORBRP) = 0;
/* Reset RIRB write pointer */
REG16(controller, RIRBWP) = RIRBWP_RST;
/* Generate interrupt for every response */
REG16(controller, RINTCNT) = 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;
return B_OK;
}
// #pragma mark -
/*! Setup hardware for use; detect codecs; etc */
status_t
hda_hw_init(hda_controller* controller)
@ -626,7 +638,7 @@ hda_hw_init(hda_controller* controller)
goto reset_failed;
/* Setup CORB/RIRB/DMA POS */
rc = hda_hw_corb_rirb_pos_init(controller);
rc = init_corb_rirb_pos(controller);
if (rc != B_OK)
goto corb_rirb_failed;
@ -714,7 +726,7 @@ hda_hw_uninit(hda_controller* controller)
REG32(controller, RIRBLBASE) = 0;
REG32(controller, DMA_POSITION_BASE_LOWER) = 0;
/* Disable interrupts and remove interrupt handler */
/* Disable interrupts, reset controller, and remove interrupt handler */
REG32(controller, INTCTL) = 0;
REG32(controller, GCTL) &= ~GCTL_CRST;
remove_io_interrupt_handler(controller->irq,