From 2d498c889c489534c879ad4d682dc98dbd18ed7d Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 1 Sep 2014 21:47:04 +0200 Subject: [PATCH] Use correct sequence for resetting CORB read pointer * Spec requires software to wait for hardware to acknowledge the reset by waiting for the bit to become set and then manually unset it and again wait for it to become unset. (cf. HDA spec 1.0a ch. 3.3.21) * Also, do read-modify-write to protect preserved bits --- .../drivers/audio/hda/hda_controller.cpp | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) 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 b812b7caf5..74cdbf54f6 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp @@ -495,11 +495,38 @@ init_corb_rirb_pos(hda_controller* controller) ((uint8*)controller->corb + posOffset); controller->Write16(HDAC_CORB_WRITE_POS, 0); - // Reset CORB read pointer - controller->Write16(HDAC_CORB_READ_POS, CORB_READ_POS_RESET); - // Reading CORB_READ_POS_RESET as zero fails on some chips. - // We reset the bit here. - controller->Write16(HDAC_CORB_READ_POS, 0); + + // Reset CORB read pointer. Preseve bits marked as RsvdP. + // After setting the reset bit, we must wait for the hardware + // to acknowledge it, then manually unset it and wait for that + // to be acknowledged as well. + uint16 corbReadPointer = controller->Read16(HDAC_CORB_READ_POS); + + corbReadPointer |= CORB_READ_POS_RESET; + controller->Write16(HDAC_CORB_READ_POS, corbReadPointer); + for (int timeout = 0; timeout < 10; timeout++) { + snooze(100); + corbReadPointer = controller->Read16(HDAC_CORB_READ_POS); + if ((corbReadPointer & CORB_READ_POS_RESET) != 0) + break; + } + if ((corbReadPointer & CORB_READ_POS_RESET) == 0) { + dprintf("hda: CORB read pointer reset failed\n"); + return B_BUSY; + } + + corbReadPointer &= ~CORB_READ_POS_RESET; + controller->Write16(HDAC_CORB_READ_POS, corbReadPointer); + for (int timeout = 0; timeout < 10; timeout++) { + snooze(100); + corbReadPointer = controller->Read16(HDAC_CORB_READ_POS); + if ((corbReadPointer & CORB_READ_POS_RESET) == 0) + break; + } + if ((corbReadPointer & CORB_READ_POS_RESET) != 0) { + dprintf("hda: CORB read pointer reset failed\n"); + return B_BUSY; + } // Reset RIRB write pointer controller->Write16(HDAC_RIRB_WRITE_POS, RIRB_WRITE_POS_RESET);