/*
 * AR1000 FM receiver support
 *
 *  Refer AR1000/AR1010 data sheet to understand the meaning of
 *  this file.
 *  Most of code is based on the software written by Yukio Kawakami.
 *  Please refer http://yuki-lab.jp/hw/spk-fm-301/index.html.
 */
#include <t_services.h>
#include "lpc23xx.h"

#define	CONT_AA		(1<<2)
#define	CONT_SI		(1<<3)
#define	CONT_STO	(1<<4)
#define	CONT_STA	(1<<5)
#define	CONT_I2EN	(1<<6)

#define	FREQ_BASE	690

#define	AR1000_ADDR 0x10
#define	TEST_FREQ	800		// 80.0MHz FM Tokyo

#define	R0		0x00
#define	R1		0x01
#define	R2		0x02
#define	R3		0x03
#define	R4		0x04
#define	R5		0x05
#define	R6		0x06
#define	R7		0x07
#define	R8		0x08
#define	R9		0x09
#define	R10		0x0A
#define	R11		0x0B
#define	R12		0x0C
#define	R13		0x0D
#define	R14		0x0E
#define	R15		0x0F
#define	R16		0x10
#define	R17		0x11
#define	RSSI		0x12
#define	STATUS	0x13
#define	RBS		0x14
#define	RDS1	0x15
#define	RDS2	0x16
#define	RDS3	0x17
#define	RDS4	0x18
#define	RDS5	0x19
#define	RDS6	0x1A
#define	DEVID	0x1B
#define	CHIPID	0x1C

#define	R0_DEFAULT		0xFFFC
#define	R1_DEFAULT		0x5B05
#define	R2_DEFAULT		0xF4B9
#define	R3_DEFAULT		0x9842

#define	R0_ENABLE		0x0001
#define	R0_XO_EN		0x0002
#define	R1_HMUTE		0x0002
#define	R1_SMUTE		0x0004

#define	R2_CHAN			0x01FF
#define	R2_TUNE			0x0200

#define	R3_SEEKTH		0x007F
#define	R3_VOLUMN		0x0780
#define	R3_BAND			0x1800	// 11:76-108 10:76-90 00:87.5-108
#define	R3_SPACE		0x2000
#define	R3_SEEK			0x4000
#define	R3_SEEKUP		0x8000

#define	R17_SEEK_SETTING	0x2000
#define	R17_SEEK_MASK		0xC3FF

#define	STATUS_STC		0x0020
#define	STATUS_SF		0x0010
#define	STATUS_READCHAN		0xFF80

void i2c_mode(void)
{
    /* Initialize I2C as master transmitter mode */
    I21CONSET = CONT_I2EN;
    I21SCLL = I21SCLH = 200;    /* about 150K Hz */
}

UH ar1000_readreg(int reg)
{
    UB stat;
    UH val;

    I21CONSET = CONT_I2EN; 	/* Master transmitter mode */
    dly_tsk(1);
    I21CONSET = CONT_STA;   /* Send START */
    while (I21STAT != 0x08)
        ;
    I21DAT = (AR1000_ADDR << 1) | 0;	/* Add write bit */
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_STA | CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        ;
    stat = I21STAT & 0xff;
    if (stat != 0x18 ) {
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        cons_printf("ar1000_readreg: expect 0x18, but got 0x%x\n", stat);
        return 0xffff;
    }
    I21DAT = reg;       	/* send register address */
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        ;
    stat = I21STAT & 0xff;
    if (stat != 0x28 ) {
        cons_printf("ar1000_readreg: expect 0x28, but got 0x%x (%x)\n", stat, I21CONSET);
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        return 0xffff;
    }
    I21CONSET = CONT_I2EN | CONT_AA | CONT_STA;		/* repeated start */
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        dly_tsk(1);
    stat = I21STAT & 0xff;
    if (stat != 0x10 ) {
        cons_printf("ar1000_readreg: expect 0x10, but got 0x%x (%x)\n", stat, I21CONSET);
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        return 0xffff;
    }

    I21DAT = (AR1000_ADDR << 1) | 1;	/* Add read bit */
    I21CONCLR = CONT_STA | CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        dly_tsk(1);
    stat = I21STAT & 0xff;
    if (stat != 0x40 ) {
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        cons_printf("ar1000_readreg: expect 0x40, but got 0x%x\n", stat);
        return 0xffff;
    }
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        dly_tsk(1);
    stat = I21STAT & 0xff;
    if (stat != 0x50 ) {
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        cons_printf("ar1000_readreg: expect 0x50, but got 0x%x\n", stat);
        return 0xffff;
    }
    val = I21DAT << 8;
    I21CONSET = CONT_I2EN;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        dly_tsk(1);
    if (stat != 0x50 ) {
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        cons_printf("ar1000_readreg: expect 0x50, but got 0x%x\n", stat);
        return 0xffff;
    }
    val |= I21DAT & 0xff;
    I21CONCLR = CONT_SI | CONT_AA;	/* Clear SI and AA bit */
    while ((I21CONSET & CONT_SI) == 0)
        dly_tsk(1);
    if (stat != 0x50 ) {
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        cons_printf("ar1000_readreg: expect 0x58, but got 0x%x\n", stat);
        return 0xffff;
    }
    I21CONSET = CONT_I2EN | CONT_STO;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    return val;
}

void ar1000_writereg(int reg, UH bdata)
{
    UB stat;

    I21CONSET = CONT_I2EN;		/* Master transmitter mode */
    dly_tsk(1);
    I21CONSET = CONT_STA;		/* Send START */
    while (I21STAT != 0x08)
        ;
    I21DAT = (AR1000_ADDR << 1) | 0;	/* Add write bit */
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_STA | CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        ;
    stat = I21STAT & 0xff;
    if (stat != 0x18 ) {
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        cons_printf("ar1000_writereg: expect 0x18, but got 0x%x\n", stat);
        return;
    }
    I21DAT = reg;
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        ;
    stat = I21STAT & 0xff;
    if (stat != 0x28 ) {
        cons_printf("ar1000_writereg: expect 0x28, but got 0x%x (%x)\n", stat, I21CONSET);
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        return;
    }
    I21DAT = bdata >> 8;
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        ;
    stat = I21STAT & 0xff;
    if (stat != 0x28 ) {
        cons_printf("ar1000_writereg: expect 0x28, but got 0x%x\n", stat);
        I21CONSET = CONT_I2EN | CONT_AA | CONT_STO;
        I21CONCLR = CONT_SI;	/* Clear SI bit */
        return;
    }
    I21DAT = bdata;
    I21CONSET = CONT_I2EN | CONT_AA;
    I21CONCLR = CONT_SI;	/* Clear SI bit */
    while ((I21CONSET & CONT_SI) == 0)
        ;
    I21CONSET = CONT_I2EN | CONT_STO;
    I21CONCLR = CONT_SI | CONT_AA;	/* Clear SI bit */
}

void ar1000_dump()
{
    int i;

    i2c_mode();

    for (i = R0; i <= CHIPID; i++)
        cons_printf("%2d %04x\n", i, ar1000_readreg(i));
}

void ar1000_init()
{
    i2c_mode();
    ar1000_writereg(R0, R0_DEFAULT | R0_XO_EN);	// Be sure that ENABLE=OFF
    ar1000_writereg(R1, R1_DEFAULT | R1_HMUTE);	// Mute
    ar1000_writereg(R2, R2_DEFAULT);
    ar1000_writereg(R3, R3_DEFAULT);
    ar1000_writereg(R4, 0x0400);
    ar1000_writereg(R5, 0x28AA);
    ar1000_writereg(R6, 0x4400);
    ar1000_writereg(R7, 0x1EE7);
    ar1000_writereg(R8, 0x7141);
    ar1000_writereg(R9, 0x007D);
#ifdef ALLOW_WRAP
    ar1000_writereg(R10, 0x82CE);
#else
    ar1000_writereg(R10, 0x82C6);
#endif
    ar1000_writereg(R11, 0x4F55);
    ar1000_writereg(R12, 0x970C);
    ar1000_writereg(R13, 0xB845);
    ar1000_writereg(R14, 0xFC2D);
    ar1000_writereg(R15, 0x8097);
    ar1000_writereg(R16, 0x04A1);
    ar1000_writereg(R17, 0xDF6A);

    /* Now, enable the device */
    ar1000_writereg(R0, R0_DEFAULT | R0_XO_EN | R0_ENABLE);

    for (;;) {
        dly_tsk(10);
        if (ar1000_readreg(STATUS) & STATUS_STC)
            break;	// STC=H 待ち
    }

    ar1000_writereg(R2, (R2_DEFAULT & ~(R2_TUNE | R2_CHAN))	// 周波数設定
                        | (TEST_FREQ - FREQ_BASE));
    ar1000_writereg(R2, (R2_DEFAULT & ~(R2_TUNE | R2_CHAN))
             | (TEST_FREQ - FREQ_BASE) | R2_TUNE);			// TUNE=ON

    for (;;) {
        dly_tsk(10);
        if (ar1000_readreg(STATUS) & STATUS_STC)
            break;	// STC=H 待ち
    }

    ar1000_writereg(R1, R1_DEFAULT);	// ミュート解除
}

void ar1000_tune(int chan)
{
    int st;

    if (chan > 0) {
        ar1000_writereg(R1, R1_DEFAULT | R1_HMUTE);	// Mute
        ar1000_writereg(R2,
            (R2_DEFAULT & ~(R2_TUNE | R2_CHAN)) | chan); // 周波数設定
        ar1000_writereg(R2,
            (R2_DEFAULT & ~(R2_TUNE | R2_CHAN)) | chan | R2_TUNE); // TUNE=ON 
        for (;;) {
            dly_tsk(10);
            if (ar1000_readreg(STATUS) & STATUS_STC) {
                break;	// STC=H 待ち
            }
        }

        ar1000_writereg(R1, R1_DEFAULT);	// ミュート解除
    }

    st = ar1000_readreg(STATUS);
    chan = (st & STATUS_READCHAN) >> 7;
    disp_station_freq(chan + FREQ_BASE);
}

void ar1000_seek(int direc, int thres)
{
    int chan, st;
    UH r3, r17;

    thres &= R3_SEEKTH;
    chan = (ar1000_readreg(STATUS) >> 7) & 0x1ff;
    ar1000_writereg(R1, R1_DEFAULT | R1_HMUTE);	// Mute
// Clear TUNE bit
// Set CHAN bits
    ar1000_writereg(R2, (R2_DEFAULT & ~(R2_TUNE | R2_CHAN))	// 周波数設定
                                 | chan);
// Clear SEEK bit
    r3 = ar1000_readreg(R3);
    r3 &= ~(R3_SEEK | R3_SEEKTH);
// Set SEEKUP/SPACE/BAND/SEEKTH Bits
//    r3 |= R3_SEEKUP | R3_SPACE | thres;
    r3 |= R3_SPACE | R3_BAND | thres;
    if (direc)
        r3 &= ~R3_SEEKUP;
    else
        r3 |= R3_SEEKUP;
    ar1000_writereg(R3, r3);

    // Setting before seek
    r17 = ar1000_readreg(R17);
    ar1000_writereg(R17, (r17 & R17_SEEK_MASK) | R17_SEEK_SETTING);
    dly_tsk(2);
    // Enable SEEK bit
    ar1000_writereg(R3, r3 | R3_SEEK);

    do {
        st = ar1000_readreg(STATUS);
        chan = (st & STATUS_READCHAN) >> 7;
        disp_station_freq(chan + FREQ_BASE);
        dly_tsk(20);
    } while (!(st & STATUS_STC)); // STC=H 待ち
    cons_printf("%d\r", chan + FREQ_BASE);
    // restore original R17 value
    ar1000_writereg(R17, r17);

    ar1000_writereg(R1, R1_DEFAULT);	// ミュート解除
}

void ar1000_find_station(int direc)
{
    int if_cnt;

    do {
        ar1000_seek(direc, 30);
        if_cnt = ar1000_readreg(RSSI) & 0x1ff;
    } while (if_cnt > 260);
}

int ar1000_status()
{
    int chan, st;

    st = ar1000_readreg(STATUS);
    chan = (st & STATUS_READCHAN) >> 7;
    cons_printf("%s: chan = %d\n",
                (st & STATUS_SF)? "FAIL" : "OK",
                chan + FREQ_BASE);
    st = ar1000_readreg(RSSI);
    cons_printf("RSSI: %d, IF_CNT: %d\n",
        (st >> 9), st & 0x1ff);
    disp_station_freq(chan + FREQ_BASE);
    return chan;
}

/*
 * Following functions are prepared for console commands.
 */
void ar1000_command(int argc, char **argv)
{
    if (argc > 1)
        ar1000_init();
    else
        ar1000_dump();
}

void tune_command(int argc, char **argv)
{
    int chan;

    if (argc > 0) {
        chan = atoi(argv[1]) - FREQ_BASE;
        if (chan < 0)
            cons_printf("Bad channel.\n");
        else
            ar1000_tune(chan);
    }
    ar1000_status();
}

void seek_command(int argc, char **argv)
{
    int thres;
    int direc = 0;

    thres = 40;
    if (argc > 1)
        thres = atoi(argv[1]);
    if (thres < 0) {
        direc = 1;
        thres = -thres;
    }
    ar1000_seek(direc, thres);
    ar1000_status();
}

void reg_command(int argc, char **argv)
{
    int regno, bit;
    UH val;

    if (argc > 1)
        regno = atoi(argv[1]);
    switch (argc) {
    case 2:
        cons_printf("%02d: %04x\n", regno, ar1000_readreg(regno));
        break;
    case 3:
        bit = atoi(argv[2]) & 15;
        val = ar1000_readreg(regno);
        val ^= 1 << bit;
        ar1000_writereg(regno, val);
        cons_printf("%02d: %04x\n", regno, ar1000_readreg(regno));
        break;
    }
}
