/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id: uda1380.c 17847 2008-06-28 18:10:04Z bagder $
 *
 * Copyright (C) 2005 by Andy Young
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "logf.h"
#include "system.h"
#include "string.h"
#include "audio.h"
#include "debug.h"

#include "i2c-coldfire.h"
#include "audiohw.h"
#include "pcf50606.h"

const struct sound_settings_info audiohw_settings[] = {
    [SOUND_VOLUME]        = {"dB", 0,  1, -84,   0, -25},
    [SOUND_BASS]          = {"dB", 0,  2,   0,  24,   0},
    [SOUND_TREBLE]        = {"dB", 0,  2,   0,   6,   0},
    [SOUND_BALANCE]       = {"%",  0,  1,-100, 100,   0},
    [SOUND_CHANNELS]      = {"",   0,  1,   0,   5,   0},
    [SOUND_STEREO_WIDTH]  = {"%",  0,  5,   0, 250, 100},
#ifdef HAVE_RECORDING
    [SOUND_LEFT_GAIN]     = {"dB", 1,  1,-128,  96,   0},
    [SOUND_RIGHT_GAIN]    = {"dB", 1,  1,-128,  96,   0},
    [SOUND_MIC_GAIN]      = {"dB", 1,  1,-128, 108,  16},
#endif
};

/* convert tenth of dB volume (-840..0) to master volume register value */
int tenthdb2master(int db)
{
    if (db < -720)                  /* 1.5 dB steps */
        return (2940 - db) / 15;
    else if (db < -660)             /* 0.75 dB steps */
        return (1110 - db) * 2 / 15;
    else if (db < -520)             /* 0.5 dB steps */
        return (520 - db) / 5;
    else                            /* 0.25 dB steps */
        return -db * 2 / 5;
}

/* convert tenth of dB volume (-780..0) to mixer volume register value */
int tenthdb2mixer(int db)
{
    if (db < -660)                 /* 1.5 dB steps */
        return (2640 - db) / 15;
    else if (db < -600)            /* 0.75 dB steps */
        return (990 - db) * 2 / 15;
    else if (db < -460)            /* 0.5 dB steps */
        return (460 - db) / 5; 
    else                           /* 0.25 dB steps */
        return -db * 2 / 5;
}

/* ------------------------------------------------- */
/* Local functions and variables */
/* ------------------------------------------------- */

static int uda1380_write_reg(unsigned char reg, unsigned short value);
unsigned short uda1380_regs[0x30];
short recgain_mic;
short recgain_line;

/* Definition of a playback configuration to start with */

#define NUM_DEFAULT_REGS 13
unsigned short uda1380_defaults[2*NUM_DEFAULT_REGS] =
{
   REG_0,          EN_DAC | EN_INT | EN_DEC | ADC_CLK | DAC_CLK |
                   SYSCLK_256FS | WSPLL_25_50,
   REG_I2S,        I2S_IFMT_IIS,
   REG_PWR,        PON_PLL | PON_BIAS,
                   /* PON_HP & PON_DAC is enabled later */
   REG_AMIX,       AMIX_RIGHT(0x3f) | AMIX_LEFT(0x3f),
                   /* 00=max, 3f=mute */
   REG_MASTER_VOL, MASTER_VOL_LEFT(0x20) | MASTER_VOL_RIGHT(0x20),
                   /* 00=max, ff=mute */
   REG_MIX_VOL,    MIX_VOL_CH_1(0) | MIX_VOL_CH_2(0xff),
                   /* 00=max, ff=mute */
   REG_EQ,         EQ_MODE_MAX,
                   /* Bass and treble = 0 dB */
   REG_MUTE,       MUTE_MASTER | MUTE_CH2,
                   /* Mute everything to start with */
   REG_MIX_CTL,    MIX_CTL_MIX,
                   /* Enable mixer */
   REG_DEC_VOL,    0,
   REG_PGA,        MUTE_ADC,
   REG_ADC,        SKIP_DCFIL,
   REG_AGC,        0
};


/* Returns 0 if register was written or -1 if write failed */
static int uda1380_write_reg(unsigned char reg, unsigned short value)
{
    unsigned char data[3];

    data[0] = reg;
    data[1] = value >> 8;
    data[2] = value & 0xff;

    if (i2c_write(I2C_IFACE_0, UDA1380_ADDR, data, 3) != 3)
    {
        DEBUGF("uda1380 error reg=0x%x", reg);
        return -1;
    } 

    uda1380_regs[reg] = value;

    return 0;
}

/**
 * Sets left and right master volume  (0(max) to 252(muted))
 */
void audiohw_set_master_vol(int vol_l, int vol_r)
{
    uda1380_write_reg(REG_MASTER_VOL,
                             MASTER_VOL_LEFT(vol_l) | MASTER_VOL_RIGHT(vol_r));
}

/**
 * Sets mixer volume for both channels (0(max) to 228(muted))
 */
void audiohw_set_mixer_vol(int channel1, int channel2)
{
    uda1380_write_reg(REG_MIX_VOL,
                             MIX_VOL_CH_1(channel1) | MIX_VOL_CH_2(channel2));
}

/**
 * Sets the bass value (0-12)
 */
void audiohw_set_bass(int value)
{
    uda1380_write_reg(REG_EQ, (uda1380_regs[REG_EQ] & ~BASS_MASK)
                              | BASSL(value) | BASSR(value));
}

/**
 * Sets the treble value (0-3)
 */
void audiohw_set_treble(int value)
{
    uda1380_write_reg(REG_EQ, (uda1380_regs[REG_EQ] & ~TREBLE_MASK)
                              | TREBLEL(value) | TREBLER(value));
}

void audiohw_mute(bool mute)
{
    unsigned int value = uda1380_regs[REG_MUTE];

    if (mute)
        value = value | MUTE_MASTER;
    else
        value = value & ~MUTE_MASTER;

    uda1380_write_reg(REG_MUTE, value);
}

/* Returns 0 if successful or -1 if some register failed */
static int audiohw_set_regs(void)
{
    int i;
    memset(uda1380_regs, 0, sizeof(uda1380_regs));

    /* Initialize all registers */
    for (i=0; i<NUM_DEFAULT_REGS; i++)
    {
        unsigned char reg = uda1380_defaults[i*2+0];
        unsigned short value = uda1380_defaults[i*2+1];

        if (uda1380_write_reg(reg, value) == -1)
            return -1;
    }

    return 0;
}

/* Silently enable / disable audio output */
void audiohw_enable_output(bool enable)
{
    if (enable) {
        uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_DAC | PON_HP);
    } else {
        uda1380_write_reg(REG_MUTE, MUTE_MASTER);
        uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] & ~PON_DAC);
    }
}

static void reset(void)
{
#ifdef IRIVER_H300_SERIES
    int mask = disable_irq_save();
    pcf50606_write(0x3b, 0x00);  /* GPOOD2 high Z */
    pcf50606_write(0x3b, 0x07);  /* GPOOD2 low */
    restore_irq(mask);
#else
    /* RESET signal */
    or_l(1<<29, &GPIO_OUT);
    or_l(1<<29, &GPIO_ENABLE);
    or_l(1<<29, &GPIO_FUNCTION);
    sleep(HZ/100);
    and_l(~(1<<29), &GPIO_OUT);
#endif
}

/**
 * Sets frequency settings for DAC and ADC relative to MCLK
 *
 * Selection for frequency ranges:
 *  Fs:        range:       with:
 *  11025: 0 = 6.25 to 12.5 MCLK/2 SCLK, LRCK: Audio Clk / 16
 *  22050: 1 = 12.5 to 25   MCLK/2 SCLK, LRCK: Audio Clk / 8
 *  44100: 2 = 25   to 50   MCLK   SCLK, LRCK: Audio Clk / 4 (default)
 *  88200: 3 = 50   to 100  MCLK   SCLK, LRCK: Audio Clk / 2 <= TODO: Needs WSPLL
 */
void audiohw_set_frequency(unsigned fsel)
{
    static const unsigned short values_reg[4][2] =
    {
                                                          /* Fs:   */
        { 0,              WSPLL_625_125 | SYSCLK_512FS }, /* 11025 */
        { 0,              WSPLL_125_25  | SYSCLK_256FS }, /* 22050 */
        { MIX_CTL_SEL_NS, WSPLL_25_50   | SYSCLK_256FS }, /* 44100 */
        { MIX_CTL_SEL_NS, WSPLL_50_100  | SYSCLK_256FS }, /* 88200 */
    };

    const unsigned short *ent;

    if (fsel >= ARRAYLEN(values_reg))
        fsel = 2;

    ent = values_reg[fsel];

    /* Set WSPLL input frequency range or SYSCLK divider */
    uda1380_regs[REG_0] &= ~0xf;
    uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ent[1]);

    /* Choose 3rd order or 5th order noise shaper */
    uda1380_regs[REG_MIX_CTL] &= ~MIX_CTL_SEL_NS;
    uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL] | ent[0]);
}

/* Initialize UDA1380 codec with default register values (uda1380_defaults) */
void audiohw_init(void)
{
    recgain_mic = 0;
    recgain_line = 0;

    reset();

    if (audiohw_set_regs() == -1)
    {
        /* this shoud never (!) happen. */
        logf("uda1380: audiohw_init failed");
    }
}

void audiohw_postinit(void)
{
    /* Sleep a while so the power can stabilize (especially a long
       delay is needed for the line out connector). */
    sleep(HZ);
    /* Power on FSDAC and HP amp. */
    audiohw_enable_output(true);

    /* UDA1380: Unmute the master channel
       (DAC should be at zero point now). */
    audiohw_mute(false);
}

void audiohw_set_prescaler(int val)
{
    audiohw_set_mixer_vol(tenthdb2mixer(-val), tenthdb2mixer(-val));
}

/* Nice shutdown of UDA1380 codec */
void audiohw_close(void)
{
    /* First enable mute and sleep a while */
    uda1380_write_reg(REG_MUTE, MUTE_MASTER);
    sleep(HZ/8);

    /* Then power off the rest of the chip */
    uda1380_write_reg(REG_PWR, 0);
    uda1380_write_reg(REG_0, 0);    /* Disable codec    */
}

/**
 * Calling this function enables the UDA1380 to send
 * sound samples over the I2S bus, which is connected
 * to the processor's IIS1 interface. 
 *
 * source_mic: true=record from microphone, false=record from line-in (or radio)
 */
void audiohw_enable_recording(bool source_mic)
{
    uda1380_regs[REG_0] &= ~(ADC_CLK | DAC_CLK);
    uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC);

    if (source_mic)
    {
        /* VGA_GAIN: 0=0 dB, F=30dB */
        /* Output of left ADC is fed into right bitstream */
        uda1380_regs[REG_PWR] &= ~(PON_PGAR | PON_ADCR);
        uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_LNA | PON_ADCL);
        uda1380_regs[REG_ADC] &= ~SKIP_DCFIL;
        uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] & VGA_GAIN_MASK)
                                   | SEL_LNA | SEL_MIC | EN_DCFIL);
        uda1380_write_reg(REG_PGA, 0);
    }
    else
    {
        /* PGA_GAIN: 0=0 dB, F=24dB */
        uda1380_regs[REG_PWR] &= ~PON_LNA;
        uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_PGAL | PON_ADCL
                                   | PON_PGAR | PON_ADCR);
        uda1380_write_reg(REG_ADC, EN_DCFIL);
        uda1380_write_reg(REG_PGA, uda1380_regs[REG_PGA] & PGA_GAIN_MASK);
    }

    sleep(HZ/8);

    uda1380_write_reg(REG_I2S,     uda1380_regs[REG_I2S] | I2S_MODE_MASTER);
    uda1380_write_reg(REG_MIX_CTL, MIX_MODE(1)); 
}

/** 
 * Stop sending samples on the I2S bus
 */
void audiohw_disable_recording(void)
{
    uda1380_write_reg(REG_PGA, MUTE_ADC);
    sleep(HZ/8);
    
    uda1380_write_reg(REG_I2S, I2S_IFMT_IIS);

    uda1380_regs[REG_PWR] &= ~(PON_LNA | PON_ADCL | PON_ADCR |
                               PON_PGAL | PON_PGAR);
    uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR]);

    uda1380_regs[REG_0] &= ~EN_ADC;
    uda1380_write_reg(REG_0,   uda1380_regs[REG_0] | ADC_CLK | DAC_CLK);

    uda1380_write_reg(REG_ADC, SKIP_DCFIL);
}

/**
 * Set recording gain and volume
 * 
 * type:                params:        ranges:
 * AUDIO_GAIN_MIC:      left           -128 .. 108 -> -64 .. 54 dB gain
 * AUDIO_GAIN_LINEIN    left & right   -128 ..  96 -> -64 .. 48 dB gain
 *
 * Note: - For all types the value 0 gives 0 dB gain.
 *       - order of setting both values determines if the small glitch will
           be a peak or a dip. The small glitch is caused by the time between
           setting the two gains
 */
void audiohw_set_recvol(int left, int right, int type)
{
    int left_ag, right_ag;

    switch (type)
    {
        case AUDIO_GAIN_MIC:
            left_ag = MIN(MAX(0, left / 4), 15);
            left -= left_ag * 4;

            if(left < recgain_mic)
            {
                uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left)
                                                   | DEC_VOLR(left));
                uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] 
                                               & ~VGA_GAIN_MASK) 
                                            | VGA_GAIN(left_ag));
            }
            else
            {
                uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] 
                                               & ~VGA_GAIN_MASK) 
                                            | VGA_GAIN(left_ag));
                uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left) 
                                                   | DEC_VOLR(left));
            }
            recgain_mic = left;
            logf("Mic: %dA/%dD", left_ag, left);
            break;
        
        case AUDIO_GAIN_LINEIN:
            left_ag = MIN(MAX(0, left / 6), 8);
            left -= left_ag * 6;
            right_ag = MIN(MAX(0, right / 6), 8);
            right -= right_ag * 6;

            if(left < recgain_line)
            {
                /* for this order we can combine both registers,
                    making the glitch even smaller */
                unsigned char data[5];
                unsigned short value_dec;
                unsigned short value_pga;
                value_dec = DEC_VOLL(left) | DEC_VOLR(right);
                value_pga = (uda1380_regs[REG_PGA] & ~PGA_GAIN_MASK)
                                | PGA_GAINL(left_ag) | PGA_GAINR(right_ag);

                data[0] = REG_DEC_VOL;
                data[1] = value_dec >> 8;
                data[2] = value_dec & 0xff;
                data[3] = value_pga >> 8;
                data[4] = value_pga & 0xff;

                if (i2c_write(I2C_IFACE_0, UDA1380_ADDR, data, 5) != 5)
                {
                    DEBUGF("uda1380 error reg=combi rec gain");
                }
                else
                {
                    uda1380_regs[REG_DEC_VOL] = value_dec;
                    uda1380_regs[REG_PGA] = value_pga;
                }
            }
            else
            {
                uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] 
                                               & ~PGA_GAIN_MASK)
                                            | PGA_GAINL(left_ag)
                                            | PGA_GAINR(right_ag));
                uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left)
                                                   | DEC_VOLR(right));
            }

            recgain_line = left;
            logf("Line L: %dA/%dD", left_ag, left);
            logf("Line R: %dA/%dD", right_ag, right);
            break;
    }
}


/** 
 * Enable or disable recording monitor (so one can listen to the recording)
 * 
 */
void audiohw_set_monitor(bool enable)
{
    if (enable)    /* enable channel 2 */
        uda1380_write_reg(REG_MUTE, uda1380_regs[REG_MUTE] & ~MUTE_CH2);
    else           /* mute channel 2 */
        uda1380_write_reg(REG_MUTE, uda1380_regs[REG_MUTE] | MUTE_CH2);
}
