* added an ALIGN macro for easing readability and use it for 128 alignments
* use HDAC_STREAM_POSITION register value to check the current buffer cycle in interrupt handler * added B_FULL_LOCK flags for area allocation, not sure it's handled but at least more correct * buffer descriptors now use a low and high address fields * applied a byte mask on format * enabled PCI bus mastering if not yet done * the PCI space register TCSEL (Traffic Class Select Register, which sets PCI express QOS) is now reset to TC0 (clear 0-2 bits) as needed on some boards like mine to ensure good playback Playback is finally working correctly here on ICH8 HDA! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26847 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
28fd262f0e
commit
1708513ca8
@ -26,6 +26,7 @@
|
|||||||
(((controller)->num_input_streams + (controller)->num_output_streams \
|
(((controller)->num_input_streams + (controller)->num_output_streams \
|
||||||
+ (index)) * HDAC_STREAM_SIZE)
|
+ (index)) * HDAC_STREAM_SIZE)
|
||||||
|
|
||||||
|
#define ALIGN(size, align) (((size) + align - 1) & ~(align - 1))
|
||||||
#define PAGE_ALIGN(size) (((size) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1))
|
#define PAGE_ALIGN(size) (((size) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1))
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
@ -73,6 +74,7 @@ static void
|
|||||||
stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
|
stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
|
||||||
{
|
{
|
||||||
uint8 status;
|
uint8 status;
|
||||||
|
uint32 position, bufferSize;
|
||||||
|
|
||||||
if (!stream->running)
|
if (!stream->running)
|
||||||
return;
|
return;
|
||||||
@ -83,20 +85,24 @@ stream_handle_interrupt(hda_controller* controller, hda_stream* stream)
|
|||||||
|
|
||||||
stream->Write8(HDAC_STREAM_STATUS, status);
|
stream->Write8(HDAC_STREAM_STATUS, status);
|
||||||
|
|
||||||
if ((status & STATUS_BUFFER_COMPLETED) != 0) {
|
if ((status & STATUS_BUFFER_COMPLETED) == 0) {
|
||||||
|
dprintf("hda: stream status %x\n", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
position = stream->Read32(HDAC_STREAM_POSITION);
|
||||||
|
bufferSize = ALIGN(stream->sample_size * stream->num_channels * stream->buffer_length, 128);
|
||||||
|
|
||||||
// Buffer Completed Interrupt
|
// Buffer Completed Interrupt
|
||||||
acquire_spinlock(&stream->lock);
|
acquire_spinlock(&stream->lock);
|
||||||
|
|
||||||
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 = (stream->buffer_cycle + 1)
|
stream->buffer_cycle = 1 - (position / bufferSize);
|
||||||
% stream->num_buffers;
|
|
||||||
|
|
||||||
release_spinlock(&stream->lock);
|
release_spinlock(&stream->lock);
|
||||||
|
|
||||||
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
|
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||||
} else
|
|
||||||
dprintf("hda: stream status %x\n", status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -301,9 +307,8 @@ init_corb_rirb_pos(hda_controller* controller)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
|
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
|
||||||
rirbOffset = (controller->corb_length * sizeof(corb_t) + 0x7f) & ~0x7f;
|
rirbOffset = ALIGN(controller->corb_length * sizeof(corb_t), 128);
|
||||||
posOffset = (rirbOffset + controller->rirb_length * sizeof(rirb_t) + 0x7f)
|
posOffset = ALIGN(rirbOffset + controller->rirb_length * sizeof(rirb_t), 128);
|
||||||
& ~0x7f;
|
|
||||||
posSize = 8 * (controller->num_input_streams
|
posSize = 8 * (controller->num_input_streams
|
||||||
+ controller->num_output_streams + controller->num_bidir_streams);
|
+ controller->num_output_streams + controller->num_bidir_streams);
|
||||||
|
|
||||||
@ -312,7 +317,7 @@ init_corb_rirb_pos(hda_controller* controller)
|
|||||||
/* Allocate memory area */
|
/* Allocate memory area */
|
||||||
controller->corb_rirb_pos_area = create_area("hda corb/rirb/pos",
|
controller->corb_rirb_pos_area = create_area("hda corb/rirb/pos",
|
||||||
(void**)&controller->corb, B_ANY_KERNEL_ADDRESS, memSize,
|
(void**)&controller->corb, B_ANY_KERNEL_ADDRESS, memSize,
|
||||||
B_CONTIGUOUS, 0);
|
B_FULL_LOCK | B_CONTIGUOUS, 0);
|
||||||
if (controller->corb_rirb_pos_area < 0)
|
if (controller->corb_rirb_pos_area < 0)
|
||||||
return controller->corb_rirb_pos_area;
|
return controller->corb_rirb_pos_area;
|
||||||
|
|
||||||
@ -418,7 +423,6 @@ hda_stream_new(hda_audio_group* audioGroup, int type)
|
|||||||
dprintf("%s: Unknown stream type %d!\n", __func__, type);
|
dprintf("%s: Unknown stream type %d!\n", __func__, type);
|
||||||
free(stream);
|
free(stream);
|
||||||
stream = NULL;
|
stream = NULL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// find I/O and Pin widgets for this stream
|
// find I/O and Pin widgets for this stream
|
||||||
@ -500,7 +504,8 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
|
|||||||
/* 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
|
||||||
* stream->buffer_length;
|
* stream->buffer_length;
|
||||||
bufferSize = (bufferSize + 127) & (~127);
|
bufferSize = ALIGN(bufferSize, 128);
|
||||||
|
|
||||||
dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld ****************\n",
|
dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld ****************\n",
|
||||||
stream->sample_size, stream->num_channels, stream->buffer_length);
|
stream->sample_size, stream->num_channels, stream->buffer_length);
|
||||||
|
|
||||||
@ -510,7 +515,7 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
|
|||||||
|
|
||||||
/* Allocate memory for buffers */
|
/* Allocate memory for buffers */
|
||||||
stream->buffer_area = create_area("hda buffers", (void**)&buffer,
|
stream->buffer_area = create_area("hda buffers", (void**)&buffer,
|
||||||
B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
|
B_ANY_KERNEL_ADDRESS, alloc, B_FULL_LOCK | B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
|
||||||
if (stream->buffer_area < B_OK)
|
if (stream->buffer_area < B_OK)
|
||||||
return stream->buffer_area;
|
return stream->buffer_area;
|
||||||
|
|
||||||
@ -539,7 +544,7 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
|
|||||||
|
|
||||||
stream->buffer_descriptors_area = create_area("hda buffer descriptors",
|
stream->buffer_descriptors_area = create_area("hda buffer descriptors",
|
||||||
(void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
|
(void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
|
||||||
B_CONTIGUOUS, 0);
|
B_FULL_LOCK | B_CONTIGUOUS, 0);
|
||||||
if (stream->buffer_descriptors_area < B_OK) {
|
if (stream->buffer_descriptors_area < B_OK) {
|
||||||
delete_area(stream->buffer_area);
|
delete_area(stream->buffer_area);
|
||||||
return stream->buffer_descriptors_area;
|
return stream->buffer_descriptors_area;
|
||||||
@ -560,14 +565,15 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
|
|||||||
|
|
||||||
/* Setup buffer descriptor list (BDL) entries */
|
/* Setup buffer descriptor list (BDL) entries */
|
||||||
for (index = 0; index < stream->num_buffers; index++, bufferDescriptors++) {
|
for (index = 0; index < stream->num_buffers; index++, bufferDescriptors++) {
|
||||||
bufferDescriptors->address = stream->physical_buffers[index];
|
bufferDescriptors->lower = stream->physical_buffers[index];
|
||||||
|
bufferDescriptors->upper = 0;
|
||||||
bufferDescriptors->length = bufferSize;
|
bufferDescriptors->length = bufferSize;
|
||||||
bufferDescriptors->ioc = 1;
|
bufferDescriptors->ioc = 1;
|
||||||
// we want an interrupt after every buffer
|
// we want an interrupt after every buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure stream registers */
|
/* Configure stream registers */
|
||||||
format = stream->num_channels - 1;
|
format = (stream->num_channels - 1) & 0xf;
|
||||||
switch (stream->sample_format) {
|
switch (stream->sample_format) {
|
||||||
case B_FMT_8BIT_S: format |= FORMAT_8BIT; stream->bps = 8; break;
|
case B_FMT_8BIT_S: format |= FORMAT_8BIT; stream->bps = 8; break;
|
||||||
case B_FMT_16BIT: format |= FORMAT_16BIT; stream->bps = 16; break;
|
case B_FMT_16BIT: format |= FORMAT_16BIT; stream->bps = 16; break;
|
||||||
@ -598,8 +604,8 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld *************
|
|||||||
stream->Write32(HDAC_STREAM_BUFFERS_BASE_UPPER, 0);
|
stream->Write32(HDAC_STREAM_BUFFERS_BASE_UPPER, 0);
|
||||||
stream->Write16(HDAC_STREAM_LAST_VALID, stream->num_buffers - 1);
|
stream->Write16(HDAC_STREAM_LAST_VALID, stream->num_buffers - 1);
|
||||||
/* total cyclic buffer size in _bytes_ */
|
/* total cyclic buffer size in _bytes_ */
|
||||||
stream->Write32(HDAC_STREAM_BUFFER_SIZE, stream->sample_size
|
stream->Write32(HDAC_STREAM_BUFFER_SIZE, bufferSize
|
||||||
* stream->num_channels * stream->num_buffers * stream->buffer_length);
|
* stream->num_buffers);
|
||||||
stream->Write8(HDAC_STREAM_CONTROL2, stream->id << 4);
|
stream->Write8(HDAC_STREAM_CONTROL2, stream->id << 4);
|
||||||
|
|
||||||
stream->controller->Write32(HDAC_DMA_POSITION_BASE_LOWER,
|
stream->controller->Write32(HDAC_DMA_POSITION_BASE_LOWER,
|
||||||
@ -667,8 +673,9 @@ hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count)
|
|||||||
status_t
|
status_t
|
||||||
hda_hw_init(hda_controller* controller)
|
hda_hw_init(hda_controller* controller)
|
||||||
{
|
{
|
||||||
uint16 capabilities, stateStatus;
|
uint16 capabilities, stateStatus, cmd;
|
||||||
status_t status;
|
status_t status;
|
||||||
|
uint8 tcsel;
|
||||||
|
|
||||||
/* Map MMIO registers */
|
/* Map MMIO registers */
|
||||||
controller->regs_area = map_physical_memory("hda_hw_regs",
|
controller->regs_area = map_physical_memory("hda_hw_regs",
|
||||||
@ -680,6 +687,15 @@ hda_hw_init(hda_controller* controller)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd = (gPci->read_pci_config)(controller->pci_info.bus,
|
||||||
|
controller->pci_info.device, controller->pci_info.function, PCI_command, 2);
|
||||||
|
if (!(cmd & PCI_command_master)) {
|
||||||
|
(gPci->write_pci_config)(controller->pci_info.bus,
|
||||||
|
controller->pci_info.device, controller->pci_info.function,
|
||||||
|
PCI_command, 2, cmd | PCI_command_master);
|
||||||
|
dprintf("hda: enabling PCI bus mastering\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* Absolute minimum hw is online; we can now install interrupt handler */
|
/* Absolute minimum hw is online; we can now install interrupt handler */
|
||||||
controller->irq = controller->pci_info.u.h0.interrupt_line;
|
controller->irq = controller->pci_info.u.h0.interrupt_line;
|
||||||
status = install_io_interrupt_handler(controller->irq,
|
status = install_io_interrupt_handler(controller->irq,
|
||||||
@ -687,6 +703,13 @@ hda_hw_init(hda_controller* controller)
|
|||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
goto no_irq;
|
goto no_irq;
|
||||||
|
|
||||||
|
/* TCSEL is reset to TC0 (clear 0-2 bits) */
|
||||||
|
tcsel = (gPci->read_pci_config)(controller->pci_info.bus,
|
||||||
|
controller->pci_info.device, controller->pci_info.function, PCI_HDA_TCSEL, 1);
|
||||||
|
(gPci->write_pci_config)(controller->pci_info.bus,
|
||||||
|
controller->pci_info.device, controller->pci_info.function,
|
||||||
|
PCI_HDA_TCSEL, 1, tcsel & 0xf8);
|
||||||
|
|
||||||
capabilities = controller->Read16(HDAC_GLOBAL_CAP);
|
capabilities = controller->Read16(HDAC_GLOBAL_CAP);
|
||||||
controller->num_input_streams = GLOBAL_CAP_INPUT_STREAMS(capabilities);
|
controller->num_input_streams = GLOBAL_CAP_INPUT_STREAMS(capabilities);
|
||||||
controller->num_output_streams = GLOBAL_CAP_OUTPUT_STREAMS(capabilities);
|
controller->num_output_streams = GLOBAL_CAP_OUTPUT_STREAMS(capabilities);
|
||||||
|
@ -131,6 +131,9 @@
|
|||||||
#define HDAC_STREAM_BUFFERS_BASE_LOWER 0x18 // 32bits, BDPL
|
#define HDAC_STREAM_BUFFERS_BASE_LOWER 0x18 // 32bits, BDPL
|
||||||
#define HDAC_STREAM_BUFFERS_BASE_UPPER 0x1c // 32bits, BDPU
|
#define HDAC_STREAM_BUFFERS_BASE_UPPER 0x1c // 32bits, BDPU
|
||||||
|
|
||||||
|
/* PCI space register definitions */
|
||||||
|
#define PCI_HDA_TCSEL 0x44
|
||||||
|
|
||||||
typedef uint32 corb_t;
|
typedef uint32 corb_t;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32 response;
|
uint32 response;
|
||||||
@ -141,7 +144,8 @@ typedef struct {
|
|||||||
#define RESPONSE_FLAGS_UNSOLICITED (1 << 4)
|
#define RESPONSE_FLAGS_UNSOLICITED (1 << 4)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64 address;
|
uint32 lower;
|
||||||
|
uint32 upper;
|
||||||
uint32 length;
|
uint32 length;
|
||||||
uint32 ioc;
|
uint32 ioc;
|
||||||
} bdl_entry_t;
|
} bdl_entry_t;
|
||||||
|
Loading…
Reference in New Issue
Block a user