struct v4l2_mbus_framefmt *fmt = &format->format;
        struct cx25840_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
-       int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+       u32 HSC, VSC, Vsrc, Hsrc, Vadd;
+       int filter;
        int is_50Hz = !(state->std & V4L2_STD_525_60);
 
        if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED)
                Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4;
        }
 
-       Vlines = fmt->height;
-       if (!state->generic_mode)
-               Vlines += is_50Hz ? 4 : 7;
+       if (!state->generic_mode) {
+               Vadd = is_50Hz ? 4 : 7;
 
-       /*
-        * We keep 1 margin for the Vsrc < Vlines check since the
-        * cx23888 reports a Vsrc of 486 instead of 487 for the NTSC
-        * height. Without that margin the cx23885 fails in this
-        * check.
-        */
-       if ((fmt->width == 0) || (Vlines == 0) ||
-           (fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
-           (Vlines * 8 < Vsrc) || (Vsrc + 1 < Vlines)) {
-               v4l_err(client, "%dx%d is not a valid size!\n",
-                               fmt->width, fmt->height);
-               return -ERANGE;
+               /*
+                * cx23888 in 525-line mode is programmed for 486 active lines
+                * while other chips use 487 active lines.
+                *
+                * See reg 0x428 bits [21:12] in cx23888_std_setup() vs
+                * vactive in cx25840_std_setup().
+                */
+               if (is_cx23888(state) && !is_50Hz)
+                       Vadd--;
+       } else
+               Vadd = 0;
+
+       if (Hsrc == 0 ||
+           Vsrc <= Vadd) {
+               v4l_err(client,
+                       "chip reported picture size (%u x %u) is far too small\n",
+                       (unsigned int)Hsrc, (unsigned int)Vsrc);
+               /*
+                * that's the best we can do since the output picture
+                * size is completely unknown in this case
+                */
+               return -EINVAL;
        }
+
+       fmt->width = clamp(fmt->width, (Hsrc + 15) / 16, Hsrc);
+
+       if (Vadd * 8 >= Vsrc)
+               fmt->height = clamp(fmt->height, (u32)1, Vsrc - Vadd);
+       else
+               fmt->height = clamp(fmt->height, (Vsrc - Vadd * 8 + 7) / 8,
+                                   Vsrc - Vadd);
+
        if (format->which == V4L2_SUBDEV_FORMAT_TRY)
                return 0;
 
        HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20);
-       VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+       VSC = (1 << 16) - (Vsrc * (1 << 9) / (fmt->height + Vadd) - (1 << 9));
        VSC &= 0x1fff;
 
        if (fmt->width >= 385)
        else
                filter = 3;
 
-       v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale  %ux%u\n",
-                       fmt->width, fmt->height, HSC, VSC);
+       v4l_dbg(1, cx25840_debug, client,
+               "decoder set size %u x %u with scale %x x %x\n",
+               (unsigned int)fmt->width, (unsigned int)fmt->height,
+               (unsigned int)HSC, (unsigned int)VSC);
 
        /* HSCALE=HSC */
        if (is_cx23888(state)) {