Re: [PATCH 3/3] radio-si470x: support RDS on si470x i2c driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

unfortunately I have no possibility to test this patch. Nevertheless it looks clean to me.

Acked-by: Tobias Lorenz <tobias.lorenz@xxxxxxx>

Bye,
Toby

Am Mittwoch 18 November 2009 07:21:37 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.shim@xxxxxxxxxxx>
> ---
>  drivers/media/radio/si470x/radio-si470x-i2c.c |  159 +++++++++++++++++++++++--
>  drivers/media/radio/si470x/radio-si470x.h     |    1 +
>  2 files changed, 152 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
> index 4816a6d..77532e6 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.shim@xxxxxxxxxxx>";
> -#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*");
> +
>  
>  
>  /**************************************************************************
> @@ -242,6 +251,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 +365,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 +418,34 @@ 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);
> +
> +	/* enable RDS interrupt */
> +	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN;
> +	radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
> +	radio->registers[SYSCONFIG1] |= 0x1 << 2;
> +	retval = si470x_set_register(radio, SYSCONFIG1);
> +	if (retval < 0)
> +		goto err_rds;
> +
> +	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 +457,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 +476,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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux