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); }; _______________________________________________ pvrusb2 mailing list [email protected] http://www.isely.net/cgi-bin/mailman/listinfo/pvrusb2
