Skip to content

Commit

Permalink
Andorkos FM radio driver with RDS support.
Browse files Browse the repository at this point in the history
  • Loading branch information
burstlam committed Sep 7, 2011
1 parent 5f2e5d1 commit 7bffbd1
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 15 deletions.
7 changes: 6 additions & 1 deletion arch/arm/mach-msm/include/mach/fm_si4708.h
Expand Up @@ -14,7 +14,8 @@
#define SI4708_RESET_DELAY 110 //ms
#define SI4708_POWERUP_DELAY 110 //ms
#define SI4708_POWERDOWN_DELAY 110 //ms
#define SI4708_SEEK_TUNE_DELAY 888 //ms,change from 60 to 888 for tune test
#define SI4708_SEEK_TUNE_DELAY 550 //ms,change from 60 to 888 for tune test
#define SI4708_TUNE_DELAY 60
/****************************INTERNAL VARIBLE******************************/

#define SEEKUP 1 /* seek up*/
Expand Down Expand Up @@ -51,6 +52,10 @@

#define FM_REG_STATUSRSSI 0
#define FM_REG_READCHAN 2
#define FM_REG_RDSA 4
#define FM_REG_RDSB 6
#define FM_REG_RDSC 8
#define FM_REG_RDSD 10

#define FM_REG_DEVICEID 12
#define FM_REG_CHIPID 14
Expand Down
160 changes: 146 additions & 14 deletions drivers/misc/fm_si4708.c
Expand Up @@ -37,7 +37,9 @@
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/slab.h>
Expand All @@ -55,7 +57,7 @@

#define FM_DBG(x...) printk(KERN_DEBUG"[FM]"x)
#define FM_INFO(x...) printk(KERN_INFO"[FM]"x)
#define FYA_TAG "[FYA@FM_SI4708]"
#define FYA_TAG "[FYA@FM_SI4708]"

struct fm_dev {
int initialized; /* device opend times */
Expand All @@ -69,11 +71,15 @@ struct fm_dev {
char vol; /* cached vol */
char regs[32]; /* buffer for reading */
struct i2c_client *client;
unsigned char* buffer;
unsigned int read_index;
unsigned int write_index;
spinlock_t lock;
};

static struct fm_dev *fm_si4708_dev;

static unsigned int buffer_size = 50;
static int si4708_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int si4708_remove(struct i2c_client *client);

Expand All @@ -90,6 +96,11 @@ static int si4708_remove(struct i2c_client *client);
* ----------------------------------------------------
* 2009/11/11 1.0 fengyuao modify
**********************************************************************/

static struct timer_list rds_timer;
static int rds_enabled = 1;
static int rds_read_index = 0;
static unsigned char rds_read_buffer[8];
static int si4708_regs_read(struct i2c_client * client, char * buf, int count)
{
int ret = 0;
Expand Down Expand Up @@ -583,7 +594,7 @@ static int si4708_set_frequency(int frequency)
return res;
}

mdelay(SI4708_SEEK_TUNE_DELAY); /*seek time*/
mdelay(SI4708_TUNE_DELAY); /*seek time*/

/*check STC status*/
res = si4708_regs_read(fm_si4708_dev->client, &fm_si4708_dev->regs[FM_REG_RD_START], 4);
Expand Down Expand Up @@ -733,11 +744,108 @@ static int si4708_seek(int dir, int *p_frequency)
/* ZTE_Audio_CJ_100511, chenjun, 2010-5-11, start */
extern int snd_hph_amp_ctl(uint32_t on);


static void setRDSEnabled(int enabled){
int ret;
ret = 0;
rds_enabled = enabled;
fm_si4708_dev->regs[FM_REG_SYSCONFIG1] &= ~0x90;
fm_si4708_dev->regs[FM_REG_SYSCONFIG1+1] &= ~0x04;
if (rds_enabled == 1) { //switch on rds
fm_si4708_dev->regs[FM_REG_SYSCONFIG1] |= 0x90;
fm_si4708_dev->regs[FM_REG_SYSCONFIG1+1] |= 0x04;
}
ret = si4708_regs_write(fm_si4708_dev->client, &fm_si4708_dev->regs[FM_REG_WR_START], 8);
if(ret < 0)
fm_si4708_dev->initialized = 0;
if (rds_enabled == 1){
mod_timer(&rds_timer, jiffies + msecs_to_jiffies(100));
}
}



static int si4708_get_rds(void){
int avail = 0;
int i=0;

if (rds_read_index == 0){
if (fm_si4708_dev->read_index == fm_si4708_dev->write_index) return 0;
memcpy(&rds_read_buffer,&fm_si4708_dev->buffer[fm_si4708_dev->read_index],8);
fm_si4708_dev->read_index+=8;
if (rds_enabled==2) setRDSEnabled(1);
if (fm_si4708_dev->read_index >= buffer_size*8) fm_si4708_dev->read_index = 0;
}
i=rds_read_index<<24 | rds_read_buffer[rds_read_index*3]<<16 | rds_read_buffer[rds_read_index*3+1]<<8;
if (rds_read_index != 2) {
i|=rds_read_buffer[rds_read_index*3+2];
rds_read_index++;
}
else {
if (fm_si4708_dev->read_index != fm_si4708_dev->write_index) avail = 1;
i|=avail;
rds_read_index = 0;
}
return i;
}

static void resetRDSBuffer(void){
fm_si4708_dev->read_index = 0;
fm_si4708_dev->write_index = 0;
}


static unsigned int readRDS(void){
int res = si4708_regs_read(fm_si4708_dev->client, &fm_si4708_dev->regs[FM_REG_STATUSRSSI], 12);
if(res <0)
{
fm_si4708_dev->initialized = 0;
return res;
}
// printk("RDS data %d, %d\n",fm_si4708_dev->read_index,fm_si4708_dev->write_index);
if ((fm_si4708_dev->regs[0] & 0x80) == 0x80){
if ( ((fm_si4708_dev->regs[0] & 0x06) <=1) &&
((fm_si4708_dev->regs[2] & 0xC0) <=1) &&
((fm_si4708_dev->regs[2] & 0x30) <=1) &&
((fm_si4708_dev->regs[2] & 0x0C) <=1) ){
memcpy(&fm_si4708_dev->buffer[fm_si4708_dev->write_index],&fm_si4708_dev->regs[FM_REG_RDSA],2);
memcpy(&fm_si4708_dev->buffer[fm_si4708_dev->write_index+2],&fm_si4708_dev->regs[FM_REG_RDSB],2);
memcpy(&fm_si4708_dev->buffer[fm_si4708_dev->write_index+4],&fm_si4708_dev->regs[FM_REG_RDSC],2);
memcpy(&fm_si4708_dev->buffer[fm_si4708_dev->write_index+6],&fm_si4708_dev->regs[FM_REG_RDSD],2);
fm_si4708_dev->write_index += 8;
if (fm_si4708_dev->write_index >= buffer_size*8){
fm_si4708_dev->write_index = 0;
}
if (fm_si4708_dev->write_index == fm_si4708_dev->read_index){
setRDSEnabled(2);
fm_si4708_dev->read_index+=8;
if (fm_si4708_dev->read_index >= buffer_size*8){
fm_si4708_dev->read_index = 0;
}
}
}
return 1;
}
else{
// printk("RDS data MISSED\n");
return 0;
}
return 0;

}

static void timer_func(unsigned long data){
unsigned int next = 70+readRDS()*10;
if (rds_enabled == 1) mod_timer(&rds_timer,jiffies+msecs_to_jiffies(next));
}


static int si4708_open(struct inode *inode, struct file *filp)
{
int res;
int sdio_state;


snd_hph_amp_ctl(0);

/*checkout if the device is open*/
Expand All @@ -762,7 +870,7 @@ static int si4708_open(struct inode *inode, struct file *filp)

res = si4708_init2normal();
if( res > 0)
fm_si4708_dev->initialized++;
fm_si4708_dev->initialized++;
}

snd_hph_amp_ctl(1);
Expand All @@ -780,6 +888,8 @@ static int si4708_open(struct inode *inode, struct file *filp)
/*end of getting status*/
return res < 0 ? res : 0;
}


/* ZTE_Audio_CJ_100511, chenjun, 2010-5-11, end */

/**********************************************************************
Expand All @@ -802,6 +912,7 @@ static int si4708_release(struct inode *inode, struct file *filp)
/* Make sure the device is power down. */
res = si4708_normal2standby();

rds_enabled=0;
return res < 0 ? res : 0;
}

Expand Down Expand Up @@ -837,20 +948,25 @@ static int si4708_ioctl(struct inode *inode, struct file *filp,
break;

case FM_NORMAL2STANDBY:

setRDSEnabled(0);
res = si4708_normal2standby();
break;

case FM_STANDBY2NORMAL:

setRDSEnabled(1);
res = si4708_standby2normal();
break;

case FM_TUNE:
if (get_user(user_data,(int *)arg))
{
res = -EFAULT;
res = -EFAULT;
goto exit;
}
}
res = si4708_set_frequency(user_data);
resetRDSBuffer();
break;

case FM_SEEK:
Expand All @@ -859,7 +975,7 @@ static int si4708_ioctl(struct inode *inode, struct file *filp,
res = -EFAULT;
goto exit;
}
res = si4708_seek(user_value[0], &user_value[1]);
res = si4708_seek(user_value[0], &user_value[1]);
if(res < 0)
goto exit;
if (copy_to_user((int *)arg,user_value, 2*sizeof(int)))
Expand All @@ -870,11 +986,11 @@ static int si4708_ioctl(struct inode *inode, struct file *filp,
break;

case FM_GET_VOL:
if (put_user((int)si4708_get_vol(), (int*)arg))
{
res = -EFAULT;
goto exit;
}
if (put_user((int)si4708_get_rds(), (int*)arg))
{
res = -EFAULT;
goto exit;
}
break;

case FM_SET_VOL:
Expand All @@ -883,7 +999,12 @@ static int si4708_ioctl(struct inode *inode, struct file *filp,
res = -EFAULT;
goto exit;
}
res = si4708_set_vol(int_to_char(user_data));
res = si4708_set_vol(int_to_char(user_data));
if (user_data>15){
setRDSEnabled(1);
}else{
setRDSEnabled(0);
}
break;

case FM_GET_BAND:
Expand Down Expand Up @@ -949,7 +1070,7 @@ static int si4708_ioctl(struct inode *inode, struct file *filp,
return -ENOTTY;
}

exit:
exit:
return res < 0 ? res : 0;

}
Expand Down Expand Up @@ -990,13 +1111,24 @@ static int si4708_probe(struct i2c_client *client, const struct i2c_device_id *i
}

fm_si4708_dev->initialized = 0;

fm_si4708_dev->buffer = kmalloc(buffer_size*8,GFP_KERNEL);
if (!fm_si4708_dev->buffer){
goto out;
}
fm_si4708_dev->read_index=0;
fm_si4708_dev->write_index=0;

setup_timer(&rds_timer, timer_func, 1234);

/* Register a misc device */
res = misc_register(&si4708_device);
if(res)
goto out;

FM_INFO(FYA_TAG"register fm_si4708 device successful!\n");


FM_INFO(FYA_TAG"register fm_si4708 device successful! 1\n");
fm_si4708_dev->client = client;

return 0;
Expand Down

0 comments on commit 7bffbd1

Please sign in to comment.