Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: cx25840: set_fmt operation should clamp out-of-range picture sizes

According to V4L2 API set_fmt subdev operation should not return an error
on out-of-range picture sizes, the values should be clamped instead to the
supported range.

The cx25840 datasheet says that the chip is capable of scaling down the
picture width and height, respectively, 16 and 8 times.
These values agree with what the old implementation enforced.

Signed-off-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

authored by

Maciej S. Szmigiero and committed by
Mauro Carvalho Chehab
65efeca0 e81a9076

+40 -19
+40 -19
drivers/media/i2c/cx25840/cx25840-core.c
··· 1702 1702 struct v4l2_mbus_framefmt *fmt = &format->format; 1703 1703 struct cx25840_state *state = to_state(sd); 1704 1704 struct i2c_client *client = v4l2_get_subdevdata(sd); 1705 - int HSC, VSC, Vsrc, Hsrc, filter, Vlines; 1705 + u32 HSC, VSC, Vsrc, Hsrc, Vadd; 1706 + int filter; 1706 1707 int is_50Hz = !(state->std & V4L2_STD_525_60); 1707 1708 1708 1709 if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED) ··· 1728 1727 Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; 1729 1728 } 1730 1729 1731 - Vlines = fmt->height; 1732 - if (!state->generic_mode) 1733 - Vlines += is_50Hz ? 4 : 7; 1730 + if (!state->generic_mode) { 1731 + Vadd = is_50Hz ? 4 : 7; 1734 1732 1735 - /* 1736 - * We keep 1 margin for the Vsrc < Vlines check since the 1737 - * cx23888 reports a Vsrc of 486 instead of 487 for the NTSC 1738 - * height. Without that margin the cx23885 fails in this 1739 - * check. 1740 - */ 1741 - if ((fmt->width == 0) || (Vlines == 0) || 1742 - (fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || 1743 - (Vlines * 8 < Vsrc) || (Vsrc + 1 < Vlines)) { 1744 - v4l_err(client, "%dx%d is not a valid size!\n", 1745 - fmt->width, fmt->height); 1746 - return -ERANGE; 1733 + /* 1734 + * cx23888 in 525-line mode is programmed for 486 active lines 1735 + * while other chips use 487 active lines. 1736 + * 1737 + * See reg 0x428 bits [21:12] in cx23888_std_setup() vs 1738 + * vactive in cx25840_std_setup(). 1739 + */ 1740 + if (is_cx23888(state) && !is_50Hz) 1741 + Vadd--; 1742 + } else 1743 + Vadd = 0; 1744 + 1745 + if (Hsrc == 0 || 1746 + Vsrc <= Vadd) { 1747 + v4l_err(client, 1748 + "chip reported picture size (%u x %u) is far too small\n", 1749 + (unsigned int)Hsrc, (unsigned int)Vsrc); 1750 + /* 1751 + * that's the best we can do since the output picture 1752 + * size is completely unknown in this case 1753 + */ 1754 + return -EINVAL; 1747 1755 } 1756 + 1757 + fmt->width = clamp(fmt->width, (Hsrc + 15) / 16, Hsrc); 1758 + 1759 + if (Vadd * 8 >= Vsrc) 1760 + fmt->height = clamp(fmt->height, (u32)1, Vsrc - Vadd); 1761 + else 1762 + fmt->height = clamp(fmt->height, (Vsrc - Vadd * 8 + 7) / 8, 1763 + Vsrc - Vadd); 1764 + 1748 1765 if (format->which == V4L2_SUBDEV_FORMAT_TRY) 1749 1766 return 0; 1750 1767 1751 1768 HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); 1752 - VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); 1769 + VSC = (1 << 16) - (Vsrc * (1 << 9) / (fmt->height + Vadd) - (1 << 9)); 1753 1770 VSC &= 0x1fff; 1754 1771 1755 1772 if (fmt->width >= 385) ··· 1779 1760 else 1780 1761 filter = 3; 1781 1762 1782 - v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", 1783 - fmt->width, fmt->height, HSC, VSC); 1763 + v4l_dbg(1, cx25840_debug, client, 1764 + "decoder set size %u x %u with scale %x x %x\n", 1765 + (unsigned int)fmt->width, (unsigned int)fmt->height, 1766 + (unsigned int)HSC, (unsigned int)VSC); 1784 1767 1785 1768 /* HSCALE=HSC */ 1786 1769 if (is_cx23888(state)) {