/*
 * File: HdaCodecInfo.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.
 */

#include "HdaCodec.h"

/**
  Gets the codec's name.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] CodecName         A pointer to the buffer to return the codec name.

  @retval EFI_SUCCESS           The codec name was retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetCodecName(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT CHAR16 **CodecName) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetCodecName(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;

    // If parameters are null, fail.
    if ((This == NULL) || (CodecName == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and fill codec name parameter.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    *CodecName = HdaPrivateData->HdaCodecDev->Name;
    return EFI_SUCCESS;
}

/**
  Gets the codec's vendor and device ID.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] VendorId          The vendor and device ID of the codec.

  @retval EFI_SUCCESS           The vendor and device ID was retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetVendorId(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT UINT32 *VendorId) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetVendorId(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;

    // If parameters are null, fail.
    if ((This == NULL) || (VendorId == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and fill vendor ID parameter.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    *VendorId = HdaPrivateData->HdaCodecDev->VendorId;
    return EFI_SUCCESS;
}

/**
  Gets the codec's revision ID.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] RevisionId        The revision ID of the codec.

  @retval EFI_SUCCESS           The revision ID was retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetRevisionId(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT UINT32 *RevisionId) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetRevisionId(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;

    // If parameters are null, fail.
    if ((This == NULL) || (RevisionId == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and fill revision ID parameter.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    *RevisionId = HdaPrivateData->HdaCodecDev->RevisionId;
    return EFI_SUCCESS;
}

/**
  Gets the node ID of the codec's audio function.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] AudioFuncId       The node ID of the codec's audio function.

  @retval EFI_SUCCESS           The node ID was retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetAudioFuncId(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT UINT8 *AudioFuncId,
    OUT BOOLEAN *UnsolCapable) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetAudioFuncId(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;

    // If parameters are null, fail.
    if ((This == NULL) || (AudioFuncId == NULL) || (UnsolCapable == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and fill node ID parameter.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    *AudioFuncId = HdaPrivateData->HdaCodecDev->AudioFuncGroup->NodeId;
    *UnsolCapable = HdaPrivateData->HdaCodecDev->AudioFuncGroup->UnsolCapable;
    return EFI_SUCCESS;
}

/**
  Gets the codec's default supported stream rates and formats.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] Rates             The default supported rates.
  @param[out] Formats           The default supported formats.

  @retval EFI_SUCCESS           The stream rates and formats were retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetDefaultRatesFormats(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT UINT32 *Rates,
    OUT UINT32 *Formats) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetDefaultRatesFormats(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;

    // If parameters are null, fail.
    if ((This == NULL) || (Rates == NULL) || (Formats == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and fill rates and formats parameters.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    *Rates = HdaPrivateData->HdaCodecDev->AudioFuncGroup->SupportedPcmRates;
    *Formats = HdaPrivateData->HdaCodecDev->AudioFuncGroup->SupportedFormats;
    return EFI_SUCCESS;
}

/**
  Gets the codec's default amp capabilities.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] AmpInCaps         The default input amp capabilities.
  @param[out] AmpOutCaps        The default output amp capabilities.

  @retval EFI_SUCCESS           The default amp capabilities were retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetDefaultAmpCaps(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT UINT32 *AmpInCaps,
    OUT UINT32 *AmpOutCaps) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetDefaultAmpCaps(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;

    // If parameters are null, fail.
    if ((This == NULL) || (AmpInCaps == NULL) || (AmpOutCaps == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and fill amp caps parameters.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    *AmpInCaps = HdaPrivateData->HdaCodecDev->AudioFuncGroup->AmpInCapabilities;
    *AmpOutCaps = HdaPrivateData->HdaCodecDev->AudioFuncGroup->AmpOutCapabilities;
    return EFI_SUCCESS;
}

/**
  Gets the codec's widgets.

  @param[in]  This              A pointer to the EFI_HDA_CODEC_INFO_PROTOCOL instance.
  @param[out] Widgets           A pointer to the buffer to return the requested array of widgets.
  @param[out] WidgetCount       The number of widgets returned in Widgets.

  @retval EFI_SUCCESS           The widgets were retrieved.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
  @retval EFI_OUT_OF_RESOURCES  A buffer couldn't be allocated.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoGetWidgets(
    IN  EFI_HDA_CODEC_INFO_PROTOCOL *This,
    OUT HDA_WIDGET **Widgets,
    OUT UINTN *WidgetCount) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoGetWidgets(): start\n"));

    // Create variables.
    HDA_CODEC_INFO_PRIVATE_DATA *HdaPrivateData;
    HDA_WIDGET_DEV *HdaWidgetDev;
    UINT8 AmpInCount;
    HDA_WIDGET *HdaWidgets;
    UINTN HdaWidgetsCount;

    // If parameters are null, fail.
    if ((This == NULL) || (Widgets == NULL) || (WidgetCount == NULL))
        return EFI_INVALID_PARAMETER;

    // Get private data and allocate widgets array.
    HdaPrivateData = HDA_CODEC_INFO_PRIVATE_DATA_FROM_THIS(This);
    HdaWidgetsCount = HdaPrivateData->HdaCodecDev->AudioFuncGroup->WidgetsCount;
    HdaWidgets = AllocateZeroPool(sizeof(HDA_WIDGET) * HdaWidgetsCount);
    if (HdaWidgets == NULL)
        return EFI_OUT_OF_RESOURCES;

    // Populate widgets array.
    for (UINTN w = 0; w < HdaWidgetsCount; w++) {
        // Get widget.
        HdaWidgetDev = HdaPrivateData->HdaCodecDev->AudioFuncGroup->Widgets + w;

        // Get basic data.
        HdaWidgets[w].NodeId = HdaWidgetDev->NodeId;
        HdaWidgets[w].Capabilities = HdaWidgetDev->Capabilities;
        HdaWidgets[w].DefaultUnSol = HdaWidgetDev->DefaultUnSol;
        HdaWidgets[w].DefaultEapd = HdaWidgetDev->DefaultEapd;

        // Get connections.
        HdaWidgets[w].ConnectionListLength = HdaWidgetDev->ConnectionListLength;
        HdaWidgets[w].Connections = AllocateZeroPool(sizeof(UINT16) * HdaWidgetDev->ConnectionCount);
        if (HdaWidgets[w].Connections == NULL)
            goto FREE_WIDGETS;
        CopyMem(HdaWidgets[w].Connections, HdaWidgetDev->Connections, sizeof(UINT16) * HdaWidgetDev->ConnectionCount);

        // Get power info.
        HdaWidgets[w].SupportedPowerStates = HdaWidgetDev->SupportedPowerStates;
        HdaWidgets[w].DefaultPowerState = HdaWidgetDev->DefaultPowerState;

        // Get input amps.
        HdaWidgets[w].AmpInCapabilities = HdaWidgetDev->AmpInCapabilities;
        if (HdaWidgetDev->Capabilities & HDA_PARAMETER_WIDGET_CAPS_IN_AMP) {
            // Determine number of input amps.
            AmpInCount = HdaWidgetDev->ConnectionCount;
            if (AmpInCount < 1)
                AmpInCount = 1;

            // Allocate arrays.
            HdaWidgets[w].AmpInLeftDefaultGainMute = AllocateZeroPool(sizeof(UINT8) * AmpInCount);
            HdaWidgets[w].AmpInRightDefaultGainMute = AllocateZeroPool(sizeof(UINT8) * AmpInCount);
            if ((HdaWidgets[w].AmpInLeftDefaultGainMute == NULL) ||
                (HdaWidgets[w].AmpInRightDefaultGainMute == NULL))
                goto FREE_WIDGETS;

            // Copy arrays.
            CopyMem(HdaWidgets[w].AmpInLeftDefaultGainMute, HdaWidgetDev->AmpInLeftDefaultGainMute,
                sizeof(UINT8) * AmpInCount);
            CopyMem(HdaWidgets[w].AmpInRightDefaultGainMute, HdaWidgetDev->AmpInRightDefaultGainMute,
                sizeof(UINT8) * AmpInCount);
        }

        // Get output amp.
        HdaWidgets[w].AmpOutCapabilities = HdaWidgetDev->AmpOutCapabilities;
        HdaWidgets[w].AmpOutLeftDefaultGainMute = HdaWidgetDev->AmpOutLeftDefaultGainMute;
        HdaWidgets[w].AmpOutRightDefaultGainMute = HdaWidgetDev->AmpOutRightDefaultGainMute;

        // Get input/output data.
        HdaWidgets[w].SupportedPcmRates = HdaWidgetDev->SupportedPcmRates;
        HdaWidgets[w].SupportedFormats = HdaWidgetDev->SupportedFormats;
        HdaWidgets[w].DefaultConvFormat = HdaWidgetDev->DefaultConvFormat;
        HdaWidgets[w].DefaultConvStreamChannel = HdaWidgetDev->DefaultConvStreamChannel;
        HdaWidgets[w].DefaultConvChannelCount = HdaWidgetDev->DefaultConvChannelCount;

        // Get pin complex data.
        HdaWidgets[w].PinCapabilities = HdaWidgetDev->PinCapabilities;
        HdaWidgets[w].DefaultPinControl = HdaWidgetDev->DefaultPinControl;
        HdaWidgets[w].DefaultConfiguration = HdaWidgetDev->DefaultConfiguration;

        // Get volume knob data.
        HdaWidgets[w].VolumeCapabilities = HdaWidgetDev->VolumeCapabilities;
        HdaWidgets[w].DefaultVolume = HdaWidgetDev->DefaultVolume;
    }

    // Fill parameters.
    *WidgetCount = HdaWidgetsCount;
    *Widgets = HdaWidgets;
    return EFI_SUCCESS;

FREE_WIDGETS:
    HdaCodecInfoFreeWidgetsBuffer(HdaWidgets, HdaWidgetsCount);
    return EFI_OUT_OF_RESOURCES;
}

/**
  Frees an array of HDA_WIDGET.

  @param[in] Widgets            A pointer to the buffer array of widgets that is to be freed.
  @param[in] WidgetCount        The number of widgets in Widgets.

  @retval EFI_SUCCESS           The buffer was freed.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
**/
EFI_STATUS
EFIAPI
HdaCodecInfoFreeWidgetsBuffer(
    IN  HDA_WIDGET *Widgets,
    IN  UINTN WidgetCount) {
    //DEBUG((DEBUG_INFO, "HdaCodecInfoFreeWidgetsBuffer(): start\n"));

    // If parameter is null, fail.
    if (Widgets == NULL)
        return EFI_INVALID_PARAMETER;

    // Free pool buffers.
    for (UINTN w = 0; w < WidgetCount; w++) {
        if (Widgets[w].Connections != NULL)
            FreePool(Widgets[w].Connections);
        if (Widgets[w].AmpInLeftDefaultGainMute != NULL)
            FreePool(Widgets[w].AmpInLeftDefaultGainMute);
        if (Widgets[w].AmpInRightDefaultGainMute != NULL)
            FreePool(Widgets[w].AmpInRightDefaultGainMute);
    }
    FreePool(Widgets);
    return EFI_SUCCESS;
}
