* stream_handle_interrupt():

- Round the DMA position for the buffer cycle computation. Apparently some
    chipsets trigger the interrupt before the position has been updated.
  - Don't just assume that stream->buffer_length frames have been processed
    at that time. Use the exact stream position at that time. This makes the
    performance time computation more precise and immune to the interrupt
    being delayed.
* Init hda_stream::frames_count.

Audio skips on I/O seem to be gone for me, now. Not obviously motivated skips
still happen.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34671 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2009-12-15 18:38:00 +00:00
parent cd803f23e1
commit e0a187dd59
2 changed files with 57 additions and 29 deletions

View File

@ -57,7 +57,7 @@ struct hda_codec;
struct hda_stream;
struct hda_multi;
/*! This structure describes a single HDA compliant
/*! 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.
@ -91,7 +91,7 @@ struct hda_controller {
hda_stream* streams[HDA_MAX_STREAMS];
sem_id buffer_ready_sem;
uint8 Read8(uint32 reg)
{
return *(regs + reg);
@ -140,20 +140,22 @@ struct hda_stream {
uint32 io_widgets[MAX_IO_WIDGETS]; /* Input/Output Converter Widget ID */
uint32 num_io_widgets;
uint32 sample_rate;
uint32 sample_rate;
uint32 sample_format;
uint32 num_buffers;
uint32 num_channels;
uint32 buffer_length; /* size of buffer in samples */
uint32 buffer_length; /* size of buffer in samples */
uint32 buffer_size; /* actual (aligned) size of buffer in bytes */
uint32 sample_size;
uint8* buffers[STREAM_MAX_BUFFERS];
/* Virtual addresses for buffer */
uint32 physical_buffers[STREAM_MAX_BUFFERS];
/* Physical addresses for buffer */
volatile bigtime_t real_time;
volatile uint64 frames_count;
uint32 last_link_frame_position;
volatile int32 buffer_cycle;
uint32 rate, bps; /* Samplerate & bits per sample */
@ -275,16 +277,16 @@ struct hda_codec {
uint8 revision;
uint8 stepping;
uint8 addr;
uint32 quirks;
sem_id response_sem;
uint32 responses[MAX_CODEC_RESPONSES];
uint32 response_count;
uint32 unsol_responses[MAX_CODEC_UNSOL_RESPONSES];
uint32 unsol_response_count;
hda_audio_group* audio_groups[HDA_MAX_AUDIO_GROUPS];
uint32 num_audio_groups;
@ -314,7 +316,7 @@ struct hda_multi {
hda_audio_group *group;
hda_multi_mixer_control controls[MULTI_MAX_CONTROLS];
uint32 control_count;
multi_channel_info chans[MULTI_MAX_CHANNELS];
uint32 output_channel_count;
uint32 input_channel_count;

View File

@ -8,8 +8,11 @@
*/
#include "driver.h"
#include "hda_controller_defs.h"
#include <algorithm>
#include "driver.h"
#include "hda_codec_defs.h"
@ -89,13 +92,10 @@ static bool
stream_handle_interrupt(hda_controller* controller, hda_stream* stream,
uint32 index)
{
uint8 status;
uint32 position, bufferSize;
if (!stream->running)
return false;
status = stream->Read8(HDAC_STREAM_STATUS);
uint8 status = stream->Read8(HDAC_STREAM_STATUS);
if (status == 0)
return false;
@ -111,16 +111,40 @@ stream_handle_interrupt(hda_controller* controller, hda_stream* stream,
return false;
}
position = controller->stream_positions[index * 2];
bufferSize = ALIGN(stream->sample_size * stream->num_channels
* stream->buffer_length, 128);
// 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;
// Buffer Completed Interrupt
// get the current recording/playing position and the system time
uint32 linkBytePosition = stream->Read32(HDAC_STREAM_POSITION);
bigtime_t now = system_time();
// compute the frame position for the byte position
uint32 linkFramePosition = 0;
while (linkBytePosition >= stream->buffer_size) {
linkFramePosition += stream->buffer_length;
linkBytePosition -= stream->buffer_size;
}
linkFramePosition += std::min(
linkBytePosition / (stream->num_channels * stream->sample_size),
stream->buffer_length);
// compute the number of frames processed since the previous interrupt
int32 framesProcessed = (int32)linkFramePosition
- (int32)stream->last_link_frame_position;
if (framesProcessed < 0)
framesProcessed += stream->num_buffers * stream->buffer_length;
stream->last_link_frame_position = linkFramePosition;
// update stream playing/recording state and notify buffer_exchange()
acquire_spinlock(&stream->lock);
stream->real_time = system_time();
stream->frames_count += stream->buffer_length;
stream->buffer_cycle = position / bufferSize;
stream->real_time = now;
stream->frames_count += framesProcessed;
stream->buffer_cycle = bufferIndex;
release_spinlock(&stream->lock);
@ -489,6 +513,9 @@ hda_stream_start(hda_controller* controller, hda_stream* stream)
{
dprintf("hda_stream_start() offset %lx\n", stream->offset);
stream->frames_count = 0;
stream->last_link_frame_position = 0;
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)
@ -566,9 +593,8 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
}
/* Calculate size of buffer (aligned to 128 bytes) */
uint32 bufferSize = stream->sample_size * stream->num_channels
* stream->buffer_length;
bufferSize = ALIGN(bufferSize, 128);
stream->buffer_size = ALIGN(stream->buffer_length * stream->num_channels
* stream->sample_size, 128);
dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld, **********\n",
stream->sample_size, stream->num_channels, stream->buffer_length);
@ -576,7 +602,7 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
stream->rate, stream->bps, format, stream->sample_format);
/* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */
uint32 alloc = bufferSize * stream->num_buffers;
uint32 alloc = stream->buffer_size * stream->num_buffers;
alloc = PAGE_ALIGN(alloc);
/* Allocate memory for buffers */
@ -599,9 +625,9 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
/* Store pointers (both virtual/physical) */
for (uint32 index = 0; index < stream->num_buffers; index++) {
stream->buffers[index] = buffer + (index * bufferSize);
stream->buffers[index] = buffer + (index * stream->buffer_size);
stream->physical_buffers[index] = bufferPhysicalAddress
+ (index * bufferSize);
+ (index * stream->buffer_size);
}
/* Now allocate BDL for buffer range */
@ -638,7 +664,7 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
bufferDescriptors->lower = stream->physical_buffers[index];
bufferDescriptors->upper = 0;
fragments++;
bufferDescriptors->length = bufferSize;
bufferDescriptors->length = stream->buffer_size;
bufferDescriptors->ioc = 1;
// we want an interrupt after every buffer
}
@ -650,7 +676,7 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
stream->Write32(HDAC_STREAM_BUFFERS_BASE_UPPER, 0);
stream->Write16(HDAC_STREAM_LAST_VALID, fragments);
/* total cyclic buffer size in _bytes_ */
stream->Write32(HDAC_STREAM_BUFFER_SIZE, bufferSize
stream->Write32(HDAC_STREAM_BUFFER_SIZE, stream->buffer_size
* stream->num_buffers);
stream->Write8(HDAC_STREAM_CONTROL2, stream->id << CONTROL2_STREAM_SHIFT);