V4L/DVB (8900): pvrusb2: Implement cropping pass through
authorMike Isely <isely@pobox.com>
Mon, 1 Sep 2008 00:02:20 +0000 (21:02 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 12 Oct 2008 11:36:57 +0000 (09:36 -0200)
This builds upon the previous pvrusb2 change to more formally
implement full cropping support.  This enables access from the
driver's V4L interface, and enables access to full capabilities from
sysfs as well.  Note that this is only effective when in analog mode.
It also will only work when the underlying digitizer's driver (saa7115
or cx25840 depending on the hardware) also implements the appropriate
functions.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
drivers/media/video/pvrusb2/pvrusb2-hdw.c
drivers/media/video/pvrusb2/pvrusb2-hdw.h
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
drivers/media/video/pvrusb2/pvrusb2-v4l2.c

index 8bc9669733df193ed8f219bddb9c3d44cdde0c1d..de7ee7264be63bc8855380942b1e14ae222ca7d6 100644 (file)
@@ -308,6 +308,10 @@ struct pvr2_hdw {
        struct v4l2_tuner tuner_signal_info;
        int tuner_signal_stale;
 
+       /* Cropping capability info */
+       struct v4l2_cropcap cropcap_info;
+       int cropcap_stale;
+
        /* Video standard handling */
        v4l2_std_id std_mask_eeprom; // Hardware supported selections
        v4l2_std_id std_mask_avail;  // Which standards we may select from
@@ -320,7 +324,6 @@ 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];
index e29a39ffdcca854b279a1d03fef33d8133f7e6fa..9bb59b2de91769460765b776bd3929c17d0d0510 100644 (file)
@@ -298,6 +298,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
                                unsigned int timeout,int probe_fl,
                                void *write_data,unsigned int write_len,
                                void *read_data,unsigned int read_len);
+static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
 
 
 static void trace_stbit(const char *name,int val)
@@ -404,70 +405,220 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
 
 static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
 {
-       struct v4l2_cropcap *cap = &cptr->hdw->cropcap;
-       if (cap->bounds.width > 0) {
-               /* This statement is present purely to shut up
-                  checkpatch.pl */
-               *left = cap->bounds.left - cap->defrect.left;
-       } else {
-               /* This statement is present purely to shut up
-                  checkpatch.pl */
-               *left = -119;
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
        }
+       *left = cap->bounds.left;
        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 {
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *left = cap->bounds.left;
+       if (cap->bounds.width > cptr->hdw->cropw_val) {
                /* This statement is present purely to shut up
                   checkpatch.pl */
-               *left = 340;
+               *left += cap->bounds.width - cptr->hdw->cropw_val;
        }
        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) {
-               /* This statement is present purely to shut up
-                  checkpatch.pl */
-               *top = cap->bounds.top - cap->defrect.top;
-       } else {
-               /* This statement is present purely to shut up
-                  checkpatch.pl */
-               *top = -19;
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
        }
+       *top = cap->bounds.top;
        return 0;
 }
 
-static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
+static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
 {
-       /* Actual maximum depends on the video standard in effect. */
-       if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
-               *vp = 480;
-       } else {
-               *vp = 576;
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *top = cap->bounds.top;
+       if (cap->bounds.height > cptr->hdw->croph_val) {
+               /* Keep checkpatch.pl quiet */
+               *top += cap->bounds.height - cptr->hdw->croph_val;
        }
        return 0;
 }
 
-static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
+static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val)
 {
-       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;
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = 0;
+       if (cap->bounds.width > cptr->hdw->cropl_val) {
+               /* Keep checkpatch.pl quiet */
+               *val = cap->bounds.width - cptr->hdw->cropl_val;
+       }
+       return 0;
+}
+
+static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = 0;
+       if (cap->bounds.height > cptr->hdw->cropt_val) {
+               /* Keep checkpatch.pl quiet */
+               *val = cap->bounds.height - cptr->hdw->cropt_val;
+       }
+       return 0;
+}
+
+static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->bounds.left;
+       return 0;
+}
+
+static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->bounds.top;
+       return 0;
+}
+
+static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->bounds.width;
+       return 0;
+}
+
+static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->bounds.height;
+       return 0;
+}
+
+static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->defrect.left;
+       return 0;
+}
+
+static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->defrect.top;
+       return 0;
+}
+
+static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->defrect.width;
+       return 0;
+}
+
+static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->defrect.height;
+       return 0;
+}
+
+static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->pixelaspect.numerator;
+       return 0;
+}
+
+static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val)
+{
+       struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+       int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+       if (stat != 0) {
+               /* Keep checkpatch.pl quiet */
+               return stat;
+       }
+       *val = cap->pixelaspect.denominator;
+       return 0;
+}
+
+static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
+{
+       /* Actual maximum depends on the video standard in effect. */
+       if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
+               *vp = 480;
        } else {
-               ctrl_vres_max_get(cptr, top);
-               *top -= 32;
+               *vp = 576;
        }
        return 0;
 }
@@ -913,7 +1064,7 @@ static const struct pvr2_ctl_info control_defs[] = {
                DEFREF(mute),
                DEFBOOL,
        }, {
-               .desc = "Capture left margin",
+               .desc = "Capture crop left margin",
                .name = "crop_left",
                .internal_id = PVR2_CID_CROPL,
                .default_value = 0,
@@ -921,8 +1072,9 @@ static const struct pvr2_ctl_info control_defs[] = {
                DEFINT(-129, 340),
                .get_min_value = ctrl_cropl_min_get,
                .get_max_value = ctrl_cropl_max_get,
+               .get_def_value = ctrl_get_cropcapdl,
        }, {
-               .desc = "Capture top margin",
+               .desc = "Capture crop top margin",
                .name = "crop_top",
                .internal_id = PVR2_CID_CROPT,
                .default_value = 0,
@@ -930,21 +1082,53 @@ static const struct pvr2_ctl_info control_defs[] = {
                DEFINT(-35, 544),
                .get_min_value = ctrl_cropt_min_get,
                .get_max_value = ctrl_cropt_max_get,
+               .get_def_value = ctrl_get_cropcapdt,
        }, {
-               .desc = "Capture width",
+               .desc = "Capture crop width",
                .name = "crop_width",
                .internal_id = PVR2_CID_CROPW,
                .default_value = 720,
                DEFREF(cropw),
-               DEFINT(388, 849), /* determined empirically, any res_hor>=64 */
+               .get_max_value = ctrl_cropw_max_get,
+               .get_def_value = ctrl_get_cropcapdw,
        }, {
-               .desc = "Capture height",
+               .desc = "Capture crop height",
                .name = "crop_height",
                .internal_id = PVR2_CID_CROPH,
                .default_value = 480,
                DEFREF(croph),
-               DEFINT(32, 576),
-               .get_max_value = ctrl_vres_max_get,
+               .get_max_value = ctrl_croph_max_get,
+               .get_def_value = ctrl_get_cropcapdh,
+       }, {
+               .desc = "Capture capability pixel aspect numerator",
+               .name = "cropcap_pixel_numerator",
+               .internal_id = PVR2_CID_CROPCAPPAN,
+               .get_value = ctrl_get_cropcappan,
+       }, {
+               .desc = "Capture capability pixel aspect denominator",
+               .name = "cropcap_pixel_denominator",
+               .internal_id = PVR2_CID_CROPCAPPAD,
+               .get_value = ctrl_get_cropcappad,
+       }, {
+               .desc = "Capture capability bounds top",
+               .name = "cropcap_bounds_top",
+               .internal_id = PVR2_CID_CROPCAPBT,
+               .get_value = ctrl_get_cropcapbt,
+       }, {
+               .desc = "Capture capability bounds left",
+               .name = "cropcap_bounds_left",
+               .internal_id = PVR2_CID_CROPCAPBL,
+               .get_value = ctrl_get_cropcapbl,
+       }, {
+               .desc = "Capture capability bounds width",
+               .name = "cropcap_bounds_width",
+               .internal_id = PVR2_CID_CROPCAPBW,
+               .get_value = ctrl_get_cropcapbw,
+       }, {
+               .desc = "Capture capability bounds height",
+               .name = "cropcap_bounds_height",
+               .internal_id = PVR2_CID_CROPCAPBH,
+               .get_value = ctrl_get_cropcapbh,
        },{
                .desc = "Video Source",
                .name = "input",
@@ -2188,7 +2372,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
                        valid_std_mask;
        }
 
-       memset(&hdw->cropcap, 0, sizeof hdw->cropcap);
+       hdw->cropcap_stale = !0;
        hdw->eeprom_addr = -1;
        hdw->unit_number = -1;
        hdw->v4l_minor_number_video = -1;
@@ -2728,6 +2912,9 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
        }
 
        hdw->state_pipeline_config = !0;
+       /* Hardware state may have changed in a way to cause the cropping
+          capabilities to have changed.  So mark it stale, which will
+          cause a later re-fetch. */
        trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
        return !0;
 }
@@ -2818,6 +3005,36 @@ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
 }
 
 
+static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
+{
+       if (!hdw->cropcap_stale) {
+               /* Keep checkpatch.pl quiet */
+               return 0;
+       }
+       pvr2_i2c_core_status_poll(hdw);
+       if (hdw->cropcap_stale) {
+               /* Keep checkpatch.pl quiet */
+               return -EIO;
+       }
+       return 0;
+}
+
+
+/* Return information about cropping capabilities */
+int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
+{
+       int stat = 0;
+       LOCK_TAKE(hdw->big_lock);
+       stat = pvr2_hdw_check_cropcap(hdw);
+       if (!stat) {
+               /* Keep checkpatch.pl quiet */
+               memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
+       }
+       LOCK_GIVE(hdw->big_lock);
+       return stat;
+}
+
+
 /* Return information about the tuner */
 int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
 {
index b4dcda8af64a28bb5f82f5c35628dfcd0412d7cb..49482d1f2b28d71b87ccd3318e3b0b45afeda2e1 100644 (file)
 #define PVR2_CID_CROPT 10
 #define PVR2_CID_CROPW 11
 #define PVR2_CID_CROPH 12
+#define PVR2_CID_CROPCAPPAN 13
+#define PVR2_CID_CROPCAPPAD 14
+#define PVR2_CID_CROPCAPBL 15
+#define PVR2_CID_CROPCAPBT 16
+#define PVR2_CID_CROPCAPBW 17
+#define PVR2_CID_CROPCAPBH 18
 
 /* Legal values for the INPUT state variable */
 #define PVR2_CVAL_INPUT_TV 0
@@ -174,6 +180,9 @@ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *);
 /* Return information about the tuner */
 int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *);
 
+/* Return information about cropping capabilities */
+int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *);
+
 /* Query device and see if it thinks it is on a high-speed USB link */
 int pvr2_hdw_is_hsm(struct pvr2_hdw *);
 
index 440857902c92c36d783379ca766b56a27681f352..543fa30b777e2031275cf7a2582c0d0da5c45784 100644 (file)
@@ -37,6 +37,7 @@ static void set_standard(struct pvr2_hdw *hdw)
                pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs);
        }
        hdw->tuner_signal_stale = !0;
+       hdw->cropcap_stale = !0;
 }
 
 
@@ -235,34 +236,20 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = {
 
 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.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,
+                  "i2c v4l2 set_crop crop=%d:%d:%d:%d",
                   crop.c.width, crop.c.height, crop.c.left, crop.c.top);
 
-       if (stat >= 0) {
-               /* This comment is present purely to keep
-                  checkpatch.pl quiet */
-               pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop);
-       }
+       pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop);
 }
 
 static int check_crop(struct pvr2_hdw *hdw)
@@ -312,7 +299,19 @@ void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl)
 
 void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp)
 {
-       pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&cp->hdw->tuner_signal_info);
+       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);
+               if (stat == 0) {
+                       /* Check was successful, so the data is no
+                          longer considered stale. */
+                       hdw->cropcap_stale = 0;
+               }
+       }
+       pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info);
 }
 
 
index e600576a6c4b5b7092293052230e54ff3a857cec..afb53b49b31a0433a7cecb21fa5bbd063e86828e 100644 (file)
@@ -891,6 +891,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client)
        INIT_LIST_HEAD(&cp->list);
        cp->client = client;
        mutex_lock(&hdw->i2c_list_lock); do {
+               hdw->cropcap_stale = !0;
                list_add_tail(&cp->list,&hdw->i2c_clients);
                hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
        } while (0); mutex_unlock(&hdw->i2c_list_lock);
@@ -905,6 +906,7 @@ static int pvr2_i2c_detach_inform(struct i2c_client *client)
        unsigned long amask = 0;
        int foundfl = 0;
        mutex_lock(&hdw->i2c_list_lock); do {
+               hdw->cropcap_stale = !0;
                list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
                        if (cp->client == client) {
                                trace_i2c("pvr2_i2c_detach"
index c53037a255700c4df55696133a5e86f5e037091e..f048d80b77e58b8e7e5e05ff9c2fec798929e135 100644 (file)
@@ -755,6 +755,92 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
+       case VIDIOC_CROPCAP:
+       {
+               struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg;
+               if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = pvr2_hdw_get_cropcap(hdw, cap);
+               cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
+               break;
+       }
+       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;
+       }
+       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;
+               }
+       }
        case VIDIOC_LOG_STATUS:
        {
                pvr2_hdw_trigger_module_log(hdw);