diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-03-28 07:03:58 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-03-28 07:03:58 -0400 |
commit | cdccfc8dc0bf62a1da327324a8d639139acc9279 (patch) | |
tree | dca7934b27d510c9c006558979ebc48e07a531cf /drivers/media/radio | |
parent | b21a8ee67013372f439fbd1591e91d09de49bb9c (diff) | |
parent | c6b358748e19ce7e230b0926ac42696bc485a562 (diff) |
Merge branch 'fix/misc' into topic/misc
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/Kconfig | 4 | ||||
-rw-r--r-- | drivers/media/radio/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/radio/dsbr100.c | 128 | ||||
-rw-r--r-- | drivers/media/radio/radio-si4713.c | 3 | ||||
-rw-r--r-- | drivers/media/radio/radio-timb.c | 3 | ||||
-rw-r--r-- | drivers/media/radio/radio-wl1273.c | 367 | ||||
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-common.c | 1 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/Kconfig | 17 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/Makefile | 6 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv.h | 244 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_common.c | 1677 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_common.h | 402 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_rx.c | 847 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_rx.h | 59 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_tx.c | 425 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_tx.h | 37 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_v4l2.c | 580 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_v4l2.h | 33 |
18 files changed, 4481 insertions, 353 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 851716a7da0e..e4c97fd6f05a 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig | |||
@@ -426,6 +426,7 @@ config RADIO_TIMBERDALE | |||
426 | config RADIO_WL1273 | 426 | config RADIO_WL1273 |
427 | tristate "Texas Instruments WL1273 I2C FM Radio" | 427 | tristate "Texas Instruments WL1273 I2C FM Radio" |
428 | depends on I2C && VIDEO_V4L2 | 428 | depends on I2C && VIDEO_V4L2 |
429 | select MFD_CORE | ||
429 | select MFD_WL1273_CORE | 430 | select MFD_WL1273_CORE |
430 | select FW_LOADER | 431 | select FW_LOADER |
431 | ---help--- | 432 | ---help--- |
@@ -439,4 +440,7 @@ config RADIO_WL1273 | |||
439 | To compile this driver as a module, choose M here: the | 440 | To compile this driver as a module, choose M here: the |
440 | module will be called radio-wl1273. | 441 | module will be called radio-wl1273. |
441 | 442 | ||
443 | # TI's ST based wl128x FM radio | ||
444 | source "drivers/media/radio/wl128x/Kconfig" | ||
445 | |||
442 | endif # RADIO_ADAPTERS | 446 | endif # RADIO_ADAPTERS |
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index d68907fc7925..f484a6e04eb2 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile | |||
@@ -25,5 +25,6 @@ obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o | |||
25 | obj-$(CONFIG_RADIO_TEF6862) += tef6862.o | 25 | obj-$(CONFIG_RADIO_TEF6862) += tef6862.o |
26 | obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o | 26 | obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o |
27 | obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o | 27 | obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o |
28 | obj-$(CONFIG_RADIO_WL128X) += wl128x/ | ||
28 | 29 | ||
29 | EXTRA_CFLAGS += -Isound | 30 | EXTRA_CFLAGS += -Isound |
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index ed9cd7ad0604..3d8cc425fa6b 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c | |||
@@ -129,7 +129,7 @@ devices, that would be 76 and 91. */ | |||
129 | #define STARTED 0 | 129 | #define STARTED 0 |
130 | #define STOPPED 1 | 130 | #define STOPPED 1 |
131 | 131 | ||
132 | #define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev) | 132 | #define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev) |
133 | 133 | ||
134 | static int usb_dsbr100_probe(struct usb_interface *intf, | 134 | static int usb_dsbr100_probe(struct usb_interface *intf, |
135 | const struct usb_device_id *id); | 135 | const struct usb_device_id *id); |
@@ -148,10 +148,9 @@ struct dsbr100_device { | |||
148 | struct v4l2_device v4l2_dev; | 148 | struct v4l2_device v4l2_dev; |
149 | 149 | ||
150 | u8 *transfer_buffer; | 150 | u8 *transfer_buffer; |
151 | struct mutex lock; /* buffer locking */ | 151 | struct mutex v4l2_lock; |
152 | int curfreq; | 152 | int curfreq; |
153 | int stereo; | 153 | int stereo; |
154 | int removed; | ||
155 | int status; | 154 | int status; |
156 | }; | 155 | }; |
157 | 156 | ||
@@ -182,8 +181,6 @@ static int dsbr100_start(struct dsbr100_device *radio) | |||
182 | int retval; | 181 | int retval; |
183 | int request; | 182 | int request; |
184 | 183 | ||
185 | mutex_lock(&radio->lock); | ||
186 | |||
187 | retval = usb_control_msg(radio->usbdev, | 184 | retval = usb_control_msg(radio->usbdev, |
188 | usb_rcvctrlpipe(radio->usbdev, 0), | 185 | usb_rcvctrlpipe(radio->usbdev, 0), |
189 | USB_REQ_GET_STATUS, | 186 | USB_REQ_GET_STATUS, |
@@ -207,11 +204,9 @@ static int dsbr100_start(struct dsbr100_device *radio) | |||
207 | } | 204 | } |
208 | 205 | ||
209 | radio->status = STARTED; | 206 | radio->status = STARTED; |
210 | mutex_unlock(&radio->lock); | ||
211 | return (radio->transfer_buffer)[0]; | 207 | return (radio->transfer_buffer)[0]; |
212 | 208 | ||
213 | usb_control_msg_failed: | 209 | usb_control_msg_failed: |
214 | mutex_unlock(&radio->lock); | ||
215 | dev_err(&radio->usbdev->dev, | 210 | dev_err(&radio->usbdev->dev, |
216 | "%s - usb_control_msg returned %i, request %i\n", | 211 | "%s - usb_control_msg returned %i, request %i\n", |
217 | __func__, retval, request); | 212 | __func__, retval, request); |
@@ -225,8 +220,6 @@ static int dsbr100_stop(struct dsbr100_device *radio) | |||
225 | int retval; | 220 | int retval; |
226 | int request; | 221 | int request; |
227 | 222 | ||
228 | mutex_lock(&radio->lock); | ||
229 | |||
230 | retval = usb_control_msg(radio->usbdev, | 223 | retval = usb_control_msg(radio->usbdev, |
231 | usb_rcvctrlpipe(radio->usbdev, 0), | 224 | usb_rcvctrlpipe(radio->usbdev, 0), |
232 | USB_REQ_GET_STATUS, | 225 | USB_REQ_GET_STATUS, |
@@ -250,11 +243,9 @@ static int dsbr100_stop(struct dsbr100_device *radio) | |||
250 | } | 243 | } |
251 | 244 | ||
252 | radio->status = STOPPED; | 245 | radio->status = STOPPED; |
253 | mutex_unlock(&radio->lock); | ||
254 | return (radio->transfer_buffer)[0]; | 246 | return (radio->transfer_buffer)[0]; |
255 | 247 | ||
256 | usb_control_msg_failed: | 248 | usb_control_msg_failed: |
257 | mutex_unlock(&radio->lock); | ||
258 | dev_err(&radio->usbdev->dev, | 249 | dev_err(&radio->usbdev->dev, |
259 | "%s - usb_control_msg returned %i, request %i\n", | 250 | "%s - usb_control_msg returned %i, request %i\n", |
260 | __func__, retval, request); | 251 | __func__, retval, request); |
@@ -269,8 +260,6 @@ static int dsbr100_setfreq(struct dsbr100_device *radio) | |||
269 | int request; | 260 | int request; |
270 | int freq = (radio->curfreq / 16 * 80) / 1000 + 856; | 261 | int freq = (radio->curfreq / 16 * 80) / 1000 + 856; |
271 | 262 | ||
272 | mutex_lock(&radio->lock); | ||
273 | |||
274 | retval = usb_control_msg(radio->usbdev, | 263 | retval = usb_control_msg(radio->usbdev, |
275 | usb_rcvctrlpipe(radio->usbdev, 0), | 264 | usb_rcvctrlpipe(radio->usbdev, 0), |
276 | DSB100_TUNE, | 265 | DSB100_TUNE, |
@@ -306,12 +295,10 @@ static int dsbr100_setfreq(struct dsbr100_device *radio) | |||
306 | } | 295 | } |
307 | 296 | ||
308 | radio->stereo = !((radio->transfer_buffer)[0] & 0x01); | 297 | radio->stereo = !((radio->transfer_buffer)[0] & 0x01); |
309 | mutex_unlock(&radio->lock); | ||
310 | return (radio->transfer_buffer)[0]; | 298 | return (radio->transfer_buffer)[0]; |
311 | 299 | ||
312 | usb_control_msg_failed: | 300 | usb_control_msg_failed: |
313 | radio->stereo = -1; | 301 | radio->stereo = -1; |
314 | mutex_unlock(&radio->lock); | ||
315 | dev_err(&radio->usbdev->dev, | 302 | dev_err(&radio->usbdev->dev, |
316 | "%s - usb_control_msg returned %i, request %i\n", | 303 | "%s - usb_control_msg returned %i, request %i\n", |
317 | __func__, retval, request); | 304 | __func__, retval, request); |
@@ -324,8 +311,6 @@ static void dsbr100_getstat(struct dsbr100_device *radio) | |||
324 | { | 311 | { |
325 | int retval; | 312 | int retval; |
326 | 313 | ||
327 | mutex_lock(&radio->lock); | ||
328 | |||
329 | retval = usb_control_msg(radio->usbdev, | 314 | retval = usb_control_msg(radio->usbdev, |
330 | usb_rcvctrlpipe(radio->usbdev, 0), | 315 | usb_rcvctrlpipe(radio->usbdev, 0), |
331 | USB_REQ_GET_STATUS, | 316 | USB_REQ_GET_STATUS, |
@@ -340,33 +325,8 @@ static void dsbr100_getstat(struct dsbr100_device *radio) | |||
340 | } else { | 325 | } else { |
341 | radio->stereo = !(radio->transfer_buffer[0] & 0x01); | 326 | radio->stereo = !(radio->transfer_buffer[0] & 0x01); |
342 | } | 327 | } |
343 | |||
344 | mutex_unlock(&radio->lock); | ||
345 | } | 328 | } |
346 | 329 | ||
347 | /* USB subsystem interface begins here */ | ||
348 | |||
349 | /* | ||
350 | * Handle unplugging of the device. | ||
351 | * We call video_unregister_device in any case. | ||
352 | * The last function called in this procedure is | ||
353 | * usb_dsbr100_video_device_release | ||
354 | */ | ||
355 | static void usb_dsbr100_disconnect(struct usb_interface *intf) | ||
356 | { | ||
357 | struct dsbr100_device *radio = usb_get_intfdata(intf); | ||
358 | |||
359 | usb_set_intfdata (intf, NULL); | ||
360 | |||
361 | mutex_lock(&radio->lock); | ||
362 | radio->removed = 1; | ||
363 | mutex_unlock(&radio->lock); | ||
364 | |||
365 | video_unregister_device(&radio->videodev); | ||
366 | v4l2_device_disconnect(&radio->v4l2_dev); | ||
367 | } | ||
368 | |||
369 | |||
370 | static int vidioc_querycap(struct file *file, void *priv, | 330 | static int vidioc_querycap(struct file *file, void *priv, |
371 | struct v4l2_capability *v) | 331 | struct v4l2_capability *v) |
372 | { | 332 | { |
@@ -385,10 +345,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
385 | { | 345 | { |
386 | struct dsbr100_device *radio = video_drvdata(file); | 346 | struct dsbr100_device *radio = video_drvdata(file); |
387 | 347 | ||
388 | /* safety check */ | ||
389 | if (radio->removed) | ||
390 | return -EIO; | ||
391 | |||
392 | if (v->index > 0) | 348 | if (v->index > 0) |
393 | return -EINVAL; | 349 | return -EINVAL; |
394 | 350 | ||
@@ -410,16 +366,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
410 | static int vidioc_s_tuner(struct file *file, void *priv, | 366 | static int vidioc_s_tuner(struct file *file, void *priv, |
411 | struct v4l2_tuner *v) | 367 | struct v4l2_tuner *v) |
412 | { | 368 | { |
413 | struct dsbr100_device *radio = video_drvdata(file); | 369 | return v->index ? -EINVAL : 0; |
414 | |||
415 | /* safety check */ | ||
416 | if (radio->removed) | ||
417 | return -EIO; | ||
418 | |||
419 | if (v->index > 0) | ||
420 | return -EINVAL; | ||
421 | |||
422 | return 0; | ||
423 | } | 370 | } |
424 | 371 | ||
425 | static int vidioc_s_frequency(struct file *file, void *priv, | 372 | static int vidioc_s_frequency(struct file *file, void *priv, |
@@ -428,13 +375,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, | |||
428 | struct dsbr100_device *radio = video_drvdata(file); | 375 | struct dsbr100_device *radio = video_drvdata(file); |
429 | int retval; | 376 | int retval; |
430 | 377 | ||
431 | /* safety check */ | ||
432 | if (radio->removed) | ||
433 | return -EIO; | ||
434 | |||
435 | mutex_lock(&radio->lock); | ||
436 | radio->curfreq = f->frequency; | 378 | radio->curfreq = f->frequency; |
437 | mutex_unlock(&radio->lock); | ||
438 | 379 | ||
439 | retval = dsbr100_setfreq(radio); | 380 | retval = dsbr100_setfreq(radio); |
440 | if (retval < 0) | 381 | if (retval < 0) |
@@ -447,10 +388,6 @@ static int vidioc_g_frequency(struct file *file, void *priv, | |||
447 | { | 388 | { |
448 | struct dsbr100_device *radio = video_drvdata(file); | 389 | struct dsbr100_device *radio = video_drvdata(file); |
449 | 390 | ||
450 | /* safety check */ | ||
451 | if (radio->removed) | ||
452 | return -EIO; | ||
453 | |||
454 | f->type = V4L2_TUNER_RADIO; | 391 | f->type = V4L2_TUNER_RADIO; |
455 | f->frequency = radio->curfreq; | 392 | f->frequency = radio->curfreq; |
456 | return 0; | 393 | return 0; |
@@ -472,10 +409,6 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
472 | { | 409 | { |
473 | struct dsbr100_device *radio = video_drvdata(file); | 410 | struct dsbr100_device *radio = video_drvdata(file); |
474 | 411 | ||
475 | /* safety check */ | ||
476 | if (radio->removed) | ||
477 | return -EIO; | ||
478 | |||
479 | switch (ctrl->id) { | 412 | switch (ctrl->id) { |
480 | case V4L2_CID_AUDIO_MUTE: | 413 | case V4L2_CID_AUDIO_MUTE: |
481 | ctrl->value = radio->status; | 414 | ctrl->value = radio->status; |
@@ -490,10 +423,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, | |||
490 | struct dsbr100_device *radio = video_drvdata(file); | 423 | struct dsbr100_device *radio = video_drvdata(file); |
491 | int retval; | 424 | int retval; |
492 | 425 | ||
493 | /* safety check */ | ||
494 | if (radio->removed) | ||
495 | return -EIO; | ||
496 | |||
497 | switch (ctrl->id) { | 426 | switch (ctrl->id) { |
498 | case V4L2_CID_AUDIO_MUTE: | 427 | case V4L2_CID_AUDIO_MUTE: |
499 | if (ctrl->value) { | 428 | if (ctrl->value) { |
@@ -535,25 +464,44 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | |||
535 | 464 | ||
536 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 465 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) |
537 | { | 466 | { |
538 | if (i != 0) | 467 | return i ? -EINVAL : 0; |
539 | return -EINVAL; | ||
540 | return 0; | ||
541 | } | 468 | } |
542 | 469 | ||
543 | static int vidioc_s_audio(struct file *file, void *priv, | 470 | static int vidioc_s_audio(struct file *file, void *priv, |
544 | struct v4l2_audio *a) | 471 | struct v4l2_audio *a) |
545 | { | 472 | { |
546 | if (a->index != 0) | 473 | return a->index ? -EINVAL : 0; |
547 | return -EINVAL; | 474 | } |
548 | return 0; | 475 | |
476 | /* USB subsystem interface begins here */ | ||
477 | |||
478 | /* | ||
479 | * Handle unplugging of the device. | ||
480 | * We call video_unregister_device in any case. | ||
481 | * The last function called in this procedure is | ||
482 | * usb_dsbr100_video_device_release | ||
483 | */ | ||
484 | static void usb_dsbr100_disconnect(struct usb_interface *intf) | ||
485 | { | ||
486 | struct dsbr100_device *radio = usb_get_intfdata(intf); | ||
487 | |||
488 | v4l2_device_get(&radio->v4l2_dev); | ||
489 | mutex_lock(&radio->v4l2_lock); | ||
490 | usb_set_intfdata(intf, NULL); | ||
491 | video_unregister_device(&radio->videodev); | ||
492 | v4l2_device_disconnect(&radio->v4l2_dev); | ||
493 | mutex_unlock(&radio->v4l2_lock); | ||
494 | v4l2_device_put(&radio->v4l2_dev); | ||
549 | } | 495 | } |
550 | 496 | ||
497 | |||
551 | /* Suspend device - stop device. */ | 498 | /* Suspend device - stop device. */ |
552 | static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) | 499 | static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) |
553 | { | 500 | { |
554 | struct dsbr100_device *radio = usb_get_intfdata(intf); | 501 | struct dsbr100_device *radio = usb_get_intfdata(intf); |
555 | int retval; | 502 | int retval; |
556 | 503 | ||
504 | mutex_lock(&radio->v4l2_lock); | ||
557 | if (radio->status == STARTED) { | 505 | if (radio->status == STARTED) { |
558 | retval = dsbr100_stop(radio); | 506 | retval = dsbr100_stop(radio); |
559 | if (retval < 0) | 507 | if (retval < 0) |
@@ -564,11 +512,9 @@ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) | |||
564 | * we set status equal to STARTED. | 512 | * we set status equal to STARTED. |
565 | * On resume we will check status and run radio if needed. | 513 | * On resume we will check status and run radio if needed. |
566 | */ | 514 | */ |
567 | |||
568 | mutex_lock(&radio->lock); | ||
569 | radio->status = STARTED; | 515 | radio->status = STARTED; |
570 | mutex_unlock(&radio->lock); | ||
571 | } | 516 | } |
517 | mutex_unlock(&radio->v4l2_lock); | ||
572 | 518 | ||
573 | dev_info(&intf->dev, "going into suspend..\n"); | 519 | dev_info(&intf->dev, "going into suspend..\n"); |
574 | 520 | ||
@@ -581,11 +527,13 @@ static int usb_dsbr100_resume(struct usb_interface *intf) | |||
581 | struct dsbr100_device *radio = usb_get_intfdata(intf); | 527 | struct dsbr100_device *radio = usb_get_intfdata(intf); |
582 | int retval; | 528 | int retval; |
583 | 529 | ||
530 | mutex_lock(&radio->v4l2_lock); | ||
584 | if (radio->status == STARTED) { | 531 | if (radio->status == STARTED) { |
585 | retval = dsbr100_start(radio); | 532 | retval = dsbr100_start(radio); |
586 | if (retval < 0) | 533 | if (retval < 0) |
587 | dev_warn(&intf->dev, "dsbr100_start failed\n"); | 534 | dev_warn(&intf->dev, "dsbr100_start failed\n"); |
588 | } | 535 | } |
536 | mutex_unlock(&radio->v4l2_lock); | ||
589 | 537 | ||
590 | dev_info(&intf->dev, "coming out of suspend..\n"); | 538 | dev_info(&intf->dev, "coming out of suspend..\n"); |
591 | 539 | ||
@@ -593,9 +541,9 @@ static int usb_dsbr100_resume(struct usb_interface *intf) | |||
593 | } | 541 | } |
594 | 542 | ||
595 | /* free data structures */ | 543 | /* free data structures */ |
596 | static void usb_dsbr100_video_device_release(struct video_device *videodev) | 544 | static void usb_dsbr100_release(struct v4l2_device *v4l2_dev) |
597 | { | 545 | { |
598 | struct dsbr100_device *radio = videodev_to_radio(videodev); | 546 | struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev); |
599 | 547 | ||
600 | v4l2_device_unregister(&radio->v4l2_dev); | 548 | v4l2_device_unregister(&radio->v4l2_dev); |
601 | kfree(radio->transfer_buffer); | 549 | kfree(radio->transfer_buffer); |
@@ -605,7 +553,7 @@ static void usb_dsbr100_video_device_release(struct video_device *videodev) | |||
605 | /* File system interface */ | 553 | /* File system interface */ |
606 | static const struct v4l2_file_operations usb_dsbr100_fops = { | 554 | static const struct v4l2_file_operations usb_dsbr100_fops = { |
607 | .owner = THIS_MODULE, | 555 | .owner = THIS_MODULE, |
608 | .ioctl = video_ioctl2, | 556 | .unlocked_ioctl = video_ioctl2, |
609 | }; | 557 | }; |
610 | 558 | ||
611 | static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { | 559 | static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { |
@@ -644,6 +592,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, | |||
644 | } | 592 | } |
645 | 593 | ||
646 | v4l2_dev = &radio->v4l2_dev; | 594 | v4l2_dev = &radio->v4l2_dev; |
595 | v4l2_dev->release = usb_dsbr100_release; | ||
647 | 596 | ||
648 | retval = v4l2_device_register(&intf->dev, v4l2_dev); | 597 | retval = v4l2_device_register(&intf->dev, v4l2_dev); |
649 | if (retval < 0) { | 598 | if (retval < 0) { |
@@ -653,15 +602,14 @@ static int usb_dsbr100_probe(struct usb_interface *intf, | |||
653 | return retval; | 602 | return retval; |
654 | } | 603 | } |
655 | 604 | ||
605 | mutex_init(&radio->v4l2_lock); | ||
656 | strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name)); | 606 | strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name)); |
657 | radio->videodev.v4l2_dev = v4l2_dev; | 607 | radio->videodev.v4l2_dev = v4l2_dev; |
658 | radio->videodev.fops = &usb_dsbr100_fops; | 608 | radio->videodev.fops = &usb_dsbr100_fops; |
659 | radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; | 609 | radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; |
660 | radio->videodev.release = usb_dsbr100_video_device_release; | 610 | radio->videodev.release = video_device_release_empty; |
661 | 611 | radio->videodev.lock = &radio->v4l2_lock; | |
662 | mutex_init(&radio->lock); | ||
663 | 612 | ||
664 | radio->removed = 0; | ||
665 | radio->usbdev = interface_to_usbdev(intf); | 613 | radio->usbdev = interface_to_usbdev(intf); |
666 | radio->curfreq = FREQ_MIN * FREQ_MUL; | 614 | radio->curfreq = FREQ_MIN * FREQ_MUL; |
667 | radio->status = STOPPED; | 615 | radio->status = STOPPED; |
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index 726d367ad8d0..444b4cf7e65c 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c | |||
@@ -224,7 +224,8 @@ static int radio_si4713_s_frequency(struct file *file, void *p, | |||
224 | s_frequency, vf); | 224 | s_frequency, vf); |
225 | } | 225 | } |
226 | 226 | ||
227 | static long radio_si4713_default(struct file *file, void *p, int cmd, void *arg) | 227 | static long radio_si4713_default(struct file *file, void *p, |
228 | bool valid_prio, int cmd, void *arg) | ||
228 | { | 229 | { |
229 | return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, | 230 | return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, |
230 | ioctl, cmd, arg); | 231 | ioctl, cmd, arg); |
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index a185610b376b..1e3a8dd820a4 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <media/v4l2-ioctl.h> | 21 | #include <media/v4l2-ioctl.h> |
22 | #include <media/v4l2-device.h> | 22 | #include <media/v4l2-device.h> |
23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
24 | #include <linux/mfd/core.h> | ||
24 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
25 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
26 | #include <linux/i2c.h> | 27 | #include <linux/i2c.h> |
@@ -148,7 +149,7 @@ static const struct v4l2_file_operations timbradio_fops = { | |||
148 | 149 | ||
149 | static int __devinit timbradio_probe(struct platform_device *pdev) | 150 | static int __devinit timbradio_probe(struct platform_device *pdev) |
150 | { | 151 | { |
151 | struct timb_radio_platform_data *pdata = pdev->dev.platform_data; | 152 | struct timb_radio_platform_data *pdata = mfd_get_data(pdev); |
152 | struct timbradio *tr; | 153 | struct timbradio *tr; |
153 | int err; | 154 | int err; |
154 | 155 | ||
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 7ecc8e657663..e2550dc2944f 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Driver for the Texas Instruments WL1273 FM radio. | 2 | * Driver for the Texas Instruments WL1273 FM radio. |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Nokia Corporation | 4 | * Copyright (C) 2011 Nokia Corporation |
5 | * Author: Matti J. Aaltonen <matti.j.aaltonen@nokia.com> | 5 | * Author: Matti J. Aaltonen <matti.j.aaltonen@nokia.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
@@ -67,7 +67,6 @@ struct wl1273_device { | |||
67 | 67 | ||
68 | /* RDS */ | 68 | /* RDS */ |
69 | unsigned int rds_on; | 69 | unsigned int rds_on; |
70 | struct delayed_work work; | ||
71 | 70 | ||
72 | wait_queue_head_t read_queue; | 71 | wait_queue_head_t read_queue; |
73 | struct mutex lock; /* for serializing fm radio operations */ | 72 | struct mutex lock; /* for serializing fm radio operations */ |
@@ -104,58 +103,6 @@ static unsigned int rds_buf = 100; | |||
104 | module_param(rds_buf, uint, 0); | 103 | module_param(rds_buf, uint, 0); |
105 | MODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100"); | 104 | MODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100"); |
106 | 105 | ||
107 | static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value) | ||
108 | { | ||
109 | struct i2c_client *client = core->client; | ||
110 | u8 b[2]; | ||
111 | int r; | ||
112 | |||
113 | r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b); | ||
114 | if (r != 2) { | ||
115 | dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg); | ||
116 | return -EREMOTEIO; | ||
117 | } | ||
118 | |||
119 | *value = (u16)b[0] << 8 | b[1]; | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param) | ||
125 | { | ||
126 | struct i2c_client *client = core->client; | ||
127 | u8 buf[] = { (param >> 8) & 0xff, param & 0xff }; | ||
128 | int r; | ||
129 | |||
130 | r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf); | ||
131 | if (r) { | ||
132 | dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd); | ||
133 | return r; | ||
134 | } | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len) | ||
140 | { | ||
141 | struct i2c_client *client = core->client; | ||
142 | struct i2c_msg msg; | ||
143 | int r; | ||
144 | |||
145 | msg.addr = client->addr; | ||
146 | msg.flags = 0; | ||
147 | msg.buf = data; | ||
148 | msg.len = len; | ||
149 | |||
150 | r = i2c_transfer(client->adapter, &msg, 1); | ||
151 | if (r != 1) { | ||
152 | dev_err(&client->dev, "%s: write error.\n", __func__); | ||
153 | return -EREMOTEIO; | ||
154 | } | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int wl1273_fm_write_fw(struct wl1273_core *core, | 106 | static int wl1273_fm_write_fw(struct wl1273_core *core, |
160 | __u8 *fw, int len) | 107 | __u8 *fw, int len) |
161 | { | 108 | { |
@@ -188,94 +135,6 @@ static int wl1273_fm_write_fw(struct wl1273_core *core, | |||
188 | return r; | 135 | return r; |
189 | } | 136 | } |
190 | 137 | ||
191 | /** | ||
192 | * wl1273_fm_set_audio() - Set audio mode. | ||
193 | * @core: A pointer to the device struct. | ||
194 | * @new_mode: The new audio mode. | ||
195 | * | ||
196 | * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG. | ||
197 | */ | ||
198 | static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode) | ||
199 | { | ||
200 | int r = 0; | ||
201 | |||
202 | if (core->mode == WL1273_MODE_OFF || | ||
203 | core->mode == WL1273_MODE_SUSPENDED) | ||
204 | return -EPERM; | ||
205 | |||
206 | if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) { | ||
207 | r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET, | ||
208 | WL1273_PCM_DEF_MODE); | ||
209 | if (r) | ||
210 | goto out; | ||
211 | |||
212 | r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, | ||
213 | core->i2s_mode); | ||
214 | if (r) | ||
215 | goto out; | ||
216 | |||
217 | r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, | ||
218 | WL1273_AUDIO_ENABLE_I2S); | ||
219 | if (r) | ||
220 | goto out; | ||
221 | |||
222 | } else if (core->mode == WL1273_MODE_RX && | ||
223 | new_mode == WL1273_AUDIO_ANALOG) { | ||
224 | r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, | ||
225 | WL1273_AUDIO_ENABLE_ANALOG); | ||
226 | if (r) | ||
227 | goto out; | ||
228 | |||
229 | } else if (core->mode == WL1273_MODE_TX && | ||
230 | new_mode == WL1273_AUDIO_DIGITAL) { | ||
231 | r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, | ||
232 | core->i2s_mode); | ||
233 | if (r) | ||
234 | goto out; | ||
235 | |||
236 | r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, | ||
237 | WL1273_AUDIO_IO_SET_I2S); | ||
238 | if (r) | ||
239 | goto out; | ||
240 | |||
241 | } else if (core->mode == WL1273_MODE_TX && | ||
242 | new_mode == WL1273_AUDIO_ANALOG) { | ||
243 | r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, | ||
244 | WL1273_AUDIO_IO_SET_ANALOG); | ||
245 | if (r) | ||
246 | goto out; | ||
247 | } | ||
248 | |||
249 | core->audio_mode = new_mode; | ||
250 | out: | ||
251 | return r; | ||
252 | } | ||
253 | |||
254 | /** | ||
255 | * wl1273_fm_set_volume() - Set volume. | ||
256 | * @core: A pointer to the device struct. | ||
257 | * @volume: The new volume value. | ||
258 | */ | ||
259 | static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume) | ||
260 | { | ||
261 | u16 val; | ||
262 | int r; | ||
263 | |||
264 | if (volume > WL1273_MAX_VOLUME) | ||
265 | return -EINVAL; | ||
266 | |||
267 | if (core->volume == volume) | ||
268 | return 0; | ||
269 | |||
270 | val = volume; | ||
271 | r = wl1273_fm_read_reg(core, WL1273_VOLUME_SET, &val); | ||
272 | if (r) | ||
273 | return r; | ||
274 | |||
275 | core->volume = volume; | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | #define WL1273_FIFO_HAS_DATA(status) (1 << 5 & status) | 138 | #define WL1273_FIFO_HAS_DATA(status) (1 << 5 & status) |
280 | #define WL1273_RDS_CORRECTABLE_ERROR (1 << 3) | 139 | #define WL1273_RDS_CORRECTABLE_ERROR (1 << 3) |
281 | #define WL1273_RDS_UNCORRECTABLE_ERROR (1 << 4) | 140 | #define WL1273_RDS_UNCORRECTABLE_ERROR (1 << 4) |
@@ -306,7 +165,7 @@ static int wl1273_fm_rds(struct wl1273_device *radio) | |||
306 | if (core->mode != WL1273_MODE_RX) | 165 | if (core->mode != WL1273_MODE_RX) |
307 | return 0; | 166 | return 0; |
308 | 167 | ||
309 | r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); | 168 | r = core->read(core, WL1273_RDS_SYNC_GET, &val); |
310 | if (r) | 169 | if (r) |
311 | return r; | 170 | return r; |
312 | 171 | ||
@@ -374,7 +233,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) | |||
374 | u16 flags; | 233 | u16 flags; |
375 | int r; | 234 | int r; |
376 | 235 | ||
377 | r = wl1273_fm_read_reg(core, WL1273_FLAG_GET, &flags); | 236 | r = core->read(core, WL1273_FLAG_GET, &flags); |
378 | if (r) | 237 | if (r) |
379 | goto out; | 238 | goto out; |
380 | 239 | ||
@@ -398,7 +257,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) | |||
398 | if (flags & WL1273_LEV_EVENT) { | 257 | if (flags & WL1273_LEV_EVENT) { |
399 | u16 level; | 258 | u16 level; |
400 | 259 | ||
401 | r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &level); | 260 | r = core->read(core, WL1273_RSSI_LVL_GET, &level); |
402 | if (r) | 261 | if (r) |
403 | goto out; | 262 | goto out; |
404 | 263 | ||
@@ -439,8 +298,8 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) | |||
439 | dev_dbg(radio->dev, "IRQ: FR:\n"); | 298 | dev_dbg(radio->dev, "IRQ: FR:\n"); |
440 | 299 | ||
441 | if (core->mode == WL1273_MODE_RX) { | 300 | if (core->mode == WL1273_MODE_RX) { |
442 | r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, | 301 | r = core->write(core, WL1273_TUNER_MODE_SET, |
443 | TUNER_MODE_STOP_SEARCH); | 302 | TUNER_MODE_STOP_SEARCH); |
444 | if (r) { | 303 | if (r) { |
445 | dev_err(radio->dev, | 304 | dev_err(radio->dev, |
446 | "%s: TUNER_MODE_SET fails: %d\n", | 305 | "%s: TUNER_MODE_SET fails: %d\n", |
@@ -448,7 +307,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) | |||
448 | goto out; | 307 | goto out; |
449 | } | 308 | } |
450 | 309 | ||
451 | r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &freq); | 310 | r = core->read(core, WL1273_FREQ_SET, &freq); |
452 | if (r) | 311 | if (r) |
453 | goto out; | 312 | goto out; |
454 | 313 | ||
@@ -467,7 +326,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) | |||
467 | dev_dbg(radio->dev, "%dkHz\n", radio->rx_frequency); | 326 | dev_dbg(radio->dev, "%dkHz\n", radio->rx_frequency); |
468 | 327 | ||
469 | } else { | 328 | } else { |
470 | r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &freq); | 329 | r = core->read(core, WL1273_CHANL_SET, &freq); |
471 | if (r) | 330 | if (r) |
472 | goto out; | 331 | goto out; |
473 | 332 | ||
@@ -477,8 +336,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) | |||
477 | } | 336 | } |
478 | 337 | ||
479 | out: | 338 | out: |
480 | wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, | 339 | core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); |
481 | radio->irq_flags); | ||
482 | complete(&radio->busy); | 340 | complete(&radio->busy); |
483 | 341 | ||
484 | return IRQ_HANDLED; | 342 | return IRQ_HANDLED; |
@@ -512,7 +370,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq) | |||
512 | dev_dbg(radio->dev, "%s: freq: %d kHz\n", __func__, freq); | 370 | dev_dbg(radio->dev, "%s: freq: %d kHz\n", __func__, freq); |
513 | 371 | ||
514 | /* Set the current tx channel */ | 372 | /* Set the current tx channel */ |
515 | r = wl1273_fm_write_cmd(core, WL1273_CHANL_SET, freq / 10); | 373 | r = core->write(core, WL1273_CHANL_SET, freq / 10); |
516 | if (r) | 374 | if (r) |
517 | return r; | 375 | return r; |
518 | 376 | ||
@@ -526,7 +384,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq) | |||
526 | dev_dbg(radio->dev, "WL1273_CHANL_SET: %d\n", r); | 384 | dev_dbg(radio->dev, "WL1273_CHANL_SET: %d\n", r); |
527 | 385 | ||
528 | /* Enable the output power */ | 386 | /* Enable the output power */ |
529 | r = wl1273_fm_write_cmd(core, WL1273_POWER_ENB_SET, 1); | 387 | r = core->write(core, WL1273_POWER_ENB_SET, 1); |
530 | if (r) | 388 | if (r) |
531 | return r; | 389 | return r; |
532 | 390 | ||
@@ -566,20 +424,20 @@ static int wl1273_fm_set_rx_freq(struct wl1273_device *radio, unsigned int freq) | |||
566 | 424 | ||
567 | dev_dbg(radio->dev, "%s: %dkHz\n", __func__, freq); | 425 | dev_dbg(radio->dev, "%s: %dkHz\n", __func__, freq); |
568 | 426 | ||
569 | wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags); | 427 | core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); |
570 | 428 | ||
571 | if (radio->band == WL1273_BAND_JAPAN) | 429 | if (radio->band == WL1273_BAND_JAPAN) |
572 | f = (freq - WL1273_BAND_JAPAN_LOW) / 50; | 430 | f = (freq - WL1273_BAND_JAPAN_LOW) / 50; |
573 | else | 431 | else |
574 | f = (freq - WL1273_BAND_OTHER_LOW) / 50; | 432 | f = (freq - WL1273_BAND_OTHER_LOW) / 50; |
575 | 433 | ||
576 | r = wl1273_fm_write_cmd(core, WL1273_FREQ_SET, f); | 434 | r = core->write(core, WL1273_FREQ_SET, f); |
577 | if (r) { | 435 | if (r) { |
578 | dev_err(radio->dev, "FREQ_SET fails\n"); | 436 | dev_err(radio->dev, "FREQ_SET fails\n"); |
579 | goto err; | 437 | goto err; |
580 | } | 438 | } |
581 | 439 | ||
582 | r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET); | 440 | r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET); |
583 | if (r) { | 441 | if (r) { |
584 | dev_err(radio->dev, "TUNER_MODE_SET fails\n"); | 442 | dev_err(radio->dev, "TUNER_MODE_SET fails\n"); |
585 | goto err; | 443 | goto err; |
@@ -609,7 +467,7 @@ static int wl1273_fm_get_freq(struct wl1273_device *radio) | |||
609 | int r; | 467 | int r; |
610 | 468 | ||
611 | if (core->mode == WL1273_MODE_RX) { | 469 | if (core->mode == WL1273_MODE_RX) { |
612 | r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &f); | 470 | r = core->read(core, WL1273_FREQ_SET, &f); |
613 | if (r) | 471 | if (r) |
614 | return r; | 472 | return r; |
615 | 473 | ||
@@ -619,7 +477,7 @@ static int wl1273_fm_get_freq(struct wl1273_device *radio) | |||
619 | else | 477 | else |
620 | freq = WL1273_BAND_OTHER_LOW + 50 * f; | 478 | freq = WL1273_BAND_OTHER_LOW + 50 * f; |
621 | } else { | 479 | } else { |
622 | r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &f); | 480 | r = core->read(core, WL1273_CHANL_SET, &f); |
623 | if (r) | 481 | if (r) |
624 | return r; | 482 | return r; |
625 | 483 | ||
@@ -670,7 +528,7 @@ static int wl1273_fm_upload_firmware_patch(struct wl1273_device *radio) | |||
670 | } | 528 | } |
671 | 529 | ||
672 | /* ignore possible error here */ | 530 | /* ignore possible error here */ |
673 | wl1273_fm_write_cmd(core, WL1273_RESET, 0); | 531 | core->write(core, WL1273_RESET, 0); |
674 | 532 | ||
675 | dev_dbg(dev, "%s - download OK, r: %d\n", __func__, r); | 533 | dev_dbg(dev, "%s - download OK, r: %d\n", __func__, r); |
676 | out: | 534 | out: |
@@ -683,14 +541,14 @@ static int wl1273_fm_stop(struct wl1273_device *radio) | |||
683 | struct wl1273_core *core = radio->core; | 541 | struct wl1273_core *core = radio->core; |
684 | 542 | ||
685 | if (core->mode == WL1273_MODE_RX) { | 543 | if (core->mode == WL1273_MODE_RX) { |
686 | int r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, | 544 | int r = core->write(core, WL1273_POWER_SET, |
687 | WL1273_POWER_SET_OFF); | 545 | WL1273_POWER_SET_OFF); |
688 | if (r) | 546 | if (r) |
689 | dev_err(radio->dev, "%s: POWER_SET fails: %d\n", | 547 | dev_err(radio->dev, "%s: POWER_SET fails: %d\n", |
690 | __func__, r); | 548 | __func__, r); |
691 | } else if (core->mode == WL1273_MODE_TX) { | 549 | } else if (core->mode == WL1273_MODE_TX) { |
692 | int r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, | 550 | int r = core->write(core, WL1273_PUPD_SET, |
693 | WL1273_PUPD_SET_OFF); | 551 | WL1273_PUPD_SET_OFF); |
694 | if (r) | 552 | if (r) |
695 | dev_err(radio->dev, | 553 | dev_err(radio->dev, |
696 | "%s: PUPD_SET fails: %d\n", __func__, r); | 554 | "%s: PUPD_SET fails: %d\n", __func__, r); |
@@ -725,11 +583,11 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) | |||
725 | val |= WL1273_POWER_SET_RDS; | 583 | val |= WL1273_POWER_SET_RDS; |
726 | 584 | ||
727 | /* If this fails try again */ | 585 | /* If this fails try again */ |
728 | r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val); | 586 | r = core->write(core, WL1273_POWER_SET, val); |
729 | if (r) { | 587 | if (r) { |
730 | msleep(100); | 588 | msleep(100); |
731 | 589 | ||
732 | r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val); | 590 | r = core->write(core, WL1273_POWER_SET, val); |
733 | if (r) { | 591 | if (r) { |
734 | dev_err(dev, "%s: POWER_SET fails\n", __func__); | 592 | dev_err(dev, "%s: POWER_SET fails\n", __func__); |
735 | goto fail; | 593 | goto fail; |
@@ -742,11 +600,10 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) | |||
742 | 600 | ||
743 | } else if (new_mode == WL1273_MODE_TX) { | 601 | } else if (new_mode == WL1273_MODE_TX) { |
744 | /* If this fails try again once */ | 602 | /* If this fails try again once */ |
745 | r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, | 603 | r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_ON); |
746 | WL1273_PUPD_SET_ON); | ||
747 | if (r) { | 604 | if (r) { |
748 | msleep(100); | 605 | msleep(100); |
749 | r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, | 606 | r = core->write(core, WL1273_PUPD_SET, |
750 | WL1273_PUPD_SET_ON); | 607 | WL1273_PUPD_SET_ON); |
751 | if (r) { | 608 | if (r) { |
752 | dev_err(dev, "%s: PUPD_SET fails\n", __func__); | 609 | dev_err(dev, "%s: PUPD_SET fails\n", __func__); |
@@ -755,9 +612,9 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) | |||
755 | } | 612 | } |
756 | 613 | ||
757 | if (radio->rds_on) | 614 | if (radio->rds_on) |
758 | r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 1); | 615 | r = core->write(core, WL1273_RDS_DATA_ENB, 1); |
759 | else | 616 | else |
760 | r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 0); | 617 | r = core->write(core, WL1273_RDS_DATA_ENB, 0); |
761 | } else { | 618 | } else { |
762 | dev_warn(dev, "%s: Illegal mode.\n", __func__); | 619 | dev_warn(dev, "%s: Illegal mode.\n", __func__); |
763 | } | 620 | } |
@@ -777,14 +634,14 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) | |||
777 | if (radio->rds_on) | 634 | if (radio->rds_on) |
778 | val |= WL1273_POWER_SET_RDS; | 635 | val |= WL1273_POWER_SET_RDS; |
779 | 636 | ||
780 | r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val); | 637 | r = core->write(core, WL1273_POWER_SET, val); |
781 | if (r) { | 638 | if (r) { |
782 | dev_err(dev, "%s: POWER_SET fails\n", __func__); | 639 | dev_err(dev, "%s: POWER_SET fails\n", __func__); |
783 | goto fail; | 640 | goto fail; |
784 | } | 641 | } |
785 | } else if (new_mode == WL1273_MODE_TX) { | 642 | } else if (new_mode == WL1273_MODE_TX) { |
786 | r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, | 643 | r = core->write(core, WL1273_PUPD_SET, |
787 | WL1273_PUPD_SET_ON); | 644 | WL1273_PUPD_SET_ON); |
788 | if (r) { | 645 | if (r) { |
789 | dev_err(dev, "%s: PUPD_SET fails\n", __func__); | 646 | dev_err(dev, "%s: PUPD_SET fails\n", __func__); |
790 | goto fail; | 647 | goto fail; |
@@ -808,10 +665,10 @@ static int wl1273_fm_suspend(struct wl1273_device *radio) | |||
808 | 665 | ||
809 | /* Cannot go from OFF to SUSPENDED */ | 666 | /* Cannot go from OFF to SUSPENDED */ |
810 | if (core->mode == WL1273_MODE_RX) | 667 | if (core->mode == WL1273_MODE_RX) |
811 | r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, | 668 | r = core->write(core, WL1273_POWER_SET, |
812 | WL1273_POWER_SET_RETENTION); | 669 | WL1273_POWER_SET_RETENTION); |
813 | else if (core->mode == WL1273_MODE_TX) | 670 | else if (core->mode == WL1273_MODE_TX) |
814 | r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, | 671 | r = core->write(core, WL1273_PUPD_SET, |
815 | WL1273_PUPD_SET_RETENTION); | 672 | WL1273_PUPD_SET_RETENTION); |
816 | else | 673 | else |
817 | r = -EINVAL; | 674 | r = -EINVAL; |
@@ -852,8 +709,7 @@ static int wl1273_fm_set_mode(struct wl1273_device *radio, int mode) | |||
852 | } | 709 | } |
853 | 710 | ||
854 | core->mode = mode; | 711 | core->mode = mode; |
855 | r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, | 712 | r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); |
856 | radio->irq_flags); | ||
857 | if (r) { | 713 | if (r) { |
858 | dev_err(dev, "INT_MASK_SET fails.\n"); | 714 | dev_err(dev, "INT_MASK_SET fails.\n"); |
859 | goto out; | 715 | goto out; |
@@ -951,22 +807,21 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio, | |||
951 | INIT_COMPLETION(radio->busy); | 807 | INIT_COMPLETION(radio->busy); |
952 | dev_dbg(radio->dev, "%s: BUSY\n", __func__); | 808 | dev_dbg(radio->dev, "%s: BUSY\n", __func__); |
953 | 809 | ||
954 | r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags); | 810 | r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); |
955 | if (r) | 811 | if (r) |
956 | goto out; | 812 | goto out; |
957 | 813 | ||
958 | dev_dbg(radio->dev, "%s\n", __func__); | 814 | dev_dbg(radio->dev, "%s\n", __func__); |
959 | 815 | ||
960 | r = wl1273_fm_write_cmd(core, WL1273_SEARCH_LVL_SET, level); | 816 | r = core->write(core, WL1273_SEARCH_LVL_SET, level); |
961 | if (r) | 817 | if (r) |
962 | goto out; | 818 | goto out; |
963 | 819 | ||
964 | r = wl1273_fm_write_cmd(core, WL1273_SEARCH_DIR_SET, dir); | 820 | r = core->write(core, WL1273_SEARCH_DIR_SET, dir); |
965 | if (r) | 821 | if (r) |
966 | goto out; | 822 | goto out; |
967 | 823 | ||
968 | r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, | 824 | r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK); |
969 | TUNER_MODE_AUTO_SEEK); | ||
970 | if (r) | 825 | if (r) |
971 | goto out; | 826 | goto out; |
972 | 827 | ||
@@ -994,8 +849,7 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio, | |||
994 | INIT_COMPLETION(radio->busy); | 849 | INIT_COMPLETION(radio->busy); |
995 | dev_dbg(radio->dev, "%s: BUSY\n", __func__); | 850 | dev_dbg(radio->dev, "%s: BUSY\n", __func__); |
996 | 851 | ||
997 | r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, | 852 | r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK); |
998 | TUNER_MODE_AUTO_SEEK); | ||
999 | if (r) | 853 | if (r) |
1000 | goto out; | 854 | goto out; |
1001 | 855 | ||
@@ -1020,7 +874,7 @@ static unsigned int wl1273_fm_get_tx_ctune(struct wl1273_device *radio) | |||
1020 | core->mode == WL1273_MODE_SUSPENDED) | 874 | core->mode == WL1273_MODE_SUSPENDED) |
1021 | return -EPERM; | 875 | return -EPERM; |
1022 | 876 | ||
1023 | r = wl1273_fm_read_reg(core, WL1273_READ_FMANT_TUNE_VALUE, &val); | 877 | r = core->read(core, WL1273_READ_FMANT_TUNE_VALUE, &val); |
1024 | if (r) { | 878 | if (r) { |
1025 | dev_err(dev, "%s: read error: %d\n", __func__, r); | 879 | dev_err(dev, "%s: read error: %d\n", __func__, r); |
1026 | goto out; | 880 | goto out; |
@@ -1066,7 +920,7 @@ static int wl1273_fm_set_preemphasis(struct wl1273_device *radio, | |||
1066 | goto out; | 920 | goto out; |
1067 | } | 921 | } |
1068 | 922 | ||
1069 | r = wl1273_fm_write_cmd(core, WL1273_PREMPH_SET, em); | 923 | r = core->write(core, WL1273_PREMPH_SET, em); |
1070 | if (r) | 924 | if (r) |
1071 | goto out; | 925 | goto out; |
1072 | 926 | ||
@@ -1086,7 +940,7 @@ static int wl1273_fm_rds_on(struct wl1273_device *radio) | |||
1086 | if (radio->rds_on) | 940 | if (radio->rds_on) |
1087 | return 0; | 941 | return 0; |
1088 | 942 | ||
1089 | r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, | 943 | r = core->write(core, WL1273_POWER_SET, |
1090 | WL1273_POWER_SET_FM | WL1273_POWER_SET_RDS); | 944 | WL1273_POWER_SET_FM | WL1273_POWER_SET_RDS); |
1091 | if (r) | 945 | if (r) |
1092 | goto out; | 946 | goto out; |
@@ -1108,19 +962,16 @@ static int wl1273_fm_rds_off(struct wl1273_device *radio) | |||
1108 | 962 | ||
1109 | radio->irq_flags &= ~WL1273_RDS_EVENT; | 963 | radio->irq_flags &= ~WL1273_RDS_EVENT; |
1110 | 964 | ||
1111 | r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags); | 965 | r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); |
1112 | if (r) | 966 | if (r) |
1113 | goto out; | 967 | goto out; |
1114 | 968 | ||
1115 | /* stop rds reception */ | ||
1116 | cancel_delayed_work(&radio->work); | ||
1117 | |||
1118 | /* Service pending read */ | 969 | /* Service pending read */ |
1119 | wake_up_interruptible(&radio->read_queue); | 970 | wake_up_interruptible(&radio->read_queue); |
1120 | 971 | ||
1121 | dev_dbg(radio->dev, "%s\n", __func__); | 972 | dev_dbg(radio->dev, "%s\n", __func__); |
1122 | 973 | ||
1123 | r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, WL1273_POWER_SET_FM); | 974 | r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_FM); |
1124 | if (r) | 975 | if (r) |
1125 | goto out; | 976 | goto out; |
1126 | 977 | ||
@@ -1143,14 +994,14 @@ static int wl1273_fm_set_rds(struct wl1273_device *radio, unsigned int new_mode) | |||
1143 | return -EPERM; | 994 | return -EPERM; |
1144 | 995 | ||
1145 | if (new_mode == WL1273_RDS_RESET) { | 996 | if (new_mode == WL1273_RDS_RESET) { |
1146 | r = wl1273_fm_write_cmd(core, WL1273_RDS_CNTRL_SET, 1); | 997 | r = core->write(core, WL1273_RDS_CNTRL_SET, 1); |
1147 | return r; | 998 | return r; |
1148 | } | 999 | } |
1149 | 1000 | ||
1150 | if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_OFF) { | 1001 | if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_OFF) { |
1151 | r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 0); | 1002 | r = core->write(core, WL1273_RDS_DATA_ENB, 0); |
1152 | } else if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_ON) { | 1003 | } else if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_ON) { |
1153 | r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 1); | 1004 | r = core->write(core, WL1273_RDS_DATA_ENB, 1); |
1154 | } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_OFF) { | 1005 | } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_OFF) { |
1155 | r = wl1273_fm_rds_off(radio); | 1006 | r = wl1273_fm_rds_off(radio); |
1156 | } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_ON) { | 1007 | } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_ON) { |
@@ -1171,12 +1022,13 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, | |||
1171 | size_t count, loff_t *ppos) | 1022 | size_t count, loff_t *ppos) |
1172 | { | 1023 | { |
1173 | struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); | 1024 | struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); |
1025 | struct wl1273_core *core = radio->core; | ||
1174 | u16 val; | 1026 | u16 val; |
1175 | int r; | 1027 | int r; |
1176 | 1028 | ||
1177 | dev_dbg(radio->dev, "%s\n", __func__); | 1029 | dev_dbg(radio->dev, "%s\n", __func__); |
1178 | 1030 | ||
1179 | if (radio->core->mode != WL1273_MODE_TX) | 1031 | if (core->mode != WL1273_MODE_TX) |
1180 | return count; | 1032 | return count; |
1181 | 1033 | ||
1182 | if (radio->rds_users == 0) { | 1034 | if (radio->rds_users == 0) { |
@@ -1184,7 +1036,7 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, | |||
1184 | return 0; | 1036 | return 0; |
1185 | } | 1037 | } |
1186 | 1038 | ||
1187 | if (mutex_lock_interruptible(&radio->core->lock)) | 1039 | if (mutex_lock_interruptible(&core->lock)) |
1188 | return -EINTR; | 1040 | return -EINTR; |
1189 | /* | 1041 | /* |
1190 | * Multiple processes can open the device, but only | 1042 | * Multiple processes can open the device, but only |
@@ -1202,7 +1054,7 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, | |||
1202 | else | 1054 | else |
1203 | val = count; | 1055 | val = count; |
1204 | 1056 | ||
1205 | wl1273_fm_write_cmd(radio->core, WL1273_RDS_CONFIG_DATA_SET, val); | 1057 | core->write(core, WL1273_RDS_CONFIG_DATA_SET, val); |
1206 | 1058 | ||
1207 | if (copy_from_user(radio->write_buf + 1, buf, val)) { | 1059 | if (copy_from_user(radio->write_buf + 1, buf, val)) { |
1208 | r = -EFAULT; | 1060 | r = -EFAULT; |
@@ -1213,11 +1065,11 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, | |||
1213 | dev_dbg(radio->dev, "From user: \"%s\"\n", radio->write_buf); | 1065 | dev_dbg(radio->dev, "From user: \"%s\"\n", radio->write_buf); |
1214 | 1066 | ||
1215 | radio->write_buf[0] = WL1273_RDS_DATA_SET; | 1067 | radio->write_buf[0] = WL1273_RDS_DATA_SET; |
1216 | wl1273_fm_write_data(radio->core, radio->write_buf, val + 1); | 1068 | core->write_data(core, radio->write_buf, val + 1); |
1217 | 1069 | ||
1218 | r = val; | 1070 | r = val; |
1219 | out: | 1071 | out: |
1220 | mutex_unlock(&radio->core->lock); | 1072 | mutex_unlock(&core->lock); |
1221 | 1073 | ||
1222 | return r; | 1074 | return r; |
1223 | } | 1075 | } |
@@ -1263,8 +1115,8 @@ static int wl1273_fm_fops_open(struct file *file) | |||
1263 | 1115 | ||
1264 | radio->irq_flags |= WL1273_RDS_EVENT; | 1116 | radio->irq_flags |= WL1273_RDS_EVENT; |
1265 | 1117 | ||
1266 | r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, | 1118 | r = core->write(core, WL1273_INT_MASK_SET, |
1267 | radio->irq_flags); | 1119 | radio->irq_flags); |
1268 | if (r) { | 1120 | if (r) { |
1269 | mutex_unlock(&core->lock); | 1121 | mutex_unlock(&core->lock); |
1270 | goto out; | 1122 | goto out; |
@@ -1295,9 +1147,9 @@ static int wl1273_fm_fops_release(struct file *file) | |||
1295 | radio->irq_flags &= ~WL1273_RDS_EVENT; | 1147 | radio->irq_flags &= ~WL1273_RDS_EVENT; |
1296 | 1148 | ||
1297 | if (core->mode == WL1273_MODE_RX) { | 1149 | if (core->mode == WL1273_MODE_RX) { |
1298 | r = wl1273_fm_write_cmd(core, | 1150 | r = core->write(core, |
1299 | WL1273_INT_MASK_SET, | 1151 | WL1273_INT_MASK_SET, |
1300 | radio->irq_flags); | 1152 | radio->irq_flags); |
1301 | if (r) { | 1153 | if (r) { |
1302 | mutex_unlock(&core->lock); | 1154 | mutex_unlock(&core->lock); |
1303 | goto out; | 1155 | goto out; |
@@ -1324,7 +1176,7 @@ static ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf, | |||
1324 | 1176 | ||
1325 | dev_dbg(radio->dev, "%s\n", __func__); | 1177 | dev_dbg(radio->dev, "%s\n", __func__); |
1326 | 1178 | ||
1327 | if (radio->core->mode != WL1273_MODE_RX) | 1179 | if (core->mode != WL1273_MODE_RX) |
1328 | return 0; | 1180 | return 0; |
1329 | 1181 | ||
1330 | if (radio->rds_users == 0) { | 1182 | if (radio->rds_users == 0) { |
@@ -1345,7 +1197,7 @@ static ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf, | |||
1345 | } | 1197 | } |
1346 | radio->owner = file; | 1198 | radio->owner = file; |
1347 | 1199 | ||
1348 | r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); | 1200 | r = core->read(core, WL1273_RDS_SYNC_GET, &val); |
1349 | if (r) { | 1201 | if (r) { |
1350 | dev_err(radio->dev, "%s: Get RDS_SYNC fails.\n", __func__); | 1202 | dev_err(radio->dev, "%s: Get RDS_SYNC fails.\n", __func__); |
1351 | goto out; | 1203 | goto out; |
@@ -1466,23 +1318,24 @@ static int wl1273_fm_vidioc_s_input(struct file *file, void *priv, | |||
1466 | */ | 1318 | */ |
1467 | static int wl1273_fm_set_tx_power(struct wl1273_device *radio, u16 power) | 1319 | static int wl1273_fm_set_tx_power(struct wl1273_device *radio, u16 power) |
1468 | { | 1320 | { |
1321 | struct wl1273_core *core = radio->core; | ||
1469 | int r; | 1322 | int r; |
1470 | 1323 | ||
1471 | if (radio->core->mode == WL1273_MODE_OFF || | 1324 | if (core->mode == WL1273_MODE_OFF || |
1472 | radio->core->mode == WL1273_MODE_SUSPENDED) | 1325 | core->mode == WL1273_MODE_SUSPENDED) |
1473 | return -EPERM; | 1326 | return -EPERM; |
1474 | 1327 | ||
1475 | mutex_lock(&radio->core->lock); | 1328 | mutex_lock(&core->lock); |
1476 | 1329 | ||
1477 | /* Convert the dBuV value to chip presentation */ | 1330 | /* Convert the dBuV value to chip presentation */ |
1478 | r = wl1273_fm_write_cmd(radio->core, WL1273_POWER_LEV_SET, 122 - power); | 1331 | r = core->write(core, WL1273_POWER_LEV_SET, 122 - power); |
1479 | if (r) | 1332 | if (r) |
1480 | goto out; | 1333 | goto out; |
1481 | 1334 | ||
1482 | radio->tx_power = power; | 1335 | radio->tx_power = power; |
1483 | 1336 | ||
1484 | out: | 1337 | out: |
1485 | mutex_unlock(&radio->core->lock); | 1338 | mutex_unlock(&core->lock); |
1486 | return r; | 1339 | return r; |
1487 | } | 1340 | } |
1488 | 1341 | ||
@@ -1493,23 +1346,24 @@ out: | |||
1493 | static int wl1273_fm_tx_set_spacing(struct wl1273_device *radio, | 1346 | static int wl1273_fm_tx_set_spacing(struct wl1273_device *radio, |
1494 | unsigned int spacing) | 1347 | unsigned int spacing) |
1495 | { | 1348 | { |
1349 | struct wl1273_core *core = radio->core; | ||
1496 | int r; | 1350 | int r; |
1497 | 1351 | ||
1498 | if (spacing == 0) { | 1352 | if (spacing == 0) { |
1499 | r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, | 1353 | r = core->write(core, WL1273_SCAN_SPACING_SET, |
1500 | WL1273_SPACING_100kHz); | 1354 | WL1273_SPACING_100kHz); |
1501 | radio->spacing = 100; | 1355 | radio->spacing = 100; |
1502 | } else if (spacing - 50000 < 25000) { | 1356 | } else if (spacing - 50000 < 25000) { |
1503 | r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, | 1357 | r = core->write(core, WL1273_SCAN_SPACING_SET, |
1504 | WL1273_SPACING_50kHz); | 1358 | WL1273_SPACING_50kHz); |
1505 | radio->spacing = 50; | 1359 | radio->spacing = 50; |
1506 | } else if (spacing - 100000 < 50000) { | 1360 | } else if (spacing - 100000 < 50000) { |
1507 | r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, | 1361 | r = core->write(core, WL1273_SCAN_SPACING_SET, |
1508 | WL1273_SPACING_100kHz); | 1362 | WL1273_SPACING_100kHz); |
1509 | radio->spacing = 100; | 1363 | radio->spacing = 100; |
1510 | } else { | 1364 | } else { |
1511 | r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, | 1365 | r = core->write(core, WL1273_SCAN_SPACING_SET, |
1512 | WL1273_SPACING_200kHz); | 1366 | WL1273_SPACING_200kHz); |
1513 | radio->spacing = 200; | 1367 | radio->spacing = 200; |
1514 | } | 1368 | } |
1515 | 1369 | ||
@@ -1567,17 +1421,17 @@ static int wl1273_fm_vidioc_s_ctrl(struct v4l2_ctrl *ctrl) | |||
1567 | return -EINTR; | 1421 | return -EINTR; |
1568 | 1422 | ||
1569 | if (core->mode == WL1273_MODE_RX && ctrl->val) | 1423 | if (core->mode == WL1273_MODE_RX && ctrl->val) |
1570 | r = wl1273_fm_write_cmd(core, | 1424 | r = core->write(core, |
1571 | WL1273_MUTE_STATUS_SET, | 1425 | WL1273_MUTE_STATUS_SET, |
1572 | WL1273_MUTE_HARD_LEFT | | 1426 | WL1273_MUTE_HARD_LEFT | |
1573 | WL1273_MUTE_HARD_RIGHT); | 1427 | WL1273_MUTE_HARD_RIGHT); |
1574 | else if (core->mode == WL1273_MODE_RX) | 1428 | else if (core->mode == WL1273_MODE_RX) |
1575 | r = wl1273_fm_write_cmd(core, | 1429 | r = core->write(core, |
1576 | WL1273_MUTE_STATUS_SET, 0x0); | 1430 | WL1273_MUTE_STATUS_SET, 0x0); |
1577 | else if (core->mode == WL1273_MODE_TX && ctrl->val) | 1431 | else if (core->mode == WL1273_MODE_TX && ctrl->val) |
1578 | r = wl1273_fm_write_cmd(core, WL1273_MUTE, 1); | 1432 | r = core->write(core, WL1273_MUTE, 1); |
1579 | else if (core->mode == WL1273_MODE_TX) | 1433 | else if (core->mode == WL1273_MODE_TX) |
1580 | r = wl1273_fm_write_cmd(core, WL1273_MUTE, 0); | 1434 | r = core->write(core, WL1273_MUTE, 0); |
1581 | 1435 | ||
1582 | mutex_unlock(&core->lock); | 1436 | mutex_unlock(&core->lock); |
1583 | break; | 1437 | break; |
@@ -1672,7 +1526,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, | |||
1672 | if (mutex_lock_interruptible(&core->lock)) | 1526 | if (mutex_lock_interruptible(&core->lock)) |
1673 | return -EINTR; | 1527 | return -EINTR; |
1674 | 1528 | ||
1675 | r = wl1273_fm_read_reg(core, WL1273_STEREO_GET, &val); | 1529 | r = core->read(core, WL1273_STEREO_GET, &val); |
1676 | if (r) | 1530 | if (r) |
1677 | goto out; | 1531 | goto out; |
1678 | 1532 | ||
@@ -1681,7 +1535,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, | |||
1681 | else | 1535 | else |
1682 | tuner->rxsubchans = V4L2_TUNER_SUB_MONO; | 1536 | tuner->rxsubchans = V4L2_TUNER_SUB_MONO; |
1683 | 1537 | ||
1684 | r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &val); | 1538 | r = core->read(core, WL1273_RSSI_LVL_GET, &val); |
1685 | if (r) | 1539 | if (r) |
1686 | goto out; | 1540 | goto out; |
1687 | 1541 | ||
@@ -1690,7 +1544,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, | |||
1690 | 1544 | ||
1691 | tuner->afc = 0; | 1545 | tuner->afc = 0; |
1692 | 1546 | ||
1693 | r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); | 1547 | r = core->read(core, WL1273_RDS_SYNC_GET, &val); |
1694 | if (r) | 1548 | if (r) |
1695 | goto out; | 1549 | goto out; |
1696 | 1550 | ||
@@ -1736,8 +1590,7 @@ static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv, | |||
1736 | dev_warn(radio->dev, "%s: RDS fails: %d\n", __func__, r); | 1590 | dev_warn(radio->dev, "%s: RDS fails: %d\n", __func__, r); |
1737 | 1591 | ||
1738 | if (tuner->audmode == V4L2_TUNER_MODE_MONO) { | 1592 | if (tuner->audmode == V4L2_TUNER_MODE_MONO) { |
1739 | r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, | 1593 | r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); |
1740 | WL1273_RX_MONO); | ||
1741 | if (r < 0) { | 1594 | if (r < 0) { |
1742 | dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", | 1595 | dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", |
1743 | __func__, r); | 1596 | __func__, r); |
@@ -1745,8 +1598,7 @@ static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv, | |||
1745 | } | 1598 | } |
1746 | radio->stereo = false; | 1599 | radio->stereo = false; |
1747 | } else if (tuner->audmode == V4L2_TUNER_MODE_STEREO) { | 1600 | } else if (tuner->audmode == V4L2_TUNER_MODE_STEREO) { |
1748 | r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, | 1601 | r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); |
1749 | WL1273_RX_STEREO); | ||
1750 | if (r < 0) { | 1602 | if (r < 0) { |
1751 | dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", | 1603 | dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", |
1752 | __func__, r); | 1604 | __func__, r); |
@@ -1885,10 +1737,10 @@ static int wl1273_fm_vidioc_s_modulator(struct file *file, void *priv, | |||
1885 | r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF); | 1737 | r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF); |
1886 | 1738 | ||
1887 | if (modulator->txsubchans & V4L2_TUNER_SUB_MONO) | 1739 | if (modulator->txsubchans & V4L2_TUNER_SUB_MONO) |
1888 | r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, WL1273_TX_MONO); | 1740 | r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); |
1889 | else | 1741 | else |
1890 | r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, | 1742 | r = core->write(core, WL1273_MONO_SET, |
1891 | WL1273_RX_STEREO); | 1743 | WL1273_RX_STEREO); |
1892 | if (r < 0) | 1744 | if (r < 0) |
1893 | dev_warn(radio->dev, WL1273_FM_DRIVER_NAME | 1745 | dev_warn(radio->dev, WL1273_FM_DRIVER_NAME |
1894 | "MONO_SET fails: %d\n", r); | 1746 | "MONO_SET fails: %d\n", r); |
@@ -1923,7 +1775,7 @@ static int wl1273_fm_vidioc_g_modulator(struct file *file, void *priv, | |||
1923 | if (mutex_lock_interruptible(&core->lock)) | 1775 | if (mutex_lock_interruptible(&core->lock)) |
1924 | return -EINTR; | 1776 | return -EINTR; |
1925 | 1777 | ||
1926 | r = wl1273_fm_read_reg(core, WL1273_MONO_SET, &val); | 1778 | r = core->read(core, WL1273_MONO_SET, &val); |
1927 | if (r) | 1779 | if (r) |
1928 | goto out; | 1780 | goto out; |
1929 | 1781 | ||
@@ -1960,38 +1812,38 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) | |||
1960 | return 0; | 1812 | return 0; |
1961 | } | 1813 | } |
1962 | 1814 | ||
1963 | r = wl1273_fm_read_reg(core, WL1273_ASIC_ID_GET, &val); | 1815 | r = core->read(core, WL1273_ASIC_ID_GET, &val); |
1964 | if (r) | 1816 | if (r) |
1965 | dev_err(dev, "%s: Get ASIC_ID fails.\n", __func__); | 1817 | dev_err(dev, "%s: Get ASIC_ID fails.\n", __func__); |
1966 | else | 1818 | else |
1967 | dev_info(dev, "ASIC_ID: 0x%04x\n", val); | 1819 | dev_info(dev, "ASIC_ID: 0x%04x\n", val); |
1968 | 1820 | ||
1969 | r = wl1273_fm_read_reg(core, WL1273_ASIC_VER_GET, &val); | 1821 | r = core->read(core, WL1273_ASIC_VER_GET, &val); |
1970 | if (r) | 1822 | if (r) |
1971 | dev_err(dev, "%s: Get ASIC_VER fails.\n", __func__); | 1823 | dev_err(dev, "%s: Get ASIC_VER fails.\n", __func__); |
1972 | else | 1824 | else |
1973 | dev_info(dev, "ASIC Version: 0x%04x\n", val); | 1825 | dev_info(dev, "ASIC Version: 0x%04x\n", val); |
1974 | 1826 | ||
1975 | r = wl1273_fm_read_reg(core, WL1273_FIRM_VER_GET, &val); | 1827 | r = core->read(core, WL1273_FIRM_VER_GET, &val); |
1976 | if (r) | 1828 | if (r) |
1977 | dev_err(dev, "%s: Get FIRM_VER fails.\n", __func__); | 1829 | dev_err(dev, "%s: Get FIRM_VER fails.\n", __func__); |
1978 | else | 1830 | else |
1979 | dev_info(dev, "FW version: %d(0x%04x)\n", val, val); | 1831 | dev_info(dev, "FW version: %d(0x%04x)\n", val, val); |
1980 | 1832 | ||
1981 | r = wl1273_fm_read_reg(core, WL1273_BAND_SET, &val); | 1833 | r = core->read(core, WL1273_BAND_SET, &val); |
1982 | if (r) | 1834 | if (r) |
1983 | dev_err(dev, "%s: Get BAND fails.\n", __func__); | 1835 | dev_err(dev, "%s: Get BAND fails.\n", __func__); |
1984 | else | 1836 | else |
1985 | dev_info(dev, "BAND: %d\n", val); | 1837 | dev_info(dev, "BAND: %d\n", val); |
1986 | 1838 | ||
1987 | if (core->mode == WL1273_MODE_TX) { | 1839 | if (core->mode == WL1273_MODE_TX) { |
1988 | r = wl1273_fm_read_reg(core, WL1273_PUPD_SET, &val); | 1840 | r = core->read(core, WL1273_PUPD_SET, &val); |
1989 | if (r) | 1841 | if (r) |
1990 | dev_err(dev, "%s: Get PUPD fails.\n", __func__); | 1842 | dev_err(dev, "%s: Get PUPD fails.\n", __func__); |
1991 | else | 1843 | else |
1992 | dev_info(dev, "PUPD: 0x%04x\n", val); | 1844 | dev_info(dev, "PUPD: 0x%04x\n", val); |
1993 | 1845 | ||
1994 | r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &val); | 1846 | r = core->read(core, WL1273_CHANL_SET, &val); |
1995 | if (r) | 1847 | if (r) |
1996 | dev_err(dev, "%s: Get CHANL fails.\n", __func__); | 1848 | dev_err(dev, "%s: Get CHANL fails.\n", __func__); |
1997 | else | 1849 | else |
@@ -1999,13 +1851,13 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) | |||
1999 | } else if (core->mode == WL1273_MODE_RX) { | 1851 | } else if (core->mode == WL1273_MODE_RX) { |
2000 | int bf = radio->rangelow; | 1852 | int bf = radio->rangelow; |
2001 | 1853 | ||
2002 | r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &val); | 1854 | r = core->read(core, WL1273_FREQ_SET, &val); |
2003 | if (r) | 1855 | if (r) |
2004 | dev_err(dev, "%s: Get FREQ fails.\n", __func__); | 1856 | dev_err(dev, "%s: Get FREQ fails.\n", __func__); |
2005 | else | 1857 | else |
2006 | dev_info(dev, "RX Frequency: %dkHz\n", bf + val*50); | 1858 | dev_info(dev, "RX Frequency: %dkHz\n", bf + val*50); |
2007 | 1859 | ||
2008 | r = wl1273_fm_read_reg(core, WL1273_MOST_MODE_SET, &val); | 1860 | r = core->read(core, WL1273_MOST_MODE_SET, &val); |
2009 | if (r) | 1861 | if (r) |
2010 | dev_err(dev, "%s: Get MOST_MODE fails.\n", | 1862 | dev_err(dev, "%s: Get MOST_MODE fails.\n", |
2011 | __func__); | 1863 | __func__); |
@@ -2016,7 +1868,7 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) | |||
2016 | else | 1868 | else |
2017 | dev_info(dev, "MOST_MODE: Unexpected value: %d\n", val); | 1869 | dev_info(dev, "MOST_MODE: Unexpected value: %d\n", val); |
2018 | 1870 | ||
2019 | r = wl1273_fm_read_reg(core, WL1273_MOST_BLEND_SET, &val); | 1871 | r = core->read(core, WL1273_MOST_BLEND_SET, &val); |
2020 | if (r) | 1872 | if (r) |
2021 | dev_err(dev, "%s: Get MOST_BLEND fails.\n", __func__); | 1873 | dev_err(dev, "%s: Get MOST_BLEND fails.\n", __func__); |
2022 | else if (val == 0) | 1874 | else if (val == 0) |
@@ -2027,7 +1879,7 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) | |||
2027 | else | 1879 | else |
2028 | dev_info(dev, "MOST_BLEND: Unexpected val: %d\n", val); | 1880 | dev_info(dev, "MOST_BLEND: Unexpected val: %d\n", val); |
2029 | 1881 | ||
2030 | r = wl1273_fm_read_reg(core, WL1273_STEREO_GET, &val); | 1882 | r = core->read(core, WL1273_STEREO_GET, &val); |
2031 | if (r) | 1883 | if (r) |
2032 | dev_err(dev, "%s: Get STEREO fails.\n", __func__); | 1884 | dev_err(dev, "%s: Get STEREO fails.\n", __func__); |
2033 | else if (val == 0) | 1885 | else if (val == 0) |
@@ -2037,25 +1889,25 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) | |||
2037 | else | 1889 | else |
2038 | dev_info(dev, "STEREO: Unexpected value: %d\n", val); | 1890 | dev_info(dev, "STEREO: Unexpected value: %d\n", val); |
2039 | 1891 | ||
2040 | r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &val); | 1892 | r = core->read(core, WL1273_RSSI_LVL_GET, &val); |
2041 | if (r) | 1893 | if (r) |
2042 | dev_err(dev, "%s: Get RSSI_LVL fails.\n", __func__); | 1894 | dev_err(dev, "%s: Get RSSI_LVL fails.\n", __func__); |
2043 | else | 1895 | else |
2044 | dev_info(dev, "RX signal strength: %d\n", (s16) val); | 1896 | dev_info(dev, "RX signal strength: %d\n", (s16) val); |
2045 | 1897 | ||
2046 | r = wl1273_fm_read_reg(core, WL1273_POWER_SET, &val); | 1898 | r = core->read(core, WL1273_POWER_SET, &val); |
2047 | if (r) | 1899 | if (r) |
2048 | dev_err(dev, "%s: Get POWER fails.\n", __func__); | 1900 | dev_err(dev, "%s: Get POWER fails.\n", __func__); |
2049 | else | 1901 | else |
2050 | dev_info(dev, "POWER: 0x%04x\n", val); | 1902 | dev_info(dev, "POWER: 0x%04x\n", val); |
2051 | 1903 | ||
2052 | r = wl1273_fm_read_reg(core, WL1273_INT_MASK_SET, &val); | 1904 | r = core->read(core, WL1273_INT_MASK_SET, &val); |
2053 | if (r) | 1905 | if (r) |
2054 | dev_err(dev, "%s: Get INT_MASK fails.\n", __func__); | 1906 | dev_err(dev, "%s: Get INT_MASK fails.\n", __func__); |
2055 | else | 1907 | else |
2056 | dev_info(dev, "INT_MASK: 0x%04x\n", val); | 1908 | dev_info(dev, "INT_MASK: 0x%04x\n", val); |
2057 | 1909 | ||
2058 | r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); | 1910 | r = core->read(core, WL1273_RDS_SYNC_GET, &val); |
2059 | if (r) | 1911 | if (r) |
2060 | dev_err(dev, "%s: Get RDS_SYNC fails.\n", | 1912 | dev_err(dev, "%s: Get RDS_SYNC fails.\n", |
2061 | __func__); | 1913 | __func__); |
@@ -2067,14 +1919,14 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) | |||
2067 | else | 1919 | else |
2068 | dev_info(dev, "RDS_SYNC: Unexpected value: %d\n", val); | 1920 | dev_info(dev, "RDS_SYNC: Unexpected value: %d\n", val); |
2069 | 1921 | ||
2070 | r = wl1273_fm_read_reg(core, WL1273_I2S_MODE_CONFIG_SET, &val); | 1922 | r = core->read(core, WL1273_I2S_MODE_CONFIG_SET, &val); |
2071 | if (r) | 1923 | if (r) |
2072 | dev_err(dev, "%s: Get I2S_MODE_CONFIG fails.\n", | 1924 | dev_err(dev, "%s: Get I2S_MODE_CONFIG fails.\n", |
2073 | __func__); | 1925 | __func__); |
2074 | else | 1926 | else |
2075 | dev_info(dev, "I2S_MODE_CONFIG: 0x%04x\n", val); | 1927 | dev_info(dev, "I2S_MODE_CONFIG: 0x%04x\n", val); |
2076 | 1928 | ||
2077 | r = wl1273_fm_read_reg(core, WL1273_VOLUME_SET, &val); | 1929 | r = core->read(core, WL1273_VOLUME_SET, &val); |
2078 | if (r) | 1930 | if (r) |
2079 | dev_err(dev, "%s: Get VOLUME fails.\n", __func__); | 1931 | dev_err(dev, "%s: Get VOLUME fails.\n", __func__); |
2080 | else | 1932 | else |
@@ -2138,7 +1990,7 @@ static int wl1273_fm_radio_remove(struct platform_device *pdev) | |||
2138 | 1990 | ||
2139 | static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) | 1991 | static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) |
2140 | { | 1992 | { |
2141 | struct wl1273_core **core = pdev->dev.platform_data; | 1993 | struct wl1273_core **core = mfd_get_data(pdev); |
2142 | struct wl1273_device *radio; | 1994 | struct wl1273_device *radio; |
2143 | struct v4l2_ctrl *ctrl; | 1995 | struct v4l2_ctrl *ctrl; |
2144 | int r = 0; | 1996 | int r = 0; |
@@ -2184,10 +2036,6 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) | |||
2184 | radio->stereo = true; | 2036 | radio->stereo = true; |
2185 | radio->bus_type = "I2C"; | 2037 | radio->bus_type = "I2C"; |
2186 | 2038 | ||
2187 | radio->core->write = wl1273_fm_write_cmd; | ||
2188 | radio->core->set_audio = wl1273_fm_set_audio; | ||
2189 | radio->core->set_volume = wl1273_fm_set_volume; | ||
2190 | |||
2191 | if (radio->core->pdata->request_resources) { | 2039 | if (radio->core->pdata->request_resources) { |
2192 | r = radio->core->pdata->request_resources(radio->core->client); | 2040 | r = radio->core->pdata->request_resources(radio->core->client); |
2193 | if (r) { | 2041 | if (r) { |
@@ -2319,7 +2167,6 @@ module_init(wl1273_fm_module_init); | |||
2319 | 2167 | ||
2320 | static void __exit wl1273_fm_module_exit(void) | 2168 | static void __exit wl1273_fm_module_exit(void) |
2321 | { | 2169 | { |
2322 | flush_scheduled_work(); | ||
2323 | platform_driver_unregister(&wl1273_fm_radio_driver); | 2170 | platform_driver_unregister(&wl1273_fm_radio_driver); |
2324 | pr_info(DRIVER_DESC ", Exiting.\n"); | 2171 | pr_info(DRIVER_DESC ", Exiting.\n"); |
2325 | } | 2172 | } |
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 60c176fe328e..38ae6cd65790 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c | |||
@@ -460,7 +460,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, | |||
460 | count /= 3; | 460 | count /= 3; |
461 | 461 | ||
462 | /* copy RDS block out of internal buffer and to user buffer */ | 462 | /* copy RDS block out of internal buffer and to user buffer */ |
463 | mutex_lock(&radio->lock); | ||
464 | while (block_count < count) { | 463 | while (block_count < count) { |
465 | if (radio->rd_index == radio->wr_index) | 464 | if (radio->rd_index == radio->wr_index) |
466 | break; | 465 | break; |
diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig new file mode 100644 index 000000000000..749f67b192e7 --- /dev/null +++ b/drivers/media/radio/wl128x/Kconfig | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # TI's wl128x FM driver based on TI's ST driver. | ||
3 | # | ||
4 | menu "Texas Instruments WL128x FM driver (ST based)" | ||
5 | config RADIO_WL128X | ||
6 | tristate "Texas Instruments WL128x FM Radio" | ||
7 | depends on VIDEO_V4L2 && RFKILL | ||
8 | select TI_ST | ||
9 | help | ||
10 | Choose Y here if you have this FM radio chip. | ||
11 | |||
12 | In order to control your radio card, you will need to use programs | ||
13 | that are compatible with the Video For Linux 2 API. Information on | ||
14 | this API and pointers to "v4l2" programs may be found at | ||
15 | <file:Documentation/video4linux/API.html>. | ||
16 | |||
17 | endmenu | ||
diff --git a/drivers/media/radio/wl128x/Makefile b/drivers/media/radio/wl128x/Makefile new file mode 100644 index 000000000000..32a0ead09845 --- /dev/null +++ b/drivers/media/radio/wl128x/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for TI's shared transport driver based wl128x | ||
3 | # FM radio. | ||
4 | # | ||
5 | obj-$(CONFIG_RADIO_WL128X) += fm_drv.o | ||
6 | fm_drv-objs := fmdrv_common.o fmdrv_rx.o fmdrv_tx.o fmdrv_v4l2.o | ||
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h new file mode 100644 index 000000000000..5db6fd14cf3c --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv.h | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * | ||
4 | * Common header for all FM driver sub-modules. | ||
5 | * | ||
6 | * Copyright (C) 2011 Texas Instruments | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #ifndef _FM_DRV_H | ||
24 | #define _FM_DRV_H | ||
25 | |||
26 | #include <linux/skbuff.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/initval.h> | ||
30 | #include <linux/timer.h> | ||
31 | #include <linux/version.h> | ||
32 | #include <media/v4l2-ioctl.h> | ||
33 | #include <media/v4l2-common.h> | ||
34 | #include <media/v4l2-ctrls.h> | ||
35 | |||
36 | #define FM_DRV_VERSION "0.10" | ||
37 | /* Should match with FM_DRV_VERSION */ | ||
38 | #define FM_DRV_RADIO_VERSION KERNEL_VERSION(0, 0, 1) | ||
39 | #define FM_DRV_NAME "ti_fmdrv" | ||
40 | #define FM_DRV_CARD_SHORT_NAME "TI FM Radio" | ||
41 | #define FM_DRV_CARD_LONG_NAME "Texas Instruments FM Radio" | ||
42 | |||
43 | /* Flag info */ | ||
44 | #define FM_INTTASK_RUNNING 0 | ||
45 | #define FM_INTTASK_SCHEDULE_PENDING 1 | ||
46 | #define FM_FW_DW_INPROGRESS 2 | ||
47 | #define FM_CORE_READY 3 | ||
48 | #define FM_CORE_TRANSPORT_READY 4 | ||
49 | #define FM_AF_SWITCH_INPROGRESS 5 | ||
50 | #define FM_CORE_TX_XMITING 6 | ||
51 | |||
52 | #define FM_TUNE_COMPLETE 0x1 | ||
53 | #define FM_BAND_LIMIT 0x2 | ||
54 | |||
55 | #define FM_DRV_TX_TIMEOUT (5*HZ) /* 5 seconds */ | ||
56 | #define FM_DRV_RX_SEEK_TIMEOUT (20*HZ) /* 20 seconds */ | ||
57 | |||
58 | #define NO_OF_ENTRIES_IN_ARRAY(array) (sizeof(array) / sizeof(array[0])) | ||
59 | |||
60 | #define fmerr(format, ...) \ | ||
61 | printk(KERN_ERR "fmdrv: " format, ## __VA_ARGS__) | ||
62 | #define fmwarn(format, ...) \ | ||
63 | printk(KERN_WARNING "fmdrv: " format, ##__VA_ARGS__) | ||
64 | #ifdef DEBUG | ||
65 | #define fmdbg(format, ...) \ | ||
66 | printk(KERN_DEBUG "fmdrv: " format, ## __VA_ARGS__) | ||
67 | #else /* DEBUG */ | ||
68 | #define fmdbg(format, ...) | ||
69 | #endif | ||
70 | enum { | ||
71 | FM_MODE_OFF, | ||
72 | FM_MODE_TX, | ||
73 | FM_MODE_RX, | ||
74 | FM_MODE_ENTRY_MAX | ||
75 | }; | ||
76 | |||
77 | #define FM_RX_RDS_INFO_FIELD_MAX 8 /* 4 Group * 2 Bytes */ | ||
78 | |||
79 | /* RX RDS data format */ | ||
80 | struct fm_rdsdata_format { | ||
81 | union { | ||
82 | struct { | ||
83 | u8 buff[FM_RX_RDS_INFO_FIELD_MAX]; | ||
84 | } groupdatabuff; | ||
85 | struct { | ||
86 | u16 pidata; | ||
87 | u8 blk_b[2]; | ||
88 | u8 blk_c[2]; | ||
89 | u8 blk_d[2]; | ||
90 | } groupgeneral; | ||
91 | struct { | ||
92 | u16 pidata; | ||
93 | u8 blk_b[2]; | ||
94 | u8 af[2]; | ||
95 | u8 ps[2]; | ||
96 | } group0A; | ||
97 | struct { | ||
98 | u16 pi[2]; | ||
99 | u8 blk_b[2]; | ||
100 | u8 ps[2]; | ||
101 | } group0B; | ||
102 | } data; | ||
103 | }; | ||
104 | |||
105 | /* FM region (Europe/US, Japan) info */ | ||
106 | struct region_info { | ||
107 | u32 chanl_space; | ||
108 | u32 bot_freq; | ||
109 | u32 top_freq; | ||
110 | u8 fm_band; | ||
111 | }; | ||
112 | struct fmdev; | ||
113 | typedef void (*int_handler_prototype) (struct fmdev *); | ||
114 | |||
115 | /* FM Interrupt processing related info */ | ||
116 | struct fm_irq { | ||
117 | u8 stage; | ||
118 | u16 flag; /* FM interrupt flag */ | ||
119 | u16 mask; /* FM interrupt mask */ | ||
120 | /* Interrupt process timeout handler */ | ||
121 | struct timer_list timer; | ||
122 | u8 retry; | ||
123 | int_handler_prototype *handlers; | ||
124 | }; | ||
125 | |||
126 | /* RDS info */ | ||
127 | struct fm_rds { | ||
128 | u8 flag; /* RX RDS on/off status */ | ||
129 | u8 last_blk_idx; /* Last received RDS block */ | ||
130 | |||
131 | /* RDS buffer */ | ||
132 | wait_queue_head_t read_queue; | ||
133 | u32 buf_size; /* Size is always multiple of 3 */ | ||
134 | u32 wr_idx; | ||
135 | u32 rd_idx; | ||
136 | u8 *buff; | ||
137 | }; | ||
138 | |||
139 | #define FM_RDS_MAX_AF_LIST 25 | ||
140 | |||
141 | /* | ||
142 | * Current RX channel Alternate Frequency cache. | ||
143 | * This info is used to switch to other freq (AF) | ||
144 | * when current channel signal strengh is below RSSI threshold. | ||
145 | */ | ||
146 | struct tuned_station_info { | ||
147 | u16 picode; | ||
148 | u32 af_cache[FM_RDS_MAX_AF_LIST]; | ||
149 | u8 afcache_size; | ||
150 | u8 af_list_max; | ||
151 | }; | ||
152 | |||
153 | /* FM RX mode info */ | ||
154 | struct fm_rx { | ||
155 | struct region_info region; /* Current selected band */ | ||
156 | u32 freq; /* Current RX frquency */ | ||
157 | u8 mute_mode; /* Current mute mode */ | ||
158 | u8 deemphasis_mode; /* Current deemphasis mode */ | ||
159 | /* RF dependent soft mute mode */ | ||
160 | u8 rf_depend_mute; | ||
161 | u16 volume; /* Current volume level */ | ||
162 | u16 rssi_threshold; /* Current RSSI threshold level */ | ||
163 | /* Holds the index of the current AF jump */ | ||
164 | u8 afjump_idx; | ||
165 | /* Will hold the frequency before the jump */ | ||
166 | u32 freq_before_jump; | ||
167 | u8 rds_mode; /* RDS operation mode (RDS/RDBS) */ | ||
168 | u8 af_mode; /* Alternate frequency on/off */ | ||
169 | struct tuned_station_info stat_info; | ||
170 | struct fm_rds rds; | ||
171 | }; | ||
172 | |||
173 | #define FMTX_RDS_TXT_STR_SIZE 25 | ||
174 | /* | ||
175 | * FM TX RDS data | ||
176 | * | ||
177 | * @ text_type: is the text following PS or RT | ||
178 | * @ text: radio text string which could either be PS or RT | ||
179 | * @ af_freq: alternate frequency for Tx | ||
180 | * TODO: to be declared in application | ||
181 | */ | ||
182 | struct tx_rds { | ||
183 | u8 text_type; | ||
184 | u8 text[FMTX_RDS_TXT_STR_SIZE]; | ||
185 | u8 flag; | ||
186 | u32 af_freq; | ||
187 | }; | ||
188 | /* | ||
189 | * FM TX global data | ||
190 | * | ||
191 | * @ pwr_lvl: Power Level of the Transmission from mixer control | ||
192 | * @ xmit_state: Transmission state = Updated locally upon Start/Stop | ||
193 | * @ audio_io: i2S/Analog | ||
194 | * @ tx_frq: Transmission frequency | ||
195 | */ | ||
196 | struct fmtx_data { | ||
197 | u8 pwr_lvl; | ||
198 | u8 xmit_state; | ||
199 | u8 audio_io; | ||
200 | u8 region; | ||
201 | u16 aud_mode; | ||
202 | u32 preemph; | ||
203 | u32 tx_frq; | ||
204 | struct tx_rds rds; | ||
205 | }; | ||
206 | |||
207 | /* FM driver operation structure */ | ||
208 | struct fmdev { | ||
209 | struct video_device *radio_dev; /* V4L2 video device pointer */ | ||
210 | struct snd_card *card; /* Card which holds FM mixer controls */ | ||
211 | u16 asci_id; | ||
212 | spinlock_t rds_buff_lock; /* To protect access to RDS buffer */ | ||
213 | spinlock_t resp_skb_lock; /* To protect access to received SKB */ | ||
214 | |||
215 | long flag; /* FM driver state machine info */ | ||
216 | u8 streg_cbdata; /* status of ST registration */ | ||
217 | |||
218 | struct sk_buff_head rx_q; /* RX queue */ | ||
219 | struct tasklet_struct rx_task; /* RX Tasklet */ | ||
220 | |||
221 | struct sk_buff_head tx_q; /* TX queue */ | ||
222 | struct tasklet_struct tx_task; /* TX Tasklet */ | ||
223 | unsigned long last_tx_jiffies; /* Timestamp of last pkt sent */ | ||
224 | atomic_t tx_cnt; /* Number of packets can send at a time */ | ||
225 | |||
226 | struct sk_buff *resp_skb; /* Response from the chip */ | ||
227 | /* Main task completion handler */ | ||
228 | struct completion maintask_comp; | ||
229 | /* Opcode of last command sent to the chip */ | ||
230 | u8 pre_op; | ||
231 | /* Handler used for wakeup when response packet is received */ | ||
232 | struct completion *resp_comp; | ||
233 | struct fm_irq irq_info; | ||
234 | u8 curr_fmmode; /* Current FM chip mode (TX, RX, OFF) */ | ||
235 | struct fm_rx rx; /* FM receiver info */ | ||
236 | struct fmtx_data tx_data; | ||
237 | |||
238 | /* V4L2 ctrl framwork handler*/ | ||
239 | struct v4l2_ctrl_handler ctrl_handler; | ||
240 | |||
241 | /* For core assisted locking */ | ||
242 | struct mutex mutex; | ||
243 | }; | ||
244 | #endif | ||
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c new file mode 100644 index 000000000000..64454d39c0ca --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_common.c | |||
@@ -0,0 +1,1677 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * | ||
4 | * This sub-module of FM driver is common for FM RX and TX | ||
5 | * functionality. This module is responsible for: | ||
6 | * 1) Forming group of Channel-8 commands to perform particular | ||
7 | * functionality (eg., frequency set require more than | ||
8 | * one Channel-8 command to be sent to the chip). | ||
9 | * 2) Sending each Channel-8 command to the chip and reading | ||
10 | * response back over Shared Transport. | ||
11 | * 3) Managing TX and RX Queues and Tasklets. | ||
12 | * 4) Handling FM Interrupt packet and taking appropriate action. | ||
13 | * 5) Loading FM firmware to the chip (common, FM TX, and FM RX | ||
14 | * firmware files based on mode selection) | ||
15 | * | ||
16 | * Copyright (C) 2011 Texas Instruments | ||
17 | * Author: Raja Mani <raja_mani@ti.com> | ||
18 | * Author: Manjunatha Halli <manjunatha_halli@ti.com> | ||
19 | * | ||
20 | * This program is free software; you can redistribute it and/or modify | ||
21 | * it under the terms of the GNU General Public License version 2 as | ||
22 | * published by the Free Software Foundation. | ||
23 | * | ||
24 | * This program is distributed in the hope that it will be useful, | ||
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
27 | * GNU General Public License for more details. | ||
28 | * | ||
29 | * You should have received a copy of the GNU General Public License | ||
30 | * along with this program; if not, write to the Free Software | ||
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | #include <linux/module.h> | ||
36 | #include <linux/firmware.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "fmdrv.h" | ||
39 | #include "fmdrv_v4l2.h" | ||
40 | #include "fmdrv_common.h" | ||
41 | #include <linux/ti_wilink_st.h> | ||
42 | #include "fmdrv_rx.h" | ||
43 | #include "fmdrv_tx.h" | ||
44 | |||
45 | /* Region info */ | ||
46 | static struct region_info region_configs[] = { | ||
47 | /* Europe/US */ | ||
48 | { | ||
49 | .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL, | ||
50 | .bot_freq = 87500, /* 87.5 MHz */ | ||
51 | .top_freq = 108000, /* 108 MHz */ | ||
52 | .fm_band = 0, | ||
53 | }, | ||
54 | /* Japan */ | ||
55 | { | ||
56 | .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL, | ||
57 | .bot_freq = 76000, /* 76 MHz */ | ||
58 | .top_freq = 90000, /* 90 MHz */ | ||
59 | .fm_band = 1, | ||
60 | }, | ||
61 | }; | ||
62 | |||
63 | /* Band selection */ | ||
64 | static u8 default_radio_region; /* Europe/US */ | ||
65 | module_param(default_radio_region, byte, 0); | ||
66 | MODULE_PARM_DESC(default_radio_region, "Region: 0=Europe/US, 1=Japan"); | ||
67 | |||
68 | /* RDS buffer blocks */ | ||
69 | static u32 default_rds_buf = 300; | ||
70 | module_param(default_rds_buf, uint, 0444); | ||
71 | MODULE_PARM_DESC(rds_buf, "RDS buffer entries"); | ||
72 | |||
73 | /* Radio Nr */ | ||
74 | static u32 radio_nr = -1; | ||
75 | module_param(radio_nr, int, 0444); | ||
76 | MODULE_PARM_DESC(radio_nr, "Radio Nr"); | ||
77 | |||
78 | /* FM irq handlers forward declaration */ | ||
79 | static void fm_irq_send_flag_getcmd(struct fmdev *); | ||
80 | static void fm_irq_handle_flag_getcmd_resp(struct fmdev *); | ||
81 | static void fm_irq_handle_hw_malfunction(struct fmdev *); | ||
82 | static void fm_irq_handle_rds_start(struct fmdev *); | ||
83 | static void fm_irq_send_rdsdata_getcmd(struct fmdev *); | ||
84 | static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *); | ||
85 | static void fm_irq_handle_rds_finish(struct fmdev *); | ||
86 | static void fm_irq_handle_tune_op_ended(struct fmdev *); | ||
87 | static void fm_irq_handle_power_enb(struct fmdev *); | ||
88 | static void fm_irq_handle_low_rssi_start(struct fmdev *); | ||
89 | static void fm_irq_afjump_set_pi(struct fmdev *); | ||
90 | static void fm_irq_handle_set_pi_resp(struct fmdev *); | ||
91 | static void fm_irq_afjump_set_pimask(struct fmdev *); | ||
92 | static void fm_irq_handle_set_pimask_resp(struct fmdev *); | ||
93 | static void fm_irq_afjump_setfreq(struct fmdev *); | ||
94 | static void fm_irq_handle_setfreq_resp(struct fmdev *); | ||
95 | static void fm_irq_afjump_enableint(struct fmdev *); | ||
96 | static void fm_irq_afjump_enableint_resp(struct fmdev *); | ||
97 | static void fm_irq_start_afjump(struct fmdev *); | ||
98 | static void fm_irq_handle_start_afjump_resp(struct fmdev *); | ||
99 | static void fm_irq_afjump_rd_freq(struct fmdev *); | ||
100 | static void fm_irq_afjump_rd_freq_resp(struct fmdev *); | ||
101 | static void fm_irq_handle_low_rssi_finish(struct fmdev *); | ||
102 | static void fm_irq_send_intmsk_cmd(struct fmdev *); | ||
103 | static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *); | ||
104 | |||
105 | /* | ||
106 | * When FM common module receives interrupt packet, following handlers | ||
107 | * will be executed one after another to service the interrupt(s) | ||
108 | */ | ||
109 | enum fmc_irq_handler_index { | ||
110 | FM_SEND_FLAG_GETCMD_IDX, | ||
111 | FM_HANDLE_FLAG_GETCMD_RESP_IDX, | ||
112 | |||
113 | /* HW malfunction irq handler */ | ||
114 | FM_HW_MAL_FUNC_IDX, | ||
115 | |||
116 | /* RDS threshold reached irq handler */ | ||
117 | FM_RDS_START_IDX, | ||
118 | FM_RDS_SEND_RDS_GETCMD_IDX, | ||
119 | FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX, | ||
120 | FM_RDS_FINISH_IDX, | ||
121 | |||
122 | /* Tune operation ended irq handler */ | ||
123 | FM_HW_TUNE_OP_ENDED_IDX, | ||
124 | |||
125 | /* TX power enable irq handler */ | ||
126 | FM_HW_POWER_ENB_IDX, | ||
127 | |||
128 | /* Low RSSI irq handler */ | ||
129 | FM_LOW_RSSI_START_IDX, | ||
130 | FM_AF_JUMP_SETPI_IDX, | ||
131 | FM_AF_JUMP_HANDLE_SETPI_RESP_IDX, | ||
132 | FM_AF_JUMP_SETPI_MASK_IDX, | ||
133 | FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX, | ||
134 | FM_AF_JUMP_SET_AF_FREQ_IDX, | ||
135 | FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX, | ||
136 | FM_AF_JUMP_ENABLE_INT_IDX, | ||
137 | FM_AF_JUMP_ENABLE_INT_RESP_IDX, | ||
138 | FM_AF_JUMP_START_AFJUMP_IDX, | ||
139 | FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX, | ||
140 | FM_AF_JUMP_RD_FREQ_IDX, | ||
141 | FM_AF_JUMP_RD_FREQ_RESP_IDX, | ||
142 | FM_LOW_RSSI_FINISH_IDX, | ||
143 | |||
144 | /* Interrupt process post action */ | ||
145 | FM_SEND_INTMSK_CMD_IDX, | ||
146 | FM_HANDLE_INTMSK_CMD_RESP_IDX, | ||
147 | }; | ||
148 | |||
149 | /* FM interrupt handler table */ | ||
150 | static int_handler_prototype int_handler_table[] = { | ||
151 | fm_irq_send_flag_getcmd, | ||
152 | fm_irq_handle_flag_getcmd_resp, | ||
153 | fm_irq_handle_hw_malfunction, | ||
154 | fm_irq_handle_rds_start, /* RDS threshold reached irq handler */ | ||
155 | fm_irq_send_rdsdata_getcmd, | ||
156 | fm_irq_handle_rdsdata_getcmd_resp, | ||
157 | fm_irq_handle_rds_finish, | ||
158 | fm_irq_handle_tune_op_ended, | ||
159 | fm_irq_handle_power_enb, /* TX power enable irq handler */ | ||
160 | fm_irq_handle_low_rssi_start, | ||
161 | fm_irq_afjump_set_pi, | ||
162 | fm_irq_handle_set_pi_resp, | ||
163 | fm_irq_afjump_set_pimask, | ||
164 | fm_irq_handle_set_pimask_resp, | ||
165 | fm_irq_afjump_setfreq, | ||
166 | fm_irq_handle_setfreq_resp, | ||
167 | fm_irq_afjump_enableint, | ||
168 | fm_irq_afjump_enableint_resp, | ||
169 | fm_irq_start_afjump, | ||
170 | fm_irq_handle_start_afjump_resp, | ||
171 | fm_irq_afjump_rd_freq, | ||
172 | fm_irq_afjump_rd_freq_resp, | ||
173 | fm_irq_handle_low_rssi_finish, | ||
174 | fm_irq_send_intmsk_cmd, /* Interrupt process post action */ | ||
175 | fm_irq_handle_intmsk_cmd_resp | ||
176 | }; | ||
177 | |||
178 | long (*g_st_write) (struct sk_buff *skb); | ||
179 | static struct completion wait_for_fmdrv_reg_comp; | ||
180 | |||
181 | static inline void fm_irq_call(struct fmdev *fmdev) | ||
182 | { | ||
183 | fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev); | ||
184 | } | ||
185 | |||
186 | /* Continue next function in interrupt handler table */ | ||
187 | static inline void fm_irq_call_stage(struct fmdev *fmdev, u8 stage) | ||
188 | { | ||
189 | fmdev->irq_info.stage = stage; | ||
190 | fm_irq_call(fmdev); | ||
191 | } | ||
192 | |||
193 | static inline void fm_irq_timeout_stage(struct fmdev *fmdev, u8 stage) | ||
194 | { | ||
195 | fmdev->irq_info.stage = stage; | ||
196 | mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT); | ||
197 | } | ||
198 | |||
199 | #ifdef FM_DUMP_TXRX_PKT | ||
200 | /* To dump outgoing FM Channel-8 packets */ | ||
201 | inline void dump_tx_skb_data(struct sk_buff *skb) | ||
202 | { | ||
203 | int len, len_org; | ||
204 | u8 index; | ||
205 | struct fm_cmd_msg_hdr *cmd_hdr; | ||
206 | |||
207 | cmd_hdr = (struct fm_cmd_msg_hdr *)skb->data; | ||
208 | printk(KERN_INFO "<<%shdr:%02x len:%02x opcode:%02x type:%s dlen:%02x", | ||
209 | fm_cb(skb)->completion ? " " : "*", cmd_hdr->hdr, | ||
210 | cmd_hdr->len, cmd_hdr->op, | ||
211 | cmd_hdr->rd_wr ? "RD" : "WR", cmd_hdr->dlen); | ||
212 | |||
213 | len_org = skb->len - FM_CMD_MSG_HDR_SIZE; | ||
214 | if (len_org > 0) { | ||
215 | printk("\n data(%d): ", cmd_hdr->dlen); | ||
216 | len = min(len_org, 14); | ||
217 | for (index = 0; index < len; index++) | ||
218 | printk("%x ", | ||
219 | skb->data[FM_CMD_MSG_HDR_SIZE + index]); | ||
220 | printk("%s", (len_org > 14) ? ".." : ""); | ||
221 | } | ||
222 | printk("\n"); | ||
223 | } | ||
224 | |||
225 | /* To dump incoming FM Channel-8 packets */ | ||
226 | inline void dump_rx_skb_data(struct sk_buff *skb) | ||
227 | { | ||
228 | int len, len_org; | ||
229 | u8 index; | ||
230 | struct fm_event_msg_hdr *evt_hdr; | ||
231 | |||
232 | evt_hdr = (struct fm_event_msg_hdr *)skb->data; | ||
233 | printk(KERN_INFO ">> hdr:%02x len:%02x sts:%02x numhci:%02x " | ||
234 | "opcode:%02x type:%s dlen:%02x", evt_hdr->hdr, evt_hdr->len, | ||
235 | evt_hdr->status, evt_hdr->num_fm_hci_cmds, evt_hdr->op, | ||
236 | (evt_hdr->rd_wr) ? "RD" : "WR", evt_hdr->dlen); | ||
237 | |||
238 | len_org = skb->len - FM_EVT_MSG_HDR_SIZE; | ||
239 | if (len_org > 0) { | ||
240 | printk("\n data(%d): ", evt_hdr->dlen); | ||
241 | len = min(len_org, 14); | ||
242 | for (index = 0; index < len; index++) | ||
243 | printk("%x ", | ||
244 | skb->data[FM_EVT_MSG_HDR_SIZE + index]); | ||
245 | printk("%s", (len_org > 14) ? ".." : ""); | ||
246 | } | ||
247 | printk("\n"); | ||
248 | } | ||
249 | #endif | ||
250 | |||
251 | void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set) | ||
252 | { | ||
253 | fmdev->rx.region = region_configs[region_to_set]; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * FM common sub-module will schedule this tasklet whenever it receives | ||
258 | * FM packet from ST driver. | ||
259 | */ | ||
260 | static void recv_tasklet(unsigned long arg) | ||
261 | { | ||
262 | struct fmdev *fmdev; | ||
263 | struct fm_irq *irq_info; | ||
264 | struct fm_event_msg_hdr *evt_hdr; | ||
265 | struct sk_buff *skb; | ||
266 | u8 num_fm_hci_cmds; | ||
267 | unsigned long flags; | ||
268 | |||
269 | fmdev = (struct fmdev *)arg; | ||
270 | irq_info = &fmdev->irq_info; | ||
271 | /* Process all packets in the RX queue */ | ||
272 | while ((skb = skb_dequeue(&fmdev->rx_q))) { | ||
273 | if (skb->len < sizeof(struct fm_event_msg_hdr)) { | ||
274 | fmerr("skb(%p) has only %d bytes, " | ||
275 | "at least need %zu bytes to decode\n", skb, | ||
276 | skb->len, sizeof(struct fm_event_msg_hdr)); | ||
277 | kfree_skb(skb); | ||
278 | continue; | ||
279 | } | ||
280 | |||
281 | evt_hdr = (void *)skb->data; | ||
282 | num_fm_hci_cmds = evt_hdr->num_fm_hci_cmds; | ||
283 | |||
284 | /* FM interrupt packet? */ | ||
285 | if (evt_hdr->op == FM_INTERRUPT) { | ||
286 | /* FM interrupt handler started already? */ | ||
287 | if (!test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) { | ||
288 | set_bit(FM_INTTASK_RUNNING, &fmdev->flag); | ||
289 | if (irq_info->stage != 0) { | ||
290 | fmerr("Inval stage resetting to zero\n"); | ||
291 | irq_info->stage = 0; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Execute first function in interrupt handler | ||
296 | * table. | ||
297 | */ | ||
298 | irq_info->handlers[irq_info->stage](fmdev); | ||
299 | } else { | ||
300 | set_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag); | ||
301 | } | ||
302 | kfree_skb(skb); | ||
303 | } | ||
304 | /* Anyone waiting for this with completion handler? */ | ||
305 | else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp != NULL) { | ||
306 | |||
307 | spin_lock_irqsave(&fmdev->resp_skb_lock, flags); | ||
308 | fmdev->resp_skb = skb; | ||
309 | spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); | ||
310 | complete(fmdev->resp_comp); | ||
311 | |||
312 | fmdev->resp_comp = NULL; | ||
313 | atomic_set(&fmdev->tx_cnt, 1); | ||
314 | } | ||
315 | /* Is this for interrupt handler? */ | ||
316 | else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp == NULL) { | ||
317 | if (fmdev->resp_skb != NULL) | ||
318 | fmerr("Response SKB ptr not NULL\n"); | ||
319 | |||
320 | spin_lock_irqsave(&fmdev->resp_skb_lock, flags); | ||
321 | fmdev->resp_skb = skb; | ||
322 | spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); | ||
323 | |||
324 | /* Execute interrupt handler where state index points */ | ||
325 | irq_info->handlers[irq_info->stage](fmdev); | ||
326 | |||
327 | kfree_skb(skb); | ||
328 | atomic_set(&fmdev->tx_cnt, 1); | ||
329 | } else { | ||
330 | fmerr("Nobody claimed SKB(%p),purging\n", skb); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Check flow control field. If Num_FM_HCI_Commands field is | ||
335 | * not zero, schedule FM TX tasklet. | ||
336 | */ | ||
337 | if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt)) | ||
338 | if (!skb_queue_empty(&fmdev->tx_q)) | ||
339 | tasklet_schedule(&fmdev->tx_task); | ||
340 | } | ||
341 | } | ||
342 | |||
343 | /* FM send tasklet: is scheduled when FM packet has to be sent to chip */ | ||
344 | static void send_tasklet(unsigned long arg) | ||
345 | { | ||
346 | struct fmdev *fmdev; | ||
347 | struct sk_buff *skb; | ||
348 | int len; | ||
349 | |||
350 | fmdev = (struct fmdev *)arg; | ||
351 | |||
352 | if (!atomic_read(&fmdev->tx_cnt)) | ||
353 | return; | ||
354 | |||
355 | /* Check, is there any timeout happenned to last transmitted packet */ | ||
356 | if ((jiffies - fmdev->last_tx_jiffies) > FM_DRV_TX_TIMEOUT) { | ||
357 | fmerr("TX timeout occurred\n"); | ||
358 | atomic_set(&fmdev->tx_cnt, 1); | ||
359 | } | ||
360 | |||
361 | /* Send queued FM TX packets */ | ||
362 | skb = skb_dequeue(&fmdev->tx_q); | ||
363 | if (!skb) | ||
364 | return; | ||
365 | |||
366 | atomic_dec(&fmdev->tx_cnt); | ||
367 | fmdev->pre_op = fm_cb(skb)->fm_op; | ||
368 | |||
369 | if (fmdev->resp_comp != NULL) | ||
370 | fmerr("Response completion handler is not NULL\n"); | ||
371 | |||
372 | fmdev->resp_comp = fm_cb(skb)->completion; | ||
373 | |||
374 | /* Write FM packet to ST driver */ | ||
375 | len = g_st_write(skb); | ||
376 | if (len < 0) { | ||
377 | kfree_skb(skb); | ||
378 | fmdev->resp_comp = NULL; | ||
379 | fmerr("TX tasklet failed to send skb(%p)\n", skb); | ||
380 | atomic_set(&fmdev->tx_cnt, 1); | ||
381 | } else { | ||
382 | fmdev->last_tx_jiffies = jiffies; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for | ||
388 | * transmission | ||
389 | */ | ||
390 | static u32 fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, | ||
391 | int payload_len, struct completion *wait_completion) | ||
392 | { | ||
393 | struct sk_buff *skb; | ||
394 | struct fm_cmd_msg_hdr *hdr; | ||
395 | int size; | ||
396 | |||
397 | if (fm_op >= FM_INTERRUPT) { | ||
398 | fmerr("Invalid fm opcode - %d\n", fm_op); | ||
399 | return -EINVAL; | ||
400 | } | ||
401 | if (test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) && payload == NULL) { | ||
402 | fmerr("Payload data is NULL during fw download\n"); | ||
403 | return -EINVAL; | ||
404 | } | ||
405 | if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag)) | ||
406 | size = | ||
407 | FM_CMD_MSG_HDR_SIZE + ((payload == NULL) ? 0 : payload_len); | ||
408 | else | ||
409 | size = payload_len; | ||
410 | |||
411 | skb = alloc_skb(size, GFP_ATOMIC); | ||
412 | if (!skb) { | ||
413 | fmerr("No memory to create new SKB\n"); | ||
414 | return -ENOMEM; | ||
415 | } | ||
416 | /* | ||
417 | * Don't fill FM header info for the commands which come from | ||
418 | * FM firmware file. | ||
419 | */ | ||
420 | if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) || | ||
421 | test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) { | ||
422 | /* Fill command header info */ | ||
423 | hdr = (struct fm_cmd_msg_hdr *)skb_put(skb, FM_CMD_MSG_HDR_SIZE); | ||
424 | hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER; /* 0x08 */ | ||
425 | |||
426 | /* 3 (fm_opcode,rd_wr,dlen) + payload len) */ | ||
427 | hdr->len = ((payload == NULL) ? 0 : payload_len) + 3; | ||
428 | |||
429 | /* FM opcode */ | ||
430 | hdr->op = fm_op; | ||
431 | |||
432 | /* read/write type */ | ||
433 | hdr->rd_wr = type; | ||
434 | hdr->dlen = payload_len; | ||
435 | fm_cb(skb)->fm_op = fm_op; | ||
436 | |||
437 | /* | ||
438 | * If firmware download has finished and the command is | ||
439 | * not a read command then payload is != NULL - a write | ||
440 | * command with u16 payload - convert to be16 | ||
441 | */ | ||
442 | if (payload != NULL) | ||
443 | *(u16 *)payload = cpu_to_be16(*(u16 *)payload); | ||
444 | |||
445 | } else if (payload != NULL) { | ||
446 | fm_cb(skb)->fm_op = *((u8 *)payload + 2); | ||
447 | } | ||
448 | if (payload != NULL) | ||
449 | memcpy(skb_put(skb, payload_len), payload, payload_len); | ||
450 | |||
451 | fm_cb(skb)->completion = wait_completion; | ||
452 | skb_queue_tail(&fmdev->tx_q, skb); | ||
453 | tasklet_schedule(&fmdev->tx_task); | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | /* Sends FM Channel-8 command to the chip and waits for the response */ | ||
459 | u32 fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, | ||
460 | unsigned int payload_len, void *response, int *response_len) | ||
461 | { | ||
462 | struct sk_buff *skb; | ||
463 | struct fm_event_msg_hdr *evt_hdr; | ||
464 | unsigned long flags; | ||
465 | u32 ret; | ||
466 | |||
467 | init_completion(&fmdev->maintask_comp); | ||
468 | ret = fm_send_cmd(fmdev, fm_op, type, payload, payload_len, | ||
469 | &fmdev->maintask_comp); | ||
470 | if (ret) | ||
471 | return ret; | ||
472 | |||
473 | ret = wait_for_completion_timeout(&fmdev->maintask_comp, FM_DRV_TX_TIMEOUT); | ||
474 | if (!ret) { | ||
475 | fmerr("Timeout(%d sec),didn't get reg" | ||
476 | "completion signal from RX tasklet\n", | ||
477 | jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); | ||
478 | return -ETIMEDOUT; | ||
479 | } | ||
480 | if (!fmdev->resp_skb) { | ||
481 | fmerr("Reponse SKB is missing\n"); | ||
482 | return -EFAULT; | ||
483 | } | ||
484 | spin_lock_irqsave(&fmdev->resp_skb_lock, flags); | ||
485 | skb = fmdev->resp_skb; | ||
486 | fmdev->resp_skb = NULL; | ||
487 | spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); | ||
488 | |||
489 | evt_hdr = (void *)skb->data; | ||
490 | if (evt_hdr->status != 0) { | ||
491 | fmerr("Received event pkt status(%d) is not zero\n", | ||
492 | evt_hdr->status); | ||
493 | kfree_skb(skb); | ||
494 | return -EIO; | ||
495 | } | ||
496 | /* Send response data to caller */ | ||
497 | if (response != NULL && response_len != NULL && evt_hdr->dlen) { | ||
498 | /* Skip header info and copy only response data */ | ||
499 | skb_pull(skb, sizeof(struct fm_event_msg_hdr)); | ||
500 | memcpy(response, skb->data, evt_hdr->dlen); | ||
501 | *response_len = evt_hdr->dlen; | ||
502 | } else if (response_len != NULL && evt_hdr->dlen == 0) { | ||
503 | *response_len = 0; | ||
504 | } | ||
505 | kfree_skb(skb); | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | /* --- Helper functions used in FM interrupt handlers ---*/ | ||
511 | static inline u32 check_cmdresp_status(struct fmdev *fmdev, | ||
512 | struct sk_buff **skb) | ||
513 | { | ||
514 | struct fm_event_msg_hdr *fm_evt_hdr; | ||
515 | unsigned long flags; | ||
516 | |||
517 | del_timer(&fmdev->irq_info.timer); | ||
518 | |||
519 | spin_lock_irqsave(&fmdev->resp_skb_lock, flags); | ||
520 | *skb = fmdev->resp_skb; | ||
521 | fmdev->resp_skb = NULL; | ||
522 | spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); | ||
523 | |||
524 | fm_evt_hdr = (void *)(*skb)->data; | ||
525 | if (fm_evt_hdr->status != 0) { | ||
526 | fmerr("irq: opcode %x response status is not zero " | ||
527 | "Initiating irq recovery process\n", | ||
528 | fm_evt_hdr->op); | ||
529 | |||
530 | mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT); | ||
531 | return -1; | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static inline void fm_irq_common_cmd_resp_helper(struct fmdev *fmdev, u8 stage) | ||
538 | { | ||
539 | struct sk_buff *skb; | ||
540 | |||
541 | if (!check_cmdresp_status(fmdev, &skb)) | ||
542 | fm_irq_call_stage(fmdev, stage); | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * Interrupt process timeout handler. | ||
547 | * One of the irq handler did not get proper response from the chip. So take | ||
548 | * recovery action here. FM interrupts are disabled in the beginning of | ||
549 | * interrupt process. Therefore reset stage index to re-enable default | ||
550 | * interrupts. So that next interrupt will be processed as usual. | ||
551 | */ | ||
552 | static void int_timeout_handler(unsigned long data) | ||
553 | { | ||
554 | struct fmdev *fmdev; | ||
555 | struct fm_irq *fmirq; | ||
556 | |||
557 | fmdbg("irq: timeout,trying to re-enable fm interrupts\n"); | ||
558 | fmdev = (struct fmdev *)data; | ||
559 | fmirq = &fmdev->irq_info; | ||
560 | fmirq->retry++; | ||
561 | |||
562 | if (fmirq->retry > FM_IRQ_TIMEOUT_RETRY_MAX) { | ||
563 | /* Stop recovery action (interrupt reenable process) and | ||
564 | * reset stage index & retry count values */ | ||
565 | fmirq->stage = 0; | ||
566 | fmirq->retry = 0; | ||
567 | fmerr("Recovery action failed during" | ||
568 | "irq processing, max retry reached\n"); | ||
569 | return; | ||
570 | } | ||
571 | fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX); | ||
572 | } | ||
573 | |||
574 | /* --------- FM interrupt handlers ------------*/ | ||
575 | static void fm_irq_send_flag_getcmd(struct fmdev *fmdev) | ||
576 | { | ||
577 | u16 flag; | ||
578 | |||
579 | /* Send FLAG_GET command , to know the source of interrupt */ | ||
580 | if (!fm_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, sizeof(flag), NULL)) | ||
581 | fm_irq_timeout_stage(fmdev, FM_HANDLE_FLAG_GETCMD_RESP_IDX); | ||
582 | } | ||
583 | |||
584 | static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev) | ||
585 | { | ||
586 | struct sk_buff *skb; | ||
587 | struct fm_event_msg_hdr *fm_evt_hdr; | ||
588 | |||
589 | if (check_cmdresp_status(fmdev, &skb)) | ||
590 | return; | ||
591 | |||
592 | fm_evt_hdr = (void *)skb->data; | ||
593 | |||
594 | /* Skip header info and copy only response data */ | ||
595 | skb_pull(skb, sizeof(struct fm_event_msg_hdr)); | ||
596 | memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen); | ||
597 | |||
598 | fmdev->irq_info.flag = be16_to_cpu(fmdev->irq_info.flag); | ||
599 | fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag); | ||
600 | |||
601 | /* Continue next function in interrupt handler table */ | ||
602 | fm_irq_call_stage(fmdev, FM_HW_MAL_FUNC_IDX); | ||
603 | } | ||
604 | |||
605 | static void fm_irq_handle_hw_malfunction(struct fmdev *fmdev) | ||
606 | { | ||
607 | if (fmdev->irq_info.flag & FM_MAL_EVENT & fmdev->irq_info.mask) | ||
608 | fmerr("irq: HW MAL int received - do nothing\n"); | ||
609 | |||
610 | /* Continue next function in interrupt handler table */ | ||
611 | fm_irq_call_stage(fmdev, FM_RDS_START_IDX); | ||
612 | } | ||
613 | |||
614 | static void fm_irq_handle_rds_start(struct fmdev *fmdev) | ||
615 | { | ||
616 | if (fmdev->irq_info.flag & FM_RDS_EVENT & fmdev->irq_info.mask) { | ||
617 | fmdbg("irq: rds threshold reached\n"); | ||
618 | fmdev->irq_info.stage = FM_RDS_SEND_RDS_GETCMD_IDX; | ||
619 | } else { | ||
620 | /* Continue next function in interrupt handler table */ | ||
621 | fmdev->irq_info.stage = FM_HW_TUNE_OP_ENDED_IDX; | ||
622 | } | ||
623 | |||
624 | fm_irq_call(fmdev); | ||
625 | } | ||
626 | |||
627 | static void fm_irq_send_rdsdata_getcmd(struct fmdev *fmdev) | ||
628 | { | ||
629 | /* Send the command to read RDS data from the chip */ | ||
630 | if (!fm_send_cmd(fmdev, RDS_DATA_GET, REG_RD, NULL, | ||
631 | (FM_RX_RDS_FIFO_THRESHOLD * 3), NULL)) | ||
632 | fm_irq_timeout_stage(fmdev, FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX); | ||
633 | } | ||
634 | |||
635 | /* Keeps track of current RX channel AF (Alternate Frequency) */ | ||
636 | static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af) | ||
637 | { | ||
638 | struct tuned_station_info *stat_info = &fmdev->rx.stat_info; | ||
639 | u8 reg_idx = fmdev->rx.region.fm_band; | ||
640 | u8 index; | ||
641 | u32 freq; | ||
642 | |||
643 | /* First AF indicates the number of AF follows. Reset the list */ | ||
644 | if ((af >= FM_RDS_1_AF_FOLLOWS) && (af <= FM_RDS_25_AF_FOLLOWS)) { | ||
645 | fmdev->rx.stat_info.af_list_max = (af - FM_RDS_1_AF_FOLLOWS + 1); | ||
646 | fmdev->rx.stat_info.afcache_size = 0; | ||
647 | fmdbg("No of expected AF : %d\n", fmdev->rx.stat_info.af_list_max); | ||
648 | return; | ||
649 | } | ||
650 | |||
651 | if (af < FM_RDS_MIN_AF) | ||
652 | return; | ||
653 | if (reg_idx == FM_BAND_EUROPE_US && af > FM_RDS_MAX_AF) | ||
654 | return; | ||
655 | if (reg_idx == FM_BAND_JAPAN && af > FM_RDS_MAX_AF_JAPAN) | ||
656 | return; | ||
657 | |||
658 | freq = fmdev->rx.region.bot_freq + (af * 100); | ||
659 | if (freq == fmdev->rx.freq) { | ||
660 | fmdbg("Current freq(%d) is matching with received AF(%d)\n", | ||
661 | fmdev->rx.freq, freq); | ||
662 | return; | ||
663 | } | ||
664 | /* Do check in AF cache */ | ||
665 | for (index = 0; index < stat_info->afcache_size; index++) { | ||
666 | if (stat_info->af_cache[index] == freq) | ||
667 | break; | ||
668 | } | ||
669 | /* Reached the limit of the list - ignore the next AF */ | ||
670 | if (index == stat_info->af_list_max) { | ||
671 | fmdbg("AF cache is full\n"); | ||
672 | return; | ||
673 | } | ||
674 | /* | ||
675 | * If we reached the end of the list then this AF is not | ||
676 | * in the list - add it. | ||
677 | */ | ||
678 | if (index == stat_info->afcache_size) { | ||
679 | fmdbg("Storing AF %d to cache index %d\n", freq, index); | ||
680 | stat_info->af_cache[index] = freq; | ||
681 | stat_info->afcache_size++; | ||
682 | } | ||
683 | } | ||
684 | |||
685 | /* | ||
686 | * Converts RDS buffer data from big endian format | ||
687 | * to little endian format. | ||
688 | */ | ||
689 | static void fm_rdsparse_swapbytes(struct fmdev *fmdev, | ||
690 | struct fm_rdsdata_format *rds_format) | ||
691 | { | ||
692 | u8 byte1; | ||
693 | u8 index = 0; | ||
694 | u8 *rds_buff; | ||
695 | |||
696 | /* | ||
697 | * Since in Orca the 2 RDS Data bytes are in little endian and | ||
698 | * in Dolphin they are in big endian, the parsing of the RDS data | ||
699 | * is chip dependent | ||
700 | */ | ||
701 | if (fmdev->asci_id != 0x6350) { | ||
702 | rds_buff = &rds_format->data.groupdatabuff.buff[0]; | ||
703 | while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) { | ||
704 | byte1 = rds_buff[index]; | ||
705 | rds_buff[index] = rds_buff[index + 1]; | ||
706 | rds_buff[index + 1] = byte1; | ||
707 | index += 2; | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev) | ||
713 | { | ||
714 | struct sk_buff *skb; | ||
715 | struct fm_rdsdata_format rds_fmt; | ||
716 | struct fm_rds *rds = &fmdev->rx.rds; | ||
717 | unsigned long group_idx, flags; | ||
718 | u8 *rds_data, meta_data, tmpbuf[3]; | ||
719 | u8 type, blk_idx; | ||
720 | u16 cur_picode; | ||
721 | u32 rds_len; | ||
722 | |||
723 | if (check_cmdresp_status(fmdev, &skb)) | ||
724 | return; | ||
725 | |||
726 | /* Skip header info */ | ||
727 | skb_pull(skb, sizeof(struct fm_event_msg_hdr)); | ||
728 | rds_data = skb->data; | ||
729 | rds_len = skb->len; | ||
730 | |||
731 | /* Parse the RDS data */ | ||
732 | while (rds_len >= FM_RDS_BLK_SIZE) { | ||
733 | meta_data = rds_data[2]; | ||
734 | /* Get the type: 0=A, 1=B, 2=C, 3=C', 4=D, 5=E */ | ||
735 | type = (meta_data & 0x07); | ||
736 | |||
737 | /* Transform the blk type into index sequence (0, 1, 2, 3, 4) */ | ||
738 | blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1)); | ||
739 | fmdbg("Block index:%d(%s)\n", blk_idx, | ||
740 | (meta_data & FM_RDS_STATUS_ERR_MASK) ? "Bad" : "Ok"); | ||
741 | |||
742 | if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0) | ||
743 | break; | ||
744 | |||
745 | if (blk_idx < FM_RDS_BLK_IDX_A || blk_idx > FM_RDS_BLK_IDX_D) { | ||
746 | fmdbg("Block sequence mismatch\n"); | ||
747 | rds->last_blk_idx = -1; | ||
748 | break; | ||
749 | } | ||
750 | |||
751 | /* Skip checkword (control) byte and copy only data byte */ | ||
752 | memcpy(&rds_fmt.data.groupdatabuff. | ||
753 | buff[blk_idx * (FM_RDS_BLK_SIZE - 1)], | ||
754 | rds_data, (FM_RDS_BLK_SIZE - 1)); | ||
755 | |||
756 | rds->last_blk_idx = blk_idx; | ||
757 | |||
758 | /* If completed a whole group then handle it */ | ||
759 | if (blk_idx == FM_RDS_BLK_IDX_D) { | ||
760 | fmdbg("Good block received\n"); | ||
761 | fm_rdsparse_swapbytes(fmdev, &rds_fmt); | ||
762 | |||
763 | /* | ||
764 | * Extract PI code and store in local cache. | ||
765 | * We need this during AF switch processing. | ||
766 | */ | ||
767 | cur_picode = be16_to_cpu(rds_fmt.data.groupgeneral.pidata); | ||
768 | if (fmdev->rx.stat_info.picode != cur_picode) | ||
769 | fmdev->rx.stat_info.picode = cur_picode; | ||
770 | |||
771 | fmdbg("picode:%d\n", cur_picode); | ||
772 | |||
773 | group_idx = (rds_fmt.data.groupgeneral.blk_b[0] >> 3); | ||
774 | fmdbg("(fmdrv):Group:%ld%s\n", group_idx/2, | ||
775 | (group_idx % 2) ? "B" : "A"); | ||
776 | |||
777 | group_idx = 1 << (rds_fmt.data.groupgeneral.blk_b[0] >> 3); | ||
778 | if (group_idx == FM_RDS_GROUP_TYPE_MASK_0A) { | ||
779 | fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[0]); | ||
780 | fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[1]); | ||
781 | } | ||
782 | } | ||
783 | rds_len -= FM_RDS_BLK_SIZE; | ||
784 | rds_data += FM_RDS_BLK_SIZE; | ||
785 | } | ||
786 | |||
787 | /* Copy raw rds data to internal rds buffer */ | ||
788 | rds_data = skb->data; | ||
789 | rds_len = skb->len; | ||
790 | |||
791 | spin_lock_irqsave(&fmdev->rds_buff_lock, flags); | ||
792 | while (rds_len > 0) { | ||
793 | /* | ||
794 | * Fill RDS buffer as per V4L2 specification. | ||
795 | * Store control byte | ||
796 | */ | ||
797 | type = (rds_data[2] & 0x07); | ||
798 | blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1)); | ||
799 | tmpbuf[2] = blk_idx; /* Offset name */ | ||
800 | tmpbuf[2] |= blk_idx << 3; /* Received offset */ | ||
801 | |||
802 | /* Store data byte */ | ||
803 | tmpbuf[0] = rds_data[0]; | ||
804 | tmpbuf[1] = rds_data[1]; | ||
805 | |||
806 | memcpy(&rds->buff[rds->wr_idx], &tmpbuf, FM_RDS_BLK_SIZE); | ||
807 | rds->wr_idx = (rds->wr_idx + FM_RDS_BLK_SIZE) % rds->buf_size; | ||
808 | |||
809 | /* Check for overflow & start over */ | ||
810 | if (rds->wr_idx == rds->rd_idx) { | ||
811 | fmdbg("RDS buffer overflow\n"); | ||
812 | rds->wr_idx = 0; | ||
813 | rds->rd_idx = 0; | ||
814 | break; | ||
815 | } | ||
816 | rds_len -= FM_RDS_BLK_SIZE; | ||
817 | rds_data += FM_RDS_BLK_SIZE; | ||
818 | } | ||
819 | spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); | ||
820 | |||
821 | /* Wakeup read queue */ | ||
822 | if (rds->wr_idx != rds->rd_idx) | ||
823 | wake_up_interruptible(&rds->read_queue); | ||
824 | |||
825 | fm_irq_call_stage(fmdev, FM_RDS_FINISH_IDX); | ||
826 | } | ||
827 | |||
828 | static void fm_irq_handle_rds_finish(struct fmdev *fmdev) | ||
829 | { | ||
830 | fm_irq_call_stage(fmdev, FM_HW_TUNE_OP_ENDED_IDX); | ||
831 | } | ||
832 | |||
833 | static void fm_irq_handle_tune_op_ended(struct fmdev *fmdev) | ||
834 | { | ||
835 | if (fmdev->irq_info.flag & (FM_FR_EVENT | FM_BL_EVENT) & fmdev-> | ||
836 | irq_info.mask) { | ||
837 | fmdbg("irq: tune ended/bandlimit reached\n"); | ||
838 | if (test_and_clear_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag)) { | ||
839 | fmdev->irq_info.stage = FM_AF_JUMP_RD_FREQ_IDX; | ||
840 | } else { | ||
841 | complete(&fmdev->maintask_comp); | ||
842 | fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX; | ||
843 | } | ||
844 | } else | ||
845 | fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX; | ||
846 | |||
847 | fm_irq_call(fmdev); | ||
848 | } | ||
849 | |||
850 | static void fm_irq_handle_power_enb(struct fmdev *fmdev) | ||
851 | { | ||
852 | if (fmdev->irq_info.flag & FM_POW_ENB_EVENT) { | ||
853 | fmdbg("irq: Power Enabled/Disabled\n"); | ||
854 | complete(&fmdev->maintask_comp); | ||
855 | } | ||
856 | |||
857 | fm_irq_call_stage(fmdev, FM_LOW_RSSI_START_IDX); | ||
858 | } | ||
859 | |||
860 | static void fm_irq_handle_low_rssi_start(struct fmdev *fmdev) | ||
861 | { | ||
862 | if ((fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) && | ||
863 | (fmdev->irq_info.flag & FM_LEV_EVENT & fmdev->irq_info.mask) && | ||
864 | (fmdev->rx.freq != FM_UNDEFINED_FREQ) && | ||
865 | (fmdev->rx.stat_info.afcache_size != 0)) { | ||
866 | fmdbg("irq: rssi level has fallen below threshold level\n"); | ||
867 | |||
868 | /* Disable further low RSSI interrupts */ | ||
869 | fmdev->irq_info.mask &= ~FM_LEV_EVENT; | ||
870 | |||
871 | fmdev->rx.afjump_idx = 0; | ||
872 | fmdev->rx.freq_before_jump = fmdev->rx.freq; | ||
873 | fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX; | ||
874 | } else { | ||
875 | /* Continue next function in interrupt handler table */ | ||
876 | fmdev->irq_info.stage = FM_SEND_INTMSK_CMD_IDX; | ||
877 | } | ||
878 | |||
879 | fm_irq_call(fmdev); | ||
880 | } | ||
881 | |||
882 | static void fm_irq_afjump_set_pi(struct fmdev *fmdev) | ||
883 | { | ||
884 | u16 payload; | ||
885 | |||
886 | /* Set PI code - must be updated if the AF list is not empty */ | ||
887 | payload = fmdev->rx.stat_info.picode; | ||
888 | if (!fm_send_cmd(fmdev, RDS_PI_SET, REG_WR, &payload, sizeof(payload), NULL)) | ||
889 | fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_RESP_IDX); | ||
890 | } | ||
891 | |||
892 | static void fm_irq_handle_set_pi_resp(struct fmdev *fmdev) | ||
893 | { | ||
894 | fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SETPI_MASK_IDX); | ||
895 | } | ||
896 | |||
897 | /* | ||
898 | * Set PI mask. | ||
899 | * 0xFFFF = Enable PI code matching | ||
900 | * 0x0000 = Disable PI code matching | ||
901 | */ | ||
902 | static void fm_irq_afjump_set_pimask(struct fmdev *fmdev) | ||
903 | { | ||
904 | u16 payload; | ||
905 | |||
906 | payload = 0x0000; | ||
907 | if (!fm_send_cmd(fmdev, RDS_PI_MASK_SET, REG_WR, &payload, sizeof(payload), NULL)) | ||
908 | fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX); | ||
909 | } | ||
910 | |||
911 | static void fm_irq_handle_set_pimask_resp(struct fmdev *fmdev) | ||
912 | { | ||
913 | fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SET_AF_FREQ_IDX); | ||
914 | } | ||
915 | |||
916 | static void fm_irq_afjump_setfreq(struct fmdev *fmdev) | ||
917 | { | ||
918 | u16 frq_index; | ||
919 | u16 payload; | ||
920 | |||
921 | fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); | ||
922 | frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] - | ||
923 | fmdev->rx.region.bot_freq) / FM_FREQ_MUL; | ||
924 | |||
925 | payload = frq_index; | ||
926 | if (!fm_send_cmd(fmdev, AF_FREQ_SET, REG_WR, &payload, sizeof(payload), NULL)) | ||
927 | fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX); | ||
928 | } | ||
929 | |||
930 | static void fm_irq_handle_setfreq_resp(struct fmdev *fmdev) | ||
931 | { | ||
932 | fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_ENABLE_INT_IDX); | ||
933 | } | ||
934 | |||
935 | static void fm_irq_afjump_enableint(struct fmdev *fmdev) | ||
936 | { | ||
937 | u16 payload; | ||
938 | |||
939 | /* Enable FR (tuning operation ended) interrupt */ | ||
940 | payload = FM_FR_EVENT; | ||
941 | if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, sizeof(payload), NULL)) | ||
942 | fm_irq_timeout_stage(fmdev, FM_AF_JUMP_ENABLE_INT_RESP_IDX); | ||
943 | } | ||
944 | |||
945 | static void fm_irq_afjump_enableint_resp(struct fmdev *fmdev) | ||
946 | { | ||
947 | fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_START_AFJUMP_IDX); | ||
948 | } | ||
949 | |||
950 | static void fm_irq_start_afjump(struct fmdev *fmdev) | ||
951 | { | ||
952 | u16 payload; | ||
953 | |||
954 | payload = FM_TUNER_AF_JUMP_MODE; | ||
955 | if (!fm_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, | ||
956 | sizeof(payload), NULL)) | ||
957 | fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX); | ||
958 | } | ||
959 | |||
960 | static void fm_irq_handle_start_afjump_resp(struct fmdev *fmdev) | ||
961 | { | ||
962 | struct sk_buff *skb; | ||
963 | |||
964 | if (check_cmdresp_status(fmdev, &skb)) | ||
965 | return; | ||
966 | |||
967 | fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX; | ||
968 | set_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag); | ||
969 | clear_bit(FM_INTTASK_RUNNING, &fmdev->flag); | ||
970 | } | ||
971 | |||
972 | static void fm_irq_afjump_rd_freq(struct fmdev *fmdev) | ||
973 | { | ||
974 | u16 payload; | ||
975 | |||
976 | if (!fm_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, sizeof(payload), NULL)) | ||
977 | fm_irq_timeout_stage(fmdev, FM_AF_JUMP_RD_FREQ_RESP_IDX); | ||
978 | } | ||
979 | |||
980 | static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev) | ||
981 | { | ||
982 | struct sk_buff *skb; | ||
983 | u16 read_freq; | ||
984 | u32 curr_freq, jumped_freq; | ||
985 | |||
986 | if (check_cmdresp_status(fmdev, &skb)) | ||
987 | return; | ||
988 | |||
989 | /* Skip header info and copy only response data */ | ||
990 | skb_pull(skb, sizeof(struct fm_event_msg_hdr)); | ||
991 | memcpy(&read_freq, skb->data, sizeof(read_freq)); | ||
992 | read_freq = be16_to_cpu(read_freq); | ||
993 | curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL); | ||
994 | |||
995 | jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]; | ||
996 | |||
997 | /* If the frequency was changed the jump succeeded */ | ||
998 | if ((curr_freq != fmdev->rx.freq_before_jump) && (curr_freq == jumped_freq)) { | ||
999 | fmdbg("Successfully switched to alternate freq %d\n", curr_freq); | ||
1000 | fmdev->rx.freq = curr_freq; | ||
1001 | fm_rx_reset_rds_cache(fmdev); | ||
1002 | |||
1003 | /* AF feature is on, enable low level RSSI interrupt */ | ||
1004 | if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) | ||
1005 | fmdev->irq_info.mask |= FM_LEV_EVENT; | ||
1006 | |||
1007 | fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX; | ||
1008 | } else { /* jump to the next freq in the AF list */ | ||
1009 | fmdev->rx.afjump_idx++; | ||
1010 | |||
1011 | /* If we reached the end of the list - stop searching */ | ||
1012 | if (fmdev->rx.afjump_idx >= fmdev->rx.stat_info.afcache_size) { | ||
1013 | fmdbg("AF switch processing failed\n"); | ||
1014 | fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX; | ||
1015 | } else { /* AF List is not over - try next one */ | ||
1016 | |||
1017 | fmdbg("Trying next freq in AF cache\n"); | ||
1018 | fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX; | ||
1019 | } | ||
1020 | } | ||
1021 | fm_irq_call(fmdev); | ||
1022 | } | ||
1023 | |||
1024 | static void fm_irq_handle_low_rssi_finish(struct fmdev *fmdev) | ||
1025 | { | ||
1026 | fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX); | ||
1027 | } | ||
1028 | |||
1029 | static void fm_irq_send_intmsk_cmd(struct fmdev *fmdev) | ||
1030 | { | ||
1031 | u16 payload; | ||
1032 | |||
1033 | /* Re-enable FM interrupts */ | ||
1034 | payload = fmdev->irq_info.mask; | ||
1035 | |||
1036 | if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
1037 | sizeof(payload), NULL)) | ||
1038 | fm_irq_timeout_stage(fmdev, FM_HANDLE_INTMSK_CMD_RESP_IDX); | ||
1039 | } | ||
1040 | |||
1041 | static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev) | ||
1042 | { | ||
1043 | struct sk_buff *skb; | ||
1044 | |||
1045 | if (check_cmdresp_status(fmdev, &skb)) | ||
1046 | return; | ||
1047 | /* | ||
1048 | * This is last function in interrupt table to be executed. | ||
1049 | * So, reset stage index to 0. | ||
1050 | */ | ||
1051 | fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX; | ||
1052 | |||
1053 | /* Start processing any pending interrupt */ | ||
1054 | if (test_and_clear_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag)) | ||
1055 | fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev); | ||
1056 | else | ||
1057 | clear_bit(FM_INTTASK_RUNNING, &fmdev->flag); | ||
1058 | } | ||
1059 | |||
1060 | /* Returns availability of RDS data in internel buffer */ | ||
1061 | u32 fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file, | ||
1062 | struct poll_table_struct *pts) | ||
1063 | { | ||
1064 | poll_wait(file, &fmdev->rx.rds.read_queue, pts); | ||
1065 | if (fmdev->rx.rds.rd_idx != fmdev->rx.rds.wr_idx) | ||
1066 | return 0; | ||
1067 | |||
1068 | return -EAGAIN; | ||
1069 | } | ||
1070 | |||
1071 | /* Copies RDS data from internal buffer to user buffer */ | ||
1072 | u32 fmc_transfer_rds_from_internal_buff(struct fmdev *fmdev, struct file *file, | ||
1073 | u8 __user *buf, size_t count) | ||
1074 | { | ||
1075 | u32 block_count; | ||
1076 | unsigned long flags; | ||
1077 | int ret; | ||
1078 | |||
1079 | if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) { | ||
1080 | if (file->f_flags & O_NONBLOCK) | ||
1081 | return -EWOULDBLOCK; | ||
1082 | |||
1083 | ret = wait_event_interruptible(fmdev->rx.rds.read_queue, | ||
1084 | (fmdev->rx.rds.wr_idx != fmdev->rx.rds.rd_idx)); | ||
1085 | if (ret) | ||
1086 | return -EINTR; | ||
1087 | } | ||
1088 | |||
1089 | /* Calculate block count from byte count */ | ||
1090 | count /= 3; | ||
1091 | block_count = 0; | ||
1092 | ret = 0; | ||
1093 | |||
1094 | spin_lock_irqsave(&fmdev->rds_buff_lock, flags); | ||
1095 | |||
1096 | while (block_count < count) { | ||
1097 | if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) | ||
1098 | break; | ||
1099 | |||
1100 | if (copy_to_user(buf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx], | ||
1101 | FM_RDS_BLK_SIZE)) | ||
1102 | break; | ||
1103 | |||
1104 | fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE; | ||
1105 | if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size) | ||
1106 | fmdev->rx.rds.rd_idx = 0; | ||
1107 | |||
1108 | block_count++; | ||
1109 | buf += FM_RDS_BLK_SIZE; | ||
1110 | ret += FM_RDS_BLK_SIZE; | ||
1111 | } | ||
1112 | spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); | ||
1113 | return ret; | ||
1114 | } | ||
1115 | |||
1116 | u32 fmc_set_freq(struct fmdev *fmdev, u32 freq_to_set) | ||
1117 | { | ||
1118 | switch (fmdev->curr_fmmode) { | ||
1119 | case FM_MODE_RX: | ||
1120 | return fm_rx_set_freq(fmdev, freq_to_set); | ||
1121 | |||
1122 | case FM_MODE_TX: | ||
1123 | return fm_tx_set_freq(fmdev, freq_to_set); | ||
1124 | |||
1125 | default: | ||
1126 | return -EINVAL; | ||
1127 | } | ||
1128 | } | ||
1129 | |||
1130 | u32 fmc_get_freq(struct fmdev *fmdev, u32 *cur_tuned_frq) | ||
1131 | { | ||
1132 | if (fmdev->rx.freq == FM_UNDEFINED_FREQ) { | ||
1133 | fmerr("RX frequency is not set\n"); | ||
1134 | return -EPERM; | ||
1135 | } | ||
1136 | if (cur_tuned_frq == NULL) { | ||
1137 | fmerr("Invalid memory\n"); | ||
1138 | return -ENOMEM; | ||
1139 | } | ||
1140 | |||
1141 | switch (fmdev->curr_fmmode) { | ||
1142 | case FM_MODE_RX: | ||
1143 | *cur_tuned_frq = fmdev->rx.freq; | ||
1144 | return 0; | ||
1145 | |||
1146 | case FM_MODE_TX: | ||
1147 | *cur_tuned_frq = 0; /* TODO : Change this later */ | ||
1148 | return 0; | ||
1149 | |||
1150 | default: | ||
1151 | return -EINVAL; | ||
1152 | } | ||
1153 | |||
1154 | } | ||
1155 | |||
1156 | u32 fmc_set_region(struct fmdev *fmdev, u8 region_to_set) | ||
1157 | { | ||
1158 | switch (fmdev->curr_fmmode) { | ||
1159 | case FM_MODE_RX: | ||
1160 | return fm_rx_set_region(fmdev, region_to_set); | ||
1161 | |||
1162 | case FM_MODE_TX: | ||
1163 | return fm_tx_set_region(fmdev, region_to_set); | ||
1164 | |||
1165 | default: | ||
1166 | return -EINVAL; | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | u32 fmc_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) | ||
1171 | { | ||
1172 | switch (fmdev->curr_fmmode) { | ||
1173 | case FM_MODE_RX: | ||
1174 | return fm_rx_set_mute_mode(fmdev, mute_mode_toset); | ||
1175 | |||
1176 | case FM_MODE_TX: | ||
1177 | return fm_tx_set_mute_mode(fmdev, mute_mode_toset); | ||
1178 | |||
1179 | default: | ||
1180 | return -EINVAL; | ||
1181 | } | ||
1182 | } | ||
1183 | |||
1184 | u32 fmc_set_stereo_mono(struct fmdev *fmdev, u16 mode) | ||
1185 | { | ||
1186 | switch (fmdev->curr_fmmode) { | ||
1187 | case FM_MODE_RX: | ||
1188 | return fm_rx_set_stereo_mono(fmdev, mode); | ||
1189 | |||
1190 | case FM_MODE_TX: | ||
1191 | return fm_tx_set_stereo_mono(fmdev, mode); | ||
1192 | |||
1193 | default: | ||
1194 | return -EINVAL; | ||
1195 | } | ||
1196 | } | ||
1197 | |||
1198 | u32 fmc_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) | ||
1199 | { | ||
1200 | switch (fmdev->curr_fmmode) { | ||
1201 | case FM_MODE_RX: | ||
1202 | return fm_rx_set_rds_mode(fmdev, rds_en_dis); | ||
1203 | |||
1204 | case FM_MODE_TX: | ||
1205 | return fm_tx_set_rds_mode(fmdev, rds_en_dis); | ||
1206 | |||
1207 | default: | ||
1208 | return -EINVAL; | ||
1209 | } | ||
1210 | } | ||
1211 | |||
1212 | /* Sends power off command to the chip */ | ||
1213 | static u32 fm_power_down(struct fmdev *fmdev) | ||
1214 | { | ||
1215 | u16 payload; | ||
1216 | u32 ret; | ||
1217 | |||
1218 | if (!test_bit(FM_CORE_READY, &fmdev->flag)) { | ||
1219 | fmerr("FM core is not ready\n"); | ||
1220 | return -EPERM; | ||
1221 | } | ||
1222 | if (fmdev->curr_fmmode == FM_MODE_OFF) { | ||
1223 | fmdbg("FM chip is already in OFF state\n"); | ||
1224 | return 0; | ||
1225 | } | ||
1226 | |||
1227 | payload = 0x0; | ||
1228 | ret = fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload, | ||
1229 | sizeof(payload), NULL, NULL); | ||
1230 | if (ret < 0) | ||
1231 | return ret; | ||
1232 | |||
1233 | return fmc_release(fmdev); | ||
1234 | } | ||
1235 | |||
1236 | /* Reads init command from FM firmware file and loads to the chip */ | ||
1237 | static u32 fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name) | ||
1238 | { | ||
1239 | const struct firmware *fw_entry; | ||
1240 | struct bts_header *fw_header; | ||
1241 | struct bts_action *action; | ||
1242 | struct bts_action_delay *delay; | ||
1243 | u8 *fw_data; | ||
1244 | int ret, fw_len, cmd_cnt; | ||
1245 | |||
1246 | cmd_cnt = 0; | ||
1247 | set_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); | ||
1248 | |||
1249 | ret = request_firmware(&fw_entry, fw_name, | ||
1250 | &fmdev->radio_dev->dev); | ||
1251 | if (ret < 0) { | ||
1252 | fmerr("Unable to read firmware(%s) content\n", fw_name); | ||
1253 | return ret; | ||
1254 | } | ||
1255 | fmdbg("Firmware(%s) length : %d bytes\n", fw_name, fw_entry->size); | ||
1256 | |||
1257 | fw_data = (void *)fw_entry->data; | ||
1258 | fw_len = fw_entry->size; | ||
1259 | |||
1260 | fw_header = (struct bts_header *)fw_data; | ||
1261 | if (fw_header->magic != FM_FW_FILE_HEADER_MAGIC) { | ||
1262 | fmerr("%s not a legal TI firmware file\n", fw_name); | ||
1263 | ret = -EINVAL; | ||
1264 | goto rel_fw; | ||
1265 | } | ||
1266 | fmdbg("FW(%s) magic number : 0x%x\n", fw_name, fw_header->magic); | ||
1267 | |||
1268 | /* Skip file header info , we already verified it */ | ||
1269 | fw_data += sizeof(struct bts_header); | ||
1270 | fw_len -= sizeof(struct bts_header); | ||
1271 | |||
1272 | while (fw_data && fw_len > 0) { | ||
1273 | action = (struct bts_action *)fw_data; | ||
1274 | |||
1275 | switch (action->type) { | ||
1276 | case ACTION_SEND_COMMAND: /* Send */ | ||
1277 | if (fmc_send_cmd(fmdev, 0, 0, action->data, | ||
1278 | action->size, NULL, NULL)) | ||
1279 | goto rel_fw; | ||
1280 | |||
1281 | cmd_cnt++; | ||
1282 | break; | ||
1283 | |||
1284 | case ACTION_DELAY: /* Delay */ | ||
1285 | delay = (struct bts_action_delay *)action->data; | ||
1286 | mdelay(delay->msec); | ||
1287 | break; | ||
1288 | } | ||
1289 | |||
1290 | fw_data += (sizeof(struct bts_action) + (action->size)); | ||
1291 | fw_len -= (sizeof(struct bts_action) + (action->size)); | ||
1292 | } | ||
1293 | fmdbg("Firmware commands(%d) loaded to chip\n", cmd_cnt); | ||
1294 | rel_fw: | ||
1295 | release_firmware(fw_entry); | ||
1296 | clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); | ||
1297 | |||
1298 | return ret; | ||
1299 | } | ||
1300 | |||
1301 | /* Loads default RX configuration to the chip */ | ||
1302 | static u32 load_default_rx_configuration(struct fmdev *fmdev) | ||
1303 | { | ||
1304 | int ret; | ||
1305 | |||
1306 | ret = fm_rx_set_volume(fmdev, FM_DEFAULT_RX_VOLUME); | ||
1307 | if (ret < 0) | ||
1308 | return ret; | ||
1309 | |||
1310 | return fm_rx_set_rssi_threshold(fmdev, FM_DEFAULT_RSSI_THRESHOLD); | ||
1311 | } | ||
1312 | |||
1313 | /* Does FM power on sequence */ | ||
1314 | static u32 fm_power_up(struct fmdev *fmdev, u8 mode) | ||
1315 | { | ||
1316 | u16 payload, asic_id, asic_ver; | ||
1317 | int resp_len, ret; | ||
1318 | u8 fw_name[50]; | ||
1319 | |||
1320 | if (mode >= FM_MODE_ENTRY_MAX) { | ||
1321 | fmerr("Invalid firmware download option\n"); | ||
1322 | return -EINVAL; | ||
1323 | } | ||
1324 | |||
1325 | /* | ||
1326 | * Initialize FM common module. FM GPIO toggling is | ||
1327 | * taken care in Shared Transport driver. | ||
1328 | */ | ||
1329 | ret = fmc_prepare(fmdev); | ||
1330 | if (ret < 0) { | ||
1331 | fmerr("Unable to prepare FM Common\n"); | ||
1332 | return ret; | ||
1333 | } | ||
1334 | |||
1335 | payload = FM_ENABLE; | ||
1336 | if (fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload, | ||
1337 | sizeof(payload), NULL, NULL)) | ||
1338 | goto rel; | ||
1339 | |||
1340 | /* Allow the chip to settle down in Channel-8 mode */ | ||
1341 | msleep(20); | ||
1342 | |||
1343 | if (fmc_send_cmd(fmdev, ASIC_ID_GET, REG_RD, NULL, | ||
1344 | sizeof(asic_id), &asic_id, &resp_len)) | ||
1345 | goto rel; | ||
1346 | |||
1347 | if (fmc_send_cmd(fmdev, ASIC_VER_GET, REG_RD, NULL, | ||
1348 | sizeof(asic_ver), &asic_ver, &resp_len)) | ||
1349 | goto rel; | ||
1350 | |||
1351 | fmdbg("ASIC ID: 0x%x , ASIC Version: %d\n", | ||
1352 | be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); | ||
1353 | |||
1354 | sprintf(fw_name, "%s_%x.%d.bts", FM_FMC_FW_FILE_START, | ||
1355 | be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); | ||
1356 | |||
1357 | ret = fm_download_firmware(fmdev, fw_name); | ||
1358 | if (ret < 0) { | ||
1359 | fmdbg("Failed to download firmware file %s\n", fw_name); | ||
1360 | goto rel; | ||
1361 | } | ||
1362 | sprintf(fw_name, "%s_%x.%d.bts", (mode == FM_MODE_RX) ? | ||
1363 | FM_RX_FW_FILE_START : FM_TX_FW_FILE_START, | ||
1364 | be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); | ||
1365 | |||
1366 | ret = fm_download_firmware(fmdev, fw_name); | ||
1367 | if (ret < 0) { | ||
1368 | fmdbg("Failed to download firmware file %s\n", fw_name); | ||
1369 | goto rel; | ||
1370 | } else | ||
1371 | return ret; | ||
1372 | rel: | ||
1373 | return fmc_release(fmdev); | ||
1374 | } | ||
1375 | |||
1376 | /* Set FM Modes(TX, RX, OFF) */ | ||
1377 | u32 fmc_set_mode(struct fmdev *fmdev, u8 fm_mode) | ||
1378 | { | ||
1379 | int ret = 0; | ||
1380 | |||
1381 | if (fm_mode >= FM_MODE_ENTRY_MAX) { | ||
1382 | fmerr("Invalid FM mode\n"); | ||
1383 | return -EINVAL; | ||
1384 | } | ||
1385 | if (fmdev->curr_fmmode == fm_mode) { | ||
1386 | fmdbg("Already fm is in mode(%d)\n", fm_mode); | ||
1387 | return ret; | ||
1388 | } | ||
1389 | |||
1390 | switch (fm_mode) { | ||
1391 | case FM_MODE_OFF: /* OFF Mode */ | ||
1392 | ret = fm_power_down(fmdev); | ||
1393 | if (ret < 0) { | ||
1394 | fmerr("Failed to set OFF mode\n"); | ||
1395 | return ret; | ||
1396 | } | ||
1397 | break; | ||
1398 | |||
1399 | case FM_MODE_TX: /* TX Mode */ | ||
1400 | case FM_MODE_RX: /* RX Mode */ | ||
1401 | /* Power down before switching to TX or RX mode */ | ||
1402 | if (fmdev->curr_fmmode != FM_MODE_OFF) { | ||
1403 | ret = fm_power_down(fmdev); | ||
1404 | if (ret < 0) { | ||
1405 | fmerr("Failed to set OFF mode\n"); | ||
1406 | return ret; | ||
1407 | } | ||
1408 | msleep(30); | ||
1409 | } | ||
1410 | ret = fm_power_up(fmdev, fm_mode); | ||
1411 | if (ret < 0) { | ||
1412 | fmerr("Failed to load firmware\n"); | ||
1413 | return ret; | ||
1414 | } | ||
1415 | } | ||
1416 | fmdev->curr_fmmode = fm_mode; | ||
1417 | |||
1418 | /* Set default configuration */ | ||
1419 | if (fmdev->curr_fmmode == FM_MODE_RX) { | ||
1420 | fmdbg("Loading default rx configuration..\n"); | ||
1421 | ret = load_default_rx_configuration(fmdev); | ||
1422 | if (ret < 0) | ||
1423 | fmerr("Failed to load default values\n"); | ||
1424 | } | ||
1425 | |||
1426 | return ret; | ||
1427 | } | ||
1428 | |||
1429 | /* Returns current FM mode (TX, RX, OFF) */ | ||
1430 | u32 fmc_get_mode(struct fmdev *fmdev, u8 *fmmode) | ||
1431 | { | ||
1432 | if (!test_bit(FM_CORE_READY, &fmdev->flag)) { | ||
1433 | fmerr("FM core is not ready\n"); | ||
1434 | return -EPERM; | ||
1435 | } | ||
1436 | if (fmmode == NULL) { | ||
1437 | fmerr("Invalid memory\n"); | ||
1438 | return -ENOMEM; | ||
1439 | } | ||
1440 | |||
1441 | *fmmode = fmdev->curr_fmmode; | ||
1442 | return 0; | ||
1443 | } | ||
1444 | |||
1445 | /* Called by ST layer when FM packet is available */ | ||
1446 | static long fm_st_receive(void *arg, struct sk_buff *skb) | ||
1447 | { | ||
1448 | struct fmdev *fmdev; | ||
1449 | |||
1450 | fmdev = (struct fmdev *)arg; | ||
1451 | |||
1452 | if (skb == NULL) { | ||
1453 | fmerr("Invalid SKB received from ST\n"); | ||
1454 | return -EFAULT; | ||
1455 | } | ||
1456 | |||
1457 | if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) { | ||
1458 | fmerr("Received SKB (%p) is not FM Channel 8 pkt\n", skb); | ||
1459 | return -EINVAL; | ||
1460 | } | ||
1461 | |||
1462 | memcpy(skb_push(skb, 1), &skb->cb[0], 1); | ||
1463 | skb_queue_tail(&fmdev->rx_q, skb); | ||
1464 | tasklet_schedule(&fmdev->rx_task); | ||
1465 | |||
1466 | return 0; | ||
1467 | } | ||
1468 | |||
1469 | /* | ||
1470 | * Called by ST layer to indicate protocol registration completion | ||
1471 | * status. | ||
1472 | */ | ||
1473 | static void fm_st_reg_comp_cb(void *arg, char data) | ||
1474 | { | ||
1475 | struct fmdev *fmdev; | ||
1476 | |||
1477 | fmdev = (struct fmdev *)arg; | ||
1478 | fmdev->streg_cbdata = data; | ||
1479 | complete(&wait_for_fmdrv_reg_comp); | ||
1480 | } | ||
1481 | |||
1482 | /* | ||
1483 | * This function will be called from FM V4L2 open function. | ||
1484 | * Register with ST driver and initialize driver data. | ||
1485 | */ | ||
1486 | u32 fmc_prepare(struct fmdev *fmdev) | ||
1487 | { | ||
1488 | static struct st_proto_s fm_st_proto; | ||
1489 | u32 ret; | ||
1490 | |||
1491 | if (test_bit(FM_CORE_READY, &fmdev->flag)) { | ||
1492 | fmdbg("FM Core is already up\n"); | ||
1493 | return 0; | ||
1494 | } | ||
1495 | |||
1496 | memset(&fm_st_proto, 0, sizeof(fm_st_proto)); | ||
1497 | fm_st_proto.type = ST_FM; | ||
1498 | fm_st_proto.recv = fm_st_receive; | ||
1499 | fm_st_proto.match_packet = NULL; | ||
1500 | fm_st_proto.reg_complete_cb = fm_st_reg_comp_cb; | ||
1501 | fm_st_proto.write = NULL; /* TI ST driver will fill write pointer */ | ||
1502 | fm_st_proto.priv_data = fmdev; | ||
1503 | |||
1504 | ret = st_register(&fm_st_proto); | ||
1505 | if (ret == -EINPROGRESS) { | ||
1506 | init_completion(&wait_for_fmdrv_reg_comp); | ||
1507 | fmdev->streg_cbdata = -EINPROGRESS; | ||
1508 | fmdbg("%s waiting for ST reg completion signal\n", __func__); | ||
1509 | |||
1510 | ret = wait_for_completion_timeout(&wait_for_fmdrv_reg_comp, | ||
1511 | FM_ST_REG_TIMEOUT); | ||
1512 | |||
1513 | if (!ret) { | ||
1514 | fmerr("Timeout(%d sec), didn't get reg " | ||
1515 | "completion signal from ST\n", | ||
1516 | jiffies_to_msecs(FM_ST_REG_TIMEOUT) / 1000); | ||
1517 | return -ETIMEDOUT; | ||
1518 | } | ||
1519 | if (fmdev->streg_cbdata != 0) { | ||
1520 | fmerr("ST reg comp CB called with error " | ||
1521 | "status %d\n", fmdev->streg_cbdata); | ||
1522 | return -EAGAIN; | ||
1523 | } | ||
1524 | |||
1525 | ret = 0; | ||
1526 | } else if (ret == -1) { | ||
1527 | fmerr("st_register failed %d\n", ret); | ||
1528 | return -EAGAIN; | ||
1529 | } | ||
1530 | |||
1531 | if (fm_st_proto.write != NULL) { | ||
1532 | g_st_write = fm_st_proto.write; | ||
1533 | } else { | ||
1534 | fmerr("Failed to get ST write func pointer\n"); | ||
1535 | ret = st_unregister(ST_FM); | ||
1536 | if (ret < 0) | ||
1537 | fmerr("st_unregister failed %d\n", ret); | ||
1538 | return -EAGAIN; | ||
1539 | } | ||
1540 | |||
1541 | spin_lock_init(&fmdev->rds_buff_lock); | ||
1542 | spin_lock_init(&fmdev->resp_skb_lock); | ||
1543 | |||
1544 | /* Initialize TX queue and TX tasklet */ | ||
1545 | skb_queue_head_init(&fmdev->tx_q); | ||
1546 | tasklet_init(&fmdev->tx_task, send_tasklet, (unsigned long)fmdev); | ||
1547 | |||
1548 | /* Initialize RX Queue and RX tasklet */ | ||
1549 | skb_queue_head_init(&fmdev->rx_q); | ||
1550 | tasklet_init(&fmdev->rx_task, recv_tasklet, (unsigned long)fmdev); | ||
1551 | |||
1552 | fmdev->irq_info.stage = 0; | ||
1553 | atomic_set(&fmdev->tx_cnt, 1); | ||
1554 | fmdev->resp_comp = NULL; | ||
1555 | |||
1556 | init_timer(&fmdev->irq_info.timer); | ||
1557 | fmdev->irq_info.timer.function = &int_timeout_handler; | ||
1558 | fmdev->irq_info.timer.data = (unsigned long)fmdev; | ||
1559 | /*TODO: add FM_STIC_EVENT later */ | ||
1560 | fmdev->irq_info.mask = FM_MAL_EVENT; | ||
1561 | |||
1562 | /* Region info */ | ||
1563 | memcpy(&fmdev->rx.region, ®ion_configs[default_radio_region], | ||
1564 | sizeof(struct region_info)); | ||
1565 | |||
1566 | fmdev->rx.mute_mode = FM_MUTE_OFF; | ||
1567 | fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; | ||
1568 | fmdev->rx.rds.flag = FM_RDS_DISABLE; | ||
1569 | fmdev->rx.freq = FM_UNDEFINED_FREQ; | ||
1570 | fmdev->rx.rds_mode = FM_RDS_SYSTEM_RDS; | ||
1571 | fmdev->rx.af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF; | ||
1572 | fmdev->irq_info.retry = 0; | ||
1573 | |||
1574 | fm_rx_reset_rds_cache(fmdev); | ||
1575 | init_waitqueue_head(&fmdev->rx.rds.read_queue); | ||
1576 | |||
1577 | fm_rx_reset_station_info(fmdev); | ||
1578 | set_bit(FM_CORE_READY, &fmdev->flag); | ||
1579 | |||
1580 | return ret; | ||
1581 | } | ||
1582 | |||
1583 | /* | ||
1584 | * This function will be called from FM V4L2 release function. | ||
1585 | * Unregister from ST driver. | ||
1586 | */ | ||
1587 | u32 fmc_release(struct fmdev *fmdev) | ||
1588 | { | ||
1589 | u32 ret; | ||
1590 | |||
1591 | if (!test_bit(FM_CORE_READY, &fmdev->flag)) { | ||
1592 | fmdbg("FM Core is already down\n"); | ||
1593 | return 0; | ||
1594 | } | ||
1595 | /* Sevice pending read */ | ||
1596 | wake_up_interruptible(&fmdev->rx.rds.read_queue); | ||
1597 | |||
1598 | tasklet_kill(&fmdev->tx_task); | ||
1599 | tasklet_kill(&fmdev->rx_task); | ||
1600 | |||
1601 | skb_queue_purge(&fmdev->tx_q); | ||
1602 | skb_queue_purge(&fmdev->rx_q); | ||
1603 | |||
1604 | fmdev->resp_comp = NULL; | ||
1605 | fmdev->rx.freq = 0; | ||
1606 | |||
1607 | ret = st_unregister(ST_FM); | ||
1608 | if (ret < 0) | ||
1609 | fmerr("Failed to de-register FM from ST %d\n", ret); | ||
1610 | else | ||
1611 | fmdbg("Successfully unregistered from ST\n"); | ||
1612 | |||
1613 | clear_bit(FM_CORE_READY, &fmdev->flag); | ||
1614 | return ret; | ||
1615 | } | ||
1616 | |||
1617 | /* | ||
1618 | * Module init function. Ask FM V4L module to register video device. | ||
1619 | * Allocate memory for FM driver context and RX RDS buffer. | ||
1620 | */ | ||
1621 | static int __init fm_drv_init(void) | ||
1622 | { | ||
1623 | struct fmdev *fmdev = NULL; | ||
1624 | u32 ret = -ENOMEM; | ||
1625 | |||
1626 | fmdbg("FM driver version %s\n", FM_DRV_VERSION); | ||
1627 | |||
1628 | fmdev = kzalloc(sizeof(struct fmdev), GFP_KERNEL); | ||
1629 | if (NULL == fmdev) { | ||
1630 | fmerr("Can't allocate operation structure memory\n"); | ||
1631 | return ret; | ||
1632 | } | ||
1633 | fmdev->rx.rds.buf_size = default_rds_buf * FM_RDS_BLK_SIZE; | ||
1634 | fmdev->rx.rds.buff = kzalloc(fmdev->rx.rds.buf_size, GFP_KERNEL); | ||
1635 | if (NULL == fmdev->rx.rds.buff) { | ||
1636 | fmerr("Can't allocate rds ring buffer\n"); | ||
1637 | goto rel_dev; | ||
1638 | } | ||
1639 | |||
1640 | ret = fm_v4l2_init_video_device(fmdev, radio_nr); | ||
1641 | if (ret < 0) | ||
1642 | goto rel_rdsbuf; | ||
1643 | |||
1644 | fmdev->irq_info.handlers = int_handler_table; | ||
1645 | fmdev->curr_fmmode = FM_MODE_OFF; | ||
1646 | fmdev->tx_data.pwr_lvl = FM_PWR_LVL_DEF; | ||
1647 | fmdev->tx_data.preemph = FM_TX_PREEMPH_50US; | ||
1648 | return ret; | ||
1649 | |||
1650 | rel_rdsbuf: | ||
1651 | kfree(fmdev->rx.rds.buff); | ||
1652 | rel_dev: | ||
1653 | kfree(fmdev); | ||
1654 | |||
1655 | return ret; | ||
1656 | } | ||
1657 | |||
1658 | /* Module exit function. Ask FM V4L module to unregister video device */ | ||
1659 | static void __exit fm_drv_exit(void) | ||
1660 | { | ||
1661 | struct fmdev *fmdev = NULL; | ||
1662 | |||
1663 | fmdev = fm_v4l2_deinit_video_device(); | ||
1664 | if (fmdev != NULL) { | ||
1665 | kfree(fmdev->rx.rds.buff); | ||
1666 | kfree(fmdev); | ||
1667 | } | ||
1668 | } | ||
1669 | |||
1670 | module_init(fm_drv_init); | ||
1671 | module_exit(fm_drv_exit); | ||
1672 | |||
1673 | /* ------------- Module Info ------------- */ | ||
1674 | MODULE_AUTHOR("Manjunatha Halli <manjunatha_halli@ti.com>"); | ||
1675 | MODULE_DESCRIPTION("FM Driver for TI's Connectivity chip. " FM_DRV_VERSION); | ||
1676 | MODULE_VERSION(FM_DRV_VERSION); | ||
1677 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/radio/wl128x/fmdrv_common.h b/drivers/media/radio/wl128x/fmdrv_common.h new file mode 100644 index 000000000000..427c4164cece --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_common.h | |||
@@ -0,0 +1,402 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * FM Common module header file | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _FMDRV_COMMON_H | ||
23 | #define _FMDRV_COMMON_H | ||
24 | |||
25 | #define FM_ST_REG_TIMEOUT msecs_to_jiffies(6000) /* 6 sec */ | ||
26 | #define FM_PKT_LOGICAL_CHAN_NUMBER 0x08 /* Logical channel 8 */ | ||
27 | |||
28 | #define REG_RD 0x1 | ||
29 | #define REG_WR 0x0 | ||
30 | |||
31 | struct fm_reg_table { | ||
32 | u8 opcode; | ||
33 | u8 type; | ||
34 | u8 *name; | ||
35 | }; | ||
36 | |||
37 | #define STEREO_GET 0 | ||
38 | #define RSSI_LVL_GET 1 | ||
39 | #define IF_COUNT_GET 2 | ||
40 | #define FLAG_GET 3 | ||
41 | #define RDS_SYNC_GET 4 | ||
42 | #define RDS_DATA_GET 5 | ||
43 | #define FREQ_SET 10 | ||
44 | #define AF_FREQ_SET 11 | ||
45 | #define MOST_MODE_SET 12 | ||
46 | #define MOST_BLEND_SET 13 | ||
47 | #define DEMPH_MODE_SET 14 | ||
48 | #define SEARCH_LVL_SET 15 | ||
49 | #define BAND_SET 16 | ||
50 | #define MUTE_STATUS_SET 17 | ||
51 | #define RDS_PAUSE_LVL_SET 18 | ||
52 | #define RDS_PAUSE_DUR_SET 19 | ||
53 | #define RDS_MEM_SET 20 | ||
54 | #define RDS_BLK_B_SET 21 | ||
55 | #define RDS_MSK_B_SET 22 | ||
56 | #define RDS_PI_MASK_SET 23 | ||
57 | #define RDS_PI_SET 24 | ||
58 | #define RDS_SYSTEM_SET 25 | ||
59 | #define INT_MASK_SET 26 | ||
60 | #define SEARCH_DIR_SET 27 | ||
61 | #define VOLUME_SET 28 | ||
62 | #define AUDIO_ENABLE_SET 29 | ||
63 | #define PCM_MODE_SET 30 | ||
64 | #define I2S_MODE_CONFIG_SET 31 | ||
65 | #define POWER_SET 32 | ||
66 | #define INTX_CONFIG_SET 33 | ||
67 | #define PULL_EN_SET 34 | ||
68 | #define HILO_SET 35 | ||
69 | #define SWITCH2FREF 36 | ||
70 | #define FREQ_DRIFT_REPORT 37 | ||
71 | |||
72 | #define PCE_GET 40 | ||
73 | #define FIRM_VER_GET 41 | ||
74 | #define ASIC_VER_GET 42 | ||
75 | #define ASIC_ID_GET 43 | ||
76 | #define MAN_ID_GET 44 | ||
77 | #define TUNER_MODE_SET 45 | ||
78 | #define STOP_SEARCH 46 | ||
79 | #define RDS_CNTRL_SET 47 | ||
80 | |||
81 | #define WRITE_HARDWARE_REG 100 | ||
82 | #define CODE_DOWNLOAD 101 | ||
83 | #define RESET 102 | ||
84 | |||
85 | #define FM_POWER_MODE 254 | ||
86 | #define FM_INTERRUPT 255 | ||
87 | |||
88 | /* Transmitter API */ | ||
89 | |||
90 | #define CHANL_SET 55 | ||
91 | #define CHANL_BW_SET 56 | ||
92 | #define REF_SET 57 | ||
93 | #define POWER_ENB_SET 90 | ||
94 | #define POWER_ATT_SET 58 | ||
95 | #define POWER_LEV_SET 59 | ||
96 | #define AUDIO_DEV_SET 60 | ||
97 | #define PILOT_DEV_SET 61 | ||
98 | #define RDS_DEV_SET 62 | ||
99 | #define TX_BAND_SET 65 | ||
100 | #define PUPD_SET 91 | ||
101 | #define AUDIO_IO_SET 63 | ||
102 | #define PREMPH_SET 64 | ||
103 | #define MONO_SET 66 | ||
104 | #define MUTE 92 | ||
105 | #define MPX_LMT_ENABLE 67 | ||
106 | #define PI_SET 93 | ||
107 | #define ECC_SET 69 | ||
108 | #define PTY 70 | ||
109 | #define AF 71 | ||
110 | #define DISPLAY_MODE 74 | ||
111 | #define RDS_REP_SET 77 | ||
112 | #define RDS_CONFIG_DATA_SET 98 | ||
113 | #define RDS_DATA_SET 99 | ||
114 | #define RDS_DATA_ENB 94 | ||
115 | #define TA_SET 78 | ||
116 | #define TP_SET 79 | ||
117 | #define DI_SET 80 | ||
118 | #define MS_SET 81 | ||
119 | #define PS_SCROLL_SPEED 82 | ||
120 | #define TX_AUDIO_LEVEL_TEST 96 | ||
121 | #define TX_AUDIO_LEVEL_TEST_THRESHOLD 73 | ||
122 | #define TX_AUDIO_INPUT_LEVEL_RANGE_SET 54 | ||
123 | #define RX_ANTENNA_SELECT 87 | ||
124 | #define I2C_DEV_ADDR_SET 86 | ||
125 | #define REF_ERR_CALIB_PARAM_SET 88 | ||
126 | #define REF_ERR_CALIB_PERIODICITY_SET 89 | ||
127 | #define SOC_INT_TRIGGER 52 | ||
128 | #define SOC_AUDIO_PATH_SET 83 | ||
129 | #define SOC_PCMI_OVERRIDE 84 | ||
130 | #define SOC_I2S_OVERRIDE 85 | ||
131 | #define RSSI_BLOCK_SCAN_FREQ_SET 95 | ||
132 | #define RSSI_BLOCK_SCAN_START 97 | ||
133 | #define RSSI_BLOCK_SCAN_DATA_GET 5 | ||
134 | #define READ_FMANT_TUNE_VALUE 104 | ||
135 | |||
136 | /* SKB helpers */ | ||
137 | struct fm_skb_cb { | ||
138 | __u8 fm_op; | ||
139 | struct completion *completion; | ||
140 | }; | ||
141 | |||
142 | #define fm_cb(skb) ((struct fm_skb_cb *)(skb->cb)) | ||
143 | |||
144 | /* FM Channel-8 command message format */ | ||
145 | struct fm_cmd_msg_hdr { | ||
146 | __u8 hdr; /* Logical Channel-8 */ | ||
147 | __u8 len; /* Number of bytes follows */ | ||
148 | __u8 op; /* FM Opcode */ | ||
149 | __u8 rd_wr; /* Read/Write command */ | ||
150 | __u8 dlen; /* Length of payload */ | ||
151 | } __attribute__ ((packed)); | ||
152 | |||
153 | #define FM_CMD_MSG_HDR_SIZE 5 /* sizeof(struct fm_cmd_msg_hdr) */ | ||
154 | |||
155 | /* FM Channel-8 event messgage format */ | ||
156 | struct fm_event_msg_hdr { | ||
157 | __u8 header; /* Logical Channel-8 */ | ||
158 | __u8 len; /* Number of bytes follows */ | ||
159 | __u8 status; /* Event status */ | ||
160 | __u8 num_fm_hci_cmds; /* Number of pkts the host allowed to send */ | ||
161 | __u8 op; /* FM Opcode */ | ||
162 | __u8 rd_wr; /* Read/Write command */ | ||
163 | __u8 dlen; /* Length of payload */ | ||
164 | } __attribute__ ((packed)); | ||
165 | |||
166 | #define FM_EVT_MSG_HDR_SIZE 7 /* sizeof(struct fm_event_msg_hdr) */ | ||
167 | |||
168 | /* TI's magic number in firmware file */ | ||
169 | #define FM_FW_FILE_HEADER_MAGIC 0x42535442 | ||
170 | |||
171 | #define FM_ENABLE 1 | ||
172 | #define FM_DISABLE 0 | ||
173 | |||
174 | /* FLAG_GET register bits */ | ||
175 | #define FM_FR_EVENT (1 << 0) | ||
176 | #define FM_BL_EVENT (1 << 1) | ||
177 | #define FM_RDS_EVENT (1 << 2) | ||
178 | #define FM_BBLK_EVENT (1 << 3) | ||
179 | #define FM_LSYNC_EVENT (1 << 4) | ||
180 | #define FM_LEV_EVENT (1 << 5) | ||
181 | #define FM_IFFR_EVENT (1 << 6) | ||
182 | #define FM_PI_EVENT (1 << 7) | ||
183 | #define FM_PD_EVENT (1 << 8) | ||
184 | #define FM_STIC_EVENT (1 << 9) | ||
185 | #define FM_MAL_EVENT (1 << 10) | ||
186 | #define FM_POW_ENB_EVENT (1 << 11) | ||
187 | |||
188 | /* | ||
189 | * Firmware files of FM. ASIC ID and ASIC version will be appened to this, | ||
190 | * later. | ||
191 | */ | ||
192 | #define FM_FMC_FW_FILE_START ("fmc_ch8") | ||
193 | #define FM_RX_FW_FILE_START ("fm_rx_ch8") | ||
194 | #define FM_TX_FW_FILE_START ("fm_tx_ch8") | ||
195 | |||
196 | #define FM_UNDEFINED_FREQ 0xFFFFFFFF | ||
197 | |||
198 | /* Band types */ | ||
199 | #define FM_BAND_EUROPE_US 0 | ||
200 | #define FM_BAND_JAPAN 1 | ||
201 | |||
202 | /* Seek directions */ | ||
203 | #define FM_SEARCH_DIRECTION_DOWN 0 | ||
204 | #define FM_SEARCH_DIRECTION_UP 1 | ||
205 | |||
206 | /* Tunner modes */ | ||
207 | #define FM_TUNER_STOP_SEARCH_MODE 0 | ||
208 | #define FM_TUNER_PRESET_MODE 1 | ||
209 | #define FM_TUNER_AUTONOMOUS_SEARCH_MODE 2 | ||
210 | #define FM_TUNER_AF_JUMP_MODE 3 | ||
211 | |||
212 | /* Min and Max volume */ | ||
213 | #define FM_RX_VOLUME_MIN 0 | ||
214 | #define FM_RX_VOLUME_MAX 70 | ||
215 | |||
216 | /* Volume gain step */ | ||
217 | #define FM_RX_VOLUME_GAIN_STEP 0x370 | ||
218 | |||
219 | /* Mute modes */ | ||
220 | #define FM_MUTE_ON 0 | ||
221 | #define FM_MUTE_OFF 1 | ||
222 | #define FM_MUTE_ATTENUATE 2 | ||
223 | |||
224 | #define FM_RX_UNMUTE_MODE 0x00 | ||
225 | #define FM_RX_RF_DEP_MODE 0x01 | ||
226 | #define FM_RX_AC_MUTE_MODE 0x02 | ||
227 | #define FM_RX_HARD_MUTE_LEFT_MODE 0x04 | ||
228 | #define FM_RX_HARD_MUTE_RIGHT_MODE 0x08 | ||
229 | #define FM_RX_SOFT_MUTE_FORCE_MODE 0x10 | ||
230 | |||
231 | /* RF dependent mute mode */ | ||
232 | #define FM_RX_RF_DEPENDENT_MUTE_ON 1 | ||
233 | #define FM_RX_RF_DEPENDENT_MUTE_OFF 0 | ||
234 | |||
235 | /* RSSI threshold min and max */ | ||
236 | #define FM_RX_RSSI_THRESHOLD_MIN -128 | ||
237 | #define FM_RX_RSSI_THRESHOLD_MAX 127 | ||
238 | |||
239 | /* Stereo/Mono mode */ | ||
240 | #define FM_STEREO_MODE 0 | ||
241 | #define FM_MONO_MODE 1 | ||
242 | #define FM_STEREO_SOFT_BLEND 1 | ||
243 | |||
244 | /* FM RX De-emphasis filter modes */ | ||
245 | #define FM_RX_EMPHASIS_FILTER_50_USEC 0 | ||
246 | #define FM_RX_EMPHASIS_FILTER_75_USEC 1 | ||
247 | |||
248 | /* FM RDS modes */ | ||
249 | #define FM_RDS_DISABLE 0 | ||
250 | #define FM_RDS_ENABLE 1 | ||
251 | |||
252 | #define FM_NO_PI_CODE 0 | ||
253 | |||
254 | /* FM and RX RDS block enable/disable */ | ||
255 | #define FM_RX_PWR_SET_FM_ON_RDS_OFF 0x1 | ||
256 | #define FM_RX_PWR_SET_FM_AND_RDS_BLK_ON 0x3 | ||
257 | #define FM_RX_PWR_SET_FM_AND_RDS_BLK_OFF 0x0 | ||
258 | |||
259 | /* RX RDS */ | ||
260 | #define FM_RX_RDS_FLUSH_FIFO 0x1 | ||
261 | #define FM_RX_RDS_FIFO_THRESHOLD 64 /* tuples */ | ||
262 | #define FM_RDS_BLK_SIZE 3 /* 3 bytes */ | ||
263 | |||
264 | /* RDS block types */ | ||
265 | #define FM_RDS_BLOCK_A 0 | ||
266 | #define FM_RDS_BLOCK_B 1 | ||
267 | #define FM_RDS_BLOCK_C 2 | ||
268 | #define FM_RDS_BLOCK_Ctag 3 | ||
269 | #define FM_RDS_BLOCK_D 4 | ||
270 | #define FM_RDS_BLOCK_E 5 | ||
271 | |||
272 | #define FM_RDS_BLK_IDX_A 0 | ||
273 | #define FM_RDS_BLK_IDX_B 1 | ||
274 | #define FM_RDS_BLK_IDX_C 2 | ||
275 | #define FM_RDS_BLK_IDX_D 3 | ||
276 | #define FM_RDS_BLK_IDX_UNKNOWN 0xF0 | ||
277 | |||
278 | #define FM_RDS_STATUS_ERR_MASK 0x18 | ||
279 | |||
280 | /* | ||
281 | * Represents an RDS group type & version. | ||
282 | * There are 15 groups, each group has 2 versions: A and B. | ||
283 | */ | ||
284 | #define FM_RDS_GROUP_TYPE_MASK_0A ((unsigned long)1<<0) | ||
285 | #define FM_RDS_GROUP_TYPE_MASK_0B ((unsigned long)1<<1) | ||
286 | #define FM_RDS_GROUP_TYPE_MASK_1A ((unsigned long)1<<2) | ||
287 | #define FM_RDS_GROUP_TYPE_MASK_1B ((unsigned long)1<<3) | ||
288 | #define FM_RDS_GROUP_TYPE_MASK_2A ((unsigned long)1<<4) | ||
289 | #define FM_RDS_GROUP_TYPE_MASK_2B ((unsigned long)1<<5) | ||
290 | #define FM_RDS_GROUP_TYPE_MASK_3A ((unsigned long)1<<6) | ||
291 | #define FM_RDS_GROUP_TYPE_MASK_3B ((unsigned long)1<<7) | ||
292 | #define FM_RDS_GROUP_TYPE_MASK_4A ((unsigned long)1<<8) | ||
293 | #define FM_RDS_GROUP_TYPE_MASK_4B ((unsigned long)1<<9) | ||
294 | #define FM_RDS_GROUP_TYPE_MASK_5A ((unsigned long)1<<10) | ||
295 | #define FM_RDS_GROUP_TYPE_MASK_5B ((unsigned long)1<<11) | ||
296 | #define FM_RDS_GROUP_TYPE_MASK_6A ((unsigned long)1<<12) | ||
297 | #define FM_RDS_GROUP_TYPE_MASK_6B ((unsigned long)1<<13) | ||
298 | #define FM_RDS_GROUP_TYPE_MASK_7A ((unsigned long)1<<14) | ||
299 | #define FM_RDS_GROUP_TYPE_MASK_7B ((unsigned long)1<<15) | ||
300 | #define FM_RDS_GROUP_TYPE_MASK_8A ((unsigned long)1<<16) | ||
301 | #define FM_RDS_GROUP_TYPE_MASK_8B ((unsigned long)1<<17) | ||
302 | #define FM_RDS_GROUP_TYPE_MASK_9A ((unsigned long)1<<18) | ||
303 | #define FM_RDS_GROUP_TYPE_MASK_9B ((unsigned long)1<<19) | ||
304 | #define FM_RDS_GROUP_TYPE_MASK_10A ((unsigned long)1<<20) | ||
305 | #define FM_RDS_GROUP_TYPE_MASK_10B ((unsigned long)1<<21) | ||
306 | #define FM_RDS_GROUP_TYPE_MASK_11A ((unsigned long)1<<22) | ||
307 | #define FM_RDS_GROUP_TYPE_MASK_11B ((unsigned long)1<<23) | ||
308 | #define FM_RDS_GROUP_TYPE_MASK_12A ((unsigned long)1<<24) | ||
309 | #define FM_RDS_GROUP_TYPE_MASK_12B ((unsigned long)1<<25) | ||
310 | #define FM_RDS_GROUP_TYPE_MASK_13A ((unsigned long)1<<26) | ||
311 | #define FM_RDS_GROUP_TYPE_MASK_13B ((unsigned long)1<<27) | ||
312 | #define FM_RDS_GROUP_TYPE_MASK_14A ((unsigned long)1<<28) | ||
313 | #define FM_RDS_GROUP_TYPE_MASK_14B ((unsigned long)1<<29) | ||
314 | #define FM_RDS_GROUP_TYPE_MASK_15A ((unsigned long)1<<30) | ||
315 | #define FM_RDS_GROUP_TYPE_MASK_15B ((unsigned long)1<<31) | ||
316 | |||
317 | /* RX Alternate Frequency info */ | ||
318 | #define FM_RDS_MIN_AF 1 | ||
319 | #define FM_RDS_MAX_AF 204 | ||
320 | #define FM_RDS_MAX_AF_JAPAN 140 | ||
321 | #define FM_RDS_1_AF_FOLLOWS 225 | ||
322 | #define FM_RDS_25_AF_FOLLOWS 249 | ||
323 | |||
324 | /* RDS system type (RDS/RBDS) */ | ||
325 | #define FM_RDS_SYSTEM_RDS 0 | ||
326 | #define FM_RDS_SYSTEM_RBDS 1 | ||
327 | |||
328 | /* AF on/off */ | ||
329 | #define FM_RX_RDS_AF_SWITCH_MODE_ON 1 | ||
330 | #define FM_RX_RDS_AF_SWITCH_MODE_OFF 0 | ||
331 | |||
332 | /* Retry count when interrupt process goes wrong */ | ||
333 | #define FM_IRQ_TIMEOUT_RETRY_MAX 5 /* 5 times */ | ||
334 | |||
335 | /* Audio IO set values */ | ||
336 | #define FM_RX_AUDIO_ENABLE_I2S 0x01 | ||
337 | #define FM_RX_AUDIO_ENABLE_ANALOG 0x02 | ||
338 | #define FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG 0x03 | ||
339 | #define FM_RX_AUDIO_ENABLE_DISABLE 0x00 | ||
340 | |||
341 | /* HI/LO set values */ | ||
342 | #define FM_RX_IFFREQ_TO_HI_SIDE 0x0 | ||
343 | #define FM_RX_IFFREQ_TO_LO_SIDE 0x1 | ||
344 | #define FM_RX_IFFREQ_HILO_AUTOMATIC 0x2 | ||
345 | |||
346 | /* | ||
347 | * Default RX mode configuration. Chip will be configured | ||
348 | * with this default values after loading RX firmware. | ||
349 | */ | ||
350 | #define FM_DEFAULT_RX_VOLUME 10 | ||
351 | #define FM_DEFAULT_RSSI_THRESHOLD 3 | ||
352 | |||
353 | /* Range for TX power level in units for dB/uV */ | ||
354 | #define FM_PWR_LVL_LOW 91 | ||
355 | #define FM_PWR_LVL_HIGH 122 | ||
356 | |||
357 | /* Chip specific default TX power level value */ | ||
358 | #define FM_PWR_LVL_DEF 4 | ||
359 | |||
360 | /* FM TX Pre-emphasis filter values */ | ||
361 | #define FM_TX_PREEMPH_OFF 1 | ||
362 | #define FM_TX_PREEMPH_50US 0 | ||
363 | #define FM_TX_PREEMPH_75US 2 | ||
364 | |||
365 | /* FM TX antenna impedence values */ | ||
366 | #define FM_TX_ANT_IMP_50 0 | ||
367 | #define FM_TX_ANT_IMP_200 1 | ||
368 | #define FM_TX_ANT_IMP_500 2 | ||
369 | |||
370 | /* Functions exported by FM common sub-module */ | ||
371 | u32 fmc_prepare(struct fmdev *); | ||
372 | u32 fmc_release(struct fmdev *); | ||
373 | |||
374 | void fmc_update_region_info(struct fmdev *, u8); | ||
375 | u32 fmc_send_cmd(struct fmdev *, u8, u16, | ||
376 | void *, unsigned int, void *, int *); | ||
377 | u32 fmc_is_rds_data_available(struct fmdev *, struct file *, | ||
378 | struct poll_table_struct *); | ||
379 | u32 fmc_transfer_rds_from_internal_buff(struct fmdev *, struct file *, | ||
380 | u8 __user *, size_t); | ||
381 | |||
382 | u32 fmc_set_freq(struct fmdev *, u32); | ||
383 | u32 fmc_set_mode(struct fmdev *, u8); | ||
384 | u32 fmc_set_region(struct fmdev *, u8); | ||
385 | u32 fmc_set_mute_mode(struct fmdev *, u8); | ||
386 | u32 fmc_set_stereo_mono(struct fmdev *, u16); | ||
387 | u32 fmc_set_rds_mode(struct fmdev *, u8); | ||
388 | |||
389 | u32 fmc_get_freq(struct fmdev *, u32 *); | ||
390 | u32 fmc_get_region(struct fmdev *, u8 *); | ||
391 | u32 fmc_get_mode(struct fmdev *, u8 *); | ||
392 | |||
393 | /* | ||
394 | * channel spacing | ||
395 | */ | ||
396 | #define FM_CHANNEL_SPACING_50KHZ 1 | ||
397 | #define FM_CHANNEL_SPACING_100KHZ 2 | ||
398 | #define FM_CHANNEL_SPACING_200KHZ 4 | ||
399 | #define FM_FREQ_MUL 50 | ||
400 | |||
401 | #endif | ||
402 | |||
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c new file mode 100644 index 000000000000..ec529b55b040 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_rx.c | |||
@@ -0,0 +1,847 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * This sub-module of FM driver implements FM RX functionality. | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * Author: Raja Mani <raja_mani@ti.com> | ||
7 | * Author: Manjunatha Halli <manjunatha_halli@ti.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include "fmdrv.h" | ||
25 | #include "fmdrv_common.h" | ||
26 | #include "fmdrv_rx.h" | ||
27 | |||
28 | void fm_rx_reset_rds_cache(struct fmdev *fmdev) | ||
29 | { | ||
30 | fmdev->rx.rds.flag = FM_RDS_DISABLE; | ||
31 | fmdev->rx.rds.last_blk_idx = 0; | ||
32 | fmdev->rx.rds.wr_idx = 0; | ||
33 | fmdev->rx.rds.rd_idx = 0; | ||
34 | |||
35 | if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) | ||
36 | fmdev->irq_info.mask |= FM_LEV_EVENT; | ||
37 | } | ||
38 | |||
39 | void fm_rx_reset_station_info(struct fmdev *fmdev) | ||
40 | { | ||
41 | fmdev->rx.stat_info.picode = FM_NO_PI_CODE; | ||
42 | fmdev->rx.stat_info.afcache_size = 0; | ||
43 | fmdev->rx.stat_info.af_list_max = 0; | ||
44 | } | ||
45 | |||
46 | u32 fm_rx_set_freq(struct fmdev *fmdev, u32 freq) | ||
47 | { | ||
48 | unsigned long timeleft; | ||
49 | u16 payload, curr_frq, intr_flag; | ||
50 | u32 curr_frq_in_khz; | ||
51 | u32 ret, resp_len; | ||
52 | |||
53 | if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) { | ||
54 | fmerr("Invalid frequency %d\n", freq); | ||
55 | return -EINVAL; | ||
56 | } | ||
57 | |||
58 | /* Set audio enable */ | ||
59 | payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG; | ||
60 | |||
61 | ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload, | ||
62 | sizeof(payload), NULL, NULL); | ||
63 | if (ret < 0) | ||
64 | return ret; | ||
65 | |||
66 | /* Set hilo to automatic selection */ | ||
67 | payload = FM_RX_IFFREQ_HILO_AUTOMATIC; | ||
68 | ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload, | ||
69 | sizeof(payload), NULL, NULL); | ||
70 | if (ret < 0) | ||
71 | return ret; | ||
72 | |||
73 | /* Calculate frequency index and set*/ | ||
74 | payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; | ||
75 | |||
76 | ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload, | ||
77 | sizeof(payload), NULL, NULL); | ||
78 | if (ret < 0) | ||
79 | return ret; | ||
80 | |||
81 | /* Read flags - just to clear any pending interrupts if we had */ | ||
82 | ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL); | ||
83 | if (ret < 0) | ||
84 | return ret; | ||
85 | |||
86 | /* Enable FR, BL interrupts */ | ||
87 | intr_flag = fmdev->irq_info.mask; | ||
88 | fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT); | ||
89 | payload = fmdev->irq_info.mask; | ||
90 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
91 | sizeof(payload), NULL, NULL); | ||
92 | if (ret < 0) | ||
93 | return ret; | ||
94 | |||
95 | /* Start tune */ | ||
96 | payload = FM_TUNER_PRESET_MODE; | ||
97 | ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, | ||
98 | sizeof(payload), NULL, NULL); | ||
99 | if (ret < 0) | ||
100 | goto exit; | ||
101 | |||
102 | /* Wait for tune ended interrupt */ | ||
103 | init_completion(&fmdev->maintask_comp); | ||
104 | timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, | ||
105 | FM_DRV_TX_TIMEOUT); | ||
106 | if (!timeleft) { | ||
107 | fmerr("Timeout(%d sec),didn't get tune ended int\n", | ||
108 | jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); | ||
109 | ret = -ETIMEDOUT; | ||
110 | goto exit; | ||
111 | } | ||
112 | |||
113 | /* Read freq back to confirm */ | ||
114 | ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len); | ||
115 | if (ret < 0) | ||
116 | goto exit; | ||
117 | |||
118 | curr_frq = be16_to_cpu(curr_frq); | ||
119 | curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL)); | ||
120 | |||
121 | if (curr_frq_in_khz != freq) { | ||
122 | pr_info("Frequency is set to (%d) but " | ||
123 | "requested freq is (%d)\n", curr_frq_in_khz, freq); | ||
124 | } | ||
125 | |||
126 | /* Update local cache */ | ||
127 | fmdev->rx.freq = curr_frq_in_khz; | ||
128 | exit: | ||
129 | /* Re-enable default FM interrupts */ | ||
130 | fmdev->irq_info.mask = intr_flag; | ||
131 | payload = fmdev->irq_info.mask; | ||
132 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
133 | sizeof(payload), NULL, NULL); | ||
134 | if (ret < 0) | ||
135 | return ret; | ||
136 | |||
137 | /* Reset RDS cache and current station pointers */ | ||
138 | fm_rx_reset_rds_cache(fmdev); | ||
139 | fm_rx_reset_station_info(fmdev); | ||
140 | |||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | static u32 fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing) | ||
145 | { | ||
146 | u16 payload; | ||
147 | u32 ret; | ||
148 | |||
149 | if (spacing > 0 && spacing <= 50000) | ||
150 | spacing = FM_CHANNEL_SPACING_50KHZ; | ||
151 | else if (spacing > 50000 && spacing <= 100000) | ||
152 | spacing = FM_CHANNEL_SPACING_100KHZ; | ||
153 | else | ||
154 | spacing = FM_CHANNEL_SPACING_200KHZ; | ||
155 | |||
156 | /* set channel spacing */ | ||
157 | payload = spacing; | ||
158 | ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload, | ||
159 | sizeof(payload), NULL, NULL); | ||
160 | if (ret < 0) | ||
161 | return ret; | ||
162 | |||
163 | fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL; | ||
164 | |||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | u32 fm_rx_seek(struct fmdev *fmdev, u32 seek_upward, | ||
169 | u32 wrap_around, u32 spacing) | ||
170 | { | ||
171 | u32 resp_len; | ||
172 | u16 curr_frq, next_frq, last_frq; | ||
173 | u16 payload, int_reason, intr_flag; | ||
174 | u16 offset, space_idx; | ||
175 | unsigned long timeleft; | ||
176 | u32 ret; | ||
177 | |||
178 | /* Set channel spacing */ | ||
179 | ret = fm_rx_set_channel_spacing(fmdev, spacing); | ||
180 | if (ret < 0) { | ||
181 | fmerr("Failed to set channel spacing\n"); | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | /* Read the current frequency from chip */ | ||
186 | ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, | ||
187 | sizeof(curr_frq), &curr_frq, &resp_len); | ||
188 | if (ret < 0) | ||
189 | return ret; | ||
190 | |||
191 | curr_frq = be16_to_cpu(curr_frq); | ||
192 | last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; | ||
193 | |||
194 | /* Check the offset in order to be aligned to the channel spacing*/ | ||
195 | space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL; | ||
196 | offset = curr_frq % space_idx; | ||
197 | |||
198 | next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ : | ||
199 | curr_frq - space_idx /* Seek Down */ ; | ||
200 | |||
201 | /* | ||
202 | * Add or subtract offset in order to stay aligned to the channel | ||
203 | * spacing. | ||
204 | */ | ||
205 | if ((short)next_frq < 0) | ||
206 | next_frq = last_frq - offset; | ||
207 | else if (next_frq > last_frq) | ||
208 | next_frq = 0 + offset; | ||
209 | |||
210 | again: | ||
211 | /* Set calculated next frequency to perform seek */ | ||
212 | payload = next_frq; | ||
213 | ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload, | ||
214 | sizeof(payload), NULL, NULL); | ||
215 | if (ret < 0) | ||
216 | return ret; | ||
217 | |||
218 | /* Set search direction (0:Seek Down, 1:Seek Up) */ | ||
219 | payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN); | ||
220 | ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload, | ||
221 | sizeof(payload), NULL, NULL); | ||
222 | if (ret < 0) | ||
223 | return ret; | ||
224 | |||
225 | /* Read flags - just to clear any pending interrupts if we had */ | ||
226 | ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL); | ||
227 | if (ret < 0) | ||
228 | return ret; | ||
229 | |||
230 | /* Enable FR, BL interrupts */ | ||
231 | intr_flag = fmdev->irq_info.mask; | ||
232 | fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT); | ||
233 | payload = fmdev->irq_info.mask; | ||
234 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
235 | sizeof(payload), NULL, NULL); | ||
236 | if (ret < 0) | ||
237 | return ret; | ||
238 | |||
239 | /* Start seek */ | ||
240 | payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE; | ||
241 | ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, | ||
242 | sizeof(payload), NULL, NULL); | ||
243 | if (ret < 0) | ||
244 | return ret; | ||
245 | |||
246 | /* Wait for tune ended/band limit reached interrupt */ | ||
247 | init_completion(&fmdev->maintask_comp); | ||
248 | timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, | ||
249 | FM_DRV_RX_SEEK_TIMEOUT); | ||
250 | if (!timeleft) { | ||
251 | fmerr("Timeout(%d sec),didn't get tune ended int\n", | ||
252 | jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000); | ||
253 | return -ETIMEDOUT; | ||
254 | } | ||
255 | |||
256 | int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT); | ||
257 | |||
258 | /* Re-enable default FM interrupts */ | ||
259 | fmdev->irq_info.mask = intr_flag; | ||
260 | payload = fmdev->irq_info.mask; | ||
261 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
262 | sizeof(payload), NULL, NULL); | ||
263 | if (ret < 0) | ||
264 | return ret; | ||
265 | |||
266 | if (int_reason & FM_BL_EVENT) { | ||
267 | if (wrap_around == 0) { | ||
268 | fmdev->rx.freq = seek_upward ? | ||
269 | fmdev->rx.region.top_freq : | ||
270 | fmdev->rx.region.bot_freq; | ||
271 | } else { | ||
272 | fmdev->rx.freq = seek_upward ? | ||
273 | fmdev->rx.region.bot_freq : | ||
274 | fmdev->rx.region.top_freq; | ||
275 | /* Calculate frequency index to write */ | ||
276 | next_frq = (fmdev->rx.freq - | ||
277 | fmdev->rx.region.bot_freq) / FM_FREQ_MUL; | ||
278 | goto again; | ||
279 | } | ||
280 | } else { | ||
281 | /* Read freq to know where operation tune operation stopped */ | ||
282 | ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, | ||
283 | &curr_frq, &resp_len); | ||
284 | if (ret < 0) | ||
285 | return ret; | ||
286 | |||
287 | curr_frq = be16_to_cpu(curr_frq); | ||
288 | fmdev->rx.freq = (fmdev->rx.region.bot_freq + | ||
289 | ((u32)curr_frq * FM_FREQ_MUL)); | ||
290 | |||
291 | } | ||
292 | /* Reset RDS cache and current station pointers */ | ||
293 | fm_rx_reset_rds_cache(fmdev); | ||
294 | fm_rx_reset_station_info(fmdev); | ||
295 | |||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | u32 fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set) | ||
300 | { | ||
301 | u16 payload; | ||
302 | u32 ret; | ||
303 | |||
304 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
305 | return -EPERM; | ||
306 | |||
307 | if (vol_to_set < FM_RX_VOLUME_MIN || vol_to_set > FM_RX_VOLUME_MAX) { | ||
308 | fmerr("Volume is not within(%d-%d) range\n", | ||
309 | FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX); | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | vol_to_set *= FM_RX_VOLUME_GAIN_STEP; | ||
313 | |||
314 | payload = vol_to_set; | ||
315 | ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload, | ||
316 | sizeof(payload), NULL, NULL); | ||
317 | if (ret < 0) | ||
318 | return ret; | ||
319 | |||
320 | fmdev->rx.volume = vol_to_set; | ||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | /* Get volume */ | ||
325 | u32 fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol) | ||
326 | { | ||
327 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
328 | return -EPERM; | ||
329 | |||
330 | if (curr_vol == NULL) { | ||
331 | fmerr("Invalid memory\n"); | ||
332 | return -ENOMEM; | ||
333 | } | ||
334 | |||
335 | *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP; | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* To get current band's bottom and top frequency */ | ||
341 | u32 fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq) | ||
342 | { | ||
343 | if (bot_freq != NULL) | ||
344 | *bot_freq = fmdev->rx.region.bot_freq; | ||
345 | |||
346 | if (top_freq != NULL) | ||
347 | *top_freq = fmdev->rx.region.top_freq; | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | /* Returns current band index (0-Europe/US; 1-Japan) */ | ||
353 | void fm_rx_get_region(struct fmdev *fmdev, u8 *region) | ||
354 | { | ||
355 | *region = fmdev->rx.region.fm_band; | ||
356 | } | ||
357 | |||
358 | /* Sets band (0-Europe/US; 1-Japan) */ | ||
359 | u32 fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set) | ||
360 | { | ||
361 | u16 payload; | ||
362 | u32 new_frq = 0; | ||
363 | u32 ret; | ||
364 | |||
365 | if (region_to_set != FM_BAND_EUROPE_US && | ||
366 | region_to_set != FM_BAND_JAPAN) { | ||
367 | fmerr("Invalid band\n"); | ||
368 | return -EINVAL; | ||
369 | } | ||
370 | |||
371 | if (fmdev->rx.region.fm_band == region_to_set) { | ||
372 | fmerr("Requested band is already configured\n"); | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | /* Send cmd to set the band */ | ||
377 | payload = (u16)region_to_set; | ||
378 | ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload, | ||
379 | sizeof(payload), NULL, NULL); | ||
380 | if (ret < 0) | ||
381 | return ret; | ||
382 | |||
383 | fmc_update_region_info(fmdev, region_to_set); | ||
384 | |||
385 | /* Check whether current RX frequency is within band boundary */ | ||
386 | if (fmdev->rx.freq < fmdev->rx.region.bot_freq) | ||
387 | new_frq = fmdev->rx.region.bot_freq; | ||
388 | else if (fmdev->rx.freq > fmdev->rx.region.top_freq) | ||
389 | new_frq = fmdev->rx.region.top_freq; | ||
390 | |||
391 | if (new_frq) { | ||
392 | fmdbg("Current freq is not within band limit boundary," | ||
393 | "switching to %d KHz\n", new_frq); | ||
394 | /* Current RX frequency is not in range. So, update it */ | ||
395 | ret = fm_rx_set_freq(fmdev, new_frq); | ||
396 | } | ||
397 | |||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | /* Reads current mute mode (Mute Off/On/Attenuate)*/ | ||
402 | u32 fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode) | ||
403 | { | ||
404 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
405 | return -EPERM; | ||
406 | |||
407 | if (curr_mute_mode == NULL) { | ||
408 | fmerr("Invalid memory\n"); | ||
409 | return -ENOMEM; | ||
410 | } | ||
411 | |||
412 | *curr_mute_mode = fmdev->rx.mute_mode; | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | static u32 fm_config_rx_mute_reg(struct fmdev *fmdev) | ||
418 | { | ||
419 | u16 payload, muteval; | ||
420 | u32 ret; | ||
421 | |||
422 | muteval = 0; | ||
423 | switch (fmdev->rx.mute_mode) { | ||
424 | case FM_MUTE_ON: | ||
425 | muteval = FM_RX_AC_MUTE_MODE; | ||
426 | break; | ||
427 | |||
428 | case FM_MUTE_OFF: | ||
429 | muteval = FM_RX_UNMUTE_MODE; | ||
430 | break; | ||
431 | |||
432 | case FM_MUTE_ATTENUATE: | ||
433 | muteval = FM_RX_SOFT_MUTE_FORCE_MODE; | ||
434 | break; | ||
435 | } | ||
436 | if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON) | ||
437 | muteval |= FM_RX_RF_DEP_MODE; | ||
438 | else | ||
439 | muteval &= ~FM_RX_RF_DEP_MODE; | ||
440 | |||
441 | payload = muteval; | ||
442 | ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload, | ||
443 | sizeof(payload), NULL, NULL); | ||
444 | if (ret < 0) | ||
445 | return ret; | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | /* Configures mute mode (Mute Off/On/Attenuate) */ | ||
451 | u32 fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) | ||
452 | { | ||
453 | u8 org_state; | ||
454 | u32 ret; | ||
455 | |||
456 | if (fmdev->rx.mute_mode == mute_mode_toset) | ||
457 | return 0; | ||
458 | |||
459 | org_state = fmdev->rx.mute_mode; | ||
460 | fmdev->rx.mute_mode = mute_mode_toset; | ||
461 | |||
462 | ret = fm_config_rx_mute_reg(fmdev); | ||
463 | if (ret < 0) { | ||
464 | fmdev->rx.mute_mode = org_state; | ||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | /* Gets RF dependent soft mute mode enable/disable status */ | ||
472 | u32 fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode) | ||
473 | { | ||
474 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
475 | return -EPERM; | ||
476 | |||
477 | if (curr_mute_mode == NULL) { | ||
478 | fmerr("Invalid memory\n"); | ||
479 | return -ENOMEM; | ||
480 | } | ||
481 | |||
482 | *curr_mute_mode = fmdev->rx.rf_depend_mute; | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | /* Sets RF dependent soft mute mode */ | ||
488 | u32 fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute) | ||
489 | { | ||
490 | u8 org_state; | ||
491 | u32 ret; | ||
492 | |||
493 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
494 | return -EPERM; | ||
495 | |||
496 | if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON && | ||
497 | rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) { | ||
498 | fmerr("Invalid RF dependent soft mute\n"); | ||
499 | return -EINVAL; | ||
500 | } | ||
501 | if (fmdev->rx.rf_depend_mute == rfdepend_mute) | ||
502 | return 0; | ||
503 | |||
504 | org_state = fmdev->rx.rf_depend_mute; | ||
505 | fmdev->rx.rf_depend_mute = rfdepend_mute; | ||
506 | |||
507 | ret = fm_config_rx_mute_reg(fmdev); | ||
508 | if (ret < 0) { | ||
509 | fmdev->rx.rf_depend_mute = org_state; | ||
510 | return ret; | ||
511 | } | ||
512 | |||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | /* Returns the signal strength level of current channel */ | ||
517 | u32 fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl) | ||
518 | { | ||
519 | u16 curr_rssi_lel; | ||
520 | u32 resp_len; | ||
521 | u32 ret; | ||
522 | |||
523 | if (rssilvl == NULL) { | ||
524 | fmerr("Invalid memory\n"); | ||
525 | return -ENOMEM; | ||
526 | } | ||
527 | /* Read current RSSI level */ | ||
528 | ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2, | ||
529 | &curr_rssi_lel, &resp_len); | ||
530 | if (ret < 0) | ||
531 | return ret; | ||
532 | |||
533 | *rssilvl = be16_to_cpu(curr_rssi_lel); | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | * Sets the signal strength level that once reached | ||
540 | * will stop the auto search process | ||
541 | */ | ||
542 | u32 fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset) | ||
543 | { | ||
544 | u16 payload; | ||
545 | u32 ret; | ||
546 | |||
547 | if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN || | ||
548 | rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) { | ||
549 | fmerr("Invalid RSSI threshold level\n"); | ||
550 | return -EINVAL; | ||
551 | } | ||
552 | payload = (u16)rssi_lvl_toset; | ||
553 | ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload, | ||
554 | sizeof(payload), NULL, NULL); | ||
555 | if (ret < 0) | ||
556 | return ret; | ||
557 | |||
558 | fmdev->rx.rssi_threshold = rssi_lvl_toset; | ||
559 | |||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | /* Returns current RX RSSI threshold value */ | ||
564 | u32 fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl) | ||
565 | { | ||
566 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
567 | return -EPERM; | ||
568 | |||
569 | if (curr_rssi_lvl == NULL) { | ||
570 | fmerr("Invalid memory\n"); | ||
571 | return -ENOMEM; | ||
572 | } | ||
573 | |||
574 | *curr_rssi_lvl = fmdev->rx.rssi_threshold; | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | /* Sets RX stereo/mono modes */ | ||
580 | u32 fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode) | ||
581 | { | ||
582 | u16 payload; | ||
583 | u32 ret; | ||
584 | |||
585 | if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) { | ||
586 | fmerr("Invalid mode\n"); | ||
587 | return -EINVAL; | ||
588 | } | ||
589 | |||
590 | /* Set stereo/mono mode */ | ||
591 | payload = (u16)mode; | ||
592 | ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload, | ||
593 | sizeof(payload), NULL, NULL); | ||
594 | if (ret < 0) | ||
595 | return ret; | ||
596 | |||
597 | /* Set stereo blending mode */ | ||
598 | payload = FM_STEREO_SOFT_BLEND; | ||
599 | ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload, | ||
600 | sizeof(payload), NULL, NULL); | ||
601 | if (ret < 0) | ||
602 | return ret; | ||
603 | |||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | /* Gets current RX stereo/mono mode */ | ||
608 | u32 fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode) | ||
609 | { | ||
610 | u16 curr_mode; | ||
611 | u32 ret, resp_len; | ||
612 | |||
613 | if (mode == NULL) { | ||
614 | fmerr("Invalid memory\n"); | ||
615 | return -ENOMEM; | ||
616 | } | ||
617 | |||
618 | ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2, | ||
619 | &curr_mode, &resp_len); | ||
620 | if (ret < 0) | ||
621 | return ret; | ||
622 | |||
623 | *mode = be16_to_cpu(curr_mode); | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /* Choose RX de-emphasis filter mode (50us/75us) */ | ||
629 | u32 fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode) | ||
630 | { | ||
631 | u16 payload; | ||
632 | u32 ret; | ||
633 | |||
634 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
635 | return -EPERM; | ||
636 | |||
637 | if (mode != FM_RX_EMPHASIS_FILTER_50_USEC && | ||
638 | mode != FM_RX_EMPHASIS_FILTER_75_USEC) { | ||
639 | fmerr("Invalid rx de-emphasis mode (%d)\n", mode); | ||
640 | return -EINVAL; | ||
641 | } | ||
642 | |||
643 | payload = mode; | ||
644 | ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload, | ||
645 | sizeof(payload), NULL, NULL); | ||
646 | if (ret < 0) | ||
647 | return ret; | ||
648 | |||
649 | fmdev->rx.deemphasis_mode = mode; | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | /* Gets current RX de-emphasis filter mode */ | ||
655 | u32 fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode) | ||
656 | { | ||
657 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
658 | return -EPERM; | ||
659 | |||
660 | if (curr_deemphasis_mode == NULL) { | ||
661 | fmerr("Invalid memory\n"); | ||
662 | return -ENOMEM; | ||
663 | } | ||
664 | |||
665 | *curr_deemphasis_mode = fmdev->rx.deemphasis_mode; | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | /* Enable/Disable RX RDS */ | ||
671 | u32 fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) | ||
672 | { | ||
673 | u16 payload; | ||
674 | u32 ret; | ||
675 | |||
676 | if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) { | ||
677 | fmerr("Invalid rds option\n"); | ||
678 | return -EINVAL; | ||
679 | } | ||
680 | |||
681 | if (rds_en_dis == FM_RDS_ENABLE | ||
682 | && fmdev->rx.rds.flag == FM_RDS_DISABLE) { | ||
683 | /* Turn on RX RDS and RDS circuit */ | ||
684 | payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON; | ||
685 | ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload, | ||
686 | sizeof(payload), NULL, NULL); | ||
687 | if (ret < 0) | ||
688 | return ret; | ||
689 | |||
690 | /* Clear and reset RDS FIFO */ | ||
691 | payload = FM_RX_RDS_FLUSH_FIFO; | ||
692 | ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload, | ||
693 | sizeof(payload), NULL, NULL); | ||
694 | if (ret < 0) | ||
695 | return ret; | ||
696 | |||
697 | /* Read flags - just to clear any pending interrupts. */ | ||
698 | ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, | ||
699 | NULL, NULL); | ||
700 | if (ret < 0) | ||
701 | return ret; | ||
702 | |||
703 | /* Set RDS FIFO threshold value */ | ||
704 | payload = FM_RX_RDS_FIFO_THRESHOLD; | ||
705 | ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload, | ||
706 | sizeof(payload), NULL, NULL); | ||
707 | if (ret < 0) | ||
708 | return ret; | ||
709 | |||
710 | /* Enable RDS interrupt */ | ||
711 | fmdev->irq_info.mask |= FM_RDS_EVENT; | ||
712 | payload = fmdev->irq_info.mask; | ||
713 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
714 | sizeof(payload), NULL, NULL); | ||
715 | if (ret < 0) { | ||
716 | fmdev->irq_info.mask &= ~FM_RDS_EVENT; | ||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | /* Update our local flag */ | ||
721 | fmdev->rx.rds.flag = FM_RDS_ENABLE; | ||
722 | } else if (rds_en_dis == FM_RDS_DISABLE | ||
723 | && fmdev->rx.rds.flag == FM_RDS_ENABLE) { | ||
724 | /* Turn off RX RDS */ | ||
725 | payload = FM_RX_PWR_SET_FM_ON_RDS_OFF; | ||
726 | ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload, | ||
727 | sizeof(payload), NULL, NULL); | ||
728 | if (ret < 0) | ||
729 | return ret; | ||
730 | |||
731 | /* Reset RDS pointers */ | ||
732 | fmdev->rx.rds.last_blk_idx = 0; | ||
733 | fmdev->rx.rds.wr_idx = 0; | ||
734 | fmdev->rx.rds.rd_idx = 0; | ||
735 | fm_rx_reset_station_info(fmdev); | ||
736 | |||
737 | /* Update RDS local cache */ | ||
738 | fmdev->irq_info.mask &= ~(FM_RDS_EVENT); | ||
739 | fmdev->rx.rds.flag = FM_RDS_DISABLE; | ||
740 | } | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | /* Returns current RX RDS enable/disable status */ | ||
746 | u32 fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis) | ||
747 | { | ||
748 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
749 | return -EPERM; | ||
750 | |||
751 | if (curr_rds_en_dis == NULL) { | ||
752 | fmerr("Invalid memory\n"); | ||
753 | return -ENOMEM; | ||
754 | } | ||
755 | |||
756 | *curr_rds_en_dis = fmdev->rx.rds.flag; | ||
757 | |||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | /* Sets RDS operation mode (RDS/RDBS) */ | ||
762 | u32 fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode) | ||
763 | { | ||
764 | u16 payload; | ||
765 | u32 ret; | ||
766 | |||
767 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
768 | return -EPERM; | ||
769 | |||
770 | if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) { | ||
771 | fmerr("Invalid rds mode\n"); | ||
772 | return -EINVAL; | ||
773 | } | ||
774 | /* Set RDS operation mode */ | ||
775 | payload = (u16)rds_mode; | ||
776 | ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload, | ||
777 | sizeof(payload), NULL, NULL); | ||
778 | if (ret < 0) | ||
779 | return ret; | ||
780 | |||
781 | fmdev->rx.rds_mode = rds_mode; | ||
782 | |||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | /* Returns current RDS operation mode */ | ||
787 | u32 fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode) | ||
788 | { | ||
789 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
790 | return -EPERM; | ||
791 | |||
792 | if (rds_mode == NULL) { | ||
793 | fmerr("Invalid memory\n"); | ||
794 | return -ENOMEM; | ||
795 | } | ||
796 | |||
797 | *rds_mode = fmdev->rx.rds_mode; | ||
798 | |||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | /* Configures Alternate Frequency switch mode */ | ||
803 | u32 fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode) | ||
804 | { | ||
805 | u16 payload; | ||
806 | u32 ret; | ||
807 | |||
808 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
809 | return -EPERM; | ||
810 | |||
811 | if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON && | ||
812 | af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) { | ||
813 | fmerr("Invalid af mode\n"); | ||
814 | return -EINVAL; | ||
815 | } | ||
816 | /* Enable/disable low RSSI interrupt based on af_mode */ | ||
817 | if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) | ||
818 | fmdev->irq_info.mask |= FM_LEV_EVENT; | ||
819 | else | ||
820 | fmdev->irq_info.mask &= ~FM_LEV_EVENT; | ||
821 | |||
822 | payload = fmdev->irq_info.mask; | ||
823 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
824 | sizeof(payload), NULL, NULL); | ||
825 | if (ret < 0) | ||
826 | return ret; | ||
827 | |||
828 | fmdev->rx.af_mode = af_mode; | ||
829 | |||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | /* Returns Alternate Frequency switch status */ | ||
834 | u32 fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode) | ||
835 | { | ||
836 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
837 | return -EPERM; | ||
838 | |||
839 | if (af_mode == NULL) { | ||
840 | fmerr("Invalid memory\n"); | ||
841 | return -ENOMEM; | ||
842 | } | ||
843 | |||
844 | *af_mode = fmdev->rx.af_mode; | ||
845 | |||
846 | return 0; | ||
847 | } | ||
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h new file mode 100644 index 000000000000..329e62f6be76 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_rx.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * FM RX module header. | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _FMDRV_RX_H | ||
23 | #define _FMDRV_RX_H | ||
24 | |||
25 | u32 fm_rx_set_freq(struct fmdev *, u32); | ||
26 | u32 fm_rx_set_mute_mode(struct fmdev *, u8); | ||
27 | u32 fm_rx_set_stereo_mono(struct fmdev *, u16); | ||
28 | u32 fm_rx_set_rds_mode(struct fmdev *, u8); | ||
29 | u32 fm_rx_set_rds_system(struct fmdev *, u8); | ||
30 | u32 fm_rx_set_volume(struct fmdev *, u16); | ||
31 | u32 fm_rx_set_rssi_threshold(struct fmdev *, short); | ||
32 | u32 fm_rx_set_region(struct fmdev *, u8); | ||
33 | u32 fm_rx_set_rfdepend_softmute(struct fmdev *, u8); | ||
34 | u32 fm_rx_set_deemphasis_mode(struct fmdev *, u16); | ||
35 | u32 fm_rx_set_af_switch(struct fmdev *, u8); | ||
36 | |||
37 | void fm_rx_reset_rds_cache(struct fmdev *); | ||
38 | void fm_rx_reset_station_info(struct fmdev *); | ||
39 | |||
40 | u32 fm_rx_seek(struct fmdev *, u32, u32, u32); | ||
41 | |||
42 | u32 fm_rx_get_rds_mode(struct fmdev *, u8 *); | ||
43 | u32 fm_rx_get_rds_system(struct fmdev *, u8 *); | ||
44 | u32 fm_rx_get_mute_mode(struct fmdev *, u8 *); | ||
45 | u32 fm_rx_get_volume(struct fmdev *, u16 *); | ||
46 | u32 fm_rx_get_band_freq_range(struct fmdev *, | ||
47 | u32 *, u32 *); | ||
48 | u32 fm_rx_get_stereo_mono(struct fmdev *, u16 *); | ||
49 | u32 fm_rx_get_rssi_level(struct fmdev *, u16 *); | ||
50 | u32 fm_rx_get_rssi_threshold(struct fmdev *, short *); | ||
51 | u32 fm_rx_get_rfdepend_softmute(struct fmdev *, u8 *); | ||
52 | u32 fm_rx_get_deemph_mode(struct fmdev *, u16 *); | ||
53 | u32 fm_rx_get_af_switch(struct fmdev *, u8 *); | ||
54 | void fm_rx_get_region(struct fmdev *, u8 *); | ||
55 | |||
56 | u32 fm_rx_set_chanl_spacing(struct fmdev *, u8); | ||
57 | u32 fm_rx_get_chanl_spacing(struct fmdev *, u8 *); | ||
58 | #endif | ||
59 | |||
diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c new file mode 100644 index 000000000000..be54068b56a8 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_tx.c | |||
@@ -0,0 +1,425 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * This sub-module of FM driver implements FM TX functionality. | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include "fmdrv.h" | ||
24 | #include "fmdrv_common.h" | ||
25 | #include "fmdrv_tx.h" | ||
26 | |||
27 | u32 fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode) | ||
28 | { | ||
29 | u16 payload; | ||
30 | u32 ret; | ||
31 | |||
32 | if (fmdev->tx_data.aud_mode == mode) | ||
33 | return 0; | ||
34 | |||
35 | fmdbg("stereo mode: %d\n", mode); | ||
36 | |||
37 | /* Set Stereo/Mono mode */ | ||
38 | payload = (1 - mode); | ||
39 | ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload, | ||
40 | sizeof(payload), NULL, NULL); | ||
41 | if (ret < 0) | ||
42 | return ret; | ||
43 | |||
44 | fmdev->tx_data.aud_mode = mode; | ||
45 | |||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | static u32 set_rds_text(struct fmdev *fmdev, u8 *rds_text) | ||
50 | { | ||
51 | u16 payload; | ||
52 | u32 ret; | ||
53 | |||
54 | ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text, | ||
55 | strlen(rds_text), NULL, NULL); | ||
56 | if (ret < 0) | ||
57 | return ret; | ||
58 | |||
59 | /* Scroll mode */ | ||
60 | payload = (u16)0x1; | ||
61 | ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload, | ||
62 | sizeof(payload), NULL, NULL); | ||
63 | if (ret < 0) | ||
64 | return ret; | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static u32 set_rds_data_mode(struct fmdev *fmdev, u8 mode) | ||
70 | { | ||
71 | u16 payload; | ||
72 | u32 ret; | ||
73 | |||
74 | /* Setting unique PI TODO: how unique? */ | ||
75 | payload = (u16)0xcafe; | ||
76 | ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload, | ||
77 | sizeof(payload), NULL, NULL); | ||
78 | if (ret < 0) | ||
79 | return ret; | ||
80 | |||
81 | /* Set decoder id */ | ||
82 | payload = (u16)0xa; | ||
83 | ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload, | ||
84 | sizeof(payload), NULL, NULL); | ||
85 | if (ret < 0) | ||
86 | return ret; | ||
87 | |||
88 | /* TODO: RDS_MODE_GET? */ | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static u32 set_rds_len(struct fmdev *fmdev, u8 type, u16 len) | ||
93 | { | ||
94 | u16 payload; | ||
95 | u32 ret; | ||
96 | |||
97 | len |= type << 8; | ||
98 | payload = len; | ||
99 | ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload, | ||
100 | sizeof(payload), NULL, NULL); | ||
101 | if (ret < 0) | ||
102 | return ret; | ||
103 | |||
104 | /* TODO: LENGTH_GET? */ | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | u32 fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) | ||
109 | { | ||
110 | u16 payload; | ||
111 | u32 ret; | ||
112 | u8 rds_text[] = "Zoom2\n"; | ||
113 | |||
114 | fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis, | ||
115 | FM_RDS_ENABLE, FM_RDS_DISABLE); | ||
116 | |||
117 | if (rds_en_dis == FM_RDS_ENABLE) { | ||
118 | /* Set RDS length */ | ||
119 | set_rds_len(fmdev, 0, strlen(rds_text)); | ||
120 | |||
121 | /* Set RDS text */ | ||
122 | set_rds_text(fmdev, rds_text); | ||
123 | |||
124 | /* Set RDS mode */ | ||
125 | set_rds_data_mode(fmdev, 0x0); | ||
126 | } | ||
127 | |||
128 | /* Send command to enable RDS */ | ||
129 | if (rds_en_dis == FM_RDS_ENABLE) | ||
130 | payload = 0x01; | ||
131 | else | ||
132 | payload = 0x00; | ||
133 | |||
134 | ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, | ||
135 | sizeof(payload), NULL, NULL); | ||
136 | if (ret < 0) | ||
137 | return ret; | ||
138 | |||
139 | if (rds_en_dis == FM_RDS_ENABLE) { | ||
140 | /* Set RDS length */ | ||
141 | set_rds_len(fmdev, 0, strlen(rds_text)); | ||
142 | |||
143 | /* Set RDS text */ | ||
144 | set_rds_text(fmdev, rds_text); | ||
145 | } | ||
146 | fmdev->tx_data.rds.flag = rds_en_dis; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | u32 fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type) | ||
152 | { | ||
153 | u16 payload; | ||
154 | u32 ret; | ||
155 | |||
156 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
157 | return -EPERM; | ||
158 | |||
159 | fm_tx_set_rds_mode(fmdev, 0); | ||
160 | |||
161 | /* Set RDS length */ | ||
162 | set_rds_len(fmdev, rds_type, strlen(rds_text)); | ||
163 | |||
164 | /* Set RDS text */ | ||
165 | set_rds_text(fmdev, rds_text); | ||
166 | |||
167 | /* Set RDS mode */ | ||
168 | set_rds_data_mode(fmdev, 0x0); | ||
169 | |||
170 | payload = 1; | ||
171 | ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, | ||
172 | sizeof(payload), NULL, NULL); | ||
173 | if (ret < 0) | ||
174 | return ret; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | u32 fm_tx_set_af(struct fmdev *fmdev, u32 af) | ||
180 | { | ||
181 | u16 payload; | ||
182 | u32 ret; | ||
183 | |||
184 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
185 | return -EPERM; | ||
186 | |||
187 | fmdbg("AF: %d\n", af); | ||
188 | |||
189 | af = (af - 87500) / 100; | ||
190 | payload = (u16)af; | ||
191 | ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload, | ||
192 | sizeof(payload), NULL, NULL); | ||
193 | if (ret < 0) | ||
194 | return ret; | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | u32 fm_tx_set_region(struct fmdev *fmdev, u8 region) | ||
200 | { | ||
201 | u16 payload; | ||
202 | u32 ret; | ||
203 | |||
204 | if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) { | ||
205 | fmerr("Invalid band\n"); | ||
206 | return -EINVAL; | ||
207 | } | ||
208 | |||
209 | /* Send command to set the band */ | ||
210 | payload = (u16)region; | ||
211 | ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload, | ||
212 | sizeof(payload), NULL, NULL); | ||
213 | if (ret < 0) | ||
214 | return ret; | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | u32 fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) | ||
220 | { | ||
221 | u16 payload; | ||
222 | u32 ret; | ||
223 | |||
224 | fmdbg("tx: mute mode %d\n", mute_mode_toset); | ||
225 | |||
226 | payload = mute_mode_toset; | ||
227 | ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload, | ||
228 | sizeof(payload), NULL, NULL); | ||
229 | if (ret < 0) | ||
230 | return ret; | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | /* Set TX Audio I/O */ | ||
236 | static u32 set_audio_io(struct fmdev *fmdev) | ||
237 | { | ||
238 | struct fmtx_data *tx = &fmdev->tx_data; | ||
239 | u16 payload; | ||
240 | u32 ret; | ||
241 | |||
242 | /* Set Audio I/O Enable */ | ||
243 | payload = tx->audio_io; | ||
244 | ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload, | ||
245 | sizeof(payload), NULL, NULL); | ||
246 | if (ret < 0) | ||
247 | return ret; | ||
248 | |||
249 | /* TODO: is audio set? */ | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | /* Start TX Transmission */ | ||
254 | static u32 enable_xmit(struct fmdev *fmdev, u8 new_xmit_state) | ||
255 | { | ||
256 | struct fmtx_data *tx = &fmdev->tx_data; | ||
257 | unsigned long timeleft; | ||
258 | u16 payload; | ||
259 | u32 ret; | ||
260 | |||
261 | /* Enable POWER_ENB interrupts */ | ||
262 | payload = FM_POW_ENB_EVENT; | ||
263 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
264 | sizeof(payload), NULL, NULL); | ||
265 | if (ret < 0) | ||
266 | return ret; | ||
267 | |||
268 | /* Set Power Enable */ | ||
269 | payload = new_xmit_state; | ||
270 | ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload, | ||
271 | sizeof(payload), NULL, NULL); | ||
272 | if (ret < 0) | ||
273 | return ret; | ||
274 | |||
275 | /* Wait for Power Enabled */ | ||
276 | init_completion(&fmdev->maintask_comp); | ||
277 | timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, | ||
278 | FM_DRV_TX_TIMEOUT); | ||
279 | if (!timeleft) { | ||
280 | fmerr("Timeout(%d sec),didn't get tune ended interrupt\n", | ||
281 | jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); | ||
282 | return -ETIMEDOUT; | ||
283 | } | ||
284 | |||
285 | set_bit(FM_CORE_TX_XMITING, &fmdev->flag); | ||
286 | tx->xmit_state = new_xmit_state; | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* Set TX power level */ | ||
292 | u32 fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl) | ||
293 | { | ||
294 | u16 payload; | ||
295 | struct fmtx_data *tx = &fmdev->tx_data; | ||
296 | u32 ret; | ||
297 | |||
298 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
299 | return -EPERM; | ||
300 | fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl); | ||
301 | |||
302 | /* If the core isn't ready update global variable */ | ||
303 | if (!test_bit(FM_CORE_READY, &fmdev->flag)) { | ||
304 | tx->pwr_lvl = new_pwr_lvl; | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | /* Set power level: Application will specify power level value in | ||
309 | * units of dB/uV, whereas range and step are specific to FM chip. | ||
310 | * For TI's WL chips, convert application specified power level value | ||
311 | * to chip specific value by subtracting 122 from it. Refer to TI FM | ||
312 | * data sheet for details. | ||
313 | * */ | ||
314 | |||
315 | payload = (FM_PWR_LVL_HIGH - new_pwr_lvl); | ||
316 | ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload, | ||
317 | sizeof(payload), NULL, NULL); | ||
318 | if (ret < 0) | ||
319 | return ret; | ||
320 | |||
321 | /* TODO: is the power level set? */ | ||
322 | tx->pwr_lvl = new_pwr_lvl; | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us) | ||
329 | * Convert V4L2 specified filter values to chip specific filter values. | ||
330 | */ | ||
331 | u32 fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis) | ||
332 | { | ||
333 | struct fmtx_data *tx = &fmdev->tx_data; | ||
334 | u16 payload; | ||
335 | u32 ret; | ||
336 | |||
337 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
338 | return -EPERM; | ||
339 | |||
340 | switch (preemphasis) { | ||
341 | case V4L2_PREEMPHASIS_DISABLED: | ||
342 | payload = FM_TX_PREEMPH_OFF; | ||
343 | break; | ||
344 | case V4L2_PREEMPHASIS_50_uS: | ||
345 | payload = FM_TX_PREEMPH_50US; | ||
346 | break; | ||
347 | case V4L2_PREEMPHASIS_75_uS: | ||
348 | payload = FM_TX_PREEMPH_75US; | ||
349 | break; | ||
350 | } | ||
351 | |||
352 | ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload, | ||
353 | sizeof(payload), NULL, NULL); | ||
354 | if (ret < 0) | ||
355 | return ret; | ||
356 | |||
357 | tx->preemph = payload; | ||
358 | |||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | /* Get the TX tuning capacitor value.*/ | ||
363 | u32 fm_tx_get_tune_cap_val(struct fmdev *fmdev) | ||
364 | { | ||
365 | u16 curr_val; | ||
366 | u32 ret, resp_len; | ||
367 | |||
368 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
369 | return -EPERM; | ||
370 | |||
371 | ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD, | ||
372 | NULL, sizeof(curr_val), &curr_val, &resp_len); | ||
373 | if (ret < 0) | ||
374 | return ret; | ||
375 | |||
376 | curr_val = be16_to_cpu(curr_val); | ||
377 | |||
378 | return curr_val; | ||
379 | } | ||
380 | |||
381 | /* Set TX Frequency */ | ||
382 | u32 fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set) | ||
383 | { | ||
384 | struct fmtx_data *tx = &fmdev->tx_data; | ||
385 | u16 payload, chanl_index; | ||
386 | u32 ret; | ||
387 | |||
388 | if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) { | ||
389 | enable_xmit(fmdev, 0); | ||
390 | clear_bit(FM_CORE_TX_XMITING, &fmdev->flag); | ||
391 | } | ||
392 | |||
393 | /* Enable FR, BL interrupts */ | ||
394 | payload = (FM_FR_EVENT | FM_BL_EVENT); | ||
395 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
396 | sizeof(payload), NULL, NULL); | ||
397 | if (ret < 0) | ||
398 | return ret; | ||
399 | |||
400 | tx->tx_frq = (unsigned long)freq_to_set; | ||
401 | fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq); | ||
402 | |||
403 | chanl_index = freq_to_set / 10; | ||
404 | |||
405 | /* Set current tuner channel */ | ||
406 | payload = chanl_index; | ||
407 | ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload, | ||
408 | sizeof(payload), NULL, NULL); | ||
409 | if (ret < 0) | ||
410 | return ret; | ||
411 | |||
412 | fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl); | ||
413 | fm_tx_set_preemph_filter(fmdev, tx->preemph); | ||
414 | |||
415 | tx->audio_io = 0x01; /* I2S */ | ||
416 | set_audio_io(fmdev); | ||
417 | |||
418 | enable_xmit(fmdev, 0x01); /* Enable transmission */ | ||
419 | |||
420 | tx->aud_mode = FM_STEREO_MODE; | ||
421 | tx->rds.flag = FM_RDS_DISABLE; | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
diff --git a/drivers/media/radio/wl128x/fmdrv_tx.h b/drivers/media/radio/wl128x/fmdrv_tx.h new file mode 100644 index 000000000000..e393a2bdd49e --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_tx.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * FM TX module header. | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _FMDRV_TX_H | ||
23 | #define _FMDRV_TX_H | ||
24 | |||
25 | u32 fm_tx_set_freq(struct fmdev *, u32); | ||
26 | u32 fm_tx_set_pwr_lvl(struct fmdev *, u8); | ||
27 | u32 fm_tx_set_region(struct fmdev *, u8); | ||
28 | u32 fm_tx_set_mute_mode(struct fmdev *, u8); | ||
29 | u32 fm_tx_set_stereo_mono(struct fmdev *, u16); | ||
30 | u32 fm_tx_set_rds_mode(struct fmdev *, u8); | ||
31 | u32 fm_tx_set_radio_text(struct fmdev *, u8 *, u8); | ||
32 | u32 fm_tx_set_af(struct fmdev *, u32); | ||
33 | u32 fm_tx_set_preemph_filter(struct fmdev *, u32); | ||
34 | u32 fm_tx_get_tune_cap_val(struct fmdev *); | ||
35 | |||
36 | #endif | ||
37 | |||
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c new file mode 100644 index 000000000000..d50e5ac75ab6 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c | |||
@@ -0,0 +1,580 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * This file provides interfaces to V4L2 subsystem. | ||
4 | * | ||
5 | * This module registers with V4L2 subsystem as Radio | ||
6 | * data system interface (/dev/radio). During the registration, | ||
7 | * it will expose two set of function pointers. | ||
8 | * | ||
9 | * 1) File operation related API (open, close, read, write, poll...etc). | ||
10 | * 2) Set of V4L2 IOCTL complaint API. | ||
11 | * | ||
12 | * Copyright (C) 2011 Texas Instruments | ||
13 | * Author: Raja Mani <raja_mani@ti.com> | ||
14 | * Author: Manjunatha Halli <manjunatha_halli@ti.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License version 2 as | ||
18 | * published by the Free Software Foundation. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include "fmdrv.h" | ||
32 | #include "fmdrv_v4l2.h" | ||
33 | #include "fmdrv_common.h" | ||
34 | #include "fmdrv_rx.h" | ||
35 | #include "fmdrv_tx.h" | ||
36 | |||
37 | static struct video_device *gradio_dev; | ||
38 | static u8 radio_disconnected; | ||
39 | |||
40 | /* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */ | ||
41 | |||
42 | /* Read RX RDS data */ | ||
43 | static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf, | ||
44 | size_t count, loff_t *ppos) | ||
45 | { | ||
46 | u8 rds_mode; | ||
47 | int ret; | ||
48 | struct fmdev *fmdev; | ||
49 | |||
50 | fmdev = video_drvdata(file); | ||
51 | |||
52 | if (!radio_disconnected) { | ||
53 | fmerr("FM device is already disconnected\n"); | ||
54 | return -EIO; | ||
55 | } | ||
56 | |||
57 | /* Turn on RDS mode , if it is disabled */ | ||
58 | ret = fm_rx_get_rds_mode(fmdev, &rds_mode); | ||
59 | if (ret < 0) { | ||
60 | fmerr("Unable to read current rds mode\n"); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | if (rds_mode == FM_RDS_DISABLE) { | ||
65 | ret = fmc_set_rds_mode(fmdev, FM_RDS_ENABLE); | ||
66 | if (ret < 0) { | ||
67 | fmerr("Failed to enable rds mode\n"); | ||
68 | return ret; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* Copy RDS data from internal buffer to user buffer */ | ||
73 | return fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count); | ||
74 | } | ||
75 | |||
76 | /* Write TX RDS data */ | ||
77 | static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf, | ||
78 | size_t count, loff_t *ppos) | ||
79 | { | ||
80 | struct tx_rds rds; | ||
81 | int ret; | ||
82 | struct fmdev *fmdev; | ||
83 | |||
84 | ret = copy_from_user(&rds, buf, sizeof(rds)); | ||
85 | fmdbg("(%d)type: %d, text %s, af %d\n", | ||
86 | ret, rds.text_type, rds.text, rds.af_freq); | ||
87 | |||
88 | fmdev = video_drvdata(file); | ||
89 | fm_tx_set_radio_text(fmdev, rds.text, rds.text_type); | ||
90 | fm_tx_set_af(fmdev, rds.af_freq); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts) | ||
96 | { | ||
97 | int ret; | ||
98 | struct fmdev *fmdev; | ||
99 | |||
100 | fmdev = video_drvdata(file); | ||
101 | ret = fmc_is_rds_data_available(fmdev, file, pts); | ||
102 | if (ret < 0) | ||
103 | return POLLIN | POLLRDNORM; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Handle open request for "/dev/radioX" device. | ||
110 | * Start with FM RX mode as default. | ||
111 | */ | ||
112 | static int fm_v4l2_fops_open(struct file *file) | ||
113 | { | ||
114 | int ret; | ||
115 | struct fmdev *fmdev = NULL; | ||
116 | |||
117 | /* Don't allow multiple open */ | ||
118 | if (radio_disconnected) { | ||
119 | fmerr("FM device is already opened\n"); | ||
120 | return -EBUSY; | ||
121 | } | ||
122 | |||
123 | fmdev = video_drvdata(file); | ||
124 | |||
125 | ret = fmc_prepare(fmdev); | ||
126 | if (ret < 0) { | ||
127 | fmerr("Unable to prepare FM CORE\n"); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | fmdbg("Load FM RX firmware..\n"); | ||
132 | |||
133 | ret = fmc_set_mode(fmdev, FM_MODE_RX); | ||
134 | if (ret < 0) { | ||
135 | fmerr("Unable to load FM RX firmware\n"); | ||
136 | return ret; | ||
137 | } | ||
138 | radio_disconnected = 1; | ||
139 | |||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static int fm_v4l2_fops_release(struct file *file) | ||
144 | { | ||
145 | int ret; | ||
146 | struct fmdev *fmdev; | ||
147 | |||
148 | fmdev = video_drvdata(file); | ||
149 | if (!radio_disconnected) { | ||
150 | fmdbg("FM device is already closed\n"); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | ret = fmc_set_mode(fmdev, FM_MODE_OFF); | ||
155 | if (ret < 0) { | ||
156 | fmerr("Unable to turn off the chip\n"); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | ret = fmc_release(fmdev); | ||
161 | if (ret < 0) { | ||
162 | fmerr("FM CORE release failed\n"); | ||
163 | return ret; | ||
164 | } | ||
165 | radio_disconnected = 0; | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | /* V4L2 RADIO (/dev/radioX) device IOCTL interfaces */ | ||
171 | static int fm_v4l2_vidioc_querycap(struct file *file, void *priv, | ||
172 | struct v4l2_capability *capability) | ||
173 | { | ||
174 | strlcpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver)); | ||
175 | strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME, | ||
176 | sizeof(capability->card)); | ||
177 | sprintf(capability->bus_info, "UART"); | ||
178 | capability->version = FM_DRV_RADIO_VERSION; | ||
179 | capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | | ||
180 | V4L2_CAP_RADIO | V4L2_CAP_MODULATOR | | ||
181 | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | | ||
182 | V4L2_CAP_RDS_CAPTURE; | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | ||
188 | { | ||
189 | struct fmdev *fmdev = container_of(ctrl->handler, | ||
190 | struct fmdev, ctrl_handler); | ||
191 | |||
192 | switch (ctrl->id) { | ||
193 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
194 | ctrl->val = fm_tx_get_tune_cap_val(fmdev); | ||
195 | break; | ||
196 | default: | ||
197 | fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int fm_v4l2_s_ctrl(struct v4l2_ctrl *ctrl) | ||
205 | { | ||
206 | struct fmdev *fmdev = container_of(ctrl->handler, | ||
207 | struct fmdev, ctrl_handler); | ||
208 | |||
209 | switch (ctrl->id) { | ||
210 | case V4L2_CID_AUDIO_VOLUME: /* set volume */ | ||
211 | return fm_rx_set_volume(fmdev, (u16)ctrl->val); | ||
212 | |||
213 | case V4L2_CID_AUDIO_MUTE: /* set mute */ | ||
214 | return fmc_set_mute_mode(fmdev, (u8)ctrl->val); | ||
215 | |||
216 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
217 | /* set TX power level - ext control */ | ||
218 | return fm_tx_set_pwr_lvl(fmdev, (u8)ctrl->val); | ||
219 | |||
220 | case V4L2_CID_TUNE_PREEMPHASIS: | ||
221 | return fm_tx_set_preemph_filter(fmdev, (u8) ctrl->val); | ||
222 | |||
223 | default: | ||
224 | return -EINVAL; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv, | ||
229 | struct v4l2_audio *audio) | ||
230 | { | ||
231 | memset(audio, 0, sizeof(*audio)); | ||
232 | strcpy(audio->name, "Radio"); | ||
233 | audio->capability = V4L2_AUDCAP_STEREO; | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, | ||
239 | struct v4l2_audio *audio) | ||
240 | { | ||
241 | if (audio->index != 0) | ||
242 | return -EINVAL; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* Get tuner attributes. If current mode is NOT RX, return error */ | ||
248 | static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, | ||
249 | struct v4l2_tuner *tuner) | ||
250 | { | ||
251 | struct fmdev *fmdev = video_drvdata(file); | ||
252 | u32 bottom_freq; | ||
253 | u32 top_freq; | ||
254 | u16 stereo_mono_mode; | ||
255 | u16 rssilvl; | ||
256 | int ret; | ||
257 | |||
258 | if (tuner->index != 0) | ||
259 | return -EINVAL; | ||
260 | |||
261 | if (fmdev->curr_fmmode != FM_MODE_RX) | ||
262 | return -EPERM; | ||
263 | |||
264 | ret = fm_rx_get_band_freq_range(fmdev, &bottom_freq, &top_freq); | ||
265 | if (ret != 0) | ||
266 | return ret; | ||
267 | |||
268 | ret = fm_rx_get_stereo_mono(fmdev, &stereo_mono_mode); | ||
269 | if (ret != 0) | ||
270 | return ret; | ||
271 | |||
272 | ret = fm_rx_get_rssi_level(fmdev, &rssilvl); | ||
273 | if (ret != 0) | ||
274 | return ret; | ||
275 | |||
276 | strcpy(tuner->name, "FM"); | ||
277 | tuner->type = V4L2_TUNER_RADIO; | ||
278 | /* Store rangelow and rangehigh freq in unit of 62.5 Hz */ | ||
279 | tuner->rangelow = bottom_freq * 16; | ||
280 | tuner->rangehigh = top_freq * 16; | ||
281 | tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | | ||
282 | ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); | ||
283 | tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | | ||
284 | V4L2_TUNER_CAP_LOW; | ||
285 | tuner->audmode = (stereo_mono_mode ? | ||
286 | V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO); | ||
287 | |||
288 | /* | ||
289 | * Actual rssi value lies in between -128 to +127. | ||
290 | * Convert this range from 0 to 255 by adding +128 | ||
291 | */ | ||
292 | rssilvl += 128; | ||
293 | |||
294 | /* | ||
295 | * Return signal strength value should be within 0 to 65535. | ||
296 | * Find out correct signal radio by multiplying (65535/255) = 257 | ||
297 | */ | ||
298 | tuner->signal = rssilvl * 257; | ||
299 | tuner->afc = 0; | ||
300 | |||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Set tuner attributes. If current mode is NOT RX, set to RX. | ||
306 | * Currently, we set only audio mode (mono/stereo) and RDS state (on/off). | ||
307 | * Should we set other tuner attributes, too? | ||
308 | */ | ||
309 | static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv, | ||
310 | struct v4l2_tuner *tuner) | ||
311 | { | ||
312 | struct fmdev *fmdev = video_drvdata(file); | ||
313 | u16 aud_mode; | ||
314 | u8 rds_mode; | ||
315 | int ret; | ||
316 | |||
317 | if (tuner->index != 0) | ||
318 | return -EINVAL; | ||
319 | |||
320 | aud_mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ? | ||
321 | FM_STEREO_MODE : FM_MONO_MODE; | ||
322 | rds_mode = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ? | ||
323 | FM_RDS_ENABLE : FM_RDS_DISABLE; | ||
324 | |||
325 | if (fmdev->curr_fmmode != FM_MODE_RX) { | ||
326 | ret = fmc_set_mode(fmdev, FM_MODE_RX); | ||
327 | if (ret < 0) { | ||
328 | fmerr("Failed to set RX mode\n"); | ||
329 | return ret; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | ret = fmc_set_stereo_mono(fmdev, aud_mode); | ||
334 | if (ret < 0) { | ||
335 | fmerr("Failed to set RX stereo/mono mode\n"); | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | ret = fmc_set_rds_mode(fmdev, rds_mode); | ||
340 | if (ret < 0) | ||
341 | fmerr("Failed to set RX RDS mode\n"); | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | /* Get tuner or modulator radio frequency */ | ||
347 | static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv, | ||
348 | struct v4l2_frequency *freq) | ||
349 | { | ||
350 | struct fmdev *fmdev = video_drvdata(file); | ||
351 | int ret; | ||
352 | |||
353 | ret = fmc_get_freq(fmdev, &freq->frequency); | ||
354 | if (ret < 0) { | ||
355 | fmerr("Failed to get frequency\n"); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | /* Frequency unit of 62.5 Hz*/ | ||
360 | freq->frequency = (u32) freq->frequency * 16; | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | /* Set tuner or modulator radio frequency */ | ||
366 | static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv, | ||
367 | struct v4l2_frequency *freq) | ||
368 | { | ||
369 | struct fmdev *fmdev = video_drvdata(file); | ||
370 | |||
371 | /* | ||
372 | * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency | ||
373 | * in units of 62.5 Hz. | ||
374 | */ | ||
375 | freq->frequency = (u32)(freq->frequency / 16); | ||
376 | |||
377 | return fmc_set_freq(fmdev, freq->frequency); | ||
378 | } | ||
379 | |||
380 | /* Set hardware frequency seek. If current mode is NOT RX, set it RX. */ | ||
381 | static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv, | ||
382 | struct v4l2_hw_freq_seek *seek) | ||
383 | { | ||
384 | struct fmdev *fmdev = video_drvdata(file); | ||
385 | int ret; | ||
386 | |||
387 | if (fmdev->curr_fmmode != FM_MODE_RX) { | ||
388 | ret = fmc_set_mode(fmdev, FM_MODE_RX); | ||
389 | if (ret != 0) { | ||
390 | fmerr("Failed to set RX mode\n"); | ||
391 | return ret; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | ret = fm_rx_seek(fmdev, seek->seek_upward, seek->wrap_around, | ||
396 | seek->spacing); | ||
397 | if (ret < 0) | ||
398 | fmerr("RX seek failed - %d\n", ret); | ||
399 | |||
400 | return ret; | ||
401 | } | ||
402 | /* Get modulator attributes. If mode is not TX, return no attributes. */ | ||
403 | static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv, | ||
404 | struct v4l2_modulator *mod) | ||
405 | { | ||
406 | struct fmdev *fmdev = video_drvdata(file);; | ||
407 | |||
408 | if (mod->index != 0) | ||
409 | return -EINVAL; | ||
410 | |||
411 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
412 | return -EPERM; | ||
413 | |||
414 | mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ? | ||
415 | V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) | | ||
416 | ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ? | ||
417 | V4L2_TUNER_SUB_RDS : 0); | ||
418 | |||
419 | mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | | ||
420 | V4L2_TUNER_CAP_LOW; | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* Set modulator attributes. If mode is not TX, set to TX. */ | ||
426 | static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv, | ||
427 | struct v4l2_modulator *mod) | ||
428 | { | ||
429 | struct fmdev *fmdev = video_drvdata(file); | ||
430 | u8 rds_mode; | ||
431 | u16 aud_mode; | ||
432 | int ret; | ||
433 | |||
434 | if (mod->index != 0) | ||
435 | return -EINVAL; | ||
436 | |||
437 | if (fmdev->curr_fmmode != FM_MODE_TX) { | ||
438 | ret = fmc_set_mode(fmdev, FM_MODE_TX); | ||
439 | if (ret != 0) { | ||
440 | fmerr("Failed to set TX mode\n"); | ||
441 | return ret; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ? | ||
446 | FM_STEREO_MODE : FM_MONO_MODE; | ||
447 | rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ? | ||
448 | FM_RDS_ENABLE : FM_RDS_DISABLE; | ||
449 | ret = fm_tx_set_stereo_mono(fmdev, aud_mode); | ||
450 | if (ret < 0) { | ||
451 | fmerr("Failed to set mono/stereo mode for TX\n"); | ||
452 | return ret; | ||
453 | } | ||
454 | ret = fm_tx_set_rds_mode(fmdev, rds_mode); | ||
455 | if (ret < 0) | ||
456 | fmerr("Failed to set rds mode for TX\n"); | ||
457 | |||
458 | return ret; | ||
459 | } | ||
460 | |||
461 | static const struct v4l2_file_operations fm_drv_fops = { | ||
462 | .owner = THIS_MODULE, | ||
463 | .read = fm_v4l2_fops_read, | ||
464 | .write = fm_v4l2_fops_write, | ||
465 | .poll = fm_v4l2_fops_poll, | ||
466 | .unlocked_ioctl = video_ioctl2, | ||
467 | .open = fm_v4l2_fops_open, | ||
468 | .release = fm_v4l2_fops_release, | ||
469 | }; | ||
470 | |||
471 | static const struct v4l2_ctrl_ops fm_ctrl_ops = { | ||
472 | .s_ctrl = fm_v4l2_s_ctrl, | ||
473 | .g_volatile_ctrl = fm_g_volatile_ctrl, | ||
474 | }; | ||
475 | static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = { | ||
476 | .vidioc_querycap = fm_v4l2_vidioc_querycap, | ||
477 | .vidioc_g_audio = fm_v4l2_vidioc_g_audio, | ||
478 | .vidioc_s_audio = fm_v4l2_vidioc_s_audio, | ||
479 | .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner, | ||
480 | .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner, | ||
481 | .vidioc_g_frequency = fm_v4l2_vidioc_g_freq, | ||
482 | .vidioc_s_frequency = fm_v4l2_vidioc_s_freq, | ||
483 | .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek, | ||
484 | .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator, | ||
485 | .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator | ||
486 | }; | ||
487 | |||
488 | /* V4L2 RADIO device parent structure */ | ||
489 | static struct video_device fm_viddev_template = { | ||
490 | .fops = &fm_drv_fops, | ||
491 | .ioctl_ops = &fm_drv_ioctl_ops, | ||
492 | .name = FM_DRV_NAME, | ||
493 | .release = video_device_release, | ||
494 | }; | ||
495 | |||
496 | int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) | ||
497 | { | ||
498 | struct v4l2_ctrl *ctrl; | ||
499 | int ret; | ||
500 | |||
501 | /* Init mutex for core locking */ | ||
502 | mutex_init(&fmdev->mutex); | ||
503 | |||
504 | /* Allocate new video device */ | ||
505 | gradio_dev = video_device_alloc(); | ||
506 | if (NULL == gradio_dev) { | ||
507 | fmerr("Can't allocate video device\n"); | ||
508 | return -ENOMEM; | ||
509 | } | ||
510 | |||
511 | /* Setup FM driver's V4L2 properties */ | ||
512 | memcpy(gradio_dev, &fm_viddev_template, sizeof(fm_viddev_template)); | ||
513 | |||
514 | video_set_drvdata(gradio_dev, fmdev); | ||
515 | |||
516 | gradio_dev->lock = &fmdev->mutex; | ||
517 | |||
518 | /* Register with V4L2 subsystem as RADIO device */ | ||
519 | if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) { | ||
520 | video_device_release(gradio_dev); | ||
521 | fmerr("Could not register video device\n"); | ||
522 | return -ENOMEM; | ||
523 | } | ||
524 | |||
525 | fmdev->radio_dev = gradio_dev; | ||
526 | |||
527 | /* Register to v4l2 ctrl handler framework */ | ||
528 | fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler; | ||
529 | |||
530 | ret = v4l2_ctrl_handler_init(&fmdev->ctrl_handler, 5); | ||
531 | if (ret < 0) { | ||
532 | fmerr("(fmdev): Can't init ctrl handler\n"); | ||
533 | v4l2_ctrl_handler_free(&fmdev->ctrl_handler); | ||
534 | return -EBUSY; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Following controls are handled by V4L2 control framework. | ||
539 | * Added in ascending ID order. | ||
540 | */ | ||
541 | v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
542 | V4L2_CID_AUDIO_VOLUME, FM_RX_VOLUME_MIN, | ||
543 | FM_RX_VOLUME_MAX, 1, FM_RX_VOLUME_MAX); | ||
544 | |||
545 | v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
546 | V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); | ||
547 | |||
548 | v4l2_ctrl_new_std_menu(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
549 | V4L2_CID_TUNE_PREEMPHASIS, V4L2_PREEMPHASIS_75_uS, | ||
550 | 0, V4L2_PREEMPHASIS_75_uS); | ||
551 | |||
552 | v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
553 | V4L2_CID_TUNE_POWER_LEVEL, FM_PWR_LVL_LOW, | ||
554 | FM_PWR_LVL_HIGH, 1, FM_PWR_LVL_HIGH); | ||
555 | |||
556 | ctrl = v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, | ||
557 | V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, | ||
558 | 255, 1, 255); | ||
559 | |||
560 | if (ctrl) | ||
561 | ctrl->is_volatile = 1; | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | void *fm_v4l2_deinit_video_device(void) | ||
567 | { | ||
568 | struct fmdev *fmdev; | ||
569 | |||
570 | |||
571 | fmdev = video_get_drvdata(gradio_dev); | ||
572 | |||
573 | /* Unregister to v4l2 ctrl handler framework*/ | ||
574 | v4l2_ctrl_handler_free(&fmdev->ctrl_handler); | ||
575 | |||
576 | /* Unregister RADIO device from V4L2 subsystem */ | ||
577 | video_unregister_device(gradio_dev); | ||
578 | |||
579 | return fmdev; | ||
580 | } | ||
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.h b/drivers/media/radio/wl128x/fmdrv_v4l2.h new file mode 100644 index 000000000000..0ba79d745e2f --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * | ||
4 | * FM V4L2 module header. | ||
5 | * | ||
6 | * Copyright (C) 2011 Texas Instruments | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #ifndef _FMDRV_V4L2_H | ||
24 | #define _FMDRV_V4L2_H | ||
25 | |||
26 | #include <media/v4l2-ioctl.h> | ||
27 | #include <media/v4l2-common.h> | ||
28 | #include <media/v4l2-ctrls.h> | ||
29 | |||
30 | int fm_v4l2_init_video_device(struct fmdev *, int); | ||
31 | void *fm_v4l2_deinit_video_device(void); | ||
32 | |||
33 | #endif | ||