From 1bde02891b3d4d17ee743584bb49ed5f275dff01 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 27 Dec 2006 23:30:13 -0300 Subject: [PATCH] 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 Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 11 +- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 213 ++++++++++++++++++--- drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 2 +- 3 files changed, 196 insertions(+), 30 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index da29ae2fb05..b9df52c882a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -214,7 +214,6 @@ struct pvr2_hdw { /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; unsigned int freqProgSlot; - unsigned int freqSlot; /* Stuff for handling low level control interaction with device */ struct mutex ctl_lock_mutex; @@ -260,7 +259,11 @@ struct pvr2_hdw { /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; - unsigned int freqVal; + unsigned int freqValTelevision; /* Current freq for tv mode */ + unsigned int freqValRadio; /* Current freq for radio mode */ + unsigned int freqSlotTelevision; /* Current slot for tv mode */ + unsigned int freqSlotRadio; /* Current slot for radio mode */ + unsigned int freqSelector; /* 0=radio 1=television */ int freqDirty; /* Video standard handling */ @@ -323,6 +326,7 @@ struct pvr2_hdw { VCREATE_DATA(res_hor); VCREATE_DATA(res_ver); VCREATE_DATA(srate); + VCREATE_DATA(automodeswitch); #undef VCREATE_DATA struct pvr2_ctld_info *mpeg_ctrl_info; @@ -331,6 +335,9 @@ struct pvr2_hdw { unsigned int control_cnt; }; +/* This function gets the current frequency */ +unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); + #endif /* __PVRUSB2_HDW_INTERNAL_H */ /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 04e74693221..38e165913dd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -37,10 +37,9 @@ #include "pvrusb2-encoder.h" #include "pvrusb2-debug.h" -#define TV_MIN_FREQ 55250000L -#define TV_MAX_FREQ 850000000L - -#define RADIO_MIN_FREQ 87000000L +#define TV_MIN_FREQ 55250000L +#define TV_MAX_FREQ 850000000L +#define RADIO_MIN_FREQ 87000000L #define RADIO_MAX_FREQ 108000000L struct usb_device_id pvr2_device_table[] = { @@ -95,6 +94,7 @@ static int procreload = 0; static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; +static int auto_mode_switch[PVR_NUM]; static int init_pause_msec = 0; module_param(ctlchg, int, S_IRUGO|S_IWUSR); @@ -112,6 +112,8 @@ module_param_array(video_std, int, NULL, 0444); MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); +module_param_array(auto_mode_switch, int, NULL, 0444); +MODULE_PARM_DESC(auto_mode_switch,"Enable TV/Radio automatic mode switch based on freq"); #define PVR2_CTL_WRITE_ENDPOINT 0x01 #define PVR2_CTL_READ_ENDPOINT 0x81 @@ -258,6 +260,7 @@ static const char *control_values_subsystem[] = { [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", }; +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); 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) static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) { struct pvr2_hdw *hdw = cptr->hdw; - if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { - hdw->freqTable[hdw->freqProgSlot-1] = v; + unsigned int slotId = hdw->freqProgSlot; + if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) { + hdw->freqTable[slotId-1] = v; + /* Handle side effects correctly - if we're tuned to this + slot, then forgot the slot id relation since the stored + frequency has been changed. */ + if (hdw->freqSelector) { + if (hdw->freqSlotRadio == slotId) { + hdw->freqSlotRadio = 0; + } + } else { + if (hdw->freqSlotTelevision == slotId) { + hdw->freqSlotTelevision = 0; + } + } } return 0; } @@ -315,28 +331,32 @@ static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->freqSlot; + struct pvr2_hdw *hdw = cptr->hdw; + *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision; return 0; } -static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v) +static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId) { unsigned freq = 0; struct pvr2_hdw *hdw = cptr->hdw; - hdw->freqSlot = v; - if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) { - freq = hdw->freqTable[hdw->freqSlot-1]; - } - if (freq && (freq != hdw->freqVal)) { - hdw->freqVal = freq; - hdw->freqDirty = !0; + if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0; + if (slotId > 0) { + freq = hdw->freqTable[slotId-1]; + if (!freq) return 0; + pvr2_hdw_set_cur_freq(hdw,freq); + } + if (hdw->freqSelector) { + hdw->freqSlotRadio = slotId; + } else { + hdw->freqSlotTelevision = slotId; } return 0; } static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->freqVal; + *vp = pvr2_hdw_get_cur_freq(cptr->hdw); return 0; } @@ -352,10 +372,7 @@ static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - hdw->freqVal = v; - hdw->freqDirty = !0; - hdw->freqSlot = 0; + pvr2_hdw_set_cur_freq(cptr->hdw,v); return 0; } @@ -381,13 +398,51 @@ static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) return 0; } -static int ctrl_freq_check(struct pvr2_ctrl *cptr,int v) +static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) { - if (cptr->hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - return ((v >= RADIO_MIN_FREQ) && (v <= RADIO_MAX_FREQ)); - } else { - return ((v >= TV_MIN_FREQ) && (v <= TV_MAX_FREQ)); + *vp = cptr->hdw->input_val; + return 0; +} + +static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; } + return 0; +} + +static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->input_dirty != 0; +} + +static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr) +{ + cptr->hdw->input_dirty = 0; +} + +static int ctrl_freq_check(struct pvr2_ctrl *cptr,int v) +{ + /* Both ranges are simultaneously considered legal, in order to + permit implicit mode switching, i.e. set a frequency in the + other range and the mode will switch */ + return (((v >= RADIO_MIN_FREQ) && (v <= RADIO_MAX_FREQ)) || + ((v >= TV_MIN_FREQ) && (v <= TV_MAX_FREQ))); } static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) @@ -675,11 +730,11 @@ VCREATE_FUNCS(balance) VCREATE_FUNCS(bass) VCREATE_FUNCS(treble) VCREATE_FUNCS(mute) -VCREATE_FUNCS(input) VCREATE_FUNCS(audiomode) VCREATE_FUNCS(res_hor) VCREATE_FUNCS(res_ver) VCREATE_FUNCS(srate) +VCREATE_FUNCS(automodeswitch) /* Table definition of all controls which can be manipulated */ static const struct pvr2_ctl_info control_defs[] = { @@ -779,6 +834,13 @@ static const struct pvr2_ctl_info control_defs[] = { .get_max_value = ctrl_vres_max_get, .get_min_value = ctrl_vres_min_get, },{ + .v4l_id = V4L2_CID_AUDIO_MUTE, + .desc = "Automatic TV / Radio mode switch based on frequency", + .name = "auto_mode_switch", + .default_value = 0, + DEFREF(automodeswitch), + DEFBOOL, + },{ .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, .desc = "Audio Sampling Frequency", @@ -789,7 +851,7 @@ static const struct pvr2_ctl_info control_defs[] = { .desc = "Tuner Frequency (Hz)", .name = "frequency", .internal_id = PVR2_CID_FREQUENCY, - .default_value = 175250000L, + .default_value = 0, .set_value = ctrl_freq_set, .get_value = ctrl_freq_get, .is_dirty = ctrl_freq_is_dirty, @@ -812,6 +874,11 @@ static const struct pvr2_ctl_info control_defs[] = { .set_value = ctrl_channelfreq_set, .get_value = ctrl_channelfreq_get, DEFINT(TV_MIN_FREQ,TV_MAX_FREQ), + /* Hook in check for input value (tv/radio) and adjust + max/min values accordingly */ + .check_value = ctrl_freq_check, + .get_max_value = ctrl_freq_max_get, + .get_min_value = ctrl_freq_min_get, },{ .desc = "Channel Program ID", .name = "freq_table_channel", @@ -908,6 +975,83 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw) return hdw->serial_number; } +unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) +{ + return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; +} + +/* Set the currently tuned frequency and account for all possible + driver-core side effects of this action. */ +void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +{ + int mode = 0; + + /* If hdw->automodeswitch_val is set, then we do something clever: + Look at the desired frequency and see if it looks like FM or TV. + Execute a possible mode switch based on this result. Otherwise + we use the current input setting to determine which frequency + register we need to adjust. */ + if (hdw->automodeswitch_val) { + /* Note that since FM RADIO frequency range sits *inside* + the TV spectrum that we must therefore check the radio + range first... */ + if ((val >= RADIO_MIN_FREQ) && (val <= RADIO_MAX_FREQ)) { + mode = 1; + } else if ((val >= TV_MIN_FREQ) && (val <= TV_MAX_FREQ)) { + mode = 2; + } + } else { + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + mode = 1; + } else { + mode = 2; + } + } + + switch (mode) { + case 1: + if (hdw->freqSelector) { + /* Swing over to radio frequency selection */ + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } + if (hdw->input_val == PVR2_CVAL_INPUT_TV) { + /* Force switch to radio mode */ + hdw->input_val = PVR2_CVAL_INPUT_RADIO; + hdw->input_dirty = !0; + } + if (hdw->freqValRadio != val) { + hdw->freqValRadio = val; + hdw->freqSlotRadio = 0; + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqDirty = !0; + } + } + break; + case 2: + if (!(hdw->freqSelector)) { + /* Swing over to television frequency selection */ + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + /* Force switch to television mode */ + hdw->input_val = PVR2_CVAL_INPUT_TV; + hdw->input_dirty = !0; + } + if (hdw->freqValTelevision != val) { + hdw->freqValTelevision = val; + hdw->freqSlotTelevision = 0; + if (hdw->input_val == PVR2_CVAL_INPUT_TV) { + hdw->freqDirty = !0; + } + } + break; + default: + break; + } +} + int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) { return hdw->unit_number; @@ -1647,6 +1791,21 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->info->set_value(cptr,~0,cptr->info->default_value); } + /* Set up special default values for the television and radio + frequencies here. It's not really important what these defaults + are, but I set them to something usable in the Chicago area just + to make driver testing a little easier. */ + + /* US Broadcast channel 7 (175.25 MHz) */ + hdw->freqValTelevision = 175250000L; + /* 104.3 MHz, a usable FM station for my area */ + hdw->freqValRadio = 104300000L; + + /* Default value for auto mode switch based on module option */ + if ((hdw->unit_number >= 0) && (hdw->unit_number < PVR_NUM)) { + hdw->automodeswitch_val = auto_mode_switch[hdw->unit_number]; + } + // Do not use pvr2_reset_ctl_endpoints() here. It is not // thread-safe against the normal pvr2_send_request() mechanism. // (We should make it thread safe). diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index c885a9c25e7..a6a9e414231 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -143,7 +143,7 @@ static void set_frequency(struct pvr2_hdw *hdw) { unsigned long fv; struct v4l2_frequency freq; - fv = hdw->freqVal; + fv = pvr2_hdw_get_cur_freq(hdw); pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); memset(&freq,0,sizeof(freq)); if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { -- 2.11.4.GIT