[pvrusb2] [patch] capture window centering via VIDIOC_S_CROP

vdb128 at picaros.org vdb128 at picaros.org
Fri May 9 16:34:22 CDT 2008


The pvrusb2 patches here below implement capture window centering 
by adding the controls crop_left and crop_top.  They call the v4l2 
ioctl VIDIOC_CROPCAP and VIDIOC_S_CROP to get the capture 
rectangle default and to set the margins.  

The cx25840 kernel module patches implement these controls by 
shifting the analog capture window.  The two sets are independent.  

Horizontal center adjustment is useful when capturing from cable 
tv or a settop box.  The default is fine for vhs playback but cuts 
off the right border of a broadcast 720 dot image.  E.g.

  echo +4 >ctl_crop_left/cur_val  #shifts the window to the right.

Vertical center adjustment is needed for newer encoder firmwares.  
These capture too late and add 2 black lines at the bottom.  E.g.

  echo -2 >ctl_crop_top/cur_val  #shifts the window up.

The approach should work for any system but is as yet untested for 
broadcast M ntsc.  

----------------------------------------------------------------------
Details for 625/50 line systems.

The cx25840 patches add centering support by changing hblank and 
vblank,vblank656 from their cx25840-vbi.c vbi_setup() defaults.  
The allowed range is based on register width.  In practice, for 
720x576, horizontal adjustment works from -129..+14 before the 
encoder loses sync.  Vertical adustment works from -35..+10 but 
setting an odd value results in a misaligned interlace.  

The cx2341x encoder recovers after setting both adjustments to 0.  

Changing the capture window size works but may halt the encoder if 
impossible values are set.  The cx2341x.c assumes scaling whenever 
output res!=720x{480,576} which is not true for a cropped image 
with 1:1 scaling.  


Field synchronization signal standard itu-r.

bt.470, not for system N, with h=H/2 one half line period: 
  total vertical blanking = 50h,

  odd field, fsync is line 1, image at line 23.5: 
    5h fsync + 5h equ + text,white,text,WSS + 575h image + 5h equ,

  even field, fsync is line 313.5, image at line 336:
    5h fsync + 5h equ + bk,text,white,text  + 575h image + 5h equ.

  The decoder vertical reset ends synchronous with the trailing edge 
  of the field sync.  So the image begins after a delay of 
  total_vblank-equ-fsync=50-5-5=40h or (23.5-1)-fsync/2h=20 lines.

  The code cx25840-vbi.c initializes vblank656=40 which works with 
  firmware 2.05.032.  

bt.656
  Each field is 576h long and the line number changes at hblank start.

  odd image at line 23 up to 311 --> delay 39h --> vblank656=38,
  even image at line 336 up to 624 --> delay 40h --> vblank656=39.

  The decoder autoconfig defaults to vblank656=38 and 
  vblank=vblank656-4 which works well with firmware 2.06.039.  

  However, cx25840-vbi.c vbi_setup() sets vblank656=40 which 
  shifts the field capture window 2h or 1 line down.  

  The resulting frame has 2 bottom lines of vsync, like in 
  http://picaros.org/xorg/v36-40.png .
  The bottom left white halfline is line 336+(575h-1h)/2h=623.  

  Hence setting ctl_center_ver/cur_val to -2 solves the problem, see
  http://picaros.org/xorg/v34-38.png .
  The encoder firmware now generates the last frame line from previous 
  lines so the white halfline 623 dissapears.

----------------------------------------------------------------------
cx25840 1-0044: PLL regs = int: 15, frac: 2876158, post: 4
cx25840 1-0044: PLL = 108.000011 MHz
cx25840 1-0044: PLL/8 = 13.500001 MHz
cx25840 1-0044: ADC Sampling freq = 14.317384 MHz
cx25840 1-0044: Chroma sub-carrier freq = 4.433621 MHz
cx25840 1-0044: hblank 132, hactive 720, vblank 36, vactive 580, vblank656 40,
src_dec 543,burst 0x5d, luma_lpf 2, uv_lpf 1, comb 0x20, sc 0x0a8263
cx25840 1-0044: decoder set video input 7, audio input 8
cx25840 1-0044: decoder set video input 7, audio input 8
cx25840 1-0044: changing video std to fmt 4
cx25840 1-0044: PLL regs = int: 15, frac: 2876158, post: 4
cx25840 1-0044: PLL = 108.000011 MHz
cx25840 1-0044: PLL/8 = 13.500001 MHz
cx25840 1-0044: ADC Sampling freq = 14.317384 MHz
cx25840 1-0044: Chroma sub-carrier freq = 4.433621 MHz
cx25840 1-0044: hblank 132, hactive 720, vblank 36, vactive 580, vblank656 40,
src_dec 543, burst 0x5d, luma_lpf 2, uv_lpf 1, comb 0x20, sc 0x0a8263

## recover autoconfig values
# rmmod cx25840 && modprobe cx25840 debug=1

DEV: Unregistering device. ID = '1-0044'
cx25840 1-0044: loaded v4l-cx25840.fw firmware (16382 bytes)
cx25840 1-0044: PLL regs = int: 15, frac: 2876158, post: 4
cx25840 1-0044: PLL = 108.000011 MHz
cx25840 1-0044: PLL/8 = 13.500001 MHz
cx25840 1-0044: ADC Sampling freq = 14.317384 MHz
cx25840 1-0044: Chroma sub-carrier freq = 3.579545 MHz
cx25840 1-0044: hblank 122, hactive 720, vblank 26, vactive 487, vblank656 26, src_dec 543, burst 0x5b, luma_lpf 1, uv_lpf 1, comb 0x66, sc 0x087c1f
cx25840 1-0044: decoder set video input 7, audio input 8
cx25840 1-0044: enable output
cx25840 1-0044: disable output

# echo 0 >ctl_center_hor/cur_val && echo 0 >ctl_center_ver/cur_val

cx25840 1-0044: hblank=132->132
cx25840 1-0044: vblank=34->36 vblank656=38->40
cx25840 1-0044: enable output

----------------------------------------------------------------------
Fri May  9 23:27:51 CEST 2008
--- linux-2.6.25.1/drivers/media/video/cx25840/cx25840-core-d.c	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/cx25840/cx25840-core.c	2008-05-07 22:25:56.000000000 +0200
@@ -704,6 +704,167 @@ static int get_v4lctrl(struct i2c_client
 
 /* ----------------------------------------------------------------------- */
 
+static int origin_v4lcrop(struct i2c_client *client, v4l2_std_id *cstd, int *hbdef, int *vbdef) 
+{
+	v4l2_std_id std = cx25840_get_v4lstd(client);
+	int hblank, vblank;
+
+	if(std & V4L2_STD_625_50) {
+		hblank=132;
+		vblank=36;
+	} else {
+		hblank=122;
+		if(std == V4L2_STD_PAL_M) {
+			vblank=20;
+		} else {
+			vblank=26;
+		}
+	}
+	if(cstd) *cstd=std;
+	if(hbdef) *hbdef=hblank;
+	if(vbdef) *vbdef=vblank;
+
+	return 0;
+}
+
+static int v4lcropcap(struct i2c_client *client, struct v4l2_cropcap *cap) 
+{
+	v4l2_std_id std;
+	int htotal, hbdef, vtotal, vbdef, vactive, doth, dotv;
+
+	if(cap->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	origin_v4lcrop(client, &std, &hbdef, &vbdef);
+	if(std & V4L2_STD_625_50) {
+	  htotal=864;
+	  vtotal=625;
+	  vactive=580;
+	  doth=59; 
+	  dotv=54;
+	} else {
+	  htotal=858;
+	  vtotal=525;
+	  vactive=487;
+	  doth=10; 
+	  dotv=11;
+	}
+
+	cap->bounds.left=3-hbdef;    /* hblank delay >=3 dot */
+	cap->bounds.width=htotal-4;  /* SAV EAV bt.656 */
+	cap->bounds.top=1-vbdef;     /* vblank delay >=1 halfline */
+	cap->bounds.height=vtotal-3; /* field sync + min_vblank = 2h+1h = 3h */
+
+	cap->defrect.left=0;
+	cap->defrect.width=720;
+	cap->defrect.top=0;
+	cap->defrect.height=(vactive >> 4) << 4;
+
+	cap->pixelaspect.numerator=dotv;
+	cap->pixelaspect.denominator=doth;
+
+	return 0;
+}
+
+/* 
+ * base    [7:0]=blank_cnt_low 
+ * base+1  [7:4]=active_cnt_low  [3:2]=ss  [1:0]=blank_cnt_high
+ * base+2  [7:6]=rr  [5:0]=active_cnt_high
+ */
+static int bsa_read(struct i2c_client *client, int base, int *pbl, int *psav, int *pact) 
+{
+	int save, blank, active;
+
+	blank = cx25840_read(client, base);
+	save = cx25840_read(client, base+1);
+	blank |= ((save & 3) << 8);
+	active = ((save & 0xf0) >> 4) 
+	  | ((cx25840_read(client, base+2) & 0x3f) << 4);
+
+	if(pbl) *pbl=blank;
+	if(psav) *psav=save;
+	if(pact) *pact=active;
+	return 0;
+}
+
+static int bsa_write(struct i2c_client *client, int base, int blank, int save, int active) 
+{
+	int snew;
+
+	cx25840_write(client, base, blank & 0xff);
+	snew = (save & 0x0c) | ((active & 0xf) << 4) | ((blank >> 8) & 3);
+	cx25840_write(client, base+1, snew);
+	cx25840_write(client, base+2, active >> 4);
+	return 0;
+}
+
+static int set_v4lcrop(struct i2c_client *client, struct v4l2_crop *crop) 
+{
+	v4l2_std_id std;
+	int is_50Hz;
+	int hbdef, hbnew, hanew, hsave, hblank, hactive;
+	int vbdef, vbnew, vbnew656, vanew, vsave, vblank, vblank656, vactive;
+
+	if(crop->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	origin_v4lcrop(client, &std, &hbdef, &vbdef);
+
+	hbnew=crop->c.left+hbdef;
+	hanew=crop->c.width;
+	if(hbnew<0 || hbnew>1023 || hanew<0 || hanew>1023) {
+		v4l_err(client, "invalid horizontal crop %d %d\n", crop->c.left, crop->c.width);
+		return -ERANGE;
+	}
+
+	is_50Hz=std & V4L2_STD_625_50;
+	vbnew=crop->c.top+vbdef;
+	vbnew656=vbnew+((is_50Hz || std==V4L2_STD_PAL_M) ? 4 : 0);
+	vanew=crop->c.height+(is_50Hz ? 4 : 7);
+	if(vbnew<0 || vbnew>1023 || vanew<0 || vanew>1023) {
+		v4l_err(client, "invalid vertical crop %d %d\n", crop->c.top, crop->c.height);
+		return -ERANGE;
+	}
+
+	bsa_read(client, 0x470, &hblank, &hsave, &hactive);
+	bsa_write(client, 0x470, hbnew, hsave, hanew);
+	v4l_dbg(1, cx25840_debug, client, "hblank=%i->%i hactive=%i->%i\n", 
+		hblank, hbnew, hactive, hanew);
+
+	bsa_read(client, 0x474, &vblank, &vsave, &vactive);
+	vblank656 = cx25840_read(client, 0x477);
+	bsa_write(client, 0x474, vbnew, vsave, vanew);
+	cx25840_write(client, 0x477, vbnew656 & 0xff);  /* XXX at 256 */
+	v4l_dbg(1, cx25840_debug, client, "vblank=%i->%i vactive=%i->%i"
+		" vblank656=%i->%i\n", vblank,vbnew, vactive,vanew, 
+		vblank656,vbnew656);
+	return 0;
+}
+
+static int get_v4lcrop(struct i2c_client *client, struct v4l2_crop *crop) 
+{
+	v4l2_std_id std;
+	int hbdef, hblank, hactive;
+	int vbdef, vblank, vactive;
+
+	if(crop->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	origin_v4lcrop(client, &std, &hbdef, &vbdef);
+
+	bsa_read(client, 0x470, &hblank, NULL, &hactive);
+	crop->c.left = hblank-hbdef;
+	crop->c.width = hactive;
+
+	bsa_read(client, 0x474, &vblank, NULL, &vactive);
+	crop->c.top = vblank-vbdef;
+	crop->c.height = vactive - (std & V4L2_STD_625_50 ? 4 : 7);
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
 static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
 {
 	switch (fmt->type) {
@@ -1066,6 +1227,15 @@ static int cx25840_command(struct i2c_cl
 	case VIDIOC_S_CTRL:
 		return set_v4lctrl(client, (struct v4l2_control *)arg);
 
+	case VIDIOC_CROPCAP:
+		return v4lcropcap(client, (struct v4l2_cropcap *)arg);
+
+	case VIDIOC_G_CROP:
+		return get_v4lcrop(client, (struct v4l2_crop *)arg);
+
+	case VIDIOC_S_CROP:
+		return set_v4lcrop(client, (struct v4l2_crop *)arg);
+
 	case VIDIOC_QUERYCTRL:
 	{
 		struct v4l2_queryctrl *qc = arg;
--- linux-2.6.25.1/drivers/media/video/cx25840/cx25840-vbi-d.c	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/cx25840/cx25840-vbi.c	2008-05-07 22:28:44.000000000 +0200
@@ -98,12 +98,12 @@ void cx25840_vbi_setup(struct i2c_client
 	}
 
 	if (std & V4L2_STD_625_50) {
-		hblank=0x084;
-		hactive=0x2d0;
-		burst=0x5d;
-		vblank=0x024;
-		vactive=0x244;
-		vblank656=0x28;
+		hblank=132;
+		hactive=720;
+		burst=93;
+		vblank=36;
+		vactive=580;
+		vblank656=40;
 		src_decimation=0x21f;
 
 		luma_lpf=2;
@@ -140,7 +140,6 @@ void cx25840_vbi_setup(struct i2c_client
 			vblank656=24;
 			burst=0x61;
 			comb=0x20;
-
 			sc=555452;
 		} else {
 			vblank=26;
@@ -179,9 +178,9 @@ void cx25840_vbi_setup(struct i2c_client
 						"%d.%06d MHz\n",
 						fsc/1000000,fsc%1000000);
 
-		v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
-			"vblank %i, vactive %i, vblank656 %i, src_dec %i, "
-			"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
+		v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i,"
+			" vblank %i, vactive %i, vblank656 %i, src_dec %i,"
+			" burst %i, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
 			" sc 0x%06x\n",
 			hblank, hactive, vblank, vactive, vblank656,
 			src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-hdw-d.c	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-hdw.c	2008-05-07 22:31:38.000000000 +0200
@@ -340,6 +340,41 @@ static int ctrl_freq_set(struct pvr2_ctr
 	return 0;
 }
 
+static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
+{
+	struct v4l2_cropcap *cap=&cptr->hdw->cropcap;
+	if(cap->bounds.width>0) {
+		*left= cap->bounds.left - cap->defrect.left;
+	} else {
+		*left=-119;
+	}
+	return 0;
+}
+
+static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left)
+{
+	struct v4l2_cropcap *cap=&cptr->hdw->cropcap;
+	if(cap->bounds.width>0) {
+		*left=cap->bounds.left + cap->bounds.width - cap->defrect.left;
+		*left+=3;
+		*left-=cptr->hdw->cropw_val;
+	} else {
+		*left=340;
+	}
+	return 0;
+}
+
+static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top)
+{
+	struct v4l2_cropcap *cap=&cptr->hdw->cropcap;
+	if(cap->bounds.height>0) {
+		*top= cap->bounds.top - cap->defrect.top;
+	} else {
+		*top=-19;
+	}
+	return 0;
+}
+
 static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
 {
 	/* Actual maximum depends on the video standard in effect. */
@@ -351,6 +386,19 @@ static int ctrl_vres_max_get(struct pvr2
 	return 0;
 }
 
+static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
+{
+	struct v4l2_cropcap *cap=&cptr->hdw->cropcap;
+	if(cap->bounds.height>0) {
+		*top=cap->bounds.top + cap->bounds.height - cap->defrect.top;
+		*top-=cptr->hdw->croph_val;
+	} else {
+		ctrl_vres_max_get(cptr, top);
+		*top-=32;
+	}
+	return 0;
+}
+
 static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
 {
 	/* Actual minimum depends on device digitizer type. */
@@ -729,6 +777,10 @@ VCREATE_FUNCS(balance)
 VCREATE_FUNCS(bass)
 VCREATE_FUNCS(treble)
 VCREATE_FUNCS(mute)
+VCREATE_FUNCS(cropl)
+VCREATE_FUNCS(cropt)
+VCREATE_FUNCS(cropw)
+VCREATE_FUNCS(croph)
 VCREATE_FUNCS(audiomode)
 VCREATE_FUNCS(res_hor)
 VCREATE_FUNCS(res_ver)
@@ -800,6 +852,39 @@ static const struct pvr2_ctl_info contro
 		DEFREF(mute),
 		DEFBOOL,
 	},{
+		.desc = "Capture left margin",
+		.name = "crop_left",
+		.internal_id = PVR2_CID_CROPL,
+		.default_value = 0,
+		DEFREF(cropl),
+		DEFINT(-129,340),
+		.get_min_value = ctrl_cropl_min_get,
+		.get_max_value = ctrl_cropl_max_get,
+	},{
+		.desc = "Capture top margin",
+		.name = "crop_top",
+		.internal_id = PVR2_CID_CROPT,
+		.default_value = 0,
+		DEFREF(cropt),
+		DEFINT(-35,544),
+		.get_min_value = ctrl_cropt_min_get,
+		.get_max_value = ctrl_cropt_max_get,
+	},{
+		.desc = "Capture width",
+		.name = "crop_width",
+		.internal_id = PVR2_CID_CROPW,
+		.default_value = 720,
+		DEFREF(cropw),
+		DEFINT(388,849), /* determined empirically, any res_hor>=64 */
+	},{
+		.desc = "Capture height",
+		.name = "crop_height",
+		.internal_id = PVR2_CID_CROPH,
+		.default_value = 480,
+		DEFREF(croph),
+		DEFINT(32,576),
+		.get_max_value = ctrl_vres_max_get,
+	},{
 		.desc = "Video Source",
 		.name = "input",
 		.internal_id = PVR2_CID_INPUT,
@@ -1945,6 +2030,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct 
 			valid_std_mask;
 	}
 
+	memset(&hdw->cropcap, 0, sizeof hdw->cropcap);
 	hdw->eeprom_addr = -1;
 	hdw->unit_number = -1;
 	hdw->v4l_minor_number_video = -1;
@@ -2353,6 +2439,29 @@ static int pvr2_hdw_commit_execute(struc
 		}
 	}
 
+ 	/* The broadcast decoder can only scale down, so if
+ 	 * res_*_dirty && crop window < output format ==> enlarge crop.
+ 	 *
+ 	 * The mpeg encoder receives fields of res_hor_val dots and 
+ 	 * res_ver_val halflines.  Limits: hor<=720, ver<=576.
+ 	 */
+ 	if(hdw->res_hor_dirty && hdw->cropw_val<hdw->res_hor_val) {
+ 		hdw->cropw_val = hdw->res_hor_val;
+ 		hdw->cropw_dirty = !0;
+ 	} else if(hdw->cropw_dirty) {
+ 		hdw->res_hor_dirty = !0;           /* must rescale */
+ 		hdw->res_hor_val = min(720,hdw->cropw_val);
+ 	}
+ 	if(hdw->res_ver_dirty && hdw->croph_val<hdw->res_ver_val) {
+ 		hdw->croph_val = hdw->res_ver_val;
+ 		hdw->croph_dirty = !0;
+ 	} else if(hdw->croph_dirty) {
+ 		int nvres= hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
+ 
+ 		hdw->res_ver_dirty = !0;
+ 		hdw->res_ver_val = min(nvres,hdw->croph_val);
+ 	}
+
 	/* If any of the below has changed, then we can't do the update
 	   while the pipeline is running.  Pipeline must be paused first
 	   and decoder -> encoder connection be made quiescent before we
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2-d.c	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c	2008-05-07 22:25:56.000000000 +0200
@@ -38,8 +38,9 @@
 #define OP_VOLUME 3
 #define OP_FREQ 4
 #define OP_AUDIORATE 5
-#define OP_SIZE 6
-#define OP_LOG 7
+#define OP_CROP 6
+#define OP_SIZE 7
+#define OP_LOG 8
 
 static const struct pvr2_i2c_op * const ops[] = {
 	[OP_STANDARD] = &pvr2_i2c_op_v4l2_standard,
@@ -47,6 +48,7 @@ static const struct pvr2_i2c_op * const 
 	[OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh,
 	[OP_VOLUME] = &pvr2_i2c_op_v4l2_volume,
 	[OP_FREQ] = &pvr2_i2c_op_v4l2_frequency,
+	[OP_CROP] = &pvr2_i2c_op_v4l2_crop,
 	[OP_SIZE] = &pvr2_i2c_op_v4l2_size,
 	[OP_LOG] = &pvr2_i2c_op_v4l2_log,
 };
@@ -60,6 +62,7 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw
 			(1 << OP_BCSH) |
 			(1 << OP_VOLUME) |
 			(1 << OP_FREQ) |
+			(1 << OP_CROP) |
 			(1 << OP_SIZE) |
 			(1 << OP_LOG));
 	cp->status_poll = pvr2_v4l2_cmd_status_poll;
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2-d.c	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c	2008-05-07 22:25:56.000000000 +0200
@@ -234,6 +234,46 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l
 };
 
 
+static void set_crop(struct pvr2_hdw *hdw)
+{
+	struct v4l2_cropcap cap;
+	struct v4l2_crop crop;
+	int stat;
+
+	memset(&cap, 0, sizeof cap);
+	cap.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	stat=pvr2_i2c_core_cmd(hdw,VIDIOC_CROPCAP,&cap);
+	hdw->cropcap=cap;
+
+	memset(&crop, 0, sizeof crop);
+	crop.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	crop.c=cap.defrect;
+	crop.c.left+=hdw->cropl_val;
+	crop.c.top+=hdw->cropt_val;
+	crop.c.height=hdw->croph_val;
+	crop.c.width=hdw->cropw_val;
+
+	pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_crop stat=%d cap=%d:%d:%d:%d"
+		   " crop=%d:%d:%d:%d", stat, cap.bounds.width, 
+		   cap.bounds.height, cap.bounds.left, cap.bounds.top, 
+		   crop.c.width, crop.c.height, crop.c.left, crop.c.top);
+
+	if(stat>=0) pvr2_i2c_core_cmd(hdw,VIDIOC_S_CROP,&crop);
+}
+
+static int check_crop(struct pvr2_hdw *hdw)
+{
+	return (hdw->cropl_dirty || hdw->cropt_dirty
+		|| hdw->cropw_dirty || hdw->croph_dirty);
+}
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = {
+	.check = check_crop,
+	.update = set_crop,
+	.name = "v4l2_crop",
+};
+
+
 static void do_log(struct pvr2_hdw *hdw)
 {
 	pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()");
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-sysfs-d.c	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-sysfs.c	2008-05-07 22:25:56.000000000 +0200
@@ -399,6 +399,10 @@ CREATE_BATCH(56)
 CREATE_BATCH(57)
 CREATE_BATCH(58)
 CREATE_BATCH(59)
+CREATE_BATCH(60)
+CREATE_BATCH(61)
+CREATE_BATCH(62)
+CREATE_BATCH(63)
 
 struct pvr2_sysfs_func_set {
 	ssize_t (*show_name)(struct device *,
@@ -500,6 +504,10 @@ static struct pvr2_sysfs_func_set funcs[
 	INIT_BATCH(57),
 	INIT_BATCH(58),
 	INIT_BATCH(59),
+	INIT_BATCH(60),
+	INIT_BATCH(61),
+	INIT_BATCH(62),
+	INIT_BATCH(63),
 };
 
 
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-hdw-d.h	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-hdw.h	2008-05-07 22:25:56.000000000 +0200
@@ -35,8 +35,12 @@
 #define PVR2_CID_INPUT 4
 #define PVR2_CID_AUDIOMODE 5
 #define PVR2_CID_FREQUENCY 6
-#define PVR2_CID_HRES 7
-#define PVR2_CID_VRES 8
+#define PVR2_CID_CROPL 7
+#define PVR2_CID_CROPT 8
+#define PVR2_CID_CROPW 9
+#define PVR2_CID_CROPH 10
+#define PVR2_CID_HRES 11
+#define PVR2_CID_VRES 12
 
 /* Legal values for the INPUT state variable */
 #define PVR2_CVAL_INPUT_TV 0
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-hdw-internal-d.h	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h	2008-05-07 22:25:56.000000000 +0200
@@ -307,6 +307,7 @@ struct pvr2_hdw {
 	struct pvr2_ctl_info std_info_cur;
 	struct v4l2_standard *std_defs;
 	const char **std_enum_names;
+	struct v4l2_cropcap cropcap;
 
 	// Generated string names, one per actual V4L2 standard
 	const char *std_mask_ptrs[32];
@@ -350,6 +351,10 @@ struct pvr2_hdw {
 	VCREATE_DATA(bass);
 	VCREATE_DATA(treble);
 	VCREATE_DATA(mute);
+	VCREATE_DATA(cropl);
+	VCREATE_DATA(cropt);
+	VCREATE_DATA(cropw);
+	VCREATE_DATA(croph);
 	VCREATE_DATA(input);
 	VCREATE_DATA(audiomode);
 	VCREATE_DATA(res_hor);
--- linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2-d.h	2008-05-01 23:45:25.000000000 +0200
+++ linux-2.6.25.1/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h	2008-05-07 22:25:56.000000000 +0200
@@ -30,6 +30,7 @@ extern const struct pvr2_i2c_op pvr2_i2c
 extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh;
 extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume;
 extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop;
 extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size;
 extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode;
 extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log;



More information about the pvrusb2 mailing list