#include "hda_controller_defs.h"
#include <algorithm>
#include <vm/vm.h>
#include "driver.h"
#include "hda_codec_defs.h"
#ifdef TRACE_HDA_VERBS
# define TRACE_VERBS(x...) dprintf("\33[33mhda:\33[0m " x)
#else
# define TRACE_VERBS(x...) ;
#endif
#define MAKE_RATE(base, multiply, divide) \
((base == 44100 ? FORMAT_44_1_BASE_RATE : 0) \
| ((multiply - 1) << FORMAT_MULTIPLY_RATE_SHIFT) \
| ((divide - 1) << FORMAT_DIVIDE_RATE_SHIFT))
#define HDAC_INPUT_STREAM_OFFSET(controller, index) \
((index) * HDAC_STREAM_SIZE)
#define HDAC_OUTPUT_STREAM_OFFSET(controller, index) \
(((controller)->num_input_streams + (index)) * HDAC_STREAM_SIZE)
#define HDAC_BIDIR_STREAM_OFFSET(controller, index) \
(((controller)->num_input_streams + (controller)->num_output_streams \
+ (index)) * HDAC_STREAM_SIZE)
#define ALIGN(size, align) (((size) + align - 1) & ~(align - 1))
#define PCI_VENDOR_ATI 0x1002
#define PCI_VENDOR_AMD 0x1022
#define PCI_VENDOR_CREATIVE 0x1102
#define PCI_VENDOR_CMEDIA 0x13f6
#define PCI_VENDOR_INTEL 0x8086
#define PCI_VENDOR_NVIDIA 0x10de
#define PCI_VENDOR_VMWARE 0x15ad
#define PCI_VENDOR_SIS 0x1039
#define PCI_ALL_DEVICES 0xffffffff
#define HDA_QUIRK_SNOOP 0x0001
#define HDA_QUIRK_NO_MSI 0x0002
#define HDA_QUIRK_NO_CORBRP_RESET_ACK 0x0004
#define HDA_QUIRK_NOTCSEL 0x0008
#define HDA_QUIRK_NO_64BITDMA 0x0010
#define HDA_QUIRK_NOINIT_MISCBDCGE 0x0020
#define HDA_QUIRKS_AMD \
(HDA_QUIRK_SNOOP | HDA_QUIRK_NOTCSEL | HDA_QUIRK_NO_64BITDMA)
static const struct {
uint32 vendor_id, device_id;
uint32 quirks;
} kControllerQuirks[] = {
{ PCI_VENDOR_INTEL, 0x02c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x06c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x080a, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x0a0c, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x0c0c, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x0d0c, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x0f04, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x160c, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x1a98, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x1c20, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x1d20, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x1e20, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x2284, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x3198, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x34c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x38c8, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x3b56, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x3b57, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x3dc8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x43c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x490d, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x4b55, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x4b58, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x4d55, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x4dc8, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x4f90, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x4f91, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x4f92, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51c9, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51ca, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51cb, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51cc, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51cd, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51ce, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x51cf, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x54c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x5a98, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x7a50, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x7ad0, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x7e28, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x811b, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x8c20, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x8c21, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x8ca0, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x8d20, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x8d21, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x9c20, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x9c21, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x9ca0, HDA_QUIRK_SNOOP },
{ PCI_VENDOR_INTEL, 0x9d70, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x9d71, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0x9dc8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa0c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa170, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa171, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa1f0, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa270, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa2f0, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa348, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xa3f0, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xf0c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_INTEL, 0xf1c8, HDA_QUIRK_SNOOP | HDA_QUIRK_NOINIT_MISCBDCGE },
{ PCI_VENDOR_ATI, 0x437b, HDA_QUIRKS_AMD },
{ PCI_VENDOR_ATI, 0x4383, HDA_QUIRKS_AMD },
{ PCI_VENDOR_AMD, 0x157a, HDA_QUIRKS_AMD },
{ PCI_VENDOR_AMD, 0x780d, HDA_QUIRKS_AMD },
{ PCI_VENDOR_AMD, 0x1457, HDA_QUIRKS_AMD },
{ PCI_VENDOR_AMD, 0x1487, HDA_QUIRKS_AMD },
{ PCI_VENDOR_AMD, 0x15e3, HDA_QUIRKS_AMD },
{ PCI_VENDOR_NVIDIA, PCI_ALL_DEVICES, HDA_QUIRK_SNOOP | HDA_QUIRK_NO_MSI
| HDA_QUIRK_NO_CORBRP_RESET_ACK | HDA_QUIRK_NO_64BITDMA },
{ PCI_VENDOR_CMEDIA, 0x5011, HDA_QUIRK_NO_MSI },
{ PCI_VENDOR_CREATIVE, 0x0010, HDA_QUIRK_NO_MSI | HDA_QUIRK_NO_64BITDMA },
{ PCI_VENDOR_CREATIVE, 0x0012, HDA_QUIRK_NO_MSI | HDA_QUIRK_NO_64BITDMA },
{ PCI_VENDOR_VMWARE, PCI_ALL_DEVICES, HDA_QUIRK_NO_CORBRP_RESET_ACK },
{ PCI_VENDOR_SIS, 0x7502, HDA_QUIRK_NO_CORBRP_RESET_ACK },
{ PCI_VENDOR_ATI, PCI_ALL_DEVICES, HDA_QUIRK_NO_64BITDMA },
};
static const struct {
uint32 multi_rate;
uint32 hw_rate;
uint32 rate;
} kRates[] = {
{B_SR_8000, MAKE_RATE(48000, 1, 6), 8000},
{B_SR_11025, MAKE_RATE(44100, 1, 4), 11025},
{B_SR_16000, MAKE_RATE(48000, 1, 3), 16000},
{B_SR_22050, MAKE_RATE(44100, 1, 2), 22050},
{B_SR_32000, MAKE_RATE(48000, 2, 3), 32000},
{B_SR_44100, MAKE_RATE(44100, 1, 1), 44100},
{B_SR_48000, MAKE_RATE(48000, 1, 1), 48000},
{B_SR_88200, MAKE_RATE(44100, 2, 1), 88200},
{B_SR_96000, MAKE_RATE(48000, 2, 1), 96000},
{B_SR_176400, MAKE_RATE(44100, 4, 1), 176400},
{B_SR_192000, MAKE_RATE(48000, 4, 1), 192000},
};
static uint32
get_controller_quirks(const pci_info& info)
{
for (size_t i = 0;
i < sizeof(kControllerQuirks) / sizeof(kControllerQuirks[0]); i++) {
if (info.vendor_id == kControllerQuirks[i].vendor_id
&& (kControllerQuirks[i].device_id == PCI_ALL_DEVICES
|| kControllerQuirks[i].device_id == info.device_id))
return kControllerQuirks[i].quirks;
}
return 0;
}
template<int bits, typename base_type>
bool
wait_for_bits(base_type base, uint32 reg, uint32 mask, bool set,
bigtime_t delay = 100, int timeout = 10)
{
STATIC_ASSERT(bits == 8 || bits == 16 || bits == 32);
for (; timeout >= 0; timeout--) {
snooze(delay);
uint32 value;
switch (bits) {
case 8:
value = base->Read8(reg);
break;
case 16:
value = base->Read16(reg);
break;
case 32:
value = base->Read32(reg);
break;
}
if (((value & mask) != 0) == set)
return true;
}
return false;
}
static inline bool
update_pci_register(hda_controller* controller, uint8 reg, uint32 mask,
uint32 value, uint8 size, bool check = false)
{
uint32 originalValue = (gPci->read_pci_config)(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function, reg, size);
(gPci->write_pci_config)(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function,
reg, size, (originalValue & mask) | value);
if (!check)
return true;
uint32 newValue = (gPci->read_pci_config)(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function, reg, size);
return (newValue & ~mask) == value;
}
static inline rirb_t&
current_rirb(hda_controller* controller)
{
return controller->rirb[controller->rirb_read_pos];
}
static inline uint32
next_rirb(hda_controller* controller)
{
return (controller->rirb_read_pos + 1) % controller->rirb_length;
}
static inline uint32
next_corb(hda_controller* controller)
{
return (controller->corb_write_pos + 1) % controller->corb_length;
}
static bool
stream_handle_interrupt(hda_controller* controller, hda_stream* stream,
uint32 index)
{
if (!stream->running)
return false;
uint8 status = stream->Read8(HDAC_STREAM_STATUS);
if (status == 0)
return false;
stream->Write8(HDAC_STREAM_STATUS, status);
if ((status & STATUS_BUFFER_COMPLETED) == 0) {
dprintf("hda: stream buffer not completed (id: %" B_PRIu32 ", "
"status 0x%" B_PRIx32 ")\n", stream->id, status);
return false;
}
if ((status & STATUS_FIFO_ERROR) != 0)
dprintf("hda: stream fifo error (id:%" B_PRIu32 ")\n", stream->id);
if ((status & STATUS_DESCRIPTOR_ERROR) != 0) {
dprintf("hda: stream descriptor error (id:%" B_PRIu32 ")\n",
stream->id);
}
if (stream->use_dma_position && stream->incorrect_position_count >= 32) {
dprintf("hda: DMA position for stream (id:%" B_PRIu32 ") seems to be "
"broken. Switching to using LPIB.\n", stream->id);
stream->use_dma_position = false;
}
uint32 dmaPosition = stream->use_dma_position
? controller->stream_positions[index * 2]
: stream->Read32(HDAC_STREAM_POSITION);
uint32 bufferIndex = ((dmaPosition + stream->buffer_size / 2)
/ stream->buffer_size) % stream->num_buffers;
uint32 linkBytePosition = stream->Read32(HDAC_STREAM_POSITION);
bigtime_t now = system_time();
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);
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;
acquire_spinlock(&stream->lock);
if (bufferIndex == (stream->buffer_cycle + 1) % stream->num_buffers)
stream->incorrect_position_count = 0;
else
stream->incorrect_position_count++;
stream->real_time = now;
stream->frames_count += framesProcessed;
stream->buffer_cycle = bufferIndex;
release_spinlock(&stream->lock);
release_sem_etc(controller->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
return true;
}
static int32
hda_interrupt_handler(hda_controller* controller)
{
int32 handled = B_HANDLED_INTERRUPT;
uint32 intrStatus = controller->Read32(HDAC_INTR_STATUS);
if ((intrStatus & INTR_STATUS_GLOBAL) == 0)
return B_UNHANDLED_INTERRUPT;
if (intrStatus & INTR_STATUS_CONTROLLER) {
uint8 rirbStatus = controller->Read8(HDAC_RIRB_STATUS);
uint8 corbStatus = controller->Read8(HDAC_CORB_STATUS);
if (rirbStatus) {
controller->Write8(HDAC_RIRB_STATUS, rirbStatus);
if ((rirbStatus & RIRB_STATUS_RESPONSE) != 0) {
uint16 writePos = (controller->Read16(HDAC_RIRB_WRITE_POS) + 1)
% controller->rirb_length;
for (; controller->rirb_read_pos != writePos;
controller->rirb_read_pos = next_rirb(controller)) {
uint32 response = current_rirb(controller).response;
uint32 responseFlags = current_rirb(controller).flags;
uint32 cad = responseFlags & RESPONSE_FLAGS_CODEC_MASK;
hda_codec* codec = controller->codecs[cad];
if (codec == NULL) {
dprintf("hda: Response for unknown codec %" B_PRIu32
": %08" B_PRIx32 "/%08" B_PRIx32 "\n", cad,
response, responseFlags);
continue;
}
if ((responseFlags & RESPONSE_FLAGS_UNSOLICITED) != 0) {
dprintf("hda: Unsolicited response: %08" B_PRIx32
"/%08" B_PRIx32 "\n", response, responseFlags);
codec->unsol_responses[codec->unsol_response_write++] =
response;
codec->unsol_response_write %= MAX_CODEC_UNSOL_RESPONSES;
release_sem_etc(codec->unsol_response_sem, 1,
B_DO_NOT_RESCHEDULE);
handled = B_INVOKE_SCHEDULER;
continue;
}
if (codec->response_count >= MAX_CODEC_RESPONSES) {
dprintf("hda: too many responses received for codec %"
B_PRIu32 ": %08" B_PRIx32 "/%08" B_PRIx32 "!\n",
cad, response, responseFlags);
continue;
}
codec->responses[codec->response_count++] = response;
release_sem_etc(codec->response_sem, 1, B_DO_NOT_RESCHEDULE);
handled = B_INVOKE_SCHEDULER;
}
}
if ((rirbStatus & RIRB_STATUS_OVERRUN) != 0)
dprintf("hda: RIRB Overflow\n");
}
if (corbStatus) {
controller->Write8(HDAC_CORB_STATUS, corbStatus);
if ((corbStatus & CORB_STATUS_MEMORY_ERROR) != 0)
dprintf("hda: CORB Memory Error!\n");
}
}
if ((intrStatus & INTR_STATUS_STREAM_MASK) != 0) {
for (uint32 index = 0; index < HDA_MAX_STREAMS; index++) {
if ((intrStatus & (1 << index)) != 0) {
if (controller->streams[index]) {
if (stream_handle_interrupt(controller,
controller->streams[index], index)) {
handled = B_INVOKE_SCHEDULER;
}
} else {
dprintf("hda: Stream interrupt for unconfigured stream "
"%" B_PRIu32 "!\n", index);
}
}
}
}
return handled;
}
static status_t
reset_controller(hda_controller* controller)
{
uint32 control = controller->Read32(HDAC_GLOBAL_CONTROL);
if ((control & GLOBAL_CONTROL_RESET) != 0) {
controller->Write32(HDAC_INTR_CONTROL, 0);
for (uint32 i = 0; i < controller->num_input_streams; i++) {
controller->Write8(HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
controller->Write8(HDAC_STREAM_STATUS + HDAC_STREAM_BASE
+ HDAC_INPUT_STREAM_OFFSET(controller, i), 0);
}
for (uint32 i = 0; i < controller->num_output_streams; i++) {
controller->Write8(HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE
+ HDAC_OUTPUT_STREAM_OFFSET(controller, i), 0);
controller->Write8(HDAC_STREAM_STATUS + HDAC_STREAM_BASE
+ HDAC_OUTPUT_STREAM_OFFSET(controller, i), 0);
}
for (uint32 i = 0; i < controller->num_bidir_streams; i++) {
controller->Write8(HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE
+ HDAC_BIDIR_STREAM_OFFSET(controller, i), 0);
controller->Write8(HDAC_STREAM_STATUS + HDAC_STREAM_BASE
+ HDAC_BIDIR_STREAM_OFFSET(controller, i), 0);
}
controller->ReadModifyWrite8(HDAC_CORB_CONTROL, HDAC_CORB_CONTROL_MASK,
0);
controller->ReadModifyWrite8(HDAC_RIRB_CONTROL, HDAC_RIRB_CONTROL_MASK,
0);
if (!wait_for_bits<8>(controller, HDAC_CORB_CONTROL, ~0, false)
|| !wait_for_bits<8>(controller, HDAC_RIRB_CONTROL, ~0, false)) {
dprintf("hda: unable to stop dma\n");
return B_BUSY;
}
controller->Write32(HDAC_DMA_POSITION_BASE_LOWER, 0);
controller->Write32(HDAC_DMA_POSITION_BASE_UPPER, 0);
control = controller->Read32(HDAC_GLOBAL_CONTROL);
}
controller->Write32(HDAC_GLOBAL_CONTROL, control & ~GLOBAL_CONTROL_RESET);
if (!wait_for_bits<32>(controller, HDAC_GLOBAL_CONTROL,
GLOBAL_CONTROL_RESET, false)) {
dprintf("hda: unable to reset controller\n");
return B_BUSY;
}
snooze(1000);
control = controller->Read32(HDAC_GLOBAL_CONTROL);
controller->Write32(HDAC_GLOBAL_CONTROL, control | GLOBAL_CONTROL_RESET);
if (!wait_for_bits<32>(controller, HDAC_GLOBAL_CONTROL,
GLOBAL_CONTROL_RESET, true)) {
dprintf("hda: unable to exit reset\n");
return B_BUSY;
}
snooze(1000);
control = controller->Read32(HDAC_GLOBAL_CONTROL);
controller->Write32(HDAC_GLOBAL_CONTROL,
control | GLOBAL_CONTROL_UNSOLICITED);
return B_OK;
}
static status_t
init_corb_rirb_pos(hda_controller* controller, uint32 quirks)
{
uint8 corbSize = controller->Read8(HDAC_CORB_SIZE);
if ((corbSize & CORB_SIZE_CAP_256_ENTRIES) != 0) {
controller->corb_length = 256;
controller->ReadModifyWrite8(
HDAC_CORB_SIZE, HDAC_CORB_SIZE_MASK,
CORB_SIZE_256_ENTRIES);
} else if (corbSize & CORB_SIZE_CAP_16_ENTRIES) {
controller->corb_length = 16;
controller->ReadModifyWrite8(
HDAC_CORB_SIZE, HDAC_CORB_SIZE_MASK,
CORB_SIZE_16_ENTRIES);
} else if (corbSize & CORB_SIZE_CAP_2_ENTRIES) {
controller->corb_length = 2;
controller->ReadModifyWrite8(
HDAC_CORB_SIZE, HDAC_CORB_SIZE_MASK,
CORB_SIZE_2_ENTRIES);
}
uint8 rirbSize = controller->Read8(HDAC_RIRB_SIZE);
if (rirbSize & RIRB_SIZE_CAP_256_ENTRIES) {
controller->rirb_length = 256;
controller->ReadModifyWrite8(
HDAC_RIRB_SIZE, HDAC_RIRB_SIZE_MASK,
RIRB_SIZE_256_ENTRIES);
} else if (rirbSize & RIRB_SIZE_CAP_16_ENTRIES) {
controller->rirb_length = 16;
controller->ReadModifyWrite8(
HDAC_RIRB_SIZE, HDAC_RIRB_SIZE_MASK,
RIRB_SIZE_16_ENTRIES);
} else if (rirbSize & RIRB_SIZE_CAP_2_ENTRIES) {
controller->rirb_length = 2;
controller->ReadModifyWrite8(
HDAC_RIRB_SIZE, HDAC_RIRB_SIZE_MASK,
RIRB_SIZE_2_ENTRIES);
}
uint32 rirbOffset = ALIGN(controller->corb_length * sizeof(corb_t), 128);
uint32 posOffset = ALIGN(rirbOffset
+ controller->rirb_length * sizeof(rirb_t), 128);
uint8 posSize = 8 * (controller->num_input_streams
+ controller->num_output_streams + controller->num_bidir_streams);
uint32 memSize = PAGE_ALIGN(posOffset + posSize);
controller->corb_rirb_pos_area = create_area("hda corb/rirb/pos",
(void**)&controller->corb, B_ANY_KERNEL_ADDRESS, memSize,
controller->is_64_bit ? B_CONTIGUOUS : B_32_BIT_CONTIGUOUS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (controller->corb_rirb_pos_area < 0)
return controller->corb_rirb_pos_area;
controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirbOffset);
physical_entry pe;
status_t status = get_memory_map(controller->corb, memSize, &pe, 1);
if (status != B_OK) {
delete_area(controller->corb_rirb_pos_area);
return status;
}
if (!controller->dma_snooping) {
vm_set_area_memory_type(controller->corb_rirb_pos_area,
pe.address, B_UNCACHED_MEMORY);
}
controller->Write32(HDAC_CORB_BASE_LOWER, (uint32)pe.address);
if (controller->is_64_bit) {
controller->Write32(HDAC_CORB_BASE_UPPER,
(uint32)((uint64)pe.address >> 32));
}
controller->Write32(HDAC_RIRB_BASE_LOWER, (uint32)pe.address + rirbOffset);
if (controller->is_64_bit) {
controller->Write32(HDAC_RIRB_BASE_UPPER,
(uint32)(((uint64)pe.address + rirbOffset) >> 32));
}
controller->Write32(HDAC_DMA_POSITION_BASE_LOWER,
(uint32)pe.address + posOffset);
if (controller->is_64_bit) {
controller->Write32(HDAC_DMA_POSITION_BASE_UPPER,
(uint32)(((uint64)pe.address + posOffset) >> 32));
}
controller->stream_positions = (uint32*)
((uint8*)controller->corb + posOffset);
controller->ReadModifyWrite16(HDAC_CORB_WRITE_POS,
HDAC_CORB_WRITE_POS_MASK, 0);
uint16 corbReadPointer = controller->Read16(HDAC_CORB_READ_POS);
corbReadPointer |= CORB_READ_POS_RESET;
controller->Write16(HDAC_CORB_READ_POS, corbReadPointer);
if (!wait_for_bits<16>(controller, HDAC_CORB_READ_POS, CORB_READ_POS_RESET,
true)) {
dprintf("hda: CORB read pointer reset not acknowledged\n");
if ((quirks & HDA_QUIRK_NO_CORBRP_RESET_ACK) == 0)
return B_BUSY;
}
corbReadPointer &= ~CORB_READ_POS_RESET;
controller->Write16(HDAC_CORB_READ_POS, corbReadPointer);
if (!wait_for_bits<16>(controller, HDAC_CORB_READ_POS, CORB_READ_POS_RESET,
false)) {
dprintf("hda: CORB read pointer reset failed\n");
return B_BUSY;
}
controller->ReadModifyWrite16(HDAC_RIRB_WRITE_POS,
RIRB_WRITE_POS_RESET, RIRB_WRITE_POS_RESET);
controller->ReadModifyWrite16(HDAC_RESPONSE_INTR_COUNT,
HDAC_RESPONSE_INTR_COUNT_MASK, 1);
controller->rirb_read_pos = 1;
controller->corb_write_pos = 0;
controller->ReadModifyWrite8(HDAC_CORB_CONTROL,
HDAC_CORB_CONTROL_MASK,
CORB_CONTROL_RUN | CORB_CONTROL_MEMORY_ERROR_INTR);
controller->ReadModifyWrite8(HDAC_RIRB_CONTROL,
HDAC_RIRB_CONTROL_MASK,
RIRB_CONTROL_DMA_ENABLE | RIRB_CONTROL_OVERRUN_INTR
| RIRB_CONTROL_RESPONSE_INTR);
return B_OK;
}
void
hda_stream_delete(hda_stream* stream)
{
if (stream->buffer_area >= 0)
delete_area(stream->buffer_area);
if (stream->buffer_descriptors_area >= 0)
delete_area(stream->buffer_descriptors_area);
free(stream);
}
hda_stream*
hda_stream_new(hda_audio_group* audioGroup, int type)
{
hda_controller* controller = audioGroup->codec->controller;
hda_stream* stream = (hda_stream*)calloc(1, sizeof(hda_stream));
if (stream == NULL)
return NULL;
stream->buffer_area = B_ERROR;
stream->buffer_descriptors_area = B_ERROR;
stream->type = type;
stream->controller = controller;
stream->incorrect_position_count = 0;
stream->use_dma_position = true;
switch (type) {
case STREAM_PLAYBACK:
stream->id = 1;
stream->offset = HDAC_OUTPUT_STREAM_OFFSET(controller, 0);
break;
case STREAM_RECORD:
stream->id = 2;
stream->offset = HDAC_INPUT_STREAM_OFFSET(controller, 0);
break;
default:
dprintf("%s: Unknown stream type %d!\n", __func__, type);
free(stream);
return NULL;
}
if (hda_audio_group_get_widgets(audioGroup, stream) == B_OK) {
switch (type) {
case STREAM_PLAYBACK:
controller->streams[controller->num_input_streams] = stream;
break;
case STREAM_RECORD:
controller->streams[0] = stream;
break;
}
return stream;
}
dprintf("hda: hda_audio_group_get_widgets failed for %s stream\n",
type == STREAM_PLAYBACK ? "playback" : "record");
free(stream);
return NULL;
}
status_t
hda_stream_start(hda_controller* controller, hda_stream* stream)
{
dprintf("hda_stream_start() offset %" B_PRIx32 "\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)
| CONTROL0_BUFFER_COMPLETED_INTR | CONTROL0_FIFO_ERROR_INTR
| CONTROL0_DESCRIPTOR_ERROR_INTR | CONTROL0_RUN);
if (!wait_for_bits<8>(stream, HDAC_STREAM_CONTROL0, CONTROL0_RUN, true)) {
dprintf("hda: unable to start stream\n");
return B_BUSY;
}
stream->running = true;
return B_OK;
}
status_t
hda_stream_stop(hda_controller* controller, hda_stream* stream)
{
dprintf("hda_stream_stop()\n");
stream->Write8(HDAC_STREAM_CONTROL0, stream->Read8(HDAC_STREAM_CONTROL0)
& ~(CONTROL0_BUFFER_COMPLETED_INTR | CONTROL0_FIFO_ERROR_INTR
| CONTROL0_DESCRIPTOR_ERROR_INTR | CONTROL0_RUN));
controller->Write32(HDAC_INTR_CONTROL, controller->Read32(HDAC_INTR_CONTROL)
& ~(1 << (stream->offset / HDAC_STREAM_SIZE)));
if (!wait_for_bits<8>(stream, HDAC_STREAM_CONTROL0, CONTROL0_RUN, false)) {
dprintf("hda: unable to stop stream\n");
return B_BUSY;
}
stream->running = false;
return B_OK;
}
status_t
hda_stream_reset(hda_stream* stream)
{
if (stream->running)
hda_stream_stop(stream->controller, stream);
stream->Write8(HDAC_STREAM_CONTROL0,
stream->Read8(HDAC_STREAM_CONTROL0) | CONTROL0_RESET);
if (!wait_for_bits<8>(stream, HDAC_STREAM_CONTROL0, CONTROL0_RESET, true)) {
dprintf("hda: unable to start stream reset\n");
return B_BUSY;
}
stream->Write8(HDAC_STREAM_CONTROL0,
stream->Read8(HDAC_STREAM_CONTROL0) & ~CONTROL0_RESET);
if (!wait_for_bits<8>(stream, HDAC_STREAM_CONTROL0, CONTROL0_RESET, false))
{
dprintf("hda: unable to stop stream reset\n");
return B_BUSY;
}
return B_OK;
}
status_t
hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
const char* desc)
{
hda_stream_reset(stream);
if (stream->buffer_area >= 0) {
delete_area(stream->buffer_area);
stream->buffer_area = B_ERROR;
}
if (stream->buffer_descriptors_area >= 0) {
delete_area(stream->buffer_descriptors_area);
stream->buffer_descriptors_area = B_ERROR;
}
uint16 format = (stream->num_channels - 1) & 0xf;
switch (stream->sample_format) {
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_20BIT: format |= FORMAT_20BIT; stream->bps = 20; break;
case B_FMT_24BIT: format |= FORMAT_24BIT; stream->bps = 24; break;
case B_FMT_32BIT: format |= FORMAT_32BIT; stream->bps = 32; break;
default:
dprintf("hda: Invalid sample format: 0x%" B_PRIx32 "\n",
stream->sample_format);
break;
}
for (uint32 index = 0; index < sizeof(kRates) / sizeof(kRates[0]); index++) {
if (kRates[index].multi_rate == stream->sample_rate) {
format |= kRates[index].hw_rate;
stream->rate = kRates[index].rate;
break;
}
}
stream->buffer_size = ALIGN(stream->buffer_length * stream->num_channels
* stream->sample_size, 128);
dprintf("hda: sample size %" B_PRIu32 ", num channels %" B_PRIu32 ", "
"buffer length %" B_PRIu32 "\n", stream->sample_size,
stream->num_channels, stream->buffer_length);
dprintf("hda: %s: setup stream %" B_PRIu32 ": SR=%" B_PRIu32 ", SF=%"
B_PRIu32 " F=0x%x (0x%" B_PRIx32 ")\n", __func__, stream->id,
stream->rate, stream->bps, format, stream->sample_format);
uint32 alloc = stream->buffer_size * stream->num_buffers;
alloc = PAGE_ALIGN(alloc);
uint8* buffer;
stream->buffer_area = create_area("hda buffers", (void**)&buffer,
B_ANY_KERNEL_ADDRESS, alloc,
stream->controller->is_64_bit ? B_CONTIGUOUS : B_32_BIT_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA);
if (stream->buffer_area < B_OK)
return stream->buffer_area;
physical_entry pe;
status_t status = get_memory_map(buffer, alloc, &pe, 1);
if (status != B_OK) {
delete_area(stream->buffer_area);
return status;
}
phys_addr_t bufferPhysicalAddress = pe.address;
if (!stream->controller->dma_snooping) {
vm_set_area_memory_type(stream->buffer_area,
bufferPhysicalAddress, B_UNCACHED_MEMORY);
}
dprintf("hda: %s(%s): Allocated %" B_PRIu32 " bytes for %" B_PRIu32
" buffers\n", __func__, desc, alloc, stream->num_buffers);
for (uint32 index = 0; index < stream->num_buffers; index++) {
stream->buffers[index] = buffer + (index * stream->buffer_size);
stream->physical_buffers[index] = bufferPhysicalAddress
+ (index * stream->buffer_size);
}
uint32 bdlCount = stream->num_buffers;
alloc = bdlCount * sizeof(bdl_entry_t);
alloc = PAGE_ALIGN(alloc);
bdl_entry_t* bufferDescriptors;
stream->buffer_descriptors_area = create_area("hda buffer descriptors",
(void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
stream->controller->is_64_bit ? B_CONTIGUOUS : B_32_BIT_CONTIGUOUS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (stream->buffer_descriptors_area < B_OK) {
delete_area(stream->buffer_area);
return stream->buffer_descriptors_area;
}
status = get_memory_map(bufferDescriptors, alloc, &pe, 1);
if (status != B_OK) {
delete_area(stream->buffer_area);
delete_area(stream->buffer_descriptors_area);
return status;
}
stream->physical_buffer_descriptors = pe.address;
if (!stream->controller->dma_snooping) {
vm_set_area_memory_type(stream->buffer_descriptors_area,
stream->physical_buffer_descriptors, B_UNCACHED_MEMORY);
}
dprintf("hda: %s(%s): Allocated %" B_PRIu32 " bytes for %" B_PRIu32
" BDLEs\n", __func__, desc, alloc, bdlCount);
uint32 fragments = 0;
for (uint32 index = 0; index < stream->num_buffers;
index++, bufferDescriptors++) {
bufferDescriptors->lower = (uint32)stream->physical_buffers[index];
bufferDescriptors->upper
= (uint32)((uint64)stream->physical_buffers[index] >> 32);
fragments++;
bufferDescriptors->length = stream->buffer_size;
bufferDescriptors->ioc = 1;
}
stream->Write16(HDAC_STREAM_FORMAT, format);
stream->Write32(HDAC_STREAM_BUFFERS_BASE_LOWER,
(uint32)stream->physical_buffer_descriptors);
if (stream->controller->is_64_bit) {
stream->Write32(HDAC_STREAM_BUFFERS_BASE_UPPER,
(uint32)((uint64)stream->physical_buffer_descriptors >> 32));
}
stream->Write16(HDAC_STREAM_LAST_VALID, fragments - 1);
stream->Write32(HDAC_STREAM_BUFFER_SIZE, stream->buffer_size
* stream->num_buffers);
stream->Write8(HDAC_STREAM_CONTROL2, stream->id << CONTROL2_STREAM_SHIFT);
stream->controller->Write32(HDAC_DMA_POSITION_BASE_LOWER,
stream->controller->Read32(HDAC_DMA_POSITION_BASE_LOWER)
| DMA_POSITION_ENABLED);
dprintf("hda: stream: %" B_PRIu32 " fifo size: %d num_io_widgets: %"
B_PRIu32 "\n", stream->id, stream->Read16(HDAC_STREAM_FIFO_SIZE),
stream->num_io_widgets);
dprintf("hda: widgets: ");
hda_codec* codec = audioGroup->codec;
uint32 channelNum = 0;
for (uint32 i = 0; i < stream->num_io_widgets; i++) {
corb_t verb[2];
verb[0] = MAKE_VERB(codec->addr, stream->io_widgets[i],
VID_SET_CONVERTER_FORMAT, format);
uint32 val = stream->id << 4;
if (channelNum < stream->num_channels)
val |= channelNum;
else
val = 0;
verb[1] = MAKE_VERB(codec->addr, stream->io_widgets[i],
VID_SET_CONVERTER_STREAM_CHANNEL, val);
uint32 response[2];
hda_send_verbs(codec, verb, response, 2);
dprintf("%" B_PRIu32 " ", stream->io_widgets[i]);
hda_widget* widget = hda_audio_group_get_widget(audioGroup,
stream->io_widgets[i]);
if ((widget->capabilities.audio & AUDIO_CAP_DIGITAL) != 0) {
verb[0] = MAKE_VERB(codec->addr, stream->io_widgets[i],
VID_SET_DIGITAL_CONVERTER_CONTROL1, format);
hda_send_verbs(codec, verb, response, 1);
}
}
dprintf("\n");
snooze(1000);
return B_OK;
}
status_t
hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count)
{
hda_controller* controller = codec->controller;
uint32 sent = 0;
codec->response_count = 0;
while (sent < count) {
uint32 readPos = controller->Read16(HDAC_CORB_READ_POS);
uint32 queued = 0;
while (sent < count) {
uint32 writePos = next_corb(controller);
if (writePos == readPos) {
break;
}
controller->corb[writePos] = verbs[sent++];
TRACE_VERBS("send_verb: (%02x:%02x.%x:%u) cmd 0x%08" B_PRIx32 "\n",
controller->pci_info.bus, controller->pci_info.device,
controller->pci_info.function, codec->addr, controller->corb[writePos]);
controller->corb_write_pos = writePos;
queued++;
}
controller->Write16(HDAC_CORB_WRITE_POS, controller->corb_write_pos);
status_t status = acquire_sem_etc(codec->response_sem, queued,
B_RELATIVE_TIMEOUT, 50000ULL);
if (status != B_OK)
return status;
}
if (responses != NULL) {
TRACE_VERBS("send_verb: (%02x:%02x.%x:%u) resp 0x%08" B_PRIx32 "\n",
controller->pci_info.bus, controller->pci_info.device,
controller->pci_info.function, codec->addr, codec->responses[0]);
memcpy(responses, codec->responses, count * sizeof(uint32));
}
return B_OK;
}
status_t
hda_verb_write(hda_codec* codec, uint32 nid, uint32 vid, uint16 payload)
{
corb_t verb = MAKE_VERB(codec->addr, nid, vid, payload);
return hda_send_verbs(codec, &verb, NULL, 1);
}
status_t
hda_verb_read(hda_codec* codec, uint32 nid, uint32 vid, uint32* response)
{
corb_t verb = MAKE_VERB(codec->addr, nid, vid, 0);
return hda_send_verbs(codec, &verb, response, 1);
}
status_t
hda_hw_init(hda_controller* controller)
{
uint16 capabilities;
uint16 stateStatus;
uint16 cmd;
status_t status;
const pci_info& pciInfo = controller->pci_info;
uint32 quirks = get_controller_quirks(pciInfo);
gPci->set_powerstate(pciInfo.bus, pciInfo.device, pciInfo.function,
PCI_pm_state_d0);
phys_addr_t physicalAddress = pciInfo.u.h0.base_registers[0];
if ((pciInfo.u.h0.base_register_flags[0] & PCI_address_type)
== PCI_address_type_64) {
physicalAddress |= (uint64)pciInfo.u.h0.base_registers[1] << 32;
}
controller->regs_area = map_physical_memory("hda_hw_regs",
physicalAddress, pciInfo.u.h0.base_register_sizes[0],
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void**)&controller->regs);
if (controller->regs_area < B_OK) {
status = controller->regs_area;
goto error;
}
cmd = gPci->read_pci_config(pciInfo.bus, pciInfo.device, pciInfo.function,
PCI_command, 2);
if (!(cmd & PCI_command_master)) {
dprintf("hda: enabling PCI bus mastering\n");
cmd |= PCI_command_master;
}
if (!(cmd & PCI_command_memory)) {
dprintf("hda: enabling PCI memory access\n");
cmd |= PCI_command_memory;
}
gPci->write_pci_config(pciInfo.bus, pciInfo.device, pciInfo.function,
PCI_command, 2, cmd);
if ((quirks & HDA_QUIRK_NOINIT_MISCBDCGE) != 0) {
dprintf("hda: quirk disable miscbdcge on init\n");
update_pci_register(controller,
INTEL_SCH_HDA_CGCTL, ~INTEL_SCH_HDA_CGCTL_MISCBDCGE, 0, 1);
}
controller->Write32(HDAC_INTR_CONTROL, 0);
controller->irq = pciInfo.u.h0.interrupt_line;
controller->msi = false;
if (controller->irq == 0xff)
controller->irq = 0;
if ((quirks & HDA_QUIRK_NO_MSI) == 0
&& gPci->get_msi_count(pciInfo.bus, pciInfo.device,
pciInfo.function) >= 1) {
uint32 vector;
if (gPci->configure_msi(pciInfo.bus, pciInfo.device,
pciInfo.function, 1, &vector) == B_OK && gPci->enable_msi(
pciInfo.bus, pciInfo.device, pciInfo.function) == B_OK) {
dprintf("hda: using MSI vector %" B_PRIu32 "\n", vector);
controller->irq = vector;
controller->msi = true;
}
}
if (controller->irq == 0) {
status = ENODEV;
goto no_irq_handler;
}
status = install_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller, 0);
if (status != B_OK)
goto no_irq_handler;
cmd = gPci->read_pci_config(pciInfo.bus, pciInfo.device, pciInfo.function,
PCI_command, 2);
if (controller->msi != ((cmd & PCI_command_int_disable) != 0)) {
if ((cmd & PCI_command_int_disable) != 0) {
dprintf("hda: enabling PCI interrupts\n");
cmd &= ~PCI_command_int_disable;
} else {
dprintf("hda: disabling PCI interrupts for MSI use\n");
cmd |= PCI_command_int_disable;
}
gPci->write_pci_config(pciInfo.bus, pciInfo.device, pciInfo.function,
PCI_command, 2, cmd);
}
if ((quirks & HDA_QUIRK_NOTCSEL) == 0) {
update_pci_register(controller, PCI_HDA_TCSEL, PCI_HDA_TCSEL_MASK, 0,
1);
}
controller->dma_snooping = false;
if ((quirks & HDA_QUIRK_SNOOP) != 0) {
switch (pciInfo.vendor_id) {
case PCI_VENDOR_NVIDIA:
{
controller->dma_snooping = update_pci_register(controller,
NVIDIA_HDA_TRANSREG, NVIDIA_HDA_TRANSREG_MASK,
NVIDIA_HDA_ENABLE_COHBITS, 1, true);
if (!controller->dma_snooping)
break;
controller->dma_snooping = update_pci_register(controller,
NVIDIA_HDA_ISTRM_COH, ~NVIDIA_HDA_ENABLE_COHBIT,
NVIDIA_HDA_ENABLE_COHBIT, 1, true);
if (!controller->dma_snooping)
break;
controller->dma_snooping = update_pci_register(controller,
NVIDIA_HDA_OSTRM_COH, ~NVIDIA_HDA_ENABLE_COHBIT,
NVIDIA_HDA_ENABLE_COHBIT, 1, true);
break;
}
case PCI_VENDOR_AMD:
case PCI_VENDOR_ATI:
{
controller->dma_snooping = update_pci_register(controller,
ATI_HDA_MISC_CNTR2, ATI_HDA_MISC_CNTR2_MASK,
ATI_HDA_ENABLE_SNOOP, 1, true);
break;
}
case PCI_VENDOR_INTEL:
controller->dma_snooping = update_pci_register(controller,
INTEL_SCH_HDA_DEVC, ~INTEL_SCH_HDA_DEVC_SNOOP, 0, 2, true);
break;
}
}
dprintf("hda: DMA snooping: %s\n",
controller->dma_snooping ? "yes" : "no");
capabilities = controller->Read16(HDAC_GLOBAL_CAP);
controller->num_input_streams = GLOBAL_CAP_INPUT_STREAMS(capabilities);
controller->num_output_streams = GLOBAL_CAP_OUTPUT_STREAMS(capabilities);
controller->num_bidir_streams = GLOBAL_CAP_BIDIR_STREAMS(capabilities);
controller->is_64_bit = GLOBAL_CAP_64BIT(capabilities)
&& (quirks & HDA_QUIRK_NO_64BITDMA) == 0;
dprintf("hda: HDA v%d.%d, O:%" B_PRIu32 "/I:%" B_PRIu32 "/B:%" B_PRIu32
", #SDO:%d, 64bit:%s\n",
controller->Read8(HDAC_VERSION_MAJOR),
controller->Read8(HDAC_VERSION_MINOR),
controller->num_output_streams, controller->num_input_streams,
controller->num_bidir_streams,
GLOBAL_CAP_NUM_SDO(capabilities),
controller->is_64_bit ? "yes" : "no");
status = reset_controller(controller);
if (status != B_OK) {
dprintf("hda: reset_controller failed\n");
goto reset_failed;
}
status = init_corb_rirb_pos(controller, quirks);
if (status != B_OK) {
dprintf("hda: init_corb_rirb_pos failed\n");
goto corb_rirb_failed;
}
controller->ReadModifyWrite16(HDAC_WAKE_ENABLE, HDAC_WAKE_ENABLE_MASK, 0);
controller->Write32(HDAC_INTR_CONTROL, INTR_CONTROL_GLOBAL_ENABLE
| INTR_CONTROL_CONTROLLER_ENABLE);
if ((quirks & HDA_QUIRK_NOINIT_MISCBDCGE) != 0) {
update_pci_register(controller,
INTEL_SCH_HDA_CGCTL, ~INTEL_SCH_HDA_CGCTL_MISCBDCGE,
INTEL_SCH_HDA_CGCTL_MISCBDCGE, 1);
}
snooze(1000);
stateStatus = controller->Read16(HDAC_STATE_STATUS);
if (!stateStatus) {
dprintf("hda: bad codec status\n");
status = ENODEV;
goto corb_rirb_failed;
}
controller->Write16(HDAC_STATE_STATUS, stateStatus);
for (uint32 index = 0; index < HDA_MAX_CODECS; index++) {
if ((stateStatus & (1 << index)) != 0)
hda_codec_new(controller, index);
}
for (uint32 index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index]
&& controller->codecs[index]->num_audio_groups > 0) {
controller->active_codec = controller->codecs[index];
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);
reset_failed:
remove_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller);
no_irq_handler:
if (controller->msi) {
gPci->disable_msi(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function);
gPci->unconfigure_msi(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function);
}
delete_area(controller->regs_area);
controller->regs_area = B_ERROR;
controller->regs = NULL;
error:
dprintf("hda: ERROR: %s(%" B_PRIx32 ")\n", strerror(status), status);
return status;
}
void
hda_hw_stop(hda_controller* controller)
{
for (uint32 index = 0; index < HDA_MAX_STREAMS; index++) {
if (controller->streams[index] && controller->streams[index]->running)
hda_stream_stop(controller, controller->streams[index]);
}
for (uint32 index = 0; index < controller->active_codec->num_audio_groups; index++) {
hda_audio_group* audioGroup = controller->active_codec->audio_groups[index];
corb_t verb = MAKE_VERB(audioGroup->codec->addr, audioGroup->widget.node_id,
VID_SET_POWER_STATE, 3);
hda_send_verbs(audioGroup->codec, &verb, NULL, 1);
}
}
void
hda_hw_uninit(hda_controller* controller)
{
if (controller == NULL)
return;
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);
controller->Write32(HDAC_INTR_CONTROL, 0);
remove_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller);
if (controller->msi) {
gPci->disable_msi(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function);
gPci->unconfigure_msi(controller->pci_info.bus,
controller->pci_info.device, controller->pci_info.function);
}
if (controller->corb_rirb_pos_area >= 0) {
delete_area(controller->corb_rirb_pos_area);
controller->corb_rirb_pos_area = B_ERROR;
controller->corb = NULL;
controller->rirb = NULL;
controller->stream_positions = NULL;
}
if (controller->regs_area >= 0) {
delete_area(controller->regs_area);
controller->regs_area = B_ERROR;
controller->regs = NULL;
}
for (uint32 index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index] != NULL)
hda_codec_delete(controller->codecs[index]);
}
controller->active_codec = NULL;
}