pvrusb2@isely.net [patch] pvrusb2-mci-20100708 crop support The text here below gives a recipe to upgrade to 2.6.35 and to patch the cx25840 module for cropping support. The patches and firmwares are at http://picaros.org/pvrusb/ . Linux-2.6.35 If you intend to compile with a gcc before version 4.1 the patch linuxgcc.diff is required. This patch just reorganizes a tiny bit of code. The patch cx25840-2.6.35 adds crop support. config - relocateble kernel: off - media pvrusb2: on since this autoselects all the other modules - soft watchdog timer: on - sysrq: on, Alt+SysRq+B reboots the system make bzImage && make modules && make modules_install pvrusb2-mci-20100708 The patch pvrusb2-mci-20100708x adds the crop calls to the v4l2 subdevices. It also redefines the installation directory as INSTALL_MOD_DIR := kernel/drivers/media/video/pvrusb2 so 'make install' will overwrite the kernel module. Make sure that the current kernel remains bootable. I use 3 images: linux vmlinuz, old vmlinuz.old, keep vmlinuz.keep. Hence the linux 'make install' only touches the first two. All seems to be working well, except for 'rmmod pvrusb2' which hangs forever. If the cx25840 code works well for 525 systems then it might perhaps be worthwile to take a chance with it on the linux-media mailinglist. -vdb. Tue 10 Aug 2010 11:55:48 +0200 (CEST) Signed-off-by: Servaas Vandenberghe --- pvrusb2-mci-20100708/driver/pvrusb2-hdw-d.c 2010-07-09 04:58:55.000000000 +0200 +++ pvrusb2-mci-20100708/driver/pvrusb2-hdw.c 2010-08-09 18:04:11.000000000 +0200 @@ -1109,13 +1109,20 @@ static int ctrl_audio_modes_present_get( static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) { struct pvr2_hdw *hdw = cptr->hdw; - if (v < 0) return -EINVAL; - if (v > hdw->std_enum_cnt) return -EINVAL; + v4l2_std_id std_new; + + if (v < 0 || v > hdw->std_enum_cnt) + return -EINVAL; + hdw->std_enum_cur = v; - if (!v) return 0; - v--; - if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; - hdw->std_mask_cur = hdw->std_defs[v].id; + if (!v) + return 0; + + std_new = hdw->std_defs[v-1].id; + if (hdw->std_mask_cur == std_new) + return 0; + + hdw->std_mask_cur = std_new; hdw->std_dirty = !0; return 0; } @@ -1301,6 +1308,7 @@ static const struct pvr2_ctl_info contro .internal_id = PVR2_CID_CROPW, .default_value = 720, DEFREF(cropw), + DEFINT(0, 864), .get_max_value = ctrl_cropw_max_get, .get_def_value = ctrl_get_cropcapdw, }, { @@ -1309,6 +1317,7 @@ static const struct pvr2_ctl_info contro .internal_id = PVR2_CID_CROPH, .default_value = 480, DEFREF(croph), + DEFINT(0, 576), .get_max_value = ctrl_croph_max_get, .get_def_value = ctrl_get_cropcapdh, }, { @@ -2481,6 +2490,7 @@ static void pvr2_hdw_cx25840_vbi_hack(st /* We're not using a cx25840 so don't enable the hack */ return; } + return; pvr2_trace(PVR2_TRACE_INIT, "Module ID %u:" @@ -3572,6 +3582,110 @@ static void pvr2_subdev_set_control(stru v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl); } +/* Check if crop window is within the bounds rectangle. */ +static int pvr2_hdw_crop_check(int left, int top, int width, int height, + struct v4l2_rect *b) +{ + int err = 0; + int cr, br, cb, bb; + + cr = left+width; + br = b->left+b->width; + + cb = top+height; + bb = b->top+b->height; + + if (left < b->left || width < 0 || cr > br) + err |= 1; + + if (top < b->top || height < 0 || cb > bb) + err |= 2; + + return err; +} + +static int pvr2_hdw_crop_checks(struct pvr2_hdw *hdw) +{ + int err; + err = pvr2_hdw_crop_check(hdw->cropl_val, hdw->cropt_val, + hdw->cropw_val, hdw->croph_val, + &hdw->cropcap_info.bounds); + return err; +} + +/* The broadcast decoder can only scale down, so if + * res_*_dirty && crop window < output format ==> enlarge crop. + * + * Simply increasing the width or height may result in a crop window + * which surpasses the right or bottom boundary of the cap.bounds window. + * If the case decrease the left or top crop margin. + * + * The mpeg encoder receives fields of res_hor_val dots and + * res_ver_val halflines. Limits: hor<=720, ver<=576. + */ +static int pvr2_hdw_equalize_res_crop(struct pvr2_hdw *hdw) +{ + int err = 0; + struct v4l2_rect *b, *d; + + b = &hdw->cropcap_info.bounds; + d = &hdw->cropcap_info.defrect; + if (d->width <= 0 || d->height <= 0) + return -1; + + if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) { + int cr, br; + + hdw->cropw_val = hdw->res_hor_val; + hdw->cropw_dirty = !0; + + cr = hdw->cropl_val+hdw->cropw_val; + br = b->left+b->width; + if (cr > br) { + int cl; + cl = br-hdw->cropw_val; + + if (cl < b->left) { + hdw->cropl_val = b->left; + err |= 1; + } else + hdw->cropl_val = cl; + + hdw->cropl_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) { + int cb, bb; + + hdw->croph_val = hdw->res_ver_val; + hdw->croph_dirty = !0; + + cb = hdw->cropt_val+hdw->croph_val; + bb = b->top+b->height; + if (cb > bb) { + int ct; + ct = bb-hdw->croph_val; + + if (ct < b->top) { + hdw->cropt_val = b->top; + err |= 2; + } else + hdw->cropt_val = ct; + + hdw->cropt_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); + } + return err; +} + #define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \ if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \ pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ @@ -3622,6 +3736,48 @@ static void pvr2_subdev_update(struct pv hdw->cropcap_stale = !0; } + /* A standard change may change bounds and/or defrect. + * Detect defrect change, try to honour user settings, but + * reset the crop window if required. + * e.g. BGH-I-DK-Nc <-> M bounds and defrect + * Nc <-> N bounds only (not implemented) + * M/pal <-> M/ntsc bounds only + */ + if (hdw->cropcap_stale) { + struct v4l2_rect *d, p; + int reset = 0; + + d = &hdw->cropcap_info.defrect; + p = *d; + pvr2_hdw_status_poll(hdw); + + if ((p.left != d->left || p.width != d->width) + && !hdw->cropl_dirty && !hdw->cropw_dirty) + reset |= 1; + + if ((p.top != d->top || p.height != d->height) + && !hdw->cropt_dirty && !hdw->croph_dirty) + reset |= 2; + + if (reset != 3) { + int err; + err = pvr2_hdw_crop_checks(hdw); + if (err > 0) + reset |= err; + } + + if (reset & 1) { + hdw->cropl_val = d->left; + hdw->cropw_val = d->width; + hdw->cropl_dirty = hdw->cropw_dirty = !0; + } + if (reset & 2) { + hdw->cropt_val = d->top; + hdw->croph_val = d->height; + hdw->cropt_dirty = hdw->croph_dirty = !0; + } + } + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness); PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast); PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation); @@ -3664,6 +3820,45 @@ static void pvr2_subdev_update(struct pv s_frequency, &freq); } + pvr2_hdw_equalize_res_crop(hdw); + + /* Set crop rectangle. */ + if (hdw->cropl_dirty || hdw->cropt_dirty || hdw->cropw_dirty + || hdw->croph_dirty || hdw->std_dirty || hdw->force_dirty) { + struct v4l2_crop scrop, gcrop; + struct v4l2_rect *s = &scrop.c, *g = &gcrop.c; + + memset(&scrop, 0, sizeof(scrop)); + scrop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->left = hdw->cropl_val; + s->top = hdw->cropt_val; + s->width = hdw->cropw_val; + s->height = hdw->croph_val; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 s_crop %i:%i:%i:%i", + s->width, s->height, s->left, s->top); + + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_crop, &scrop); + + /* The crop window got may differ from the crop request. */ + memset(&gcrop, 0, sizeof(gcrop)); + gcrop.type = scrop.type; + g->left = g->top = 0; + g->width = g->height = 0; + + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, g_crop, &gcrop); + if (g->left || g->top || g->width || g->height) { + hdw->cropl_val = g->left; + hdw->cropt_val = g->top; + hdw->cropw_val = g->width; + hdw->croph_val = g->height; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 g_crop" + " %i:%i:%i:%i", + g->width, g->height, g->left, g->top); + } + } + + /* Scaling from crop rectangle to hor,ver output. */ if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) { #ifndef PVR2_ENABLE_NO_MBUS struct v4l2_mbus_framefmt fmt; @@ -3721,9 +3916,6 @@ static void pvr2_subdev_update(struct pv audio, s_clock_freq, val); } - /* Unable to set crop parameters; there is apparently no equivalent - for VIDIOC_S_CROP */ - v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { id = sd->grp_id; if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue; @@ -3732,9 +3924,8 @@ static void pvr2_subdev_update(struct pv (*fp)(hdw, sd); } - if (hdw->tuner_signal_stale || hdw->cropcap_stale) { + if (hdw->tuner_signal_stale || hdw->cropcap_stale) pvr2_hdw_status_poll(hdw); - } } #endif @@ -3797,6 +3988,19 @@ static int pvr2_hdw_commit_execute(struc struct pvr2_ctrl *cptr; int disruptive_change; + if (hdw->input_dirty && hdw->state_pathway_ok && + ((hdw->input_val == PVR2_CVAL_INPUT_DTV ? + PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != + hdw->pathway_state)) { + /* Change of mode being asked for... */ + hdw->state_pathway_ok = 0; + trace_stbit("state_pathway_ok", hdw->state_pathway_ok); + } + if (!hdw->state_pathway_ok) { + /* Can't commit anything until pathway is ok. */ + return 0; + } + /* Handle some required side effects when the video standard is changed.... */ if (hdw->std_dirty) { @@ -3844,40 +4048,6 @@ static int pvr2_hdw_commit_execute(struc #endif } - if (hdw->input_dirty && hdw->state_pathway_ok && - (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? - PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != - hdw->pathway_state)) { - /* Change of mode being asked for... */ - hdw->state_pathway_ok = 0; - trace_stbit("state_pathway_ok",hdw->state_pathway_ok); - } - if (!hdw->state_pathway_ok) { - /* Can't commit anything until pathway is ok. */ - return 0; - } - /* 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 @@ -3938,6 +4108,7 @@ static int pvr2_hdw_commit_execute(struc #endif #ifdef PVR2_ENABLE_OLD_I2COPS + pvr2_hdw_equalize_res_crop(hdw); /* 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 @@ -3967,7 +4138,6 @@ static int pvr2_hdw_commit_execute(struc #ifdef PVR2_ENABLE_V4L2SUBDEV /* Check and update state for all sub-devices. */ pvr2_subdev_update(hdw); - #endif hdw->tuner_updated = 0; hdw->force_dirty = 0; @@ -4087,30 +4257,85 @@ void pvr2_hdw_execute_tuner_poll(struct } while (0); LOCK_GIVE(hdw->big_lock); } - +/* At init: pvr2_hdw_create() kzalloc zeroes cropcap_info, + * pvr2_hdw_setup_low() sets cropcap_info.bounds to .default_value + */ static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) { - if (!hdw->cropcap_stale) { + struct v4l2_rect *b, *d; + + if (!hdw->cropcap_stale) return 0; - } + pvr2_hdw_status_poll(hdw); - if (hdw->cropcap_stale) { + b = &hdw->cropcap_info.bounds; + d = &hdw->cropcap_info.defrect; + if (hdw->cropcap_stale + || (!b->width && !b->height && !d->width && !d->height)) return -EIO; - } + return 0; } /* Return information about cropping capabilities */ -int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp) +int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *cap) { - int stat = 0; + int stat; + + LOCK_TAKE(hdw->big_lock); + stat = pvr2_hdw_check_cropcap(hdw); + if (!stat) + *cap = hdw->cropcap_info; + LOCK_GIVE(hdw->big_lock); + + return stat; +} + +int pvr2_hdw_get_crop(struct pvr2_hdw *hdw, struct v4l2_crop *crop) +{ + struct v4l2_rect *c = &crop->c; + + LOCK_TAKE(hdw->big_lock); + memset(crop, 0, sizeof(*crop)); + crop->type = hdw->cropcap_info.type; + c->left = hdw->cropl_val; + c->top = hdw->cropt_val; + c->width = hdw->cropw_val; + c->height = hdw->croph_val; + LOCK_GIVE(hdw->big_lock); + + return 0; +} + +int pvr2_hdw_set_crop(struct pvr2_hdw *hdw, struct v4l2_crop *crop) +{ + int stat; + struct v4l2_rect *c = &crop->c; + LOCK_TAKE(hdw->big_lock); stat = pvr2_hdw_check_cropcap(hdw); if (!stat) { - memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info)); + int err; + + err = pvr2_hdw_crop_check(c->left, c->top, + c->width, c->height, + &hdw->cropcap_info.bounds); + if (err) { + stat = -ERANGE; + } else { + hdw->cropl_val = c->left; + hdw->cropl_dirty = !0; + hdw->cropt_val = c->top; + hdw->cropt_dirty = !0; + hdw->cropw_val = c->width; + hdw->cropw_dirty = !0; + hdw->croph_val = c->height; + hdw->croph_dirty = !0; + } } LOCK_GIVE(hdw->big_lock); + return stat; } @@ -6001,8 +6226,16 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hd void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) { struct v4l2_tuner *vtp = &hdw->tuner_signal_info; + struct v4l2_cropcap *capp = &hdw->cropcap_info; + memset(vtp, 0, sizeof(*vtp)); - hdw->tuner_signal_stale = 0; + memset(capp, 0, sizeof(*capp)); + capp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + capp->bounds.width = 0; + capp->bounds.height = 0; + capp->defrect.width = 0; + capp->defrect.height = 0; + #ifdef PVR2_ENABLE_OLD_I2COPS pvr2_i2c_core_status_poll(hdw); #endif @@ -6011,8 +6244,9 @@ void pvr2_hdw_status_poll(struct pvr2_hd using v4l2-subdev - therefore we can't support that AT ALL right now. (Of course, no sub-drivers seem to implement it either. But now it's a a chicken and egg problem...) */ - v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, - &hdw->tuner_signal_info); + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp); + + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, cropcap, capp); #endif pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll" " type=%u strength=%u audio=0x%x cap=0x%x" @@ -6021,6 +6255,7 @@ void pvr2_hdw_status_poll(struct pvr2_hd vtp->signal, vtp->rxsubchans, vtp->capability, vtp->rangelow, vtp->rangehigh); + hdw->tuner_signal_stale = 0; /* We have to do this to avoid getting into constant polling if there's nobody to answer a poll of cropcap info. */ hdw->cropcap_stale = 0; --- pvrusb2-mci-20100708/driver/pvrusb2-v4l2-d.c 2010-05-15 05:02:33.000000000 +0200 +++ pvrusb2-mci-20100708/driver/pvrusb2-v4l2.c 2010-08-09 05:51:08.000000000 +0200 @@ -906,77 +906,22 @@ static long pvr2_v4l2_do_ioctl(struct fi case VIDIOC_G_CROP: { struct v4l2_crop *crop = (struct v4l2_crop *)arg; - int val = 0; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ret = -EINVAL; break; } - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.left = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.top = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.width = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.height = val; + ret = pvr2_hdw_get_crop(hdw, crop); + break; } case VIDIOC_S_CROP: { struct v4l2_crop *crop = (struct v4l2_crop *)arg; - struct v4l2_cropcap cap; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ret = -EINVAL; break; } - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), - crop->c.left); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), - crop->c.top); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), - crop->c.width); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), - crop->c.height); - if (ret != 0) { - ret = -EINVAL; - break; - } + ret = pvr2_hdw_set_crop(hdw, crop); + break; } #ifdef PVR2_ENABLE_LOG_STATUS case VIDIOC_LOG_STATUS: --- pvrusb2-mci-20100708/driver/pvrusb2-hdw-d.h 2010-02-06 01:57:09.000000000 +0100 +++ pvrusb2-mci-20100708/driver/pvrusb2-hdw.h 2010-08-01 05:20:12.000000000 +0200 @@ -210,6 +210,8 @@ int pvr2_hdw_get_tuner_status(struct pvr /* Return information about cropping capabilities */ int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *); +int pvr2_hdw_get_crop(struct pvr2_hdw *hdw, struct v4l2_crop *crop); +int pvr2_hdw_set_crop(struct pvr2_hdw *hdw, struct v4l2_crop *crop); /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); --- pvrusb2-mci-20100708/driver/Makefile-d 2008-05-10 06:00:45.000000000 +0200 +++ pvrusb2-mci-20100708/driver/Makefile 2010-08-07 02:55:53.000000000 +0200 @@ -36,7 +36,7 @@ ifeq ($(KERNELRELEASE),) ifeq ($(strip $(KDIR)),) KDIR := /lib/modules/$(KREL)/build endif - INSTALL_MOD_DIR := pvrusb2 + INSTALL_MOD_DIR := kernel/drivers/media/video/pvrusb2 .PHONY: all default install clean modules default: all