V4L/DVB (4244): Implement use of cx2341x module in pvrusb2 driver

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index 47e7f5d..27eadaf 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -104,12 +104,15 @@
 		   hdw->srate_val);
 	switch (hdw->srate_val) {
 	default:
-	case PVR2_CVAL_SRATE_48:
+	case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
 		val = 48000;
 		break;
-	case PVR2_CVAL_SRATE_44_1:
+	case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
 		val = 44100;
 		break;
+	case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+		val = 32000;
+		break;
 	}
 	pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
 }
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 47c8e02..d944081 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -27,34 +27,6 @@
 #include "pvrusb2-hdw-internal.h"
 #include "pvrusb2-debug.h"
 
-static u32 pvr_tbl_emphasis [] = {
-	[PVR2_CVAL_AUDIOEMPHASIS_NONE] = 0x0 << 12,
-	[PVR2_CVAL_AUDIOEMPHASIS_50_15] = 0x1 << 12,
-	[PVR2_CVAL_AUDIOEMPHASIS_CCITT] = 0x3 << 12,
-};
-
-static u32 pvr_tbl_srate[] = {
-	[PVR2_CVAL_SRATE_48] =  0x01,
-	[PVR2_CVAL_SRATE_44_1] = 0x00,
-};
-
-static u32 pvr_tbl_audiobitrate[] = {
-	[PVR2_CVAL_AUDIOBITRATE_384] = 0xe << 4,
-	[PVR2_CVAL_AUDIOBITRATE_320] = 0xd << 4,
-	[PVR2_CVAL_AUDIOBITRATE_256] = 0xc << 4,
-	[PVR2_CVAL_AUDIOBITRATE_224] = 0xb << 4,
-	[PVR2_CVAL_AUDIOBITRATE_192] = 0xa << 4,
-	[PVR2_CVAL_AUDIOBITRATE_160] = 0x9 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_128] = 0x8 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_112] = 0x7 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_96]  = 0x6 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_80]  = 0x5 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_64]  = 0x4 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_56]  = 0x3 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_48]  = 0x2 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_32]  = 0x1 << 4,
-	[PVR2_CVAL_AUDIOBITRATE_VBR] = 0x0 << 4,
-};
 
 
 /* Firmware mailbox flags - definitions found from ivtv */
@@ -316,171 +288,67 @@
 	return pvr2_encoder_cmd(hdw,cmd,args,0,data);
 }
 
-
 int pvr2_encoder_configure(struct pvr2_hdw *hdw)
 {
-	int ret = 0, audio, i;
-	v4l2_std_id vd_std = hdw->std_mask_cur;
-	int height = hdw->res_ver_val;
-	int width = hdw->res_hor_val;
-	int height_full = !hdw->interlace_val;
+	int ret;
+	pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
+		   " (cx2341x module)");
+	hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
+	hdw->enc_ctl_state.width = hdw->res_hor_val;
+	hdw->enc_ctl_state.height = hdw->res_ver_val;
+	hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur &
+				       (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ?
+				      0 : 1);
 
-	int is_30fps, is_ntsc;
+	ret = 0;
 
-	if (vd_std & V4L2_STD_NTSC) {
-		is_ntsc=1;
-		is_30fps=1;
-	} else if (vd_std & V4L2_STD_PAL_M) {
-		is_ntsc=0;
-		is_30fps=1;
-	} else {
-		is_ntsc=0;
-		is_30fps=0;
-	}
-
-	pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure (native)");
-
-	/* set stream output port.  Some notes here: The ivtv-derived
-	   encoder documentation says that this command only gets a
-	   single argument.  However the Windows driver for the model
-	   29xxx series hardware has been sending 0x01 as a second
-	   argument, while the Windows driver for the model 24xxx
-	   series hardware has been sending 0x02 as a second argument.
-	   Confusing matters further are the observations that 0x01
-	   for that second argument simply won't work on the 24xxx
-	   hardware, while 0x02 will work on the 29xxx - except that
-	   when we use 0x02 then xawtv breaks due to a loss of
-	   synchronization with the mpeg packet headers.  While xawtv
-	   should be fixed to let it resync better (I did try to
-	   contact Gerd about this but he has not answered), it has
-	   also been determined that sending 0x00 as this mystery
-	   second argument seems to work on both hardware models AND
-	   xawtv works again.  So we're going to send 0x00. */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2,
-				 0x01, 0x00);
-
-	/* set the Program Index Information. We want I,P,B frames (max 400) */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2,
-				 0x07, 0x0190);
-
-	/* NOTE : windows driver sends these */
-	/* Mike Isely <isely@pobox.com> 7-Mar-2006 The windows driver
-	   sends the following commands but if we do the same then
-	   many apps are no longer able to read the video stream.
-	   Leaving these out seems to do no harm at all, so they're
-	   commented out for that reason. */
-#ifdef notdef
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,1,0,0);
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
-#endif
-
-	/* Strange compared to ivtv data. */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
-				 0xf0, 0xf0);
+	if (!ret) ret = pvr2_encoder_vcmd(
+		hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
+		0xf0, 0xf0);
 
 	/* setup firmware to notify us about some events (don't know why...) */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
-				 0, 0, 0x10000000, 0xffffffff);
+	if (!ret) ret = pvr2_encoder_vcmd(
+		hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
+		0, 0, 0x10000000, 0xffffffff);
 
-	/* set fps to 25 or 30 (1 or 0)*/
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_FRAME_RATE, 1,
-				 is_30fps ? 0 : 1);
+	if (!ret) ret = pvr2_encoder_vcmd(
+		hdw,CX2341X_ENC_SET_VBI_LINE, 5,
+		0xffffffff,0,0,0,0);
 
-	/* set encoding resolution */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_FRAME_SIZE, 2,
-				 (height_full ? height : (height / 2)),
-				 width);
-	/* set encoding aspect ratio to 4:3 */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_ASPECT_RATIO, 1,
-				 0x02);
-
-	/* VBI */
-
-	if (hdw->config == pvr2_config_vbi) {
-		int lines = 2 * (is_30fps ? 12 : 18);
-		int size = (4*((lines*1443+3)/4)) / lines;
-		ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_VBI_CONFIG, 7,
-					 0xbd05, 1, 4,
-					 0x25256262, 0x387f7f7f,
-					 lines , size);
-//                                     0x25256262, 0x13135454, lines , size);
-		/* select vbi lines */
-#define line_used(l)  (is_30fps ? (l >= 10 && l <= 21) : (l >= 6 && l <= 23))
-		for (i = 2 ; i <= 24 ; i++){
-			ret |= pvr2_encoder_vcmd(
-				hdw,CX2341X_ENC_SET_VBI_LINE, 5,
-				i-1,line_used(i), 0, 0, 0);
-			ret |= pvr2_encoder_vcmd(
-				hdw,CX2341X_ENC_SET_VBI_LINE, 5,
-				(i-1) | (1 << 31),
-				line_used(i), 0, 0, 0);
-		}
-	} else {
-		ret |= pvr2_encoder_vcmd(
-			hdw,CX2341X_ENC_SET_VBI_LINE, 5,
-			0xffffffff,0,0,0,0);
+	if (ret) {
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "Failed to configure cx32416");
+		return ret;
 	}
 
-	/* set stream type, depending on resolution. */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_STREAM_TYPE, 1,
-				 height_full ? 0x0a : 0x0b);
-	/* set video bitrate */
-	ret |= pvr2_encoder_vcmd(
-		hdw, CX2341X_ENC_SET_BIT_RATE, 3,
-		(hdw->vbr_val ? 1 : 0),
-		hdw->videobitrate_val,
-		hdw->videopeak_val / 400);
-	/* setup GOP structure (GOP size = 0f or 0c, 3-1 = 2 B-frames) */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
-				 is_30fps ?  0x0f : 0x0c, 0x03);
-
-	/* enable 3:2 pulldown */
-	ret |= pvr2_encoder_vcmd(hdw,CX2341X_ENC_SET_3_2_PULLDOWN,1,0);
-
-	/* set GOP open/close property (open) */
-	ret |= pvr2_encoder_vcmd(hdw,CX2341X_ENC_SET_GOP_CLOSURE,1,0);
-
-	/* set audio stream properties 0x40b9? 0100 0000 1011 1001 */
-	audio = (pvr_tbl_audiobitrate[hdw->audiobitrate_val] |
-		 pvr_tbl_srate[hdw->srate_val] |
-		 hdw->audiolayer_val << 2 |
-		 (hdw->audiocrc_val ? 1 << 14 : 0) |
-		 pvr_tbl_emphasis[hdw->audioemphasis_val]);
-
-	ret |= pvr2_encoder_vcmd(hdw,CX2341X_ENC_SET_AUDIO_PROPERTIES,1,
-				 audio);
-
-	/* set dynamic noise reduction filter to manual, Horiz/Vert */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
-				 0, 0x03);
-
-	/* dynamic noise reduction filter param */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2
-				 , 0, 0);
-
-	/* dynamic noise reduction median filter */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_SET_CORING_LEVELS, 4,
-				 0, 0xff, 0, 0xff);
-
-	/* spacial prefiler parameter */
-	ret |= pvr2_encoder_vcmd(hdw,
-				 CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
-				 0x01, 0x01);
-
-	/* initialize video input */
-	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
-
-	if (!ret) {
-		hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
+	ret = cx2341x_update(hdw,pvr2_encoder_cmd,
+			     (hdw->enc_cur_valid ? &hdw->enc_cur_state : 0),
+			     &hdw->enc_ctl_state);
+	if (ret) {
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "Error from cx2341x module code=%d",ret);
+		return ret;
 	}
 
-	return ret;
+	ret = 0;
+
+	if (!ret) ret = pvr2_encoder_vcmd(
+		hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+	if (ret) {
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "Failed to initialize cx32416 video input");
+		return ret;
+	}
+
+	hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
+	memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
+	       sizeof(struct cx2341x_mpeg_params));
+	hdw->enc_cur_valid = !0;
+	return 0;
 }
 
+
 int pvr2_encoder_start(struct pvr2_hdw *hdw)
 {
 	int status;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 3887190..ba2afbf 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -322,6 +322,13 @@
 	int flag_bilingual;
 	struct pvr2_audio_stat *audio_stat;
 
+	/* Control state needed for cx2341x module */
+	struct cx2341x_mpeg_params enc_cur_state;
+	struct cx2341x_mpeg_params enc_ctl_state;
+	/* True if an encoder attribute has changed */
+	int enc_stale;
+	/* True if enc_cur_state is valid */
+	int enc_cur_valid;
 
 	/* Control state */
 #define VCREATE_DATA(lab) int lab##_val; int lab##_dirty
@@ -339,16 +346,9 @@
 	VCREATE_DATA(res_hor);
 	VCREATE_DATA(res_ver);
 	VCREATE_DATA(srate);
-	VCREATE_DATA(audiobitrate);
-	VCREATE_DATA(audiocrc);
-	VCREATE_DATA(audioemphasis);
-	VCREATE_DATA(vbr);
-	VCREATE_DATA(videobitrate);
-	VCREATE_DATA(videopeak);
-	VCREATE_DATA(interlace);
-	VCREATE_DATA(audiolayer);
 #undef VCREATE_DATA
 
+	struct pvr2_ctld_info *mpeg_ctrl_info;
 
 	struct pvr2_ctrl *controls;
 	unsigned int control_cnt;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 45faabe..7d4799a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -130,6 +130,98 @@
 /* size of a firmware chunk */
 #define FIRMWARE_CHUNK_SIZE 0x2000
 
+/* 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 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_pulldown",
+		.id = V4L2_CID_MPEG_VIDEO_PULLDOWN,
+	},{
+		.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_srate[] = {
 	[PVR2_CVAL_SRATE_48]   = "48KHz",
@@ -137,30 +229,6 @@
 };
 
 
-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 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_input[] = {
@@ -277,6 +345,76 @@
 	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 = 0;
+		}
+	} 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;
@@ -475,14 +613,6 @@
 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
@@ -581,68 +711,13 @@
 		DEFREF(res_ver),
 		DEFINT(200,625),
 	},{
-		.v4l_id = V4L2_CID_PVR_SRATE,
+		.v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
 		.desc = "Sample rate",
 		.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),
-		DEFBOOL,
-	},{
-		.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),
-		DEFBOOL,
-	},{
-		.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),
-		DEFBOOL,
-	},{
-		.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,
@@ -958,6 +1033,10 @@
 	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*/
@@ -1654,6 +1733,8 @@
 	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 >=
@@ -1668,8 +1749,10 @@
 		   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;
@@ -1686,6 +1769,54 @@
 		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);
@@ -1788,6 +1919,7 @@
 		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);
 	}
 	return 0;
@@ -1853,6 +1985,7 @@
 		}
 	} 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);
@@ -2104,10 +2237,6 @@
 			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 ||
@@ -2118,6 +2247,21 @@
 		stale_subsys_mask |= hdw->subsys_stream_mask;
 	}
 
+	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
@@ -2262,6 +2406,8 @@
 		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,0);
 		printk(KERN_INFO "pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
 	} while (0); LOCK_GIVE(hdw->big_lock);
 }
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 779c27e..63f5291 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -26,16 +26,6 @@
 #include "pvrusb2-io.h"
 #include "pvrusb2-ctrl.h"
 
-/* Private V4L2-compatible controls available in this driver, look these up
-   with pvr2_hdw_get_ctrl_v4l(). */
-#define V4L2_CID_PVR_SRATE          (V4L2_CID_PRIVATE_BASE)
-#define V4L2_CID_PVR_AUDIOBITRATE   (V4L2_CID_PRIVATE_BASE+1)
-#define V4L2_CID_PVR_AUDIOCRC       (V4L2_CID_PRIVATE_BASE+2)
-#define V4L2_CID_PVR_AUDIOEMPHASIS  (V4L2_CID_PRIVATE_BASE+3)
-#define V4L2_CID_PVR_VBR            (V4L2_CID_PRIVATE_BASE+4)
-#define V4L2_CID_PVR_VIDEOBITRATE   (V4L2_CID_PRIVATE_BASE+5)
-#define V4L2_CID_PVR_VIDEOPEAK      (V4L2_CID_PRIVATE_BASE+6)
-#define V4L2_CID_PVR_VIDEOSTANDARD  (V4L2_CID_PRIVATE_BASE+7)
 
 /* Private internal control ids, look these up with
    pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */
@@ -47,7 +37,6 @@
 #define PVR2_CID_FREQUENCY 6
 #define PVR2_CID_HRES 7
 #define PVR2_CID_VRES 8
-#define PVR2_CID_INTERLACE 9
 
 /* Legal values for the INPUT state variable */
 #define PVR2_CVAL_INPUT_TV 0
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
index 4127c82..e4ec7f2 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
@@ -91,12 +91,15 @@
 		   hdw->srate_val);
 	switch (hdw->srate_val) {
 	default:
-	case PVR2_CVAL_SRATE_48:
+	case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
 		val = 48000;
 		break;
-	case PVR2_CVAL_SRATE_44_1:
+	case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
 		val = 44100;
 		break;
+	case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+		val = 32000;
+		break;
 	}
 	pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
 }