Hi,

I'm unable to test the functionality, due to missing devices.
But the code compiles cleanly.

Acked-by: Tobias Lorenz <tobias.lor...@gmx.net>

Bye,
Toby

Am Donnerstag 03 Dezember 2009 13:57:27 schrieb Joonyoung Shim:
> This patch is to support RDS on si470x i2c driver. The routine of RDS
> operation is almost same with thing of usb driver, but this uses RDS
> interrupt.
> 
> Signed-off-by: Joonyoung Shim <jy0922.s...@samsung.com>
> ---
>  drivers/media/radio/si470x/radio-si470x-i2c.c |  164 
> +++++++++++++++++++++++--
>  drivers/media/radio/si470x/radio-si470x.h     |    1 +
>  2 files changed, 155 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c 
> b/drivers/media/radio/si470x/radio-si470x-i2c.c
> index 6a40db8..5466015 100644
> --- a/drivers/media/radio/si470x/radio-si470x-i2c.c
> +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
> @@ -22,22 +22,17 @@
>   */
>  
>  
> -/*
> - * ToDo:
> - * - RDS support
> - */
> -
> -
>  /* driver definitions */
>  #define DRIVER_AUTHOR "Joonyoung Shim <jy0922.s...@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"
>  
> @@ -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*");
> +
>  
>  
>  /**************************************************************************
> @@ -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;
>  }
>  
> @@ -242,6 +260,105 @@ int si470x_vidioc_querycap(struct file *file, void 
> *priv,
>   **************************************************************************/
>  
>  /*
> + * 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
>   */
>  static int __devinit si470x_i2c_probe(struct i2c_client *client,
> @@ -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);
> @@ -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);
> @@ -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:
> @@ -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);
> diff --git a/drivers/media/radio/si470x/radio-si470x.h 
> b/drivers/media/radio/si470x/radio-si470x.h
> index f646f79..29e05cf 100644
> --- a/drivers/media/radio/si470x/radio-si470x.h
> +++ b/drivers/media/radio/si470x/radio-si470x.h
> @@ -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
>  };
>  
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to