]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/pvrusb2/pvrusb2-hdw.c
V4L/DVB (5053): Pvrusb2: Change default volume to something sane
[linux-2.6-omap-h63xx.git] / drivers / media / video / pvrusb2 / pvrusb2-hdw.c
index d2004965187bf54e47455a067881abf27f444950..bb4c5150a4df93d71b859e75263f2d3696a57156 100644 (file)
 #include "pvrusb2-encoder.h"
 #include "pvrusb2-debug.h"
 
+#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[] = {
        [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
        [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
@@ -89,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);
@@ -106,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
@@ -159,9 +167,6 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = {
        },{
                .strid = "video_gop_closure",
                .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
-       },{
-               .strid = "video_pulldown",
-               .id = V4L2_CID_MPEG_VIDEO_PULLDOWN,
        },{
                .strid = "video_bitrate_mode",
                .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
@@ -255,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);
@@ -289,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;
 }
@@ -312,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;
 }
 
@@ -349,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;
 }
 
@@ -378,6 +398,75 @@ static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
        return 0;
 }
 
+static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
+{
+       *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)
+{
+       /* Actual maximum depends on radio/tv mode */
+       if (cptr->hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+               *vp = RADIO_MAX_FREQ;
+       } else {
+               *vp = TV_MAX_FREQ;
+       }
+       return 0;
+}
+
+static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
+{
+       /* Actual minimum depends on radio/tv mode */
+       if (cptr->hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+               *vp = RADIO_MIN_FREQ;
+       } else {
+               *vp = TV_MIN_FREQ;
+       }
+       return 0;
+}
+
 static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
 {
        return cptr->hdw->enc_stale != 0;
@@ -641,14 +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)
-
-#define MIN_FREQ 55250000L
-#define MAX_FREQ 850000000L
+VCREATE_FUNCS(automodeswitch)
 
 /* Table definition of all controls which can be manipulated */
 static const struct pvr2_ctl_info control_defs[] = {
@@ -684,7 +770,7 @@ static const struct pvr2_ctl_info control_defs[] = {
                .v4l_id = V4L2_CID_AUDIO_VOLUME,
                .desc = "Volume",
                .name = "volume",
-               .default_value = 65535,
+               .default_value = 62000,
                DEFREF(volume),
                DEFINT(0,65535),
        },{
@@ -747,6 +833,13 @@ static const struct pvr2_ctl_info control_defs[] = {
                   depending on the standard. */
                .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,
@@ -758,12 +851,17 @@ 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,
                .clear_dirty = ctrl_freq_clear_dirty,
-               DEFINT(MIN_FREQ,MAX_FREQ),
+               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",
                .name = "channel",
@@ -775,7 +873,12 @@ static const struct pvr2_ctl_info control_defs[] = {
                .name = "freq_table_value",
                .set_value = ctrl_channelfreq_set,
                .get_value = ctrl_channelfreq_get,
-               DEFINT(MIN_FREQ,MAX_FREQ),
+               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",
@@ -872,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;
@@ -1611,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).
@@ -1872,7 +2067,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
 
        hdw->eeprom_addr = -1;
        hdw->unit_number = -1;
-       hdw->v4l_minor_number = -1;
+       hdw->v4l_minor_number_video = -1;
+       hdw->v4l_minor_number_vbi = -1;
+       hdw->v4l_minor_number_radio = -1;
        hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
        if (!hdw->ctl_write_buffer) goto fail;
        hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
@@ -1929,10 +2126,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        if (hdw) {
                usb_free_urb(hdw->ctl_read_urb);
                usb_free_urb(hdw->ctl_write_urb);
-               if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer);
-               if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer);
-               if (hdw->controls) kfree(hdw->controls);
-               if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info);
+               kfree(hdw->ctl_read_buffer);
+               kfree(hdw->ctl_write_buffer);
+               kfree(hdw->controls);
+               kfree(hdw->mpeg_ctrl_info);
                kfree(hdw);
        }
        return NULL;
@@ -1997,10 +2194,10 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
                        unit_pointers[hdw->unit_number] = NULL;
                }
        } while (0); up(&pvr2_unit_sem);
-       if (hdw->controls) kfree(hdw->controls);
-       if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info);
-       if (hdw->std_defs) kfree(hdw->std_defs);
-       if (hdw->std_enum_names) kfree(hdw->std_enum_names);
+       kfree(hdw->controls);
+       kfree(hdw->mpeg_ctrl_info);
+       kfree(hdw->std_defs);
+       kfree(hdw->std_enum_names);
        kfree(hdw);
 }
 
@@ -2263,6 +2460,13 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
                stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
        }
 
+       if (hdw->input_dirty) {
+               /* pk: If input changes to or from radio, then the encoder
+                  needs to be restarted (for ENC_MUTE_VIDEO to work) */
+               stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
+       }
+
+
        if (hdw->srate_dirty) {
                /* Write new sample rate into control structure since
                 * the master copy is stale.  We must track srate
@@ -2513,16 +2717,28 @@ int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
 }
 
 
-int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw)
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
+                                 enum pvr2_v4l_type index)
 {
-       return hdw->v4l_minor_number;
+       switch (index) {
+       case pvr2_v4l_type_video: return hdw->v4l_minor_number_video;
+       case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi;
+       case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio;
+       default: return -1;
+       }
 }
 
 
-/* Store the v4l minor device number */
-void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,int v)
+/* Store a v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
+                                    enum pvr2_v4l_type index,int v)
 {
-       hdw->v4l_minor_number = v;
+       switch (index) {
+       case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;
+       case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;
+       case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;
+       default: break;
+       }
 }