/*
 * File: HdaCodecAudioIo.c
 *
 * Copyright (c) 2018 John Davis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#ifdef _WIN32
#pragma warning  (disable: 4054)
#endif
//#include "HdaCodec.h"
#include "../AudioDxe.h"
//#include <IndustryStandard/HdaCodec.h>
#include <Protocol/AudioIo.h>
/*
// HDA I/O Stream callback.
VOID
HdaCodecHdaIoStreamCallback(
    IN EFI_HDA_IO_PROTOCOL_TYPE Type,
    IN VOID *Context1,
    IN VOID *Context2,
    IN VOID *Context3)
{
//    DEBUG((DEBUG_INFO, "HdaCodecHdaIoStreamCallback(): start\n"));

    // Create variables.
    EFI_AUDIO_IO_PROTOCOL *AudioIo = (EFI_AUDIO_IO_PROTOCOL*)Context1;
    EFI_AUDIO_IO_CALLBACK AudioIoCallback = (EFI_AUDIO_IO_CALLBACK)Context2;

    // Ensure required parameters are valid.
    if ((AudioIo == NULL) || (AudioIoCallback == NULL))
        return;

    // Invoke callback.
    AudioIoCallback(AudioIo, Context3);
}
*/
/**
  Gets the collection of output ports.

  @param[in]  This              A pointer to the EFI_AUDIO_IO_PROTOCOL instance.
  @param[out] OutputPorts       A pointer to a buffer where the output ports will be placed.
  @param[out] OutputPortsCount  The number of ports in OutputPorts.

  @retval EFI_SUCCESS           The audio data was played successfully.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecAudioIoGetOutputs(
    IN  EFI_AUDIO_IO_PROTOCOL *This,
    OUT EFI_AUDIO_IO_PROTOCOL_PORT **OutputPorts,
    OUT UINTN *OutputPortsCount)
{
//  DEBUG((DEBUG_INFO, "HdaCodecAudioIoGetOutputs(): start\n"));

  // Create variables.
  EFI_STATUS Status;
  AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
  HDA_CODEC_DEV *HdaCodecDev;
  EFI_AUDIO_IO_PROTOCOL_PORT *HdaOutputPorts;
  UINT32 SupportedRates;
  UINTN i;

  // If a parameter is invalid, return error.
  if ((This == NULL) || (OutputPorts == NULL) ||
      (OutputPortsCount == NULL))
    return EFI_INVALID_PARAMETER;

  // Get private data.
  AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(This);
  if (!AudioIoPrivateData) {
    return EFI_NOT_STARTED;
  }

  HdaCodecDev = AudioIoPrivateData->HdaCodecDev;

  // Allocate buffer.
  HdaOutputPorts = AllocateZeroPool(sizeof(EFI_AUDIO_IO_PROTOCOL_PORT) * HdaCodecDev->OutputPortsCount);
  if (HdaOutputPorts == NULL)
    return EFI_OUT_OF_RESOURCES;

  // Get output ports.
  for (i = 0; i < HdaCodecDev->OutputPortsCount; i++) {
    // Port is an output.
    HdaOutputPorts[i].Type = EfiAudioIoTypeOutput;

    // Get device type.
    switch (HDA_VERB_GET_CONFIGURATION_DEFAULT_DEVICE(HdaCodecDev->OutputPorts[i]->DefaultConfiguration))
    {
      case HDA_CONFIG_DEFAULT_DEVICE_LINE_OUT:
      case HDA_CONFIG_DEFAULT_DEVICE_LINE_IN:
        HdaOutputPorts[i].Device = EfiAudioIoDeviceLine;
        break;

      case HDA_CONFIG_DEFAULT_DEVICE_SPEAKER:
        HdaOutputPorts[i].Device = EfiAudioIoDeviceSpeaker;
        break;

      case HDA_CONFIG_DEFAULT_DEVICE_HEADPHONE_OUT:
        HdaOutputPorts[i].Device = EfiAudioIoDeviceHeadphones;
        break;

      case HDA_CONFIG_DEFAULT_DEVICE_SPDIF_OUT:
      case HDA_CONFIG_DEFAULT_DEVICE_SPDIF_IN:
        HdaOutputPorts[i].Device = EfiAudioIoDeviceSpdif;
        break;

      case HDA_CONFIG_DEFAULT_DEVICE_MIC_IN:
        HdaOutputPorts[i].Device = EfiAudioIoDeviceMic;
        break;

      default:
        if (HdaCodecDev->OutputPorts[i]->PinCapabilities & HDA_PARAMETER_PIN_CAPS_HDMI)
          HdaOutputPorts[i].Device = EfiAudioIoDeviceHdmi;
        else
          HdaOutputPorts[i].Device = EfiAudioIoDeviceOther;
    }

    // Get location.
    switch (HDA_VERB_GET_CONFIGURATION_DEFAULT_LOC(HdaCodecDev->OutputPorts[i]->DefaultConfiguration)) {
      case HDA_CONFIG_DEFAULT_LOC_SPEC_NA:
        HdaOutputPorts[i].Location = EfiAudioIoLocationNone;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SPEC_REAR:
        HdaOutputPorts[i].Location = EfiAudioIoLocationRear;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SPEC_FRONT:
        HdaOutputPorts[i].Location = EfiAudioIoLocationFront;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SPEC_LEFT:
        HdaOutputPorts[i].Location = EfiAudioIoLocationLeft;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SPEC_RIGHT:
        HdaOutputPorts[i].Location = EfiAudioIoLocationRight;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SPEC_TOP:
        HdaOutputPorts[i].Location = EfiAudioIoLocationTop;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SPEC_BOTTOM:
        HdaOutputPorts[i].Location = EfiAudioIoLocationBottom;
        break;

      default:
        HdaOutputPorts[i].Location = EfiAudioIoLocationOther;
    }

    // Get surface.
    switch (HDA_VERB_GET_CONFIGURATION_DEFAULT_SURF(HdaCodecDev->OutputPorts[i]->DefaultConfiguration)) {
      case HDA_CONFIG_DEFAULT_LOC_SURF_EXTERNAL:
        HdaOutputPorts[i].Surface = EfiAudioIoSurfaceExternal;
        break;

      case HDA_CONFIG_DEFAULT_LOC_SURF_INTERNAL:
        HdaOutputPorts[i].Surface = EfiAudioIoSurfaceInternal;
        break;

      default:
        HdaOutputPorts[i].Surface = EfiAudioIoSurfaceOther;
    }

    // Get supported stream formats.
    Status = HdaCodecGetSupportedPcmRates(HdaCodecDev->OutputPorts[i], &SupportedRates);
    if (EFI_ERROR(Status))
      return Status;

    // Get supported bit depths.
    HdaOutputPorts[i].SupportedBits = 0;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_8BIT)
      HdaOutputPorts[i].SupportedBits |= EfiAudioIoBits8;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_16BIT)
      HdaOutputPorts[i].SupportedBits |= EfiAudioIoBits16;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_20BIT)
      HdaOutputPorts[i].SupportedBits |= EfiAudioIoBits20;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_24BIT)
      HdaOutputPorts[i].SupportedBits |= EfiAudioIoBits24;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_32BIT)
      HdaOutputPorts[i].SupportedBits |= EfiAudioIoBits32;

    // Get supported sample rates.
    HdaOutputPorts[i].SupportedFreqs = 0;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_8KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq8kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_11KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq11kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_16KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq16kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_22KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq22kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_32KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq32kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_44KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq44kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_48KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq48kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_88KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq88kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_96KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq96kHz;
    if (SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_192KHZ)
      HdaOutputPorts[i].SupportedFreqs |= EfiAudioIoFreq192kHz;
  }

  // Ports gotten successfully.
  *OutputPorts = HdaOutputPorts;
  *OutputPortsCount = HdaCodecDev->OutputPortsCount;
  return EFI_SUCCESS;
}

/**
  Sets up the device to play audio data.

  @param[in] This               A pointer to the EFI_AUDIO_IO_PROTOCOL instance.
  @param[in] OutputIndex        The zero-based index of the desired output.
  @param[in] Volume             The volume (0-100) to use.
  @param[in] Bits               The width in bits of the source data.
  @param[in] Freq               The frequency of the source data.
  @param[in] Channels           The number of channels the source data contains.

  @retval EFI_SUCCESS           The audio data was played successfully.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecAudioIoSetupPlayback(
    IN EFI_AUDIO_IO_PROTOCOL *This,
    IN UINT8 OutputIndex,
    IN UINT8 Volume,
    IN EFI_AUDIO_IO_PROTOCOL_FREQ Freq,
    IN EFI_AUDIO_IO_PROTOCOL_BITS Bits,
    IN UINT8 Channels)
{
//    DEBUG((DEBUG_INFO, "HdaCodecAudioIoSetupPlayback(): start\n"));

    // Create variables.
    EFI_STATUS Status;
    AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
    HDA_CODEC_DEV *HdaCodecDev;
    EFI_HDA_IO_PROTOCOL *HdaIo;

    // Widgets.
    HDA_WIDGET_DEV *PinWidget;
    HDA_WIDGET_DEV *OutputWidget;
    UINT32 SupportedRates;
    UINT8 HdaStreamId;

    // Stream.
    UINT8 StreamBits, StreamDiv, StreamMult = 0;
    BOOLEAN StreamBase44kHz = FALSE;
    UINT16 StreamFmt;

    // If a parameter is invalid, return error.
    if ((This == NULL) || (Volume > EFI_AUDIO_IO_PROTOCOL_MAX_VOLUME))
        return EFI_INVALID_PARAMETER;

    // Get private data.
    AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(This);
    HdaCodecDev = AudioIoPrivateData->HdaCodecDev;
    HdaIo = HdaCodecDev->HdaIo;

    // Check that output index is within bounds and get our desired output.
    if (OutputIndex >= HdaCodecDev->OutputPortsCount)
        return EFI_INVALID_PARAMETER;
    PinWidget = HdaCodecDev->OutputPorts[OutputIndex];

    // Get the output DAC for the path.
    Status = HdaCodecGetOutputDac(PinWidget, &OutputWidget);
    if (EFI_ERROR(Status))
        return Status;

    // Get supported stream formats.
    Status = HdaCodecGetSupportedPcmRates(OutputWidget, &SupportedRates);
    if (EFI_ERROR(Status))
        return Status;

    // Determine bitness of samples, ensuring desired bitness is supported.
    switch (Bits) {
        // 8-bit.
        case EfiAudioIoBits8:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_8BIT))
                return EFI_UNSUPPORTED;
            StreamBits = HDA_CONVERTER_FORMAT_BITS_8;
            break;

        // 16-bit.
        case EfiAudioIoBits16:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_16BIT))
                return EFI_UNSUPPORTED;
            StreamBits = HDA_CONVERTER_FORMAT_BITS_16;
            break;

        // 20-bit.
        case EfiAudioIoBits20:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_20BIT))
                return EFI_UNSUPPORTED;
            StreamBits = HDA_CONVERTER_FORMAT_BITS_20;
            break;

        // 24-bit.
        case EfiAudioIoBits24:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_24BIT))
                return EFI_UNSUPPORTED;
            StreamBits = HDA_CONVERTER_FORMAT_BITS_24;
            break;

        // 32-bit.
        case EfiAudioIoBits32:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_32BIT))
                return EFI_UNSUPPORTED;
            StreamBits = HDA_CONVERTER_FORMAT_BITS_32;
            break;

        // Others.
        default:
            return EFI_INVALID_PARAMETER;
    }

    // Determine base, divisor, and multipler.
    switch (Freq) {
        // 8 kHz.
        case EfiAudioIoFreq8kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_8KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = FALSE;
            StreamDiv = 6;
            StreamMult = 1;
            break;

        // 11.025 kHz.
        case EfiAudioIoFreq11kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_11KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = TRUE;
            StreamDiv = 4;
            StreamMult = 1;
            break;

        // 16 kHz.
        case EfiAudioIoFreq16kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_16KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = FALSE;
            StreamDiv = 3;
            StreamMult = 1;
            break;

        // 22.05 kHz.
        case EfiAudioIoFreq22kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_22KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = TRUE;
            StreamDiv = 2;
            StreamMult = 1;
            break;

        // 32 kHz.
        case EfiAudioIoFreq32kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_32KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = FALSE;
            StreamDiv = 3;
            StreamMult = 2;
            break;

        // 44.1 kHz.
        case EfiAudioIoFreq44kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_44KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = TRUE;
            StreamDiv = 1;
            StreamMult = 1;
            break;

        // 48 kHz.
        case EfiAudioIoFreq48kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_48KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = FALSE;
            StreamDiv = 1;
            StreamMult = 1;
            break;

        // 88 kHz.
        case EfiAudioIoFreq88kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_88KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = TRUE;
            StreamDiv = 1;
            StreamMult = 2;
            break;

        // 96 kHz.
        case EfiAudioIoFreq96kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_96KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = FALSE;
            StreamDiv = 1;
            StreamMult = 2;
            break;

        // 192 kHz.
        case EfiAudioIoFreq192kHz:
            if (!(SupportedRates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_192KHZ))
                return EFI_UNSUPPORTED;
            StreamBase44kHz = FALSE;
            StreamDiv = 1;
            StreamMult = 4;
            break;

        // Others.
        default:
            return EFI_INVALID_PARAMETER;
    }

    // Disable all widget paths.
    for (UINTN w = 0; w < HdaCodecDev->OutputPortsCount; w++) {
        if (w == OutputIndex)
            continue;
        Status = HdaCodecDisableWidgetPath(HdaCodecDev->OutputPorts[w]);
        if (EFI_ERROR(Status))
            return Status;
    }

    // Close stream first.
    Status = HdaIo->CloseStream(HdaIo, EfiHdaIoTypeOutput);
    if (EFI_ERROR(Status))
        return Status;

    // Calculate stream format and setup stream.
    StreamFmt = HDA_CONVERTER_FORMAT_SET(Channels - 1, StreamBits,
        StreamDiv - 1, StreamMult - 1, StreamBase44kHz);
    DEBUG((DEBUG_INFO, "HdaCodecAudioIoPlay(): Stream format 0x%X\n", StreamFmt));
    Status = HdaIo->SetupStream(HdaIo, EfiHdaIoTypeOutput, StreamFmt, &HdaStreamId);
    if (EFI_ERROR(Status))
        return Status;

    // Setup widget path for desired output.
    AudioIoPrivateData->SelectedOutputIndex = OutputIndex;
    Status = HdaCodecEnableWidgetPath(PinWidget, Volume, HdaStreamId, StreamFmt);
    if (EFI_ERROR(Status))
        goto CLOSE_STREAM;
    return EFI_SUCCESS;

CLOSE_STREAM:
    // Close stream.
    HdaIo->CloseStream(HdaIo, EfiHdaIoTypeOutput);
    return Status;
}

/**
  Begins playback on the device and waits for playback to complete.

  @param[in] This               A pointer to the EFI_AUDIO_IO_PROTOCOL instance.
  @param[in] Data               A pointer to the buffer containing the audio data to play.
  @param[in] DataLength         The size, in bytes, of the data buffer specified by Data.
  @param[in] Position           The position in the buffer to start at.

  @retval EFI_SUCCESS           The audio data was played successfully.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecAudioIoStartPlayback(
    IN EFI_AUDIO_IO_PROTOCOL *This,
    IN VOID *Data,
    IN UINTN DataLength,
    IN UINTN Position OPTIONAL)
{
//    DEBUG((DEBUG_INFO, "HdaCodecAudioIoStartPlayback(): start\n"));

    // Create variables.
    EFI_STATUS Status;
    AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
    EFI_HDA_IO_PROTOCOL *HdaIo;
    BOOLEAN StreamRunning = FALSE;

    // If a parameter is invalid, return error.
    if ((This == NULL) || (Data == NULL) || (DataLength == 0))
        return EFI_INVALID_PARAMETER;

    // Get private data.
    AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(This);
  if (!AudioIoPrivateData || !AudioIoPrivateData->HdaCodecDev || !AudioIoPrivateData->HdaCodecDev->HdaIo) {
    return EFI_NOT_FOUND;
  }
    HdaIo = AudioIoPrivateData->HdaCodecDev->HdaIo;

    // Start stream.
    Status = HdaIo->StartStream(HdaIo, EfiHdaIoTypeOutput, Data, DataLength, Position,
        NULL, NULL, NULL, NULL);
    if (EFI_ERROR(Status))
        return Status;

    // Wait for stream to stop.
    StreamRunning = TRUE;
    while (StreamRunning) {
        Status = HdaIo->GetStream(HdaIo, EfiHdaIoTypeOutput, &StreamRunning);
        if (EFI_ERROR(Status)) {
            HdaIo->StopStream(HdaIo, EfiHdaIoTypeOutput);
            return Status;
        }

        // Wait 100ms.
        gBS->Stall(MS_TO_MICROSECOND(100));
    }
    return EFI_SUCCESS;
}

/**
  Begins playback on the device asynchronously.

  @param[in] This               A pointer to the EFI_AUDIO_IO_PROTOCOL instance.
  @param[in] Data               A pointer to the buffer containing the audio data to play.
  @param[in] DataLength         The size, in bytes, of the data buffer specified by Data.
  @param[in] Position           The position in the buffer to start at.
  @param[in] Callback           A pointer to an optional callback to be invoked when playback is complete.
  @param[in] Context            A pointer to data to be passed to the callback function.

  @retval EFI_SUCCESS           The audio data was played successfully.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecAudioIoStartPlaybackAsync(
    IN EFI_AUDIO_IO_PROTOCOL *This,
    IN VOID *Data,
    IN UINTN DataLength,
    IN UINTN Position OPTIONAL,
    IN EFI_AUDIO_IO_CALLBACK Callback OPTIONAL,
    IN VOID *Context OPTIONAL)
{
 //   DEBUG((DEBUG_INFO, "HdaCodecAudioIoStartPlaybackAsync(): start\n"));

    // Create variables.
    EFI_STATUS Status;
    AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
    EFI_HDA_IO_PROTOCOL *HdaIo;

    // If a parameter is invalid, return error.
    if ((This == NULL) || (Data == NULL) || (DataLength == 0))
        return EFI_INVALID_PARAMETER;

    // Get private data.
    AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(This);
  if (!AudioIoPrivateData || !AudioIoPrivateData->HdaCodecDev || !AudioIoPrivateData->HdaCodecDev->HdaIo) {
    return EFI_NOT_FOUND;
  }
    HdaIo = AudioIoPrivateData->HdaCodecDev->HdaIo;

    // Start stream.
//    Status = HdaIo->StartStream(HdaIo, EfiHdaIoTypeOutput, Data, DataLength, Position,
//        (VOID*)HdaCodecHdaIoStreamCallback, (VOID*)This, (VOID*)Callback, Context);
  Status = HdaIo->StartStream(HdaIo, EfiHdaIoTypeOutput, Data, DataLength, Position,
                              NULL, (VOID*)This, NULL, Context);

    return Status;
}

/**
  Stops playback on the device.

  @param[in] This               A pointer to the EFI_AUDIO_IO_PROTOCOL instance.

  @retval EFI_SUCCESS           The audio data was played successfully.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecAudioIoStopPlayback(
    IN EFI_AUDIO_IO_PROTOCOL *This) {
    DEBUG((DEBUG_INFO, "HdaCodecAudioIoStopPlayback(): start\n"));

    // Create variables.
    AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
    EFI_HDA_IO_PROTOCOL *HdaIo;

    // If a parameter is invalid, return error.
    if (This == NULL)
        return EFI_INVALID_PARAMETER;

    // Get private data.
    AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(This);
  if (!AudioIoPrivateData || !AudioIoPrivateData->HdaCodecDev || !AudioIoPrivateData->HdaCodecDev->HdaIo) {
    return EFI_NOT_FOUND;
  }
    HdaIo = AudioIoPrivateData->HdaCodecDev->HdaIo;

    // Stop stream.
    return HdaIo->StopStream(HdaIo, EfiHdaIoTypeOutput);
}
