From d936f5e31fa2e40a9da45dd4e129153a82b75d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Tue, 28 Jul 2009 22:36:13 +0000 Subject: [PATCH] * now uses only one buffer_ready semaphore, this way the buffer exchange is done asap, useful when rates/formats are different between playback and record. * checked what offsets my hardware my hardware really had: it affected only playback, and was 192 for 16 bits and 256 for 20/24 bits. With these values, playback and record are crystal clear. As I can't find any references for such offset values anywhere, sorry it's not supposed to work out of the box on all hardware. Maybe we could adjust the offset at runtime. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31875 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/audio/hda/driver.h | 11 ++-- .../drivers/audio/hda/hda_controller.cpp | 62 ++++++++++--------- .../drivers/audio/hda/hda_multi_audio.cpp | 6 +- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/add-ons/kernel/drivers/audio/hda/driver.h b/src/add-ons/kernel/drivers/audio/hda/driver.h index 9e643231d4..2c3c55ef4d 100644 --- a/src/add-ons/kernel/drivers/audio/hda/driver.h +++ b/src/add-ons/kernel/drivers/audio/hda/driver.h @@ -88,7 +88,8 @@ struct hda_controller { uint32 num_codecs; hda_stream* streams[HDA_MAX_STREAMS]; - + sem_id buffer_ready_sem; + uint8 Read8(uint32 reg) { return *(regs + reg); @@ -147,10 +148,10 @@ struct hda_stream { /* Virtual addresses for buffer */ uint32 physical_buffers[STREAM_MAX_BUFFERS]; /* Physical addresses for buffer */ - sem_id buffer_ready_sem; - bigtime_t real_time; - uint64 frames_count; - int32 buffer_cycle; + + volatile bigtime_t real_time; + volatile uint64 frames_count; + volatile int32 buffer_cycle; uint32 rate, bps; /* Samplerate & bits per sample */ diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp index 3cee7fb413..6bddce1c43 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp @@ -111,11 +111,13 @@ stream_handle_interrupt(hda_controller* controller, hda_stream* stream) stream->real_time = system_time(); stream->frames_count += stream->buffer_length; - stream->buffer_cycle = ((position / bufferSize) + 1) % stream->num_buffers; + stream->buffer_cycle = position / bufferSize; release_spinlock(&stream->lock); - release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); + release_sem_etc(controller->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); + + //dprintf("stream_handle_interrupt %d %d %ld\n", stream->id, stream->buffer_cycle, position); } @@ -389,9 +391,6 @@ init_corb_rirb_pos(hda_controller* controller) void hda_stream_delete(hda_stream* stream) { - if (stream->buffer_ready_sem >= B_OK) - delete_sem(stream->buffer_ready_sem); - if (stream->buffer_area >= B_OK) delete_area(stream->buffer_area); @@ -411,7 +410,6 @@ hda_stream_new(hda_audio_group* audioGroup, int type) if (stream == NULL) return NULL; - stream->buffer_ready_sem = B_ERROR; stream->buffer_area = B_ERROR; stream->buffer_descriptors_area = B_ERROR; stream->type = type; @@ -464,11 +462,7 @@ status_t hda_stream_start(hda_controller* controller, hda_stream* stream) { dprintf("hda_stream_start() offset %lx\n", stream->offset); - 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; - + 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) @@ -494,8 +488,6 @@ hda_stream_stop(hda_controller* controller, hda_stream* stream) & ~(1 << (stream->offset / HDAC_STREAM_SIZE))); stream->running = false; - delete_sem(stream->buffer_ready_sem); - stream->buffer_ready_sem = -1; return B_OK; } @@ -547,19 +539,19 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream, } } - // Stream interrupts seem to arrive too early on most HDA - // so we adjust buffer descriptors to take this into account - uint32 offset; - if (format & FORMAT_44_1_BASE_RATE) { - offset = (stream->sample_size * stream->num_channels) * stream->rate - / 44100; - } else { - offset = (stream->sample_size * stream->num_channels) * stream->rate - / 48000; - } - if (stream->controller->pci_info.vendor_id != INTEL_VENDORID) - offset *= 32; - + // Stream interrupts seem to arrive too early on most HDA + // so we adjust buffer descriptors to take this into account + // TODO check on other vendors, uncomment last line in stream_handle_interrupt() + // Tested only on Intel ICH8 + uint32 offset = 0; + if (stream->type == STREAM_PLAYBACK) { + if (stream->sample_size == 2) + offset = 6; + else if (stream->sample_size > 2) + offset = 8; + } + offset *= 32; + /* Calculate size of buffer (aligned to 128 bytes) */ bufferSize = stream->sample_size * stream->num_channels * stream->buffer_length; @@ -567,8 +559,8 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream, dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld, offset %ld **********\n", stream->sample_size, stream->num_channels, stream->buffer_length, offset); - dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld F=0x%x\n", __func__, stream->id, - stream->rate, stream->bps, format); + dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld F=0x%x (0x%lx)\n", __func__, stream->id, + stream->rate, stream->bps, format, stream->sample_format); /* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */ alloc = bufferSize * stream->num_buffers; @@ -854,12 +846,21 @@ hda_hw_init(hda_controller* controller) break; } } + + controller->buffer_ready_sem = create_sem(0, "hda_buffer_sem"); + if (controller->buffer_ready_sem < B_OK) { + dprintf("hda: failed to create semaphore\n"); + status = ENODEV; + goto corb_rirb_failed; + } if (controller->active_codec != NULL) return B_OK; dprintf("hda: no active codec\n"); status = ENODEV; + + delete_sem(controller->buffer_ready_sem); corb_rirb_failed: controller->Write32(HDAC_INTR_CONTROL, 0); @@ -905,6 +906,11 @@ hda_hw_uninit(hda_controller* controller) /* Stop all audio streams */ hda_hw_stop(controller); + + if (controller->buffer_ready_sem >= B_OK) { + delete_sem(controller->buffer_ready_sem); + controller->buffer_ready_sem = B_ERROR; + } reset_controller(controller); diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp index b7c667f26a..292946e73d 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp @@ -847,7 +847,7 @@ buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data) #endif /* do playback */ - err = acquire_sem_etc(audioGroup->playback_stream->buffer_ready_sem, + err = acquire_sem_etc(audioGroup->codec->controller->buffer_ready_sem, 1, B_CAN_INTERRUPT, 0); if (err != B_OK) { dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__, @@ -858,7 +858,7 @@ buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data) status = disable_interrupts(); acquire_spinlock(&audioGroup->playback_stream->lock); - buffer_info.playback_buffer_cycle = audioGroup->playback_stream->buffer_cycle; + buffer_info.playback_buffer_cycle = (audioGroup->playback_stream->buffer_cycle) % audioGroup->playback_stream->num_buffers; buffer_info.played_real_time = audioGroup->playback_stream->real_time; buffer_info.played_frames_count = audioGroup->playback_stream->frames_count; @@ -866,7 +866,7 @@ buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data) if (audioGroup->record_stream) { acquire_spinlock(&audioGroup->record_stream->lock); - buffer_info.record_buffer_cycle = audioGroup->record_stream->buffer_cycle; + buffer_info.record_buffer_cycle = (audioGroup->record_stream->buffer_cycle - 1) % audioGroup->record_stream->num_buffers; buffer_info.recorded_real_time = audioGroup->record_stream->real_time; buffer_info.recorded_frames_count = audioGroup->record_stream->frames_count; release_spinlock(&audioGroup->record_stream->lock);