Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 176813
b: refs/heads/master
c: fe2137d
h: refs/heads/master
i:
  176811: b847d48
v: v3
  • Loading branch information
Joonyoung Shim authored and Mauro Carvalho Chehab committed Dec 16, 2009
1 parent 43f4a6d commit 7f046cb
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 11 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1aa925c957d37e077edb1de4553481734b8462cf
refs/heads/master: fe2137dd4e6e4b2f5e758765d5305e8dbb0d931c
164 changes: 154 additions & 10 deletions trunk/drivers/media/radio/si470x/radio-si470x-i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,17 @@
*/


/*
* ToDo:
* - RDS support
*/


/* driver definitions */
#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0)
#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 1)
#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
#define DRIVER_VERSION "1.0.0"
#define DRIVER_VERSION "1.0.1"

/* kernel includes */
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>

#include "radio-si470x.h"

Expand All @@ -62,6 +57,20 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0444);
MODULE_PARM_DESC(radio_nr, "Radio Nr");

/* RDS buffer blocks */
static unsigned int rds_buf = 100;
module_param(rds_buf, uint, 0444);
MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");

/* RDS maximum block errors */
static unsigned short max_rds_errors = 1;
/* 0 means 0 errors requiring correction */
/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
/* 2 means 3-5 errors requiring correction */
/* 3 means 6+ errors or errors in checkword, correction not possible */
module_param(max_rds_errors, ushort, 0644);
MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");



/**************************************************************************
Expand Down Expand Up @@ -181,12 +190,21 @@ int si470x_fops_open(struct file *file)
mutex_lock(&radio->lock);
radio->users++;

if (radio->users == 1)
if (radio->users == 1) {
/* start radio */
retval = si470x_start(radio);
if (retval < 0)
goto done;

/* enable RDS interrupt */
radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN;
radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
radio->registers[SYSCONFIG1] |= 0x1 << 2;
retval = si470x_set_register(radio, SYSCONFIG1);
}

done:
mutex_unlock(&radio->lock);

return retval;
}

Expand Down Expand Up @@ -241,6 +259,105 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
* I2C Interface
**************************************************************************/

/*
* si470x_i2c_interrupt_work - rds processing function
*/
static void si470x_i2c_interrupt_work(struct work_struct *work)
{
struct si470x_device *radio = container_of(work,
struct si470x_device, radio_work);
unsigned char regnr;
unsigned char blocknum;
unsigned short bler; /* rds block errors */
unsigned short rds;
unsigned char tmpbuf[3];
int retval = 0;

/* safety checks */
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
return;

/* Update RDS registers */
for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) {
retval = si470x_get_register(radio, STATUSRSSI + regnr);
if (retval < 0)
return;
}

/* get rds blocks */
if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0)
/* No RDS group ready, better luck next time */
return;

for (blocknum = 0; blocknum < 4; blocknum++) {
switch (blocknum) {
default:
bler = (radio->registers[STATUSRSSI] &
STATUSRSSI_BLERA) >> 9;
rds = radio->registers[RDSA];
break;
case 1:
bler = (radio->registers[READCHAN] &
READCHAN_BLERB) >> 14;
rds = radio->registers[RDSB];
break;
case 2:
bler = (radio->registers[READCHAN] &
READCHAN_BLERC) >> 12;
rds = radio->registers[RDSC];
break;
case 3:
bler = (radio->registers[READCHAN] &
READCHAN_BLERD) >> 10;
rds = radio->registers[RDSD];
break;
};

/* Fill the V4L2 RDS buffer */
put_unaligned_le16(rds, &tmpbuf);
tmpbuf[2] = blocknum; /* offset name */
tmpbuf[2] |= blocknum << 3; /* received offset */
if (bler > max_rds_errors)
tmpbuf[2] |= 0x80; /* uncorrectable errors */
else if (bler > 0)
tmpbuf[2] |= 0x40; /* corrected error(s) */

/* copy RDS block to internal buffer */
memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
radio->wr_index += 3;

/* wrap write pointer */
if (radio->wr_index >= radio->buf_size)
radio->wr_index = 0;

/* check for overflow */
if (radio->wr_index == radio->rd_index) {
/* increment and wrap read pointer */
radio->rd_index += 3;
if (radio->rd_index >= radio->buf_size)
radio->rd_index = 0;
}
}

if (radio->wr_index != radio->rd_index)
wake_up_interruptible(&radio->read_queue);
}


/*
* si470x_i2c_interrupt - interrupt handler
*/
static irqreturn_t si470x_i2c_interrupt(int irq, void *dev_id)
{
struct si470x_device *radio = dev_id;

if (!work_pending(&radio->radio_work))
schedule_work(&radio->radio_work);

return IRQ_HANDLED;
}


/*
* si470x_i2c_probe - probe for the device
*/
Expand All @@ -257,6 +374,8 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
retval = -ENOMEM;
goto err_initial;
}

INIT_WORK(&radio->radio_work, si470x_i2c_interrupt_work);
radio->users = 0;
radio->client = client;
mutex_init(&radio->lock);
Expand Down Expand Up @@ -308,6 +427,26 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
/* set initial frequency */
si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */

/* rds buffer allocation */
radio->buf_size = rds_buf * 3;
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
goto err_video;
}

/* rds buffer configuration */
radio->wr_index = 0;
radio->rd_index = 0;
init_waitqueue_head(&radio->read_queue);

retval = request_irq(client->irq, si470x_i2c_interrupt,
IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
if (retval) {
dev_err(&client->dev, "Failed to register interrupt\n");
goto err_rds;
}

/* register video device */
retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
radio_nr);
Expand All @@ -319,6 +458,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,

return 0;
err_all:
free_irq(client->irq, radio);
err_rds:
kfree(radio->buffer);
err_video:
video_device_release(radio->videodev);
err_radio:
Expand All @@ -335,6 +477,8 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client)
{
struct si470x_device *radio = i2c_get_clientdata(client);

free_irq(client->irq, radio);
cancel_work_sync(&radio->radio_work);
video_unregister_device(radio->videodev);
kfree(radio);
i2c_set_clientdata(client, NULL);
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/media/radio/si470x/radio-si470x.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ struct si470x_device {

#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
struct i2c_client *client;
struct work_struct radio_work;
#endif
};

Expand Down

0 comments on commit 7f046cb

Please sign in to comment.