* 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:
@@ -88,6 +88,7 @@ struct hda_controller {
|
|||||||
uint32 num_codecs;
|
uint32 num_codecs;
|
||||||
|
|
||||||
hda_stream* streams[HDA_MAX_STREAMS];
|
hda_stream* streams[HDA_MAX_STREAMS];
|
||||||
|
sem_id buffer_ready_sem;
|
||||||
|
|
||||||
uint8 Read8(uint32 reg)
|
uint8 Read8(uint32 reg)
|
||||||
{
|
{
|
||||||
@@ -147,10 +148,10 @@ struct hda_stream {
|
|||||||
/* Virtual addresses for buffer */
|
/* Virtual addresses for buffer */
|
||||||
uint32 physical_buffers[STREAM_MAX_BUFFERS];
|
uint32 physical_buffers[STREAM_MAX_BUFFERS];
|
||||||
/* Physical addresses for buffer */
|
/* Physical addresses for buffer */
|
||||||
sem_id buffer_ready_sem;
|
|
||||||
bigtime_t real_time;
|
volatile bigtime_t real_time;
|
||||||
uint64 frames_count;
|
volatile uint64 frames_count;
|
||||||
int32 buffer_cycle;
|
volatile int32 buffer_cycle;
|
||||||
|
|
||||||
uint32 rate, bps; /* Samplerate & bits per sample */
|
uint32 rate, bps; /* Samplerate & bits per sample */
|
||||||
|
|
||||||
|
|||||||
@@ -111,11 +111,13 @@ stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
|
|||||||
|
|
||||||
stream->real_time = system_time();
|
stream->real_time = system_time();
|
||||||
stream->frames_count += stream->buffer_length;
|
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_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
|
void
|
||||||
hda_stream_delete(hda_stream* stream)
|
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)
|
if (stream->buffer_area >= B_OK)
|
||||||
delete_area(stream->buffer_area);
|
delete_area(stream->buffer_area);
|
||||||
|
|
||||||
@@ -411,7 +410,6 @@ hda_stream_new(hda_audio_group* audioGroup, int type)
|
|||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
stream->buffer_ready_sem = B_ERROR;
|
|
||||||
stream->buffer_area = B_ERROR;
|
stream->buffer_area = B_ERROR;
|
||||||
stream->buffer_descriptors_area = B_ERROR;
|
stream->buffer_descriptors_area = B_ERROR;
|
||||||
stream->type = type;
|
stream->type = type;
|
||||||
@@ -464,10 +462,6 @@ status_t
|
|||||||
hda_stream_start(hda_controller* controller, hda_stream* stream)
|
hda_stream_start(hda_controller* controller, hda_stream* stream)
|
||||||
{
|
{
|
||||||
dprintf("hda_stream_start() offset %lx\n", stream->offset);
|
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)
|
controller->Write32(HDAC_INTR_CONTROL, controller->Read32(HDAC_INTR_CONTROL)
|
||||||
| (1 << (stream->offset / HDAC_STREAM_SIZE)));
|
| (1 << (stream->offset / HDAC_STREAM_SIZE)));
|
||||||
@@ -494,8 +488,6 @@ hda_stream_stop(hda_controller* controller, hda_stream* stream)
|
|||||||
& ~(1 << (stream->offset / HDAC_STREAM_SIZE)));
|
& ~(1 << (stream->offset / HDAC_STREAM_SIZE)));
|
||||||
|
|
||||||
stream->running = false;
|
stream->running = false;
|
||||||
delete_sem(stream->buffer_ready_sem);
|
|
||||||
stream->buffer_ready_sem = -1;
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -547,18 +539,18 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream interrupts seem to arrive too early on most HDA
|
// Stream interrupts seem to arrive too early on most HDA
|
||||||
// so we adjust buffer descriptors to take this into account
|
// so we adjust buffer descriptors to take this into account
|
||||||
uint32 offset;
|
// TODO check on other vendors, uncomment last line in stream_handle_interrupt()
|
||||||
if (format & FORMAT_44_1_BASE_RATE) {
|
// Tested only on Intel ICH8
|
||||||
offset = (stream->sample_size * stream->num_channels) * stream->rate
|
uint32 offset = 0;
|
||||||
/ 44100;
|
if (stream->type == STREAM_PLAYBACK) {
|
||||||
} else {
|
if (stream->sample_size == 2)
|
||||||
offset = (stream->sample_size * stream->num_channels) * stream->rate
|
offset = 6;
|
||||||
/ 48000;
|
else if (stream->sample_size > 2)
|
||||||
}
|
offset = 8;
|
||||||
if (stream->controller->pci_info.vendor_id != INTEL_VENDORID)
|
}
|
||||||
offset *= 32;
|
offset *= 32;
|
||||||
|
|
||||||
/* Calculate size of buffer (aligned to 128 bytes) */
|
/* Calculate size of buffer (aligned to 128 bytes) */
|
||||||
bufferSize = stream->sample_size * stream->num_channels
|
bufferSize = stream->sample_size * stream->num_channels
|
||||||
@@ -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",
|
dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld, offset %ld **********\n",
|
||||||
stream->sample_size, stream->num_channels, stream->buffer_length, offset);
|
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,
|
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->rate, stream->bps, format, stream->sample_format);
|
||||||
|
|
||||||
/* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */
|
/* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */
|
||||||
alloc = bufferSize * stream->num_buffers;
|
alloc = bufferSize * stream->num_buffers;
|
||||||
@@ -855,12 +847,21 @@ hda_hw_init(hda_controller* controller)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
if (controller->active_codec != NULL)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
dprintf("hda: no active codec\n");
|
dprintf("hda: no active codec\n");
|
||||||
status = ENODEV;
|
status = ENODEV;
|
||||||
|
|
||||||
|
delete_sem(controller->buffer_ready_sem);
|
||||||
|
|
||||||
corb_rirb_failed:
|
corb_rirb_failed:
|
||||||
controller->Write32(HDAC_INTR_CONTROL, 0);
|
controller->Write32(HDAC_INTR_CONTROL, 0);
|
||||||
|
|
||||||
@@ -906,6 +907,11 @@ hda_hw_uninit(hda_controller* controller)
|
|||||||
/* Stop all audio streams */
|
/* Stop all audio streams */
|
||||||
hda_hw_stop(controller);
|
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);
|
reset_controller(controller);
|
||||||
|
|
||||||
/* Disable interrupts, and remove interrupt handler */
|
/* Disable interrupts, and remove interrupt handler */
|
||||||
|
|||||||
@@ -847,7 +847,7 @@ buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* do playback */
|
/* 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);
|
1, B_CAN_INTERRUPT, 0);
|
||||||
if (err != B_OK) {
|
if (err != B_OK) {
|
||||||
dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__,
|
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();
|
status = disable_interrupts();
|
||||||
acquire_spinlock(&audioGroup->playback_stream->lock);
|
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_real_time = audioGroup->playback_stream->real_time;
|
||||||
buffer_info.played_frames_count = audioGroup->playback_stream->frames_count;
|
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) {
|
if (audioGroup->record_stream) {
|
||||||
acquire_spinlock(&audioGroup->record_stream->lock);
|
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_real_time = audioGroup->record_stream->real_time;
|
||||||
buffer_info.recorded_frames_count = audioGroup->record_stream->frames_count;
|
buffer_info.recorded_frames_count = audioGroup->record_stream->frames_count;
|
||||||
release_spinlock(&audioGroup->record_stream->lock);
|
release_spinlock(&audioGroup->record_stream->lock);
|
||||||
|
|||||||
Reference in New Issue
Block a user