[pvrusb2] [patch] 24xxx capture window cropping support

vdb128 at picaros.org vdb128 at picaros.org
Sat Sep 5 08:48:17 CDT 2009


Here below is a patch for the setup: 
Linux 2.6.29.6, 24xxx device, cx25840 decoder, firmware 2.06.039.

Notes:
1.  The vblank default puts the WSS/625 waveform at line 23 in the 
first halfline of the frame.  The odd field image starts at line 23.5. 
http://www.isely.net/pipermail/pvrusb2/2009-July/002512.html  

2. The M/PAL/525 timing seems an anachronism.  If the top of the frame 
contains a few black lines or if the image is bottom centered, try 
setting ctl_crop_top/cur_val to 6.  

3. An odd value in ctl_crop_top/cur_val swaps the spatial interlacing.  
Useful for fixing badly captured system N tapes.  

4. All is childproof since the cx25840 driver now limits width and 
height.  Read ctl_crop_*/cur_val after write to see what was set.  

Installation:
* The function call layout in v4l2-subdef.h has changed.  Removing  *
* and adding the modules pvrusb2 and cx25840 will crash the system. *
patch, 
make clean && make bzImage && make modules 
&& make install && make modules_install, 
rmmod pvrusb2 cx25840 wm8775 tuner v4l2_common videodev, 
reboot if the above doesn't work,
modprobe pvrusb2

Sat Sep  5 14:08:04 CEST 2009
--- linux-2.6.29.6/drivers/media/video/v4l2-subdev-d.c	2009-07-03 01:41:20.000000000 +0200
+++ linux-2.6.29.6/drivers/media/video/v4l2-subdev.c	2009-09-03 18:19:04.000000000 +0200
@@ -64,6 +64,8 @@ int v4l2_subdev_command(struct v4l2_subd
 		return v4l2_subdev_call(sd, tuner, g_tuner, arg);
 	case VIDIOC_S_STD:
 		return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg);
+	case VIDIOC_G_STD:
+		return v4l2_subdev_call(sd, tuner, g_std, arg);
 	case VIDIOC_S_FREQUENCY:
 		return v4l2_subdev_call(sd, tuner, s_frequency, arg);
 	case VIDIOC_G_FREQUENCY:
@@ -92,6 +94,12 @@ int v4l2_subdev_command(struct v4l2_subd
 		return v4l2_subdev_call(sd, video, g_vbi_data, arg);
 	case VIDIOC_G_SLICED_VBI_CAP:
 		return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg);
+        case VIDIOC_CROPCAP:
+		return v4l2_subdev_call(sd, video, cropcap, arg);
+        case VIDIOC_S_CROP:
+		return v4l2_subdev_call(sd, video, s_crop, arg);
+        case VIDIOC_G_CROP:
+		return v4l2_subdev_call(sd, video, g_crop, arg);
 	case VIDIOC_S_FMT:
 		return v4l2_subdev_call(sd, video, s_fmt, arg);
 	case VIDIOC_G_FMT:
--- linux-2.6.29.6/drivers/media/video/cx25840/cx25840-core-d.c	2009-07-03 01:41:20.000000000 +0200
+++ linux-2.6.29.6/drivers/media/video/cx25840/cx25840-core.c	2009-09-05 07:50:20.000000000 +0200
@@ -348,35 +348,73 @@ static void cx23885_initialize(struct i2
 
 /* ----------------------------------------------------------------------- */
 
+/* This code allows for seamless autodetect. */
+static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct cx25840_state *state = to_state(sd);
+	v4l2_std_id fmt2id[16]={
+		V4L2_STD_UNKNOWN,   V4L2_STD_NTSC_M,   /*  0, 1, */
+		V4L2_STD_NTSC_M_JP, V4L2_STD_NTSC_443, /*  2, 3, */
+		V4L2_STD_PAL,       V4L2_STD_PAL_M,    /*  4, 5, */
+		V4L2_STD_PAL_N,     V4L2_STD_PAL_Nc,   /*  6, 7, */
+		V4L2_STD_PAL_60,    V4L2_STD_UNKNOWN,  /*  8, 9, */   
+		V4L2_STD_UNKNOWN,   V4L2_STD_UNKNOWN,  /* 10, 11, */   
+		V4L2_STD_SECAM,     V4L2_STD_UNKNOWN,  /* 12, 13 */
+		V4L2_STD_UNKNOWN,   V4L2_STD_UNKNOWN   /* 14, 15 */
+	};
+	v4l2_std_id res;
+
+	res=state->std;
+	if(res == V4L2_STD_UNKNOWN) { /* unset or auto: read from decoder */
+		u8 fmt;
+		struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+		/* check VID_FMT_SEL first */
+		fmt = cx25840_read(client, 0x400) & 0xf;
+		if (!fmt) /* set to autodetect: check AFD_FMT_STAT if  */
+		  fmt = cx25840_read(client, 0x40d) & 0xf;
+
+		res=fmt2id[fmt];
+		
+		if(res == V4L2_STD_NTSC_M
+		   && !state->is_cx25836 && cx25840_read(client, 0x805) == 2)
+		  res=V4L2_STD_NTSC_M_KR;
+	}
+	*std=res;
+
+	return 0;
+}
+
 void cx25840_std_setup(struct i2c_client *client)
 {
 	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-	v4l2_std_id std = state->std;
+	v4l2_std_id std;
 	int hblank, hactive, burst, vblank, vactive, sc;
 	int vblank656, src_decimation;
 	int luma_lpf, uv_lpf, comb;
 	u32 pll_int, pll_frac, pll_post;
 
+	cx25840_g_std(i2c_get_clientdata(client), &std);
+
 	/* datasheet startup, step 8d */
 	if (std & ~V4L2_STD_NTSC)
 		cx25840_write(client, 0x49f, 0x11);
 	else
 		cx25840_write(client, 0x49f, 0x14);
 
+	src_decimation = 543;
 	if (std & V4L2_STD_625_50) {
-		hblank = 132;
+		hblank = 136; /* 132 */
 		hactive = 720;
 		burst = 93;
-		vblank = 36;
+		vblank = 34;
 		vactive = 580;
-		vblank656 = 40;
-		src_decimation = 0x21f;
 		luma_lpf = 2;
 
 		if (std & V4L2_STD_SECAM) {
 			uv_lpf = 0;
 			comb = 0;
-			sc = 0x0a425f;
+			sc = 672351;  /* 0x0a425f */
 		} else if (std == V4L2_STD_PAL_Nc) {
 			uv_lpf = 1;
 			comb = 0x20;
@@ -384,37 +422,34 @@ void cx25840_std_setup(struct i2c_client
 		} else {
 			uv_lpf = 1;
 			comb = 0x20;
-			sc = 688739;
+			sc = 688739;  /* 0x0a8263 */
 		}
 	} else {
-		hactive = 720;
 		hblank = 122;
+		hactive = 720;
 		vactive = 487;
 		luma_lpf = 1;
 		uv_lpf = 1;
 
-		src_decimation = 0x21f;
 		if (std == V4L2_STD_PAL_60) {
 			vblank = 26;
-			vblank656 = 26;
 			burst = 0x5b;
 			luma_lpf = 2;
 			comb = 0x20;
 			sc = 688739;
 		} else if (std == V4L2_STD_PAL_M) {
 			vblank = 20;
-			vblank656 = 24;
 			burst = 0x61;
 			comb = 0x20;
 			sc = 555452;
 		} else {
 			vblank = 26;
-			vblank656 = 26;
 			burst = 0x5b;
 			comb = 0x66;
 			sc = 556063;
 		}
 	}
+	vblank656 = vblank+4;
 
 	/* DEBUG: Displays configured PLL frequency */
 	pll_int = cx25840_read(client, 0x108);
@@ -446,8 +481,8 @@ void cx25840_std_setup(struct i2c_client
 
 		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, "
-			"sc 0x%06x\n",
+			"burst %i, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
+			"sc %i\n",
 			hblank, hactive, vblank, vactive, vblank656,
 			src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
 	}
@@ -491,20 +526,24 @@ void cx25840_std_setup(struct i2c_client
 		cx25840_write(client, 0x47f, 0x00);
 		state->vbi_line_offset = 8;
 	}
+	/* Alignment test: force the use of vblank656 VIP_OPT_AL */
+	/* cx25840_write(client, 0x406, 0x17); */
 }
 
 /* ----------------------------------------------------------------------- */
 
 static void input_change(struct i2c_client *client)
 {
-	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-	v4l2_std_id std = state->std;
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(sd);
+	v4l2_std_id std;
+
+	cx25840_g_std(sd, &std);
 
 	/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
 	if (std & V4L2_STD_SECAM) {
 		cx25840_write(client, 0x402, 0);
-	}
-	else {
+	} else {
 		cx25840_write(client, 0x402, 0x04);
 		cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
 	}
@@ -711,6 +750,7 @@ static int set_v4lstd(struct i2c_client 
 		cx25840_and_or(client, 0x47b, ~6, 0);
 	}
 	cx25840_and_or(client, 0x400, ~0xf, fmt);
+	if(!fmt) pal_m=2;
 	cx25840_and_or(client, 0x403, ~0x3, pal_m);
 	cx25840_std_setup(client);
 	if (!state->is_cx25836)
@@ -720,6 +760,191 @@ static int set_v4lstd(struct i2c_client 
 
 /* ----------------------------------------------------------------------- */
 
+struct cdefst {
+  int hblank, hactive, htotal;       /* per line, unit = 1 dot */
+  int vblank, vactive, vtotal, vpre; /* per field, unit = 1 halfline */
+  int doth, dotv;                /* relative size dot: horizontal, vertical */
+};
+static struct cdefst *cdefaulttiming(v4l2_std_id std) 
+{
+  static struct cdefst cdef525={  122, 720, 858,  26, 487, 525, 7,  10, 11 };
+  static struct cdefst cdef525m={ 122, 720, 858,  20, 487, 525, 7,  10, 11 };
+  static struct cdefst cdef625={  136, 720, 864,  34, 580, 625, 4,  59, 54 };
+
+  if(std & V4L2_STD_625_50) return &cdef625;
+
+  if(std == V4L2_STD_PAL_M) return &cdef525m;
+
+  return &cdef525; 
+}
+
+static int min_vblank=2;  /* min vblank delay 2 halfline */
+static int cropcap_origin(struct v4l2_subdev *sd, v4l2_std_id *cstd, struct cdefst **cdef, struct v4l2_rect *bounds) 
+{
+	v4l2_std_id std;
+	struct cdefst *def;
+
+	cx25840_g_std(sd, &std);
+	if(cstd) *cstd=std;
+
+	def=cdefaulttiming(std);
+	if(cdef) *cdef=def;
+
+	if(bounds) {
+	  bounds->left=4-def->hblank;             /* hblank delay >=4 dot */
+	  bounds->width=def->htotal-2;            /* EAV bt.656 */
+	  bounds->top=min_vblank-def->vblank;     /* vblank delay >=2h */
+	  bounds->height=def->vtotal-2-min_vblank;/* field_sync + min_vblank */
+	}
+
+	return 0;
+}
+
+static int cx25840_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cap) 
+{
+	v4l2_std_id std;
+	struct cdefst *def;
+
+	if(cap->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	cropcap_origin(sd, &std, &def, &cap->bounds);
+
+	cap->defrect.left=0;
+	cap->defrect.width=def->hactive;
+	cap->defrect.top=0;
+	cap->defrect.height=def->vactive-def->vpre;
+
+	cap->pixelaspect.numerator=def->dotv;
+	cap->pixelaspect.denominator=def->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 cx25840_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	v4l2_std_id std;
+	struct cdefst *def;
+	int hblank, hactive;
+	int vblank, vactive;
+
+	if(crop->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	cropcap_origin(sd, &std, &def, NULL);
+
+	bsa_read(client, 0x470, &hblank, NULL, &hactive);
+	crop->c.left = hblank-def->hblank;
+	crop->c.width = hactive;
+
+	bsa_read(client, 0x474, &vblank, NULL, &vactive);
+	crop->c.top = vblank-def->vblank;
+	crop->c.height = vactive-def->vpre;
+
+	return 0;
+}
+
+static int cx25840_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	v4l2_std_id std;
+	struct cdefst *def;
+	struct v4l2_rect b, c;
+	int bleftend, btopend, hwidthmax, vheightmax;
+	int hbnew, hanew, hsave, hblank, hactive;
+	int vbnew, vbnew656, vanew, vsave, vblank, vblank656, vactive;
+
+	if(crop->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	cropcap_origin(sd, &std, &def, &b);
+	c=crop->c;
+
+	if(c.width < 2 || c.height<2) {
+		v4l_err(client, "invalid crop=%d:%d:%d:%d\n", c.width, 
+			c.height, c.left, c.top);
+		return -ERANGE;
+	}
+
+	hwidthmax=def->htotal-16;
+	if(c.width > hwidthmax) c.width=hwidthmax;
+	
+	vheightmax=def->vtotal-def->vpre-min_vblank;
+	if(c.height > vheightmax) c.height=vheightmax;
+
+	/* An odd value for c.top swaps spatial interlacing.  Not enforced 
+	 * since VHS tapes with incorrect interlacing exist. 
+	 */
+
+	bleftend=b.left+b.width;
+	if(c.left < b.left || c.left+c.width > bleftend) {
+		v4l_err(client, "invalid horizontal crop %d %d\n", 
+			crop->c.left, crop->c.width);
+		return -ERANGE;
+	}
+	hbnew=c.left+def->hblank;
+	hanew=c.width;
+
+	btopend=b.top+b.height;
+	if(c.top < b.top || c.top+c.height > b.top+b.height) {
+		v4l_err(client, "invalid vertical crop %d %d\n", 
+			crop->c.top, crop->c.height);
+		return -ERANGE;
+	}
+	vbnew=c.top+def->vblank;
+	vbnew656=vbnew+4;
+	vanew=c.height+def->vpre;
+	//vbnew=34;
+
+	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 cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
 	struct cx25840_state *state = to_state(sd);
@@ -844,7 +1069,7 @@ static int cx25840_s_fmt(struct v4l2_sub
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct v4l2_pix_format *pix;
 	int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
-	int is_50Hz = !(state->std & V4L2_STD_525_60);
+	int is_50Hz = state->std & V4L2_STD_625_50;
 
 	switch (fmt->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -1272,14 +1497,16 @@ static int cx25840_g_tuner(struct v4l2_s
 {
 	struct cx25840_state *state = to_state(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	u8 vpres = cx25840_read(client, 0x40e) & 0x20;
+	u8 vpres = cx25840_read(client, 0x40e);
 	u8 mode;
-	int val = 0;
+	int signal, val = 0;
 
 	if (state->radio)
 		return 0;
 
-	vt->signal = vpres ? 0xffff : 0x0;
+	signal=	vpres & 0x7f;
+	if(vpres & 0x20) signal|= 0xff80;
+	vt->signal = signal;
 	if (state->is_cx25836)
 		return 0;
 
@@ -1382,6 +1609,7 @@ static int cx25840_log_status(struct v4l
 
 static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg)
 {
+	struct v4l2_subdev *sd;
 	/* ignore this command */
 	if (cmd == TUNER_SET_TYPE_ADDR || cmd == TUNER_SET_CONFIG)
 		return 0;
@@ -1389,8 +1617,9 @@ static int cx25840_command(struct i2c_cl
 	/* Old-style drivers rely on initialization on first use, so
 	   call the init whenever a command is issued to this driver.
 	   New-style drivers using v4l2_subdev should call init explicitly. */
-	cx25840_init(i2c_get_clientdata(client), 0);
-	return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+	sd = i2c_get_clientdata(client);
+	cx25840_init(sd, 0);
+	return v4l2_subdev_command(sd, cmd, arg);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -1412,6 +1641,7 @@ static const struct v4l2_subdev_core_ops
 static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
 	.s_frequency = cx25840_s_frequency,
 	.s_std = cx25840_s_std,
+	.g_std = cx25840_g_std,
 	.s_radio = cx25840_s_radio,
 	.g_tuner = cx25840_g_tuner,
 	.s_tuner = cx25840_s_tuner,
@@ -1424,6 +1654,9 @@ static const struct v4l2_subdev_audio_op
 
 static const struct v4l2_subdev_video_ops cx25840_video_ops = {
 	.s_routing = cx25840_s_video_routing,
+	.cropcap = cx25840_cropcap,
+	.g_crop = cx25840_g_crop,
+	.s_crop = cx25840_s_crop,
 	.g_fmt = cx25840_g_fmt,
 	.s_fmt = cx25840_s_fmt,
 	.decode_vbi_line = cx25840_decode_vbi_line,
--- linux-2.6.29.6/drivers/media/video/pvrusb2/pvrusb2-hdw-d.c	2009-07-03 01:41:20.000000000 +0200
+++ linux-2.6.29.6/drivers/media/video/pvrusb2/pvrusb2-hdw.c	2009-09-05 04:59:59.000000000 +0200
@@ -450,31 +450,35 @@ static int ctrl_cropt_max_get(struct pvr
 	return 0;
 }
 
-static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val)
+static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width)
 {
 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
-	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+	int stat, bleftend, cleft;
+
+	stat = pvr2_hdw_check_cropcap(cptr->hdw);
 	if (stat != 0) {
 		return stat;
 	}
-	*val = 0;
-	if (cap->bounds.width > cptr->hdw->cropl_val) {
-		*val = cap->bounds.width - cptr->hdw->cropl_val;
-	}
+	bleftend=cap->bounds.left+cap->bounds.width;
+	cleft=cptr->hdw->cropl_val;
+
+	*width= cleft<bleftend ? bleftend-cleft : 0;
 	return 0;
 }
 
-static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val)
+static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
 {
 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
-	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+	int stat, btopend, ctop;
+
+	stat = pvr2_hdw_check_cropcap(cptr->hdw);
 	if (stat != 0) {
 		return stat;
 	}
-	*val = 0;
-	if (cap->bounds.height > cptr->hdw->cropt_val) {
-		*val = cap->bounds.height - cptr->hdw->cropt_val;
-	}
+	btopend=cap->bounds.top+cap->bounds.height;
+	ctop=cptr->hdw->cropt_val;
+	
+	*height= ctop<btopend ? btopend-ctop : 0;
 	return 0;
 }
 
--- linux-2.6.29.6/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2-d.c	2009-07-03 01:41:20.000000000 +0200
+++ linux-2.6.29.6/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c	2009-09-05 06:25:43.000000000 +0200
@@ -237,19 +237,33 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l
 static void set_crop(struct pvr2_hdw *hdw)
 {
 	struct v4l2_crop crop;
+	struct v4l2_rect *c=&crop.c;
+	int err;
 
 	memset(&crop, 0, sizeof crop);
 	crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	crop.c.left = hdw->cropl_val;
-	crop.c.top = hdw->cropt_val;
-	crop.c.height = hdw->croph_val;
-	crop.c.width = hdw->cropw_val;
+	c->left = hdw->cropl_val;
+	c->top = hdw->cropt_val;
+	c->height = hdw->croph_val;
+	c->width = hdw->cropw_val;
 
 	pvr2_trace(PVR2_TRACE_CHIPS,
-		   "i2c v4l2 set_crop crop=%d:%d:%d:%d",
-		   crop.c.width, crop.c.height, crop.c.left, crop.c.top);
+		   "i2c v4l2 set_crop S crop=%d:%d:%d:%d",
+		   c->width, c->height, c->left, c->top);
 
-	pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop);
+	err=pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop);
+	if(!err) {
+	  err=pvr2_i2c_core_cmd(hdw, VIDIOC_G_CROP, &crop);
+	  pvr2_trace(PVR2_TRACE_CHIPS,
+		     "i2c v4l2 set_crop G err=%i %d:%d:%d:%d", err, 
+		     c->width, c->height, c->left, c->top);
+	  if(!err) {
+	    hdw->cropl_val = c->left;
+	    hdw->cropt_val = c->top;
+	    hdw->croph_val = c->height;
+	    hdw->cropw_val = c->width;
+	  }
+	}
 }
 
 static int check_crop(struct pvr2_hdw *hdw)
@@ -295,16 +309,23 @@ void pvr2_v4l2_cmd_stream(struct pvr2_i2
 
 void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp)
 {
-	int stat;
 	struct pvr2_hdw *hdw = cp->hdw;
-	if (hdw->cropcap_stale) {
-		hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-		stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP,
-					   &hdw->cropcap_info);
+	int *ccstale=&hdw->cropcap_stale;
+
+	pvr2_trace(PVR2_TRACE_CHIPS,"i2c pvr2_v4l2_cmd_status_poll cropcap %d",
+		   *ccstale);
+
+	if (*ccstale) {
+		struct v4l2_cropcap *ccinfo=&hdw->cropcap_info;
+		int stat;
+
+		memset(ccinfo, 0, sizeof(*ccinfo));
+		ccinfo->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP, ccinfo);
 		if (stat == 0) {
 			/* Check was successful, so the data is no
 			   longer considered stale. */
-			hdw->cropcap_stale = 0;
+			*ccstale = 0;
 		}
 	}
 	pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info);
--- linux-2.6.29.6/drivers/media/video/pvrusb2/pvrusb2-i2c-core-d.c	2009-07-03 01:41:20.000000000 +0200
+++ linux-2.6.29.6/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c	2009-09-05 06:01:18.000000000 +0200
@@ -564,7 +564,7 @@ int pvr2_i2c_client_cmd(struct pvr2_i2c_
 int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
 {
 	struct pvr2_i2c_client *cp, *ncp;
-	int stat = -EINVAL;
+	int stat = -EINVAL, success = 0;
 
 	if (!hdw) return stat;
 
@@ -573,10 +573,11 @@ int pvr2_i2c_core_cmd(struct pvr2_hdw *h
 		if (!cp->recv_enable) continue;
 		mutex_unlock(&hdw->i2c_list_lock);
 		stat = pvr2_i2c_client_cmd(cp,cmd,arg);
+		if(!stat) success++;
 		mutex_lock(&hdw->i2c_list_lock);
 	}
 	mutex_unlock(&hdw->i2c_list_lock);
-	return stat;
+	return success ? 0 : stat;
 }
 
 
--- linux-2.6.29.6/include/media/v4l2-subdev-d.h	2009-07-03 01:41:20.000000000 +0200
+++ linux-2.6.29.6/include/media/v4l2-subdev.h	2009-09-04 03:48:38.000000000 +0200
@@ -94,6 +94,7 @@ struct v4l2_subdev_tuner_ops {
 	int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
 	int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
 	int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
+	int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
 	int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
 	int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
 };
@@ -113,6 +114,9 @@ struct v4l2_subdev_video_ops {
 	int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
 	int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
 	int (*s_stream)(struct v4l2_subdev *sd, int enable);
+	int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cap);
+	int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
+	int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
 	int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
 	int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
 };


More information about the pvrusb2 mailing list