diff options
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-i2c.c | 164 | ||||
-rw-r--r-- | 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 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; | |||
62 | module_param(radio_nr, int, 0444); | 57 | module_param(radio_nr, int, 0444); |
63 | MODULE_PARM_DESC(radio_nr, "Radio Nr"); | 58 | MODULE_PARM_DESC(radio_nr, "Radio Nr"); |
64 | 59 | ||
60 | /* RDS buffer blocks */ | ||
61 | static unsigned int rds_buf = 100; | ||
62 | module_param(rds_buf, uint, 0444); | ||
63 | MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); | ||
64 | |||
65 | /* RDS maximum block errors */ | ||
66 | static 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 */ | ||
71 | module_param(max_rds_errors, ushort, 0644); | ||
72 | MODULE_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 | ||
206 | done: | ||
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 | */ | ||
265 | static 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 | */ | ||
350 | static 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 | */ |
247 | static int __devinit si470x_i2c_probe(struct i2c_client *client, | 364 | static 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; |
321 | err_all: | 460 | err_all: |
461 | free_irq(client->irq, radio); | ||
462 | err_rds: | ||
463 | kfree(radio->buffer); | ||
322 | err_video: | 464 | err_video: |
323 | video_device_release(radio->videodev); | 465 | video_device_release(radio->videodev); |
324 | err_radio: | 466 | err_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 | ||