Author: bonefish Date: 2009-12-16 21:39:54 +0100 (Wed, 16 Dec 2009) New Revision: 34684 Changeset: http://dev.haiku-os.org/changeset/34684/haiku Modified: haiku/trunk/src/add-ons/kernel/drivers/audio/hda/driver.h haiku/trunk/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp Log: Try to detect whether the stream's DMA position is broken and switch to using the LPIB, if it is. Modified: haiku/trunk/src/add-ons/kernel/drivers/audio/hda/driver.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/audio/hda/driver.h 2009-12-16 20:13:14 UTC (rev 34683) +++ haiku/trunk/src/add-ons/kernel/drivers/audio/hda/driver.h 2009-12-16 20:39:54 UTC (rev 34684) @@ -164,6 +164,9 @@ area_id buffer_descriptors_area; uint32 physical_buffer_descriptors; /* BDL physical address */ + int32 incorrect_position_count; + bool use_dma_position; + uint8 Read8(uint32 reg) { return controller->Read8(HDAC_STREAM_BASE + offset + reg); Modified: haiku/trunk/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp 2009-12-16 20:13:14 UTC (rev 34683) +++ haiku/trunk/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp 2009-12-16 20:39:54 UTC (rev 34684) @@ -111,12 +111,25 @@ return false; } + // Normally we should use the DMA position for the stream. Apparently there + // are broken chipsets, which don't support it correctly. If we detect this, + // we switch to using the LPIB instead. The link position is ahead of the + // DMA position for recording and behind for playback streams, but just + // for determining the currently active buffer, it should be good enough. + if (stream->use_dma_position && stream->incorrect_position_count >= 32) { + dprintf("hda: DMA position for stream (id:%ld) seems to be broken. " + "Switching to using LPIB.\n", stream->id); + stream->use_dma_position = false; + } + // Determine the buffer we're switching to. Some chipsets seem to trigger // the interrupt before the DMA position in memory has been updated. We // round it, so we still get the right buffer. - uint32 dmaPosition = controller->stream_positions[index * 2]; - uint32 bufferIndex = (dmaPosition + stream->buffer_size / 2) - / stream->buffer_size; + uint32 dmaPosition = stream->use_dma_position + ? controller->stream_positions[index * 2] + : stream->Read32(HDAC_STREAM_POSITION); + uint32 bufferIndex = ((dmaPosition + stream->buffer_size / 2) + / stream->buffer_size) % stream->num_buffers; // get the current recording/playing position and the system time uint32 linkBytePosition = stream->Read32(HDAC_STREAM_POSITION); @@ -142,6 +155,11 @@ // update stream playing/recording state and notify buffer_exchange() acquire_spinlock(&stream->lock); + if (bufferIndex == (stream->buffer_cycle + 1) % stream->num_buffers) + stream->incorrect_position_count = 0; + else + stream->incorrect_position_count++; + stream->real_time = now; stream->frames_count += framesProcessed; stream->buffer_cycle = bufferIndex; @@ -464,6 +482,8 @@ stream->type = type; stream->controller = controller; stream->warn_count = 0; + stream->incorrect_position_count = 0; + stream->use_dma_position = true; switch (type) { case STREAM_PLAYBACK: