]> 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 872705633048116483a917c6c468e3347a6a76f2..bb4c5150a4df93d71b859e75263f2d3696a57156 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/firmware.h>
 #include <linux/videodev2.h>
+#include <media/v4l2-common.h>
 #include <asm/semaphore.h>
 #include "pvrusb2.h"
 #include "pvrusb2-std.h"
 #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) },
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
        [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
-#endif
        { }
 };
 
@@ -48,9 +52,7 @@ MODULE_DEVICE_TABLE(usb, pvr2_device_table);
 
 static const char *pvr2_device_names[] = {
        [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx",
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
        [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx",
-#endif
 };
 
 struct pvr2_string_table {
@@ -58,22 +60,18 @@ struct pvr2_string_table {
        unsigned int cnt;
 };
 
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
 // Names of other client modules to request for 24xxx model hardware
 static const char *pvr2_client_24xxx[] = {
        "cx25840",
        "tuner",
-       "tda9887",
        "wm8775",
 };
-#endif
 
 // Names of other client modules to request for 29xxx model hardware
 static const char *pvr2_client_29xxx[] = {
        "msp3400",
        "saa7115",
        "tuner",
-       "tda9887",
 };
 
 static struct pvr2_string_table pvr2_client_lists[] = {
@@ -81,16 +79,14 @@ static struct pvr2_string_table pvr2_client_lists[] = {
                pvr2_client_29xxx,
                sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]),
        },
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
        [PVR2_HDW_TYPE_24XXX] = {
                pvr2_client_24xxx,
                sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]),
        },
-#endif
 };
 
-static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0};
-DECLARE_MUTEX(pvr2_unit_sem);
+static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
+static DECLARE_MUTEX(pvr2_unit_sem);
 
 static int ctlchg = 0;
 static int initusbreset = 1;
@@ -98,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);
@@ -115,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
@@ -130,39 +129,105 @@ MODULE_PARM_DESC(tolerance,"specify stream error tolerance");
 /* size of a firmware chunk */
 #define FIRMWARE_CHUNK_SIZE 0x2000
 
-
-static const char *control_values_srate[] = {
-       [PVR2_CVAL_SRATE_48]   = "48KHz",
-       [PVR2_CVAL_SRATE_44_1] = "44.1KHz",
+/* Define the list of additional controls we'll dynamically construct based
+   on query of the cx2341x module. */
+struct pvr2_mpeg_ids {
+       const char *strid;
+       int id;
 };
-
-
-static const char *control_values_audiobitrate[] = {
-       [PVR2_CVAL_AUDIOBITRATE_384] = "384kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_320] = "320kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_256] = "256kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_224] = "224kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_192] = "192kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_160] = "160kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_128] = "128kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_112] = "112kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_96]  = "96kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_80]  = "80kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_64]  = "64kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_56]  = "56kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_48]  = "48kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_32]  = "32kb/s",
-       [PVR2_CVAL_AUDIOBITRATE_VBR] = "VBR",
+static const struct pvr2_mpeg_ids mpeg_ids[] = {
+       {
+               .strid = "audio_layer",
+               .id = V4L2_CID_MPEG_AUDIO_ENCODING,
+       },{
+               .strid = "audio_bitrate",
+               .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+       },{
+               /* Already using audio_mode elsewhere :-( */
+               .strid = "mpeg_audio_mode",
+               .id = V4L2_CID_MPEG_AUDIO_MODE,
+       },{
+               .strid = "mpeg_audio_mode_extension",
+               .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+       },{
+               .strid = "audio_emphasis",
+               .id = V4L2_CID_MPEG_AUDIO_EMPHASIS,
+       },{
+               .strid = "audio_crc",
+               .id = V4L2_CID_MPEG_AUDIO_CRC,
+       },{
+               .strid = "video_aspect",
+               .id = V4L2_CID_MPEG_VIDEO_ASPECT,
+       },{
+               .strid = "video_b_frames",
+               .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+       },{
+               .strid = "video_gop_size",
+               .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+       },{
+               .strid = "video_gop_closure",
+               .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+       },{
+               .strid = "video_bitrate_mode",
+               .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+       },{
+               .strid = "video_bitrate",
+               .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+       },{
+               .strid = "video_bitrate_peak",
+               .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+       },{
+               .strid = "video_temporal_decimation",
+               .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+       },{
+               .strid = "stream_type",
+               .id = V4L2_CID_MPEG_STREAM_TYPE,
+       },{
+               .strid = "video_spatial_filter_mode",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+       },{
+               .strid = "video_spatial_filter",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+       },{
+               .strid = "video_luma_spatial_filter_type",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+       },{
+               .strid = "video_chroma_spatial_filter_type",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+       },{
+               .strid = "video_temporal_filter_mode",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+       },{
+               .strid = "video_temporal_filter",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+       },{
+               .strid = "video_median_filter_type",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+       },{
+               .strid = "video_luma_median_filter_top",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+       },{
+               .strid = "video_luma_median_filter_bottom",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+       },{
+               .strid = "video_chroma_median_filter_top",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+       },{
+               .strid = "video_chroma_median_filter_bottom",
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+       }
 };
+#define MPEGDEF_COUNT (sizeof(mpeg_ids)/sizeof(mpeg_ids[0]))
 
 
-static const char *control_values_audioemphasis[] = {
-       [PVR2_CVAL_AUDIOEMPHASIS_NONE]  = "None",
-       [PVR2_CVAL_AUDIOEMPHASIS_50_15] = "50/15us",
-       [PVR2_CVAL_AUDIOEMPHASIS_CCITT] = "CCITT J.17",
+static const char *control_values_srate[] = {
+       [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100]   = "44.1 kHz",
+       [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000]   = "48 kHz",
+       [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000]   = "32 kHz",
 };
 
 
+
 static const char *control_values_input[] = {
        [PVR2_CVAL_INPUT_TV]        = "television",  /*xawtv needs this name*/
        [PVR2_CVAL_INPUT_RADIO]     = "radio",
@@ -195,6 +260,26 @@ 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);
+static unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw);
+static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
+static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
+static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw);
+static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
+                                           unsigned long msk,
+                                           unsigned long val);
+static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
+                                                  unsigned long msk,
+                                                  unsigned long val);
+static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
+                               unsigned int timeout,int probe_fl,
+                               void *write_data,unsigned int write_len,
+                               void *read_data,unsigned int read_len);
+static int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res);
+static int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res);
 
 static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
 {
@@ -210,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;
 }
@@ -233,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;
 }
 
@@ -269,14 +371,172 @@ static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr)
 }
 
 static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+       pvr2_hdw_set_cur_freq(cptr->hdw,v);
+       return 0;
+}
+
+static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
+{
+       /* Actual maximum depends on the video standard in effect. */
+       if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
+               *vp = 480;
+       } else {
+               *vp = 576;
+       }
+       return 0;
+}
+
+static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
+{
+       /* Actual minimum depends on device type. */
+       if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+               *vp = 75;
+       } else {
+               *vp = 17;
+       }
+       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;
-       hdw->freqVal = v;
-       hdw->freqDirty = !0;
-       hdw->freqSlot = 0;
+
+       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;
+}
+
+static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
+{
+       cptr->hdw->enc_stale = 0;
+}
+
+static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
+{
+       int ret;
+       struct v4l2_ext_controls cs;
+       struct v4l2_ext_control c1;
+       memset(&cs,0,sizeof(cs));
+       memset(&c1,0,sizeof(c1));
+       cs.controls = &c1;
+       cs.count = 1;
+       c1.id = cptr->info->v4l_id;
+       ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state,&cs,
+                               VIDIOC_G_EXT_CTRLS);
+       if (ret) return ret;
+       *vp = c1.value;
+       return 0;
+}
+
+static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+       int ret;
+       struct v4l2_ext_controls cs;
+       struct v4l2_ext_control c1;
+       memset(&cs,0,sizeof(cs));
+       memset(&c1,0,sizeof(c1));
+       cs.controls = &c1;
+       cs.count = 1;
+       c1.id = cptr->info->v4l_id;
+       c1.value = v;
+       ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state,&cs,
+                               VIDIOC_S_EXT_CTRLS);
+       if (ret) return ret;
+       cptr->hdw->enc_stale = !0;
+       return 0;
+}
+
+static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
+{
+       struct v4l2_queryctrl qctrl;
+       struct pvr2_ctl_info *info;
+       qctrl.id = cptr->info->v4l_id;
+       cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
+       /* Strip out the const so we can adjust a function pointer.  It's
+          OK to do this here because we know this is a dynamically created
+          control, so the underlying storage for the info pointer is (a)
+          private to us, and (b) not in read-only storage.  Either we do
+          this or we significantly complicate the underlying control
+          implementation. */
+       info = (struct pvr2_ctl_info *)(cptr->info);
+       if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
+               if (info->set_value) {
+                       info->set_value = NULL;
+               }
+       } else {
+               if (!(info->set_value)) {
+                       info->set_value = ctrl_cx2341x_set;
+               }
+       }
+       return qctrl.flags;
+}
+
 static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
 {
        *vp = cptr->hdw->flag_streaming_enabled;
@@ -436,6 +696,9 @@ static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr)
        .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \
        .def.type_enum.value_names = tab
 
+#define DEFBOOL \
+       .type = pvr2_ctl_bool
+
 #define DEFMASK(msk,tab) \
        .type = pvr2_ctl_bitmask, \
        .def.type_bitmask.valid_bits = msk, \
@@ -467,22 +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(audiobitrate)
-VCREATE_FUNCS(audiocrc)
-VCREATE_FUNCS(audioemphasis)
-VCREATE_FUNCS(vbr)
-VCREATE_FUNCS(videobitrate)
-VCREATE_FUNCS(videopeak)
-VCREATE_FUNCS(interlace)
-VCREATE_FUNCS(audiolayer)
-
-#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[] = {
@@ -518,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),
        },{
@@ -548,7 +800,7 @@ static const struct pvr2_ctl_info control_defs[] = {
                .name = "mute",
                .default_value = 0,
                DEFREF(mute),
-               DEFINT(0,1),
+               DEFBOOL,
        },{
                .desc = "Video Source",
                .name = "input",
@@ -569,86 +821,47 @@ static const struct pvr2_ctl_info control_defs[] = {
                .internal_id = PVR2_CID_HRES,
                .default_value = 720,
                DEFREF(res_hor),
-               DEFINT(320,720),
+               DEFINT(19,720),
        },{
                .desc = "Vertical capture resolution",
                .name = "resolution_ver",
                .internal_id = PVR2_CID_VRES,
                .default_value = 480,
                DEFREF(res_ver),
-               DEFINT(200,625),
+               DEFINT(17,576),
+               /* Hook in check for video standard and adjust maximum
+                  depending on the standard. */
+               .get_max_value = ctrl_vres_max_get,
+               .get_min_value = ctrl_vres_min_get,
        },{
-               .v4l_id = V4L2_CID_PVR_SRATE,
-               .desc = "Sample rate",
+               .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",
                .name = "srate",
-               .default_value = PVR2_CVAL_SRATE_48,
                DEFREF(srate),
                DEFENUM(control_values_srate),
-       },{
-               .v4l_id = V4L2_CID_PVR_AUDIOBITRATE,
-               .desc = "Audio Bitrate",
-               .name = "audio_bitrate",
-               .default_value = PVR2_CVAL_AUDIOBITRATE_224,
-               DEFREF(audiobitrate),
-               DEFENUM(control_values_audiobitrate),
-       },{
-               .v4l_id = V4L2_CID_PVR_AUDIOCRC,
-               .desc = "Audio CRC",
-               .name = "audio_crc",
-               .default_value = 1,
-               DEFREF(audiocrc),
-               DEFINT(0,1),
-       },{
-               .v4l_id = V4L2_CID_PVR_AUDIOEMPHASIS,
-               .desc = "Audio Emphasis",
-               .name = "audio_emphasis",
-               .default_value = PVR2_CVAL_AUDIOEMPHASIS_NONE,
-               DEFREF(audioemphasis),
-               DEFENUM(control_values_audioemphasis),
-       },{
-               .v4l_id = V4L2_CID_PVR_VBR,
-               .desc = "Variable video bitrate",
-               .name = "vbr",
-               .default_value = 0,
-               DEFREF(vbr),
-               DEFINT(0,1),
-       },{
-               .v4l_id = V4L2_CID_PVR_VIDEOBITRATE,
-               .desc = "Average video bitrate",
-               .name = "video_average_bitrate",
-               .default_value = 6000000,
-               DEFREF(videobitrate),
-               DEFINT(500000,20000000),
-       },{
-               .v4l_id = V4L2_CID_PVR_VIDEOPEAK,
-               .desc = "Peak video bitrate",
-               .name = "video_peak_bitrate",
-               .default_value = 6000000,
-               DEFREF(videopeak),
-               DEFINT(500000,20000000),
-       },{
-               .desc = "Interlace mode",
-               .name = "interlace",
-               .internal_id = PVR2_CID_INTERLACE,
-               .default_value = 0,
-               DEFREF(interlace),
-               DEFINT(0,1),
-       },{
-               .desc = "Audio Layer",
-               .name = "audio_layer",
-               .default_value = 2,
-               DEFREF(audiolayer),
-               DEFINT(0,3),
        },{
                .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",
@@ -660,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",
@@ -671,7 +889,7 @@ static const struct pvr2_ctl_info control_defs[] = {
                .desc = "Streaming Enabled",
                .name = "streaming_enabled",
                .get_value = ctrl_streamingenabled_get,
-               DEFINT(0,1),
+               DEFBOOL,
        },{
                .desc = "USB Speed",
                .name = "usb_speed",
@@ -681,7 +899,7 @@ static const struct pvr2_ctl_info control_defs[] = {
                .desc = "Signal Present",
                .name = "signal_present",
                .get_value = ctrl_signal_get,
-               DEFINT(0,1),
+               DEFBOOL,
        },{
                .desc = "Video Standards Available Mask",
                .name = "video_standard_mask_available",
@@ -757,14 +975,82 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
        return hdw->serial_number;
 }
 
-
-struct pvr2_hdw *pvr2_hdw_find(int unit_number)
+unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
 {
-       if (unit_number < 0) return 0;
-       if (unit_number >= PVR_NUM) return 0;
-       return unit_pointers[unit_number];
+       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)
 {
@@ -839,9 +1125,9 @@ static int pvr2_locate_firmware(struct pvr2_hdw *hdw,
  * is not suitable for an usb transaction.
  *
  */
-int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
+static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
 {
-       const struct firmware *fw_entry = 0;
+       const struct firmware *fw_entry = NULL;
        void  *fw_ptr;
        unsigned int pipe;
        int ret;
@@ -849,22 +1135,18 @@ int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
        static const char *fw_files_29xxx[] = {
                "v4l-pvrusb2-29xxx-01.fw",
        };
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
        static const char *fw_files_24xxx[] = {
                "v4l-pvrusb2-24xxx-01.fw",
        };
-#endif
        static const struct pvr2_string_table fw_file_defs[] = {
                [PVR2_HDW_TYPE_29XXX] = {
                        fw_files_29xxx,
                        sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]),
                },
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
                [PVR2_HDW_TYPE_24XXX] = {
                        fw_files_24xxx,
                        sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]),
                },
-#endif
        };
        hdw->fw1_state = FW1_STATE_FAILED; // default result
 
@@ -937,7 +1219,7 @@ int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
 
 int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
 {
-       const struct firmware *fw_entry = 0;
+       const struct firmware *fw_entry = NULL;
        void  *fw_ptr;
        unsigned int pipe, fw_len, fw_done;
        int actual_length;
@@ -955,6 +1237,10 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
        if (ret < 0) return ret;
        fwidx = ret;
        ret = 0;
+       /* Since we're about to completely reinitialize the encoder,
+          invalidate our cached copy of its configuration state.  Next
+          time we configure the encoder, then we'll fully configure it. */
+       hdw->enc_cur_valid = 0;
 
        /* First prepare firmware loading */
        ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
@@ -1084,8 +1370,9 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
   reconfigure and start over.
 
 */
-void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
-                                    unsigned long msk,unsigned long val)
+static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
+                                           unsigned long msk,
+                                           unsigned long val)
 {
        unsigned long nmsk;
        unsigned long vmsk;
@@ -1095,12 +1382,13 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
        if (!hdw->flag_ok) return;
 
        msk &= PVR2_SUBSYS_ALL;
+       nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk);
+       nmsk &= PVR2_SUBSYS_ALL;
 
        for (;;) {
                tryCount++;
-               vmsk = hdw->subsys_enabled_mask & PVR2_SUBSYS_ALL;
-               nmsk = (vmsk & ~msk) | (val & msk);
-               if (!(nmsk ^ vmsk)) break;
+               if (!((nmsk ^ hdw->subsys_enabled_mask) &
+                     PVR2_SUBSYS_ALL)) break;
                if (tryCount > 4) {
                        pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                                   "Too many retries when configuring device;"
@@ -1235,18 +1523,6 @@ void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
 }
 
 
-void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk)
-{
-       pvr2_hdw_subsys_bit_chg(hdw,msk,msk);
-}
-
-
-void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk)
-{
-       pvr2_hdw_subsys_bit_chg(hdw,msk,0);
-}
-
-
 unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw)
 {
        return hdw->subsys_enabled_mask;
@@ -1259,9 +1535,9 @@ unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw)
 }
 
 
-void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
-                                           unsigned long msk,
-                                           unsigned long val)
+static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
+                                                  unsigned long msk,
+                                                  unsigned long val)
 {
        unsigned long val2;
        msk &= PVR2_SUBSYS_ALL;
@@ -1283,7 +1559,7 @@ void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
 }
 
 
-int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
+static int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
 {
        if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0;
        if (enableFl) {
@@ -1317,8 +1593,8 @@ int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
 }
 
 
-int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
-                                    enum pvr2_config config)
+static int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
+                                           enum pvr2_config config)
 {
        unsigned long sm = hdw->subsys_enabled_mask;
        if (!hdw->flag_ok) return -EIO;
@@ -1449,7 +1725,7 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
                return;
        }
 
-       pvr2_trace(PVR2_TRACE_EEPROM,
+       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                   "Unable to select a viable initial video standard");
 }
 
@@ -1515,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).
@@ -1650,13 +1941,15 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        int valid_std_mask;
        struct pvr2_ctrl *cptr;
        __u8 ifnum;
+       struct v4l2_queryctrl qctrl;
+       struct pvr2_ctl_info *ciptr;
 
        hdw_type = devid - pvr2_device_table;
        if (hdw_type >=
            sizeof(pvr2_device_names)/sizeof(pvr2_device_names[0])) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                           "Bogus device type of %u reported",hdw_type);
-               return 0;
+               return NULL;
        }
 
        hdw = kmalloc(sizeof(*hdw),GFP_KERNEL);
@@ -1664,8 +1957,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
                   hdw,pvr2_device_names[hdw_type]);
        if (!hdw) goto fail;
        memset(hdw,0,sizeof(*hdw));
+       cx2341x_fill_defaults(&hdw->enc_ctl_state);
 
        hdw->control_cnt = CTRLDEF_COUNT;
+       hdw->control_cnt += MPEGDEF_COUNT;
        hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
                                GFP_KERNEL);
        if (!hdw->controls) goto fail;
@@ -1682,6 +1977,54 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
                cptr = hdw->controls + idx;
                cptr->info = control_defs+idx;
        }
+       /* Define and configure additional controls from cx2341x module. */
+       hdw->mpeg_ctrl_info = kmalloc(
+               sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL);
+       if (!hdw->mpeg_ctrl_info) goto fail;
+       memset(hdw->mpeg_ctrl_info,0,
+              sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT);
+       for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
+               cptr = hdw->controls + idx + CTRLDEF_COUNT;
+               ciptr = &(hdw->mpeg_ctrl_info[idx].info);
+               ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
+               ciptr->name = mpeg_ids[idx].strid;
+               ciptr->v4l_id = mpeg_ids[idx].id;
+               ciptr->skip_init = !0;
+               ciptr->get_value = ctrl_cx2341x_get;
+               ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
+               ciptr->is_dirty = ctrl_cx2341x_is_dirty;
+               if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
+               qctrl.id = ciptr->v4l_id;
+               cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
+               if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
+                       ciptr->set_value = ctrl_cx2341x_set;
+               }
+               strncpy(hdw->mpeg_ctrl_info[idx].desc,qctrl.name,
+                       PVR2_CTLD_INFO_DESC_SIZE);
+               hdw->mpeg_ctrl_info[idx].desc[PVR2_CTLD_INFO_DESC_SIZE-1] = 0;
+               ciptr->default_value = qctrl.default_value;
+               switch (qctrl.type) {
+               default:
+               case V4L2_CTRL_TYPE_INTEGER:
+                       ciptr->type = pvr2_ctl_int;
+                       ciptr->def.type_int.min_value = qctrl.minimum;
+                       ciptr->def.type_int.max_value = qctrl.maximum;
+                       break;
+               case V4L2_CTRL_TYPE_BOOLEAN:
+                       ciptr->type = pvr2_ctl_bool;
+                       break;
+               case V4L2_CTRL_TYPE_MENU:
+                       ciptr->type = pvr2_ctl_enum;
+                       ciptr->def.type_enum.value_names =
+                               cx2341x_ctrl_get_menu(ciptr->v4l_id);
+                       for (cnt1 = 0;
+                            ciptr->def.type_enum.value_names[cnt1] != NULL;
+                            cnt1++) { }
+                       ciptr->def.type_enum.count = cnt1;
+                       break;
+               }
+               cptr->info = ciptr;
+       }
 
        // Initialize video standard enum dynamic control
        cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM);
@@ -1724,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);
@@ -1779,45 +2124,46 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        return hdw;
  fail:
        if (hdw) {
-               if (hdw->ctl_read_urb) usb_free_urb(hdw->ctl_read_urb);
-               if (hdw->ctl_write_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);
+               usb_free_urb(hdw->ctl_read_urb);
+               usb_free_urb(hdw->ctl_write_urb);
+               kfree(hdw->ctl_read_buffer);
+               kfree(hdw->ctl_write_buffer);
+               kfree(hdw->controls);
+               kfree(hdw->mpeg_ctrl_info);
                kfree(hdw);
        }
-       return 0;
+       return NULL;
 }
 
 
 /* Remove _all_ associations between this driver and the underlying USB
    layer. */
-void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
+static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
 {
        if (hdw->flag_disconnected) return;
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
        if (hdw->ctl_read_urb) {
                usb_kill_urb(hdw->ctl_read_urb);
                usb_free_urb(hdw->ctl_read_urb);
-               hdw->ctl_read_urb = 0;
+               hdw->ctl_read_urb = NULL;
        }
        if (hdw->ctl_write_urb) {
                usb_kill_urb(hdw->ctl_write_urb);
                usb_free_urb(hdw->ctl_write_urb);
-               hdw->ctl_write_urb = 0;
+               hdw->ctl_write_urb = NULL;
        }
        if (hdw->ctl_read_buffer) {
                kfree(hdw->ctl_read_buffer);
-               hdw->ctl_read_buffer = 0;
+               hdw->ctl_read_buffer = NULL;
        }
        if (hdw->ctl_write_buffer) {
                kfree(hdw->ctl_write_buffer);
-               hdw->ctl_write_buffer = 0;
+               hdw->ctl_write_buffer = NULL;
        }
        pvr2_hdw_render_useless_unlocked(hdw);
        hdw->flag_disconnected = !0;
-       hdw->usb_dev = 0;
-       hdw->usb_intf = 0;
+       hdw->usb_dev = NULL;
+       hdw->usb_intf = NULL;
 }
 
 
@@ -1827,11 +2173,11 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
        if (hdw->fw_buffer) {
                kfree(hdw->fw_buffer);
-               hdw->fw_buffer = 0;
+               hdw->fw_buffer = NULL;
        }
        if (hdw->vid_stream) {
                pvr2_stream_destroy(hdw->vid_stream);
-               hdw->vid_stream = 0;
+               hdw->vid_stream = NULL;
        }
        if (hdw->audio_stat) {
                hdw->audio_stat->detach(hdw->audio_stat->ctxt);
@@ -1845,12 +2191,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
                if ((hdw->unit_number >= 0) &&
                    (hdw->unit_number < PVR_NUM) &&
                    (unit_pointers[hdw->unit_number] == hdw)) {
-                       unit_pointers[hdw->unit_number] = 0;
+                       unit_pointers[hdw->unit_number] = NULL;
                }
        } while (0); up(&pvr2_unit_sem);
-       if (hdw->controls) kfree(hdw->controls);
-       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);
 }
 
@@ -1881,7 +2228,7 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
 
 // Attempt to autoselect an appropriate value for std_enum_cur given
 // whatever is currently in std_mask_cur
-void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw)
+static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw)
 {
        unsigned int idx;
        for (idx = 1; idx < hdw->std_enum_cnt; idx++) {
@@ -1896,7 +2243,7 @@ void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw)
 
 // Calculate correct set of enumerated standards based on currently known
 // set of available standards bits.
-void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw)
+static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw)
 {
        struct v4l2_standard *newstd;
        unsigned int std_cnt;
@@ -1906,12 +2253,12 @@ void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw)
 
        if (hdw->std_defs) {
                kfree(hdw->std_defs);
-               hdw->std_defs = 0;
+               hdw->std_defs = NULL;
        }
        hdw->std_enum_cnt = 0;
        if (hdw->std_enum_names) {
                kfree(hdw->std_enum_names);
-               hdw->std_enum_names = 0;
+               hdw->std_enum_names = NULL;
        }
 
        if (!std_cnt) {
@@ -1962,7 +2309,7 @@ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
 struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
                                             unsigned int idx)
 {
-       if (idx >= hdw->control_cnt) return 0;
+       if (idx >= hdw->control_cnt) return NULL;
        return hdw->controls + idx;
 }
 
@@ -1981,11 +2328,11 @@ struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
                i = cptr->info->internal_id;
                if (i && (i == ctl_id)) return cptr;
        }
-       return 0;
+       return NULL;
 }
 
 
-/* Given an ID, retrieve the control structure associated with it. */
+/* Given a V4L ID, retrieve the control structure associated with it. */
 struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
 {
        struct pvr2_ctrl *cptr;
@@ -1998,7 +2345,31 @@ struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id
                i = cptr->info->v4l_id;
                if (i && (i == ctl_id)) return cptr;
        }
-       return 0;
+       return NULL;
+}
+
+
+/* Given a V4L ID for its immediate predecessor, retrieve the control
+   structure associated with it. */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
+                                           unsigned int ctl_id)
+{
+       struct pvr2_ctrl *cptr,*cp2;
+       unsigned int idx;
+       int i;
+
+       /* This could be made a lot more efficient, but for now... */
+       cp2 = NULL;
+       for (idx = 0; idx < hdw->control_cnt; idx++) {
+               cptr = hdw->controls + idx;
+               i = cptr->info->v4l_id;
+               if (!i) continue;
+               if (i <= ctl_id) continue;
+               if (cp2 && (cp2->info->v4l_id < i)) continue;
+               cp2 = cptr;
+       }
+       return cp2;
+       return NULL;
 }
 
 
@@ -2007,6 +2378,7 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
        switch (tp) {
        case pvr2_ctl_int: return "integer";
        case pvr2_ctl_enum: return "enum";
+       case pvr2_ctl_bool: return "boolean";
        case pvr2_ctl_bitmask: return "bitmask";
        }
        return "";
@@ -2020,7 +2392,7 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
    state(s) back to their previous value before this function was called.
    Thus we can automatically reconfigure affected pieces of the driver as
    controls are changed. */
-int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
+static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
 {
        unsigned long saved_subsys_mask = hdw->subsys_enabled_mask;
        unsigned long stale_subsys_mask = 0;
@@ -2075,31 +2447,42 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
                        hdw->res_ver_val = nvres;
                        hdw->res_ver_dirty = !0;
                }
-               if (!hdw->interlace_val) {
-                       hdw->interlace_val = 0;
-                       hdw->interlace_dirty = !0;
-               }
        }
 
        if (hdw->std_dirty ||
-           hdw->interlace_dirty ||
-           hdw->vbr_dirty ||
-           hdw->videobitrate_dirty ||
-           hdw->videopeak_dirty ||
-           hdw->audiobitrate_dirty ||
-           hdw->audiolayer_dirty ||
-           hdw->audiocrc_dirty ||
-           hdw->audioemphasis_dirty ||
+           hdw->enc_stale ||
            hdw->srate_dirty ||
            hdw->res_ver_dirty ||
-           hdw->res_hor_dirty) {
+           hdw->res_hor_dirty ||
+           0) {
                /* If any of this changes, then the encoder needs to be
                   reconfigured, and we need to reset the stream. */
                stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-               stale_subsys_mask |= hdw->subsys_stream_mask;
+       }
+
+       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
+                * separate from the mpeg control structure because
+                * other logic also uses this value. */
+               struct v4l2_ext_controls cs;
+               struct v4l2_ext_control c1;
+               memset(&cs,0,sizeof(cs));
+               memset(&c1,0,sizeof(c1));
+               cs.controls = &c1;
+               cs.count = 1;
+               c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
+               c1.value = hdw->srate_val;
+               cx2341x_ext_ctrls(&hdw->enc_ctl_state,&cs,VIDIOC_S_EXT_CTRLS);
+       }
+
        /* Scan i2c core at this point - before we clear all the dirty
           bits.  Various parts of the i2c core will notice dirty bits as
           appropriate and arrange to broadcast or directly send updates to
@@ -2157,15 +2540,6 @@ void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw)
        }
 }
 
-
-void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw)
-{
-       LOCK_TAKE(hdw->big_lock); do {
-               pvr2_hdw_poll_trigger_unlocked(hdw);
-       } while (0); LOCK_GIVE(hdw->big_lock);
-}
-
-
 /* Return name for this driver instance */
 const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
 {
@@ -2174,7 +2548,7 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
 
 
 /* Return bit mask indicating signal status */
-unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw)
+static unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw)
 {
        unsigned int msk = 0;
        switch (hdw->input_val) {
@@ -2243,6 +2617,8 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
                pvr2_i2c_core_check_stale(hdw);
                hdw->log_requested = 0;
                pvr2_i2c_core_sync(hdw);
+               pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
+               cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
                printk(KERN_INFO "pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
        } while (0); LOCK_GIVE(hdw->big_lock);
 }
@@ -2259,7 +2635,7 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag)
                        pvr2_trace(PVR2_TRACE_FIRMWARE,
                                   "Cleaning up after CPU firmware fetch");
                        kfree(hdw->fw_buffer);
-                       hdw->fw_buffer = 0;
+                       hdw->fw_buffer = NULL;
                        hdw->fw_size = 0;
                        /* Now release the CPU.  It will disconnect and
                           reconnect later. */
@@ -2341,36 +2717,32 @@ int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
 }
 
 
-int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw)
-{
-       return hdw->v4l_minor_number;
-}
-
-
-/* Store the v4l minor device number */
-void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,int v)
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
+                                 enum pvr2_v4l_type index)
 {
-       hdw->v4l_minor_number = v;
+       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;
+       }
 }
 
 
-void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw)
+/* Store a v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
+                                    enum pvr2_v4l_type index,int v)
 {
-       if (!hdw->usb_dev) return;
-       usb_settoggle(hdw->usb_dev, PVR2_CTL_WRITE_ENDPOINT & 0xf,
-                     !(PVR2_CTL_WRITE_ENDPOINT & USB_DIR_IN), 0);
-       usb_settoggle(hdw->usb_dev, PVR2_CTL_READ_ENDPOINT & 0xf,
-                     !(PVR2_CTL_READ_ENDPOINT & USB_DIR_IN), 0);
-       usb_clear_halt(hdw->usb_dev,
-                      usb_rcvbulkpipe(hdw->usb_dev,
-                                      PVR2_CTL_READ_ENDPOINT & 0x7f));
-       usb_clear_halt(hdw->usb_dev,
-                      usb_sndbulkpipe(hdw->usb_dev,
-                                      PVR2_CTL_WRITE_ENDPOINT & 0x7f));
+       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;
+       }
 }
 
 
-static void pvr2_ctl_write_complete(struct urb *urb, struct pt_regs *regs)
+static void pvr2_ctl_write_complete(struct urb *urb)
 {
        struct pvr2_hdw *hdw = urb->context;
        hdw->ctl_write_pend_flag = 0;
@@ -2379,7 +2751,7 @@ static void pvr2_ctl_write_complete(struct urb *urb, struct pt_regs *regs)
 }
 
 
-static void pvr2_ctl_read_complete(struct urb *urb, struct pt_regs *regs)
+static void pvr2_ctl_read_complete(struct urb *urb)
 {
        struct pvr2_hdw *hdw = urb->context;
        hdw->ctl_read_pend_flag = 0;
@@ -2393,20 +2765,22 @@ static void pvr2_ctl_timeout(unsigned long data)
        struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
        if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
                hdw->ctl_timeout_flag = !0;
-               if (hdw->ctl_write_pend_flag && hdw->ctl_write_urb) {
+               if (hdw->ctl_write_pend_flag)
                        usb_unlink_urb(hdw->ctl_write_urb);
-               }
-               if (hdw->ctl_read_pend_flag && hdw->ctl_read_urb) {
+               if (hdw->ctl_read_pend_flag)
                        usb_unlink_urb(hdw->ctl_read_urb);
-               }
        }
 }
 
 
-int pvr2_send_request_ex(struct pvr2_hdw *hdw,
-                        unsigned int timeout,int probe_fl,
-                        void *write_data,unsigned int write_len,
-                        void *read_data,unsigned int read_len)
+/* Issue a command and get a response from the device.  This extended
+   version includes a probe flag (which if set means that device errors
+   should not be logged or treated as fatal) and a timeout in jiffies.
+   This can be used to non-lethally probe the health of endpoint 1. */
+static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
+                               unsigned int timeout,int probe_fl,
+                               void *write_data,unsigned int write_len,
+                               void *read_data,unsigned int read_len)
 {
        unsigned int idx;
        int status = 0;
@@ -2661,7 +3035,7 @@ int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
 }
 
 
-int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
+static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
 {
        int ret = 0;
 
@@ -2685,7 +3059,7 @@ int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
 }
 
 
-int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res)
+static int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res)
 {
        int ret;
 
@@ -2702,7 +3076,7 @@ int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res)
 }
 
 
-int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res)
+static int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res)
 {
        int ret;
 
@@ -2718,13 +3092,13 @@ int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res)
 }
 
 
-void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
+static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
 {
        if (!hdw->flag_ok) return;
        pvr2_trace(PVR2_TRACE_INIT,"render_useless");
        hdw->flag_ok = 0;
        if (hdw->vid_stream) {
-               pvr2_stream_setup(hdw->vid_stream,0,0,0);
+               pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
        }
        hdw->flag_streaming_enabled = 0;
        hdw->subsys_enabled_mask = 0;
@@ -2743,7 +3117,7 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
 {
        int ret;
        pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
-       ret = usb_lock_device_for_reset(hdw->usb_dev,0);
+       ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
        if (ret == 1) {
                ret = usb_reset_device(hdw->usb_dev);
                usb_unlock_device(hdw->usb_dev);
@@ -2792,7 +3166,7 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
                pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
                hdw->flag_ok = !0;
                hdw->cmd_buffer[0] = 0xdd;
-               status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+               status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
        } while (0); LOCK_GIVE(hdw->ctl_lock);
        return status;
 }
@@ -2804,7 +3178,7 @@ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
        LOCK_TAKE(hdw->ctl_lock); do {
                pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup");
                hdw->cmd_buffer[0] = 0xde;
-               status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+               status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
        } while (0); LOCK_GIVE(hdw->ctl_lock);
        return status;
 }
@@ -2831,12 +3205,13 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
 }
 
 
-int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
+/* Stop / start video stream transport */
+static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
 {
        int status;
        LOCK_TAKE(hdw->ctl_lock); do {
                hdw->cmd_buffer[0] = (runFl ? 0x36 : 0x37);
-               status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+               status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
        } while (0); LOCK_GIVE(hdw->ctl_lock);
        if (!status) {
                hdw->subsys_enabled_mask =
@@ -2929,7 +3304,8 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
 }
 
 
-int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
+/* Find I2C address of eeprom */
+static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
 {
        int result;
        LOCK_TAKE(hdw->ctl_lock); do {
@@ -2944,6 +3320,42 @@ int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
 }
 
 
+int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
+                            u32 chip_id,unsigned long reg_id,
+                            int setFl,u32 *val_ptr)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       struct list_head *item;
+       struct pvr2_i2c_client *cp;
+       struct v4l2_register req;
+       int stat = 0;
+       int okFl = 0;
+
+       req.i2c_id = chip_id;
+       req.reg = reg_id;
+       if (setFl) req.val = *val_ptr;
+       mutex_lock(&hdw->i2c_list_lock); do {
+               list_for_each(item,&hdw->i2c_clients) {
+                       cp = list_entry(item,struct pvr2_i2c_client,list);
+                       if (cp->client->driver->id != chip_id) continue;
+                       stat = pvr2_i2c_client_cmd(
+                               cp,(setFl ? VIDIOC_INT_S_REGISTER :
+                                   VIDIOC_INT_G_REGISTER),&req);
+                       if (!setFl) *val_ptr = req.val;
+                       okFl = !0;
+                       break;
+               }
+       } while (0); mutex_unlock(&hdw->i2c_list_lock);
+       if (okFl) {
+               return stat;
+       }
+       return -EINVAL;
+#else
+       return -ENOSYS;
+#endif
+}
+
+
 /*
   Stuff for Emacs to see, in order to encourage consistent editing style:
   *** Local Variables: ***