/*
    ReactOS Sound System
    Sound Blaster DSP support
    Mixer routines

    Author:
        Andrew Greenwood (silverblade@reactos.org)

    History:
        2 July 2008 - Created

    Notes:
        Functions documented in sbdsp.h

        Currently, input/output switches and PC speaker volume
        level are not supported.

        The I/O switches are used for muting/unmuting mic, etc.
*/

#include "precomp.h"

VOID
SbMixerReset(IN PUCHAR BasePort)
{
    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_RESET);
    /* Are we meant to send anything else? */
}

NTSTATUS
SbMixerPackLevelData(
    IN  UCHAR Line,
    IN  UCHAR Level,
    OUT PUCHAR PackedLevel)
{
    if ( ! PackedLevel )
        return STATUS_INVALID_PARAMETER_3;

    switch ( Line )
    {
        case SB_MIX_MASTER_LEFT_LEVEL :
        case SB_MIX_MASTER_RIGHT_LEVEL :
        case SB_MIX_VOC_LEFT_LEVEL :
        case SB_MIX_VOC_RIGHT_LEVEL :
        case SB_MIX_MIDI_LEFT_LEVEL :
        case SB_MIX_MIDI_RIGHT_LEVEL :
        case SB_MIX_CD_LEFT_LEVEL :
        case SB_MIX_CD_RIGHT_LEVEL :
        case SB_MIX_LINE_LEFT_LEVEL :
        case SB_MIX_LINE_RIGHT_LEVEL :
        case SB_MIX_MIC_LEVEL :
        case SB_MIX_LEGACY_MIC_LEVEL :  /* is this correct? */
        {
            if ( Level >= 0x20 )
                return STATUS_INVALID_PARAMETER_2;

            *PackedLevel = Level << 3;
            return STATUS_SUCCESS;
        }

        case SB_MIX_INPUT_LEFT_GAIN :
        case SB_MIX_INPUT_RIGHT_GAIN :
        case SB_MIX_OUTPUT_LEFT_GAIN :
        case SB_MIX_OUTPUT_RIGHT_GAIN :
        {
            if ( Level >= 0x04 )
                return STATUS_INVALID_PARAMETER_2;

            *PackedLevel = Level << 6;
            return STATUS_SUCCESS;
        }

        case SB_MIX_VOC_LEVEL :         /* legacy */
        case SB_MIX_MASTER_LEVEL :
        case SB_MIX_FM_LEVEL :
        case SB_MIX_CD_LEVEL :
        case SB_MIX_LINE_LEVEL :
        case SB_MIX_TREBLE_LEFT_LEVEL : /* bass/treble */
        case SB_MIX_TREBLE_RIGHT_LEVEL :
        case SB_MIX_BASS_LEFT_LEVEL :
        case SB_MIX_BASS_RIGHT_LEVEL :
        {
            if ( Level >= 0x10 )
                return STATUS_INVALID_PARAMETER_2;

            *PackedLevel = Level << 4;
            return STATUS_SUCCESS;
        }

        default :
            return STATUS_INVALID_PARAMETER_1;
    };
}

NTSTATUS
SbMixerUnpackLevelData(
    IN  UCHAR Line,
    IN  UCHAR PackedLevel,
    OUT PUCHAR Level)
{
    if ( ! Level )
        return STATUS_INVALID_PARAMETER_3;

    switch ( Line )
    {
        case SB_MIX_MASTER_LEFT_LEVEL :
        case SB_MIX_MASTER_RIGHT_LEVEL :
        case SB_MIX_VOC_LEFT_LEVEL :
        case SB_MIX_VOC_RIGHT_LEVEL :
        case SB_MIX_MIDI_LEFT_LEVEL :
        case SB_MIX_MIDI_RIGHT_LEVEL :
        case SB_MIX_CD_LEFT_LEVEL :
        case SB_MIX_CD_RIGHT_LEVEL :
        case SB_MIX_LINE_LEFT_LEVEL :
        case SB_MIX_LINE_RIGHT_LEVEL :
        case SB_MIX_MIC_LEVEL :
        {
            *Level = PackedLevel >> 3;
            return STATUS_SUCCESS;
        }

        case SB_MIX_INPUT_LEFT_GAIN :
        case SB_MIX_INPUT_RIGHT_GAIN :
        case SB_MIX_OUTPUT_LEFT_GAIN :
        case SB_MIX_OUTPUT_RIGHT_GAIN :
        {
            *Level = PackedLevel >> 6;
            return STATUS_SUCCESS;
        }

        case SB_MIX_VOC_LEVEL :         /* legacy */
        case SB_MIX_MASTER_LEVEL :
        case SB_MIX_FM_LEVEL :
        case SB_MIX_CD_LEVEL :
        case SB_MIX_LINE_LEVEL :
        case SB_MIX_TREBLE_LEFT_LEVEL : /* bass/treble */
        case SB_MIX_TREBLE_RIGHT_LEVEL :
        case SB_MIX_BASS_LEFT_LEVEL :
        case SB_MIX_BASS_RIGHT_LEVEL :
        {
            *Level = PackedLevel >> 4;
            return STATUS_SUCCESS;
        }

        default :
            return STATUS_INVALID_PARAMETER_1;
    };
}

NTSTATUS
SbMixerSetLevel(
    IN  PUCHAR BasePort,
    IN  UCHAR Line,
    IN  UCHAR Level)
{
    UCHAR PackedLevel = 0;
    NTSTATUS Status;

    Status = SbMixerPackLevelData(Line, Level, &PackedLevel);

    switch ( Status )
    {
        case STATUS_SUCCESS :
            break;

        case STATUS_INVALID_PARAMETER_1 :
            return STATUS_INVALID_PARAMETER_2;

        case STATUS_INVALID_PARAMETER_2 :
            return STATUS_INVALID_PARAMETER_3;

        default :
            return Status;
    };

    DbgPrint("SbMixerSetLevel: Line 0x%x, raw level 0x%x, packed 0x%x\n", Line, Level, PackedLevel);

    WRITE_SB_MIXER_REGISTER(BasePort, Line);
    WRITE_SB_MIXER_DATA(BasePort, PackedLevel);

    return STATUS_SUCCESS;
}

NTSTATUS
SbMixerGetLevel(
    IN  PUCHAR BasePort,
    IN  UCHAR Line,
    OUT PUCHAR Level)
{
    UCHAR PackedLevel = 0;
    NTSTATUS Status;

    if ( ! Level )
        return STATUS_INVALID_PARAMETER_3;

    WRITE_SB_MIXER_REGISTER(BasePort, Line);
    PackedLevel = READ_SB_MIXER_DATA(BasePort);

    Status = SbMixerUnpackLevelData(Line, PackedLevel, Level);

    switch ( Status )
    {
        case STATUS_SUCCESS :
            break;

        case STATUS_INVALID_PARAMETER_1 :
            return STATUS_INVALID_PARAMETER_2;

        case STATUS_INVALID_PARAMETER_2 :
            return STATUS_INVALID_PARAMETER_3;

        default :
            return Status;
    };

    DbgPrint("SbMixerGetLevel: Line 0x%x, raw level 0x%x, packed 0x%x\n", Line, Level, PackedLevel);

    return STATUS_SUCCESS;
}

VOID
SbMixerEnableAGC(IN PUCHAR BasePort)
{
    /* Untested... */
    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_AGC);
    WRITE_SB_MIXER_DATA(BasePort, 1);
}

VOID
SbMixerDisableAGC(IN PUCHAR BasePort)
{
    /* Untested... */
    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_AGC);
    WRITE_SB_MIXER_DATA(BasePort, 0);
}

BOOLEAN
SbMixerIsAGCEnabled(IN PUCHAR BasePort)
{
    /* Untested... */
    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_AGC);
    return (READ_SB_MIXER_DATA(BasePort) != 0);
}
