diff options
author | Mike Isely <isely@pobox.com> | 2006-12-27 21:30:13 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-02-21 10:34:33 -0500 |
commit | 1bde02891b3d4d17ee743584bb49ed5f275dff01 (patch) | |
tree | 11e758f74dbddf7b19f6a4da0bb8047cf665a258 /drivers/media/video/pvrusb2/pvrusb2-hdw.c | |
parent | 5549f54f46c2375761f42cd2741364316e3b2a13 (diff) |
V4L/DVB (5051): Pvrusb2: Better radio versus tv frequency handling
Separate track radio versus tv frequency so that when we switch modes
we can also switch to a sane frequency appropriate for the mode. Also
implement logic to automate mode switching in certain cases.
Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c')
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-hdw.c | 213 |
1 files changed, 186 insertions, 27 deletions
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 04e746932217..38e165913dd6 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c | |||
@@ -37,10 +37,9 @@ | |||
37 | #include "pvrusb2-encoder.h" | 37 | #include "pvrusb2-encoder.h" |
38 | #include "pvrusb2-debug.h" | 38 | #include "pvrusb2-debug.h" |
39 | 39 | ||
40 | #define TV_MIN_FREQ 55250000L | 40 | #define TV_MIN_FREQ 55250000L |
41 | #define TV_MAX_FREQ 850000000L | 41 | #define TV_MAX_FREQ 850000000L |
42 | 42 | #define RADIO_MIN_FREQ 87000000L | |
43 | #define RADIO_MIN_FREQ 87000000L | ||
44 | #define RADIO_MAX_FREQ 108000000L | 43 | #define RADIO_MAX_FREQ 108000000L |
45 | 44 | ||
46 | struct usb_device_id pvr2_device_table[] = { | 45 | struct usb_device_id pvr2_device_table[] = { |
@@ -95,6 +94,7 @@ static int procreload = 0; | |||
95 | static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; | 94 | static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; |
96 | static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; | 95 | static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; |
97 | static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; | 96 | static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; |
97 | static int auto_mode_switch[PVR_NUM]; | ||
98 | static int init_pause_msec = 0; | 98 | static int init_pause_msec = 0; |
99 | 99 | ||
100 | module_param(ctlchg, int, S_IRUGO|S_IWUSR); | 100 | module_param(ctlchg, int, S_IRUGO|S_IWUSR); |
@@ -112,6 +112,8 @@ module_param_array(video_std, int, NULL, 0444); | |||
112 | MODULE_PARM_DESC(video_std,"specify initial video standard"); | 112 | MODULE_PARM_DESC(video_std,"specify initial video standard"); |
113 | module_param_array(tolerance, int, NULL, 0444); | 113 | module_param_array(tolerance, int, NULL, 0444); |
114 | MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); | 114 | MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); |
115 | module_param_array(auto_mode_switch, int, NULL, 0444); | ||
116 | MODULE_PARM_DESC(auto_mode_switch,"Enable TV/Radio automatic mode switch based on freq"); | ||
115 | 117 | ||
116 | #define PVR2_CTL_WRITE_ENDPOINT 0x01 | 118 | #define PVR2_CTL_WRITE_ENDPOINT 0x01 |
117 | #define PVR2_CTL_READ_ENDPOINT 0x81 | 119 | #define PVR2_CTL_READ_ENDPOINT 0x81 |
@@ -258,6 +260,7 @@ static const char *control_values_subsystem[] = { | |||
258 | [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", | 260 | [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", |
259 | }; | 261 | }; |
260 | 262 | ||
263 | static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); | ||
261 | static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); | 264 | static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); |
262 | static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); | 265 | static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); |
263 | static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); | 266 | static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); |
@@ -292,8 +295,21 @@ static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) | |||
292 | static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) | 295 | static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) |
293 | { | 296 | { |
294 | struct pvr2_hdw *hdw = cptr->hdw; | 297 | struct pvr2_hdw *hdw = cptr->hdw; |
295 | if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { | 298 | unsigned int slotId = hdw->freqProgSlot; |
296 | hdw->freqTable[hdw->freqProgSlot-1] = v; | 299 | if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) { |
300 | hdw->freqTable[slotId-1] = v; | ||
301 | /* Handle side effects correctly - if we're tuned to this | ||
302 | slot, then forgot the slot id relation since the stored | ||
303 | frequency has been changed. */ | ||
304 | if (hdw->freqSelector) { | ||
305 | if (hdw->freqSlotRadio == slotId) { | ||
306 | hdw->freqSlotRadio = 0; | ||
307 | } | ||
308 | } else { | ||
309 | if (hdw->freqSlotTelevision == slotId) { | ||
310 | hdw->freqSlotTelevision = 0; | ||
311 | } | ||
312 | } | ||
297 | } | 313 | } |
298 | return 0; | 314 | return 0; |
299 | } | 315 | } |
@@ -315,28 +331,32 @@ static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) | |||
315 | 331 | ||
316 | static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) | 332 | static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) |
317 | { | 333 | { |
318 | *vp = cptr->hdw->freqSlot; | 334 | struct pvr2_hdw *hdw = cptr->hdw; |
335 | *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision; | ||
319 | return 0; | 336 | return 0; |
320 | } | 337 | } |
321 | 338 | ||
322 | static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v) | 339 | static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId) |
323 | { | 340 | { |
324 | unsigned freq = 0; | 341 | unsigned freq = 0; |
325 | struct pvr2_hdw *hdw = cptr->hdw; | 342 | struct pvr2_hdw *hdw = cptr->hdw; |
326 | hdw->freqSlot = v; | 343 | if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0; |
327 | if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) { | 344 | if (slotId > 0) { |
328 | freq = hdw->freqTable[hdw->freqSlot-1]; | 345 | freq = hdw->freqTable[slotId-1]; |
329 | } | 346 | if (!freq) return 0; |
330 | if (freq && (freq != hdw->freqVal)) { | 347 | pvr2_hdw_set_cur_freq(hdw,freq); |
331 | hdw->freqVal = freq; | 348 | } |
332 | hdw->freqDirty = !0; | 349 | if (hdw->freqSelector) { |
350 | hdw->freqSlotRadio = slotId; | ||
351 | } else { | ||
352 | hdw->freqSlotTelevision = slotId; | ||
333 | } | 353 | } |
334 | return 0; | 354 | return 0; |
335 | } | 355 | } |
336 | 356 | ||
337 | static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) | 357 | static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) |
338 | { | 358 | { |
339 | *vp = cptr->hdw->freqVal; | 359 | *vp = pvr2_hdw_get_cur_freq(cptr->hdw); |
340 | return 0; | 360 | return 0; |
341 | } | 361 | } |
342 | 362 | ||
@@ -352,10 +372,7 @@ static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) | |||
352 | 372 | ||
353 | static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) | 373 | static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) |
354 | { | 374 | { |
355 | struct pvr2_hdw *hdw = cptr->hdw; | 375 | pvr2_hdw_set_cur_freq(cptr->hdw,v); |
356 | hdw->freqVal = v; | ||
357 | hdw->freqDirty = !0; | ||
358 | hdw->freqSlot = 0; | ||
359 | return 0; | 376 | return 0; |
360 | } | 377 | } |
361 | 378 | ||
@@ -381,13 +398,51 @@ static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) | |||
381 | return 0; | 398 | return 0; |
382 | } | 399 | } |
383 | 400 | ||
384 | static int ctrl_freq_check(struct pvr2_ctrl *cptr,int v) | 401 | static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) |
385 | { | 402 | { |
386 | if (cptr->hdw->input_val == PVR2_CVAL_INPUT_RADIO) { | 403 | *vp = cptr->hdw->input_val; |
387 | return ((v >= RADIO_MIN_FREQ) && (v <= RADIO_MAX_FREQ)); | 404 | return 0; |
388 | } else { | 405 | } |
389 | return ((v >= TV_MIN_FREQ) && (v <= TV_MAX_FREQ)); | 406 | |
407 | static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) | ||
408 | { | ||
409 | struct pvr2_hdw *hdw = cptr->hdw; | ||
410 | |||
411 | if (hdw->input_val != v) { | ||
412 | hdw->input_val = v; | ||
413 | hdw->input_dirty = !0; | ||
414 | } | ||
415 | |||
416 | /* Handle side effects - if we switch to a mode that needs the RF | ||
417 | tuner, then select the right frequency choice as well and mark | ||
418 | it dirty. */ | ||
419 | if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { | ||
420 | hdw->freqSelector = 0; | ||
421 | hdw->freqDirty = !0; | ||
422 | } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) { | ||
423 | hdw->freqSelector = 1; | ||
424 | hdw->freqDirty = !0; | ||
390 | } | 425 | } |
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) | ||
430 | { | ||
431 | return cptr->hdw->input_dirty != 0; | ||
432 | } | ||
433 | |||
434 | static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr) | ||
435 | { | ||
436 | cptr->hdw->input_dirty = 0; | ||
437 | } | ||
438 | |||
439 | static int ctrl_freq_check(struct pvr2_ctrl *cptr,int v) | ||
440 | { | ||
441 | /* Both ranges are simultaneously considered legal, in order to | ||
442 | permit implicit mode switching, i.e. set a frequency in the | ||
443 | other range and the mode will switch */ | ||
444 | return (((v >= RADIO_MIN_FREQ) && (v <= RADIO_MAX_FREQ)) || | ||
445 | ((v >= TV_MIN_FREQ) && (v <= TV_MAX_FREQ))); | ||
391 | } | 446 | } |
392 | 447 | ||
393 | static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) | 448 | static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) |
@@ -675,11 +730,11 @@ VCREATE_FUNCS(balance) | |||
675 | VCREATE_FUNCS(bass) | 730 | VCREATE_FUNCS(bass) |
676 | VCREATE_FUNCS(treble) | 731 | VCREATE_FUNCS(treble) |
677 | VCREATE_FUNCS(mute) | 732 | VCREATE_FUNCS(mute) |
678 | VCREATE_FUNCS(input) | ||
679 | VCREATE_FUNCS(audiomode) | 733 | VCREATE_FUNCS(audiomode) |
680 | VCREATE_FUNCS(res_hor) | 734 | VCREATE_FUNCS(res_hor) |
681 | VCREATE_FUNCS(res_ver) | 735 | VCREATE_FUNCS(res_ver) |
682 | VCREATE_FUNCS(srate) | 736 | VCREATE_FUNCS(srate) |
737 | VCREATE_FUNCS(automodeswitch) | ||
683 | 738 | ||
684 | /* Table definition of all controls which can be manipulated */ | 739 | /* Table definition of all controls which can be manipulated */ |
685 | static const struct pvr2_ctl_info control_defs[] = { | 740 | static const struct pvr2_ctl_info control_defs[] = { |
@@ -779,6 +834,13 @@ static const struct pvr2_ctl_info control_defs[] = { | |||
779 | .get_max_value = ctrl_vres_max_get, | 834 | .get_max_value = ctrl_vres_max_get, |
780 | .get_min_value = ctrl_vres_min_get, | 835 | .get_min_value = ctrl_vres_min_get, |
781 | },{ | 836 | },{ |
837 | .v4l_id = V4L2_CID_AUDIO_MUTE, | ||
838 | .desc = "Automatic TV / Radio mode switch based on frequency", | ||
839 | .name = "auto_mode_switch", | ||
840 | .default_value = 0, | ||
841 | DEFREF(automodeswitch), | ||
842 | DEFBOOL, | ||
843 | },{ | ||
782 | .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, | 844 | .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, |
783 | .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, | 845 | .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, |
784 | .desc = "Audio Sampling Frequency", | 846 | .desc = "Audio Sampling Frequency", |
@@ -789,7 +851,7 @@ static const struct pvr2_ctl_info control_defs[] = { | |||
789 | .desc = "Tuner Frequency (Hz)", | 851 | .desc = "Tuner Frequency (Hz)", |
790 | .name = "frequency", | 852 | .name = "frequency", |
791 | .internal_id = PVR2_CID_FREQUENCY, | 853 | .internal_id = PVR2_CID_FREQUENCY, |
792 | .default_value = 175250000L, | 854 | .default_value = 0, |
793 | .set_value = ctrl_freq_set, | 855 | .set_value = ctrl_freq_set, |
794 | .get_value = ctrl_freq_get, | 856 | .get_value = ctrl_freq_get, |
795 | .is_dirty = ctrl_freq_is_dirty, | 857 | .is_dirty = ctrl_freq_is_dirty, |
@@ -812,6 +874,11 @@ static const struct pvr2_ctl_info control_defs[] = { | |||
812 | .set_value = ctrl_channelfreq_set, | 874 | .set_value = ctrl_channelfreq_set, |
813 | .get_value = ctrl_channelfreq_get, | 875 | .get_value = ctrl_channelfreq_get, |
814 | DEFINT(TV_MIN_FREQ,TV_MAX_FREQ), | 876 | DEFINT(TV_MIN_FREQ,TV_MAX_FREQ), |
877 | /* Hook in check for input value (tv/radio) and adjust | ||
878 | max/min values accordingly */ | ||
879 | .check_value = ctrl_freq_check, | ||
880 | .get_max_value = ctrl_freq_max_get, | ||
881 | .get_min_value = ctrl_freq_min_get, | ||
815 | },{ | 882 | },{ |
816 | .desc = "Channel Program ID", | 883 | .desc = "Channel Program ID", |
817 | .name = "freq_table_channel", | 884 | .name = "freq_table_channel", |
@@ -908,6 +975,83 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw) | |||
908 | return hdw->serial_number; | 975 | return hdw->serial_number; |
909 | } | 976 | } |
910 | 977 | ||
978 | unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) | ||
979 | { | ||
980 | return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; | ||
981 | } | ||
982 | |||
983 | /* Set the currently tuned frequency and account for all possible | ||
984 | driver-core side effects of this action. */ | ||
985 | void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) | ||
986 | { | ||
987 | int mode = 0; | ||
988 | |||
989 | /* If hdw->automodeswitch_val is set, then we do something clever: | ||
990 | Look at the desired frequency and see if it looks like FM or TV. | ||
991 | Execute a possible mode switch based on this result. Otherwise | ||
992 | we use the current input setting to determine which frequency | ||
993 | register we need to adjust. */ | ||
994 | if (hdw->automodeswitch_val) { | ||
995 | /* Note that since FM RADIO frequency range sits *inside* | ||
996 | the TV spectrum that we must therefore check the radio | ||
997 | range first... */ | ||
998 | if ((val >= RADIO_MIN_FREQ) && (val <= RADIO_MAX_FREQ)) { | ||
999 | mode = 1; | ||
1000 | } else if ((val >= TV_MIN_FREQ) && (val <= TV_MAX_FREQ)) { | ||
1001 | mode = 2; | ||
1002 | } | ||
1003 | } else { | ||
1004 | if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { | ||
1005 | mode = 1; | ||
1006 | } else { | ||
1007 | mode = 2; | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | switch (mode) { | ||
1012 | case 1: | ||
1013 | if (hdw->freqSelector) { | ||
1014 | /* Swing over to radio frequency selection */ | ||
1015 | hdw->freqSelector = 0; | ||
1016 | hdw->freqDirty = !0; | ||
1017 | } | ||
1018 | if (hdw->input_val == PVR2_CVAL_INPUT_TV) { | ||
1019 | /* Force switch to radio mode */ | ||
1020 | hdw->input_val = PVR2_CVAL_INPUT_RADIO; | ||
1021 | hdw->input_dirty = !0; | ||
1022 | } | ||
1023 | if (hdw->freqValRadio != val) { | ||
1024 | hdw->freqValRadio = val; | ||
1025 | hdw->freqSlotRadio = 0; | ||
1026 | if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { | ||
1027 | hdw->freqDirty = !0; | ||
1028 | } | ||
1029 | } | ||
1030 | break; | ||
1031 | case 2: | ||
1032 | if (!(hdw->freqSelector)) { | ||
1033 | /* Swing over to television frequency selection */ | ||
1034 | hdw->freqSelector = 1; | ||
1035 | hdw->freqDirty = !0; | ||
1036 | } | ||
1037 | if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { | ||
1038 | /* Force switch to television mode */ | ||
1039 | hdw->input_val = PVR2_CVAL_INPUT_TV; | ||
1040 | hdw->input_dirty = !0; | ||
1041 | } | ||
1042 | if (hdw->freqValTelevision != val) { | ||
1043 | hdw->freqValTelevision = val; | ||
1044 | hdw->freqSlotTelevision = 0; | ||
1045 | if (hdw->input_val == PVR2_CVAL_INPUT_TV) { | ||
1046 | hdw->freqDirty = !0; | ||
1047 | } | ||
1048 | } | ||
1049 | break; | ||
1050 | default: | ||
1051 | break; | ||
1052 | } | ||
1053 | } | ||
1054 | |||
911 | int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) | 1055 | int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) |
912 | { | 1056 | { |
913 | return hdw->unit_number; | 1057 | return hdw->unit_number; |
@@ -1647,6 +1791,21 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) | |||
1647 | cptr->info->set_value(cptr,~0,cptr->info->default_value); | 1791 | cptr->info->set_value(cptr,~0,cptr->info->default_value); |
1648 | } | 1792 | } |
1649 | 1793 | ||
1794 | /* Set up special default values for the television and radio | ||
1795 | frequencies here. It's not really important what these defaults | ||
1796 | are, but I set them to something usable in the Chicago area just | ||
1797 | to make driver testing a little easier. */ | ||
1798 | |||
1799 | /* US Broadcast channel 7 (175.25 MHz) */ | ||
1800 | hdw->freqValTelevision = 175250000L; | ||
1801 | /* 104.3 MHz, a usable FM station for my area */ | ||
1802 | hdw->freqValRadio = 104300000L; | ||
1803 | |||
1804 | /* Default value for auto mode switch based on module option */ | ||
1805 | if ((hdw->unit_number >= 0) && (hdw->unit_number < PVR_NUM)) { | ||
1806 | hdw->automodeswitch_val = auto_mode_switch[hdw->unit_number]; | ||
1807 | } | ||
1808 | |||
1650 | // Do not use pvr2_reset_ctl_endpoints() here. It is not | 1809 | // Do not use pvr2_reset_ctl_endpoints() here. It is not |
1651 | // thread-safe against the normal pvr2_send_request() mechanism. | 1810 | // thread-safe against the normal pvr2_send_request() mechanism. |
1652 | // (We should make it thread safe). | 1811 | // (We should make it thread safe). |