aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJoonyoung Shim <jy0922.shim@samsung.com>2009-12-10 14:50:34 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-12-15 21:18:35 -0500
commitfe2137dd4e6e4b2f5e758765d5305e8dbb0d931c (patch)
tree6008e6d32d40b6e3eccee0d13d86f316237b97dd /drivers
parent1aa925c957d37e077edb1de4553481734b8462cf (diff)
V4L/DVB (13600): radio-si470x: support RDS on si470x i2c driver
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@samsung.com> Acked-by: Tobias Lorenz <tobias.lorenz@gmx.net> Signed-off-by: Douglas Schilling Landgraf <dougsland@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c164
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h1
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 4816a6d501c6..b8c0d5cda2f6 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -22,22 +22,17 @@
22 */ 22 */
23 23
24 24
25/*
26 * ToDo:
27 * - RDS support
28 */
29
30
31/* driver definitions */ 25/* driver definitions */
32#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>"; 26#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
33#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0) 27#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 1)
34#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" 28#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
35#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers" 29#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
36#define DRIVER_VERSION "1.0.0" 30#define DRIVER_VERSION "1.0.1"
37 31
38/* kernel includes */ 32/* kernel includes */
39#include <linux/i2c.h> 33#include <linux/i2c.h>
40#include <linux/delay.h> 34#include <linux/delay.h>
35#include <linux/interrupt.h>
41 36
42#include "radio-si470x.h" 37#include "radio-si470x.h"
43 38
@@ -62,6 +57,20 @@ static int radio_nr = -1;
62module_param(radio_nr, int, 0444); 57module_param(radio_nr, int, 0444);
63MODULE_PARM_DESC(radio_nr, "Radio Nr"); 58MODULE_PARM_DESC(radio_nr, "Radio Nr");
64 59
60/* RDS buffer blocks */
61static unsigned int rds_buf = 100;
62module_param(rds_buf, uint, 0444);
63MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
64
65/* RDS maximum block errors */
66static unsigned short max_rds_errors = 1;
67/* 0 means 0 errors requiring correction */
68/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
69/* 2 means 3-5 errors requiring correction */
70/* 3 means 6+ errors or errors in checkword, correction not possible */
71module_param(max_rds_errors, ushort, 0644);
72MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
73
65 74
66 75
67/************************************************************************** 76/**************************************************************************
@@ -181,12 +190,21 @@ int si470x_fops_open(struct file *file)
181 mutex_lock(&radio->lock); 190 mutex_lock(&radio->lock);
182 radio->users++; 191 radio->users++;
183 192
184 if (radio->users == 1) 193 if (radio->users == 1) {
185 /* start radio */ 194 /* start radio */
186 retval = si470x_start(radio); 195 retval = si470x_start(radio);
196 if (retval < 0)
197 goto done;
198
199 /* enable RDS interrupt */
200 radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN;
201 radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
202 radio->registers[SYSCONFIG1] |= 0x1 << 2;
203 retval = si470x_set_register(radio, SYSCONFIG1);
204 }
187 205
206done:
188 mutex_unlock(&radio->lock); 207 mutex_unlock(&radio->lock);
189
190 return retval; 208 return retval;
191} 209}
192 210
@@ -242,6 +260,105 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
242 **************************************************************************/ 260 **************************************************************************/
243 261
244/* 262/*
263 * si470x_i2c_interrupt_work - rds processing function
264 */
265static void si470x_i2c_interrupt_work(struct work_struct *work)
266{
267 struct si470x_device *radio = container_of(work,
268 struct si470x_device, radio_work);
269 unsigned char regnr;
270 unsigned char blocknum;
271 unsigned short bler; /* rds block errors */
272 unsigned short rds;
273 unsigned char tmpbuf[3];
274 int retval = 0;
275
276 /* safety checks */
277 if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
278 return;
279
280 /* Update RDS registers */
281 for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) {
282 retval = si470x_get_register(radio, STATUSRSSI + regnr);
283 if (retval < 0)
284 return;
285 }
286
287 /* get rds blocks */
288 if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0)
289 /* No RDS group ready, better luck next time */
290 return;
291
292 for (blocknum = 0; blocknum < 4; blocknum++) {
293 switch (blocknum) {
294 default:
295 bler = (radio->registers[STATUSRSSI] &
296 STATUSRSSI_BLERA) >> 9;
297 rds = radio->registers[RDSA];
298 break;
299 case 1:
300 bler = (radio->registers[READCHAN] &
301 READCHAN_BLERB) >> 14;
302 rds = radio->registers[RDSB];
303 break;
304 case 2:
305 bler = (radio->registers[READCHAN] &
306 READCHAN_BLERC) >> 12;
307 rds = radio->registers[RDSC];
308 break;
309 case 3:
310 bler = (radio->registers[READCHAN] &
311 READCHAN_BLERD) >> 10;
312 rds = radio->registers[RDSD];
313 break;
314 };
315
316 /* Fill the V4L2 RDS buffer */
317 put_unaligned_le16(rds, &tmpbuf);
318 tmpbuf[2] = blocknum; /* offset name */
319 tmpbuf[2] |= blocknum << 3; /* received offset */
320 if (bler > max_rds_errors)
321 tmpbuf[2] |= 0x80; /* uncorrectable errors */
322 else if (bler > 0)
323 tmpbuf[2] |= 0x40; /* corrected error(s) */
324
325 /* copy RDS block to internal buffer */
326 memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
327 radio->wr_index += 3;
328
329 /* wrap write pointer */
330 if (radio->wr_index >= radio->buf_size)
331 radio->wr_index = 0;
332
333 /* check for overflow */
334 if (radio->wr_index == radio->rd_index) {
335 /* increment and wrap read pointer */
336 radio->rd_index += 3;
337 if (radio->rd_index >= radio->buf_size)
338 radio->rd_index = 0;
339 }
340 }
341
342 if (radio->wr_index != radio->rd_index)
343 wake_up_interruptible(&radio->read_queue);
344}
345
346
347/*
348 * si470x_i2c_interrupt - interrupt handler
349 */
350static irqreturn_t si470x_i2c_interrupt(int irq, void *dev_id)
351{
352 struct si470x_device *radio = dev_id;
353
354 if (!work_pending(&radio->radio_work))
355 schedule_work(&radio->radio_work);
356
357 return IRQ_HANDLED;
358}
359
360
361/*
245 * si470x_i2c_probe - probe for the device 362 * si470x_i2c_probe - probe for the device
246 */ 363 */
247static int __devinit si470x_i2c_probe(struct i2c_client *client, 364static int __devinit si470x_i2c_probe(struct i2c_client *client,
@@ -257,6 +374,8 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
257 retval = -ENOMEM; 374 retval = -ENOMEM;
258 goto err_initial; 375 goto err_initial;
259 } 376 }
377
378 INIT_WORK(&radio->radio_work, si470x_i2c_interrupt_work);
260 radio->users = 0; 379 radio->users = 0;
261 radio->client = client; 380 radio->client = client;
262 mutex_init(&radio->lock); 381 mutex_init(&radio->lock);
@@ -308,6 +427,26 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
308 /* set initial frequency */ 427 /* set initial frequency */
309 si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ 428 si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
310 429
430 /* rds buffer allocation */
431 radio->buf_size = rds_buf * 3;
432 radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
433 if (!radio->buffer) {
434 retval = -EIO;
435 goto err_video;
436 }
437
438 /* rds buffer configuration */
439 radio->wr_index = 0;
440 radio->rd_index = 0;
441 init_waitqueue_head(&radio->read_queue);
442
443 retval = request_irq(client->irq, si470x_i2c_interrupt,
444 IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
445 if (retval) {
446 dev_err(&client->dev, "Failed to register interrupt\n");
447 goto err_rds;
448 }
449
311 /* register video device */ 450 /* register video device */
312 retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, 451 retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
313 radio_nr); 452 radio_nr);
@@ -319,6 +458,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
319 458
320 return 0; 459 return 0;
321err_all: 460err_all:
461 free_irq(client->irq, radio);
462err_rds:
463 kfree(radio->buffer);
322err_video: 464err_video:
323 video_device_release(radio->videodev); 465 video_device_release(radio->videodev);
324err_radio: 466err_radio:
@@ -335,6 +477,8 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client)
335{ 477{
336 struct si470x_device *radio = i2c_get_clientdata(client); 478 struct si470x_device *radio = i2c_get_clientdata(client);
337 479
480 free_irq(client->irq, radio);
481 cancel_work_sync(&radio->radio_work);
338 video_unregister_device(radio->videodev); 482 video_unregister_device(radio->videodev);
339 kfree(radio); 483 kfree(radio);
340 i2c_set_clientdata(client, NULL); 484 i2c_set_clientdata(client, NULL);
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index f646f79d2f7a..29e05cf53474 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -181,6 +181,7 @@ struct si470x_device {
181 181
182#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) 182#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
183 struct i2c_client *client; 183 struct i2c_client *client;
184 struct work_struct radio_work;
184#endif 185#endif
185}; 186};
186 187