/*
 * a.lp_mp3 - VS1011B driver for Fonera 
 * Copyright (c) 2007 phrozen.org - John Crispin <john@phrozen.org>
 *
 * 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 program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
 *
 * Feedback, Bugs.... mail john@phrozen.org
 *
 */ 

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include "ar531xlnx.h"

#define AR5315_DSLBASE          0xB1000000
#define AR5315_GPIO_DI          (AR5315_DSLBASE + 0x0088)
#define AR5315_GPIO_DO          (AR5315_DSLBASE + 0x0090)
#define AR5315_GPIO_CR          (AR5315_DSLBASE + 0x0098)
#define AR5315_GPIO_INT         (AR5315_DSLBASE + 0x00a0)

#define GPIO_0			1<<0
#define GPIO_1			1<<1
#define GPIO_2			1<<2
#define GPIO_3			1<<3
#define GPIO_4			1<<4
#define GPIO_6			1<<6
#define GPIO_7			1<<7

#define DREQ				((unsigned int)GPIO_7)
#define SCK				((unsigned int)GPIO_1)
#define SI				((unsigned int)GPIO_4)
#define BSYNC				((unsigned int)GPIO_3)
#define CS				((unsigned int)GPIO_0)
#define SO				((unsigned int)GPIO_6)
#define RES				((unsigned int)GPIO_2)

#define REG_MODE			0x0
#define REG_STATUS			0x1
#define REG_BASS			0x2
#define REG_CLOCKF			0x3
#define REG_DECODETIME			0x4
#define REG_AUDATA			0x5
#define REG_WRAM			0x6
#define REG_WRAMADDR			0x7
#define REG_HDAT0			0x8
#define REG_HDAT1			0x9
#define REG_A1ADDR			0xa
#define REG_VOL				0xb
#define REG_A1CTRL0			0xc
#define REG_A1CTRL1			0xd
#define REG_A1CTRL2			0xe

#define VS1011_NEEDS_DATA  		spi_get_bit(DREQ)
#define VS1011_NEEDS_NO_DATA  		(spi_get_bit(DREQ)== 0x00)
#define VS1011_WHILE_NEEDS_NO_DATA  	while(spi_get_bit(DREQ)== 0x00){}

#define VS_CS_LO			spi_clear_bit(CS)
#define VS_CS_HI			spi_set_bit(CS)

#define VS_BSYNC_LO			spi_clear_bit(BSYNC)
#define VS_BSYNC_HI			spi_set_bit(BSYNC)

#define VS_RESET_LO			spi_clear_bit(RES)
#define VS_RESET_HI			spi_set_bit(RES)

#define VS1011_READ 			SPI_io_vs1011b(0x03)
#define VS1011_WRITE 			SPI_io_vs1011b(0x02)

void msDelay(int ms) {
	int i,a;
	int delayvar=10;

	for (a=0;a<ms;a++) {
		for (i=0;i<33084;i++) {
			delayvar*=2;        
			delayvar/=2;
		} 
	}
}   

int spi_get_bit(unsigned int pin){
	return ((sysRegRead(AR5315_GPIO_DI)&pin)?(1):(0));
}

void spi_set_bit(unsigned int pin){
	sysRegWrite(AR5315_GPIO_DO, (sysRegRead(AR5315_GPIO_DO) | pin));
}

void spi_clear_bit(unsigned int pin){
	sysRegWrite(AR5315_GPIO_DO, (sysRegRead(AR5315_GPIO_DO) & ~pin));
}

void SPI_clock_vs1011b(void){
	spi_clear_bit(SCK);
	spi_set_bit(SCK);
}

unsigned char SPI_io_vs1011b(unsigned char byte){
	int i;
	unsigned char this_bit;
	unsigned char byte_out = 0;
	for(i = 7; i>=0; i--){
		if(byte & (1<<i)){
			this_bit = 1;
		} else {
			this_bit = 0;
		}
		if(this_bit){
			spi_set_bit(SI);
		} else {
			spi_clear_bit(SI);
		}
		SPI_clock_vs1011b();
		byte_out += spi_get_bit(SO)<<i;
	}
	return byte_out;
}

void SPI_init_vs1011(void){
	sysRegWrite(AR5315_GPIO_CR, (sysRegRead(AR5315_GPIO_CR) | SI | SCK | CS | BSYNC | RES) & ~(SO|DREQ));
	spi_clear_bit(SCK);
	spi_clear_bit(SI);
	VS_CS_HI;
	VS_BSYNC_HI;
}

void VS1011_send_SCI(unsigned char reg, unsigned int data){
	VS_CS_LO;
	VS1011_WRITE;
	SPI_io_vs1011b(reg);
	SPI_io_vs1011b((data>>8)&0xff);
	SPI_io_vs1011b(data&0xff);
	VS_CS_HI;
}

unsigned int VS1011_read_SCI(unsigned char reg){
	unsigned int data;	
	VS_CS_LO;
	VS1011_READ;
	SPI_io_vs1011b(reg);
	data = 0;
	data = SPI_io_vs1011b(0x00);
	data <<= 8;
	data += SPI_io_vs1011b(0x00);
	VS_CS_HI;
	return data;
}

void VS1011_send_SDI(unsigned char byte){
	int i;
	VS_BSYNC_LO;
	for(i = 7; i>=0; i--){
		if(byte & (1<<i)){
			spi_set_bit(SI);
			
		} else {
			spi_clear_bit(SI);
		}			
		spi_clear_bit(SCK);
		spi_set_bit(SCK);
	}
	VS_BSYNC_HI;
}

void VS1011_send_SDI_32(unsigned char* data){
	int i;
	VS1011_WHILE_NEEDS_NO_DATA;
	for(i=0; i<32; i++){
		VS1011_send_SDI(data[i]);
	}
}

void VS1011_send_zeros(unsigned char count){
	do{
		VS1011_send_SDI(0x0);
		count--;
	}while(count);
}

void VS1011_set_volume(unsigned int vol){
	VS1011_send_SCI(REG_VOL, vol);
}

void VS1011_SW_reset(unsigned int _crystal_freq){
	unsigned int regval = 0x0804;
	unsigned long int i = 0;
	msDelay(100);
	VS1011_send_zeros(32);
	VS1011_send_SCI(REG_MODE, regval);
	msDelay(10);
	while((VS1011_NEEDS_NO_DATA) && (i++<0xffff)){};
	VS1011_send_SCI(REG_CLOCKF, _crystal_freq);
	VS1011_send_zeros(16);
	VS1011_set_volume(0x00);
}

void VS1011_HW_reset(void){
	
	VS_RESET_LO;
	msDelay(1);
	VS_RESET_HI;
	msDelay(1);
}

void VS1011_init(unsigned int _crystal_freq, unsigned char hw){
	if(hw){
		SPI_init_vs1011();
	}
	printk("mp3_drv.ko : Init start\n");
	if(hw){
		VS1011_HW_reset();
	}
	VS1011_SW_reset(_crystal_freq);
	printk("mp3_drv.ko : init_ok\n");
}

void VS1011_sine(unsigned char state, unsigned char freq){
	VS1011_send_zeros(16);
	if(state == 0x01){
		VS1011_send_SDI(0x53);
		VS1011_send_SDI(0xEF);
		VS1011_send_SDI(0x6E);
		VS1011_send_SDI(freq);
		VS1011_send_zeros(0x04);
	} else {
		VS1011_send_SDI(0x45);
		VS1011_send_SDI(0x78);
		VS1011_send_SDI(0x69);
		VS1011_send_SDI(0x74);
		VS1011_send_zeros(0x04);		
	}
}

unsigned int VS1011_get_volume(void){
	return VS1011_read_SCI(REG_VOL);
}

unsigned int VS1011_get_decode_time(void){
	return VS1011_read_SCI(REG_DECODETIME);
}

const unsigned int sample_rate_values[]  = {0, 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000};

void VS1011_get_audio_data(AUDIO_DATA* audio){
	unsigned int audata = VS1011_read_SCI(REG_AUDATA);
	audio->sample_rate = sample_rate_values[(audata&0x1E00)>>9];
	audio->bitrate = audata&0x1FF;
	audio->is_stereo = (audata&0x8000)>>15;
}

void VS1011_print_registers(void){
	unsigned char i;
	for(i = 0; i< 14; i++){
		unsigned int regval = VS1011_read_SCI(i);
		printk("mp3_drv.ko : %d \n", regval);		
	}
}

void VS1011_volume(unsigned char left, unsigned char right){
	unsigned int regval = left;
	regval <<=8;
	regval += right;
	VS1011_send_SCI(REG_VOL, regval);
}

void VS1011_set_bass(unsigned int regval){
	VS1011_send_SCI(REG_BASS, regval);
}

void VS1011_set_reg(unsigned int reg, unsigned int regval){
	VS1011_send_SCI(reg, regval);
}

/*
int vs_test(void) {	
	SPI_init_vs1011();
	printk("%u\n", *R_GEN_CONFIG);
	VS1001_init(_crystal_freq);
	VS1001_print_registers();
	VS1001_volume(0x30, 0x30); 
	msDelay(1000);
	VS1001_sine(1, 0x30);
	msDelay(1000);
	VS1001_sine(0, 0);
	VS1001_send_zeros(0x20);
	msDelay(1000);
	VS1001_sine(1, 0x30);
	msDelay(1000);
	VS1001_sine(0, 0);
	VS1001_send_zeros(0x20);
	msDelay(1000);
	VS1001_sine(1, 0x30);
	msDelay(1000);
	VS1001_sine(0, 0);
	
	AUDIO_DATA a;
	VS1001_get_audio_data(&a);
	printk("mp3_drv.ko : rate : %d, bit : %d, stereo : %d \n", a.sample_rate, a.bitrate, a.is_stereo);
	VS1001_SW_reset(_crystal_freq);
	return 0;
}*/