* 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
This commit is contained in:
Jérôme Duval 2009-07-28 22:36:13 +00:00
parent c212b8f649
commit d936f5e31f
3 changed files with 43 additions and 36 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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);