xref: /linux/drivers/media/v4l2-core/v4l2-isp.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
13cb6de6fSJacopo Mondi // SPDX-License-Identifier: GPL-2.0-or-later
23cb6de6fSJacopo Mondi /*
33cb6de6fSJacopo Mondi  * Video4Linux2 generic ISP parameters and statistics support
43cb6de6fSJacopo Mondi  *
53cb6de6fSJacopo Mondi  * Copyright (C) 2025 Ideas On Board Oy
63cb6de6fSJacopo Mondi  * Author: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
73cb6de6fSJacopo Mondi  */
83cb6de6fSJacopo Mondi 
93cb6de6fSJacopo Mondi #include <media/v4l2-isp.h>
103cb6de6fSJacopo Mondi 
113cb6de6fSJacopo Mondi #include <linux/bitops.h>
123cb6de6fSJacopo Mondi #include <linux/device.h>
133cb6de6fSJacopo Mondi 
143cb6de6fSJacopo Mondi #include <media/videobuf2-core.h>
153cb6de6fSJacopo Mondi 
163cb6de6fSJacopo Mondi int v4l2_isp_params_validate_buffer_size(struct device *dev,
173cb6de6fSJacopo Mondi 					 struct vb2_buffer *vb,
183cb6de6fSJacopo Mondi 					 size_t max_size)
193cb6de6fSJacopo Mondi {
203cb6de6fSJacopo Mondi 	size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
213cb6de6fSJacopo Mondi 	size_t payload_size = vb2_get_plane_payload(vb, 0);
223cb6de6fSJacopo Mondi 
233cb6de6fSJacopo Mondi 	/* Payload size can't be greater than the destination buffer size */
243cb6de6fSJacopo Mondi 	if (payload_size > max_size) {
253cb6de6fSJacopo Mondi 		dev_dbg(dev, "Payload size is too large: %zu\n", payload_size);
263cb6de6fSJacopo Mondi 		return -EINVAL;
273cb6de6fSJacopo Mondi 	}
283cb6de6fSJacopo Mondi 
293cb6de6fSJacopo Mondi 	/* Payload size can't be smaller than the header size */
303cb6de6fSJacopo Mondi 	if (payload_size < header_size) {
313cb6de6fSJacopo Mondi 		dev_dbg(dev, "Payload size is too small: %zu\n", payload_size);
323cb6de6fSJacopo Mondi 		return -EINVAL;
333cb6de6fSJacopo Mondi 	}
343cb6de6fSJacopo Mondi 
353cb6de6fSJacopo Mondi 	return 0;
363cb6de6fSJacopo Mondi }
373cb6de6fSJacopo Mondi EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer_size);
383cb6de6fSJacopo Mondi 
393cb6de6fSJacopo Mondi int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb,
403cb6de6fSJacopo Mondi 				    const struct v4l2_isp_params_buffer *buffer,
41*d619dd9aSJacopo Mondi 				    const struct v4l2_isp_params_block_type_info *type_info,
42*d619dd9aSJacopo Mondi 				    size_t num_block_types)
433cb6de6fSJacopo Mondi {
443cb6de6fSJacopo Mondi 	size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
453cb6de6fSJacopo Mondi 	size_t payload_size = vb2_get_plane_payload(vb, 0);
463cb6de6fSJacopo Mondi 	size_t block_offset = 0;
473cb6de6fSJacopo Mondi 	size_t buffer_size;
483cb6de6fSJacopo Mondi 
493cb6de6fSJacopo Mondi 	/*
503cb6de6fSJacopo Mondi 	 * Currently only the first version of the V4L2 ISP parameters format is
513cb6de6fSJacopo Mondi 	 * supported. We accept both V0 and V1 to support existing drivers
523cb6de6fSJacopo Mondi 	 * compatible with V4L2 ISP that use either 0 or 1 as their "first
533cb6de6fSJacopo Mondi 	 * version" identifiers.
543cb6de6fSJacopo Mondi 	 */
553cb6de6fSJacopo Mondi 	if (buffer->version != V4L2_ISP_PARAMS_VERSION_V0 &&
563cb6de6fSJacopo Mondi 	    buffer->version != V4L2_ISP_PARAMS_VERSION_V1) {
573cb6de6fSJacopo Mondi 		dev_dbg(dev,
583cb6de6fSJacopo Mondi 			"Unsupported V4L2 ISP parameters format version: %u\n",
593cb6de6fSJacopo Mondi 			buffer->version);
603cb6de6fSJacopo Mondi 		return -EINVAL;
613cb6de6fSJacopo Mondi 	}
623cb6de6fSJacopo Mondi 
633cb6de6fSJacopo Mondi 	/* Validate the size reported in the header */
643cb6de6fSJacopo Mondi 	buffer_size = header_size + buffer->data_size;
653cb6de6fSJacopo Mondi 	if (buffer_size != payload_size) {
663cb6de6fSJacopo Mondi 		dev_dbg(dev, "Data size %zu and payload size %zu are different\n",
673cb6de6fSJacopo Mondi 			buffer_size, payload_size);
683cb6de6fSJacopo Mondi 		return -EINVAL;
693cb6de6fSJacopo Mondi 	}
703cb6de6fSJacopo Mondi 
713cb6de6fSJacopo Mondi 	/* Walk the list of ISP configuration blocks and validate them. */
723cb6de6fSJacopo Mondi 	buffer_size = buffer->data_size;
733cb6de6fSJacopo Mondi 	while (buffer_size >= sizeof(struct v4l2_isp_params_block_header)) {
74*d619dd9aSJacopo Mondi 		const struct v4l2_isp_params_block_type_info *info;
753cb6de6fSJacopo Mondi 		const struct v4l2_isp_params_block_header *block;
763cb6de6fSJacopo Mondi 
773cb6de6fSJacopo Mondi 		block = (const struct v4l2_isp_params_block_header *)
783cb6de6fSJacopo Mondi 			(buffer->data + block_offset);
793cb6de6fSJacopo Mondi 
80*d619dd9aSJacopo Mondi 		if (block->type >= num_block_types) {
813cb6de6fSJacopo Mondi 			dev_dbg(dev,
823cb6de6fSJacopo Mondi 				"Invalid block type %u at offset %zu\n",
833cb6de6fSJacopo Mondi 				block->type, block_offset);
843cb6de6fSJacopo Mondi 			return -EINVAL;
853cb6de6fSJacopo Mondi 		}
863cb6de6fSJacopo Mondi 
873cb6de6fSJacopo Mondi 		if (block->size > buffer_size) {
883cb6de6fSJacopo Mondi 			dev_dbg(dev, "Premature end of parameters data\n");
893cb6de6fSJacopo Mondi 			return -EINVAL;
903cb6de6fSJacopo Mondi 		}
913cb6de6fSJacopo Mondi 
923cb6de6fSJacopo Mondi 		/* It's invalid to specify both ENABLE and DISABLE. */
933cb6de6fSJacopo Mondi 		if ((block->flags & (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
943cb6de6fSJacopo Mondi 				     V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) ==
953cb6de6fSJacopo Mondi 		     (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
963cb6de6fSJacopo Mondi 		     V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) {
973cb6de6fSJacopo Mondi 			dev_dbg(dev, "Invalid block flags %x at offset %zu\n",
983cb6de6fSJacopo Mondi 				block->flags, block_offset);
993cb6de6fSJacopo Mondi 			return -EINVAL;
1003cb6de6fSJacopo Mondi 		}
1013cb6de6fSJacopo Mondi 
1023cb6de6fSJacopo Mondi 		/*
103*d619dd9aSJacopo Mondi 		 * Match the block reported size against the type info provided
1043cb6de6fSJacopo Mondi 		 * one, but allow the block to only contain the header in
1053cb6de6fSJacopo Mondi 		 * case it is going to be disabled.
1063cb6de6fSJacopo Mondi 		 */
107*d619dd9aSJacopo Mondi 		info = &type_info[block->type];
108*d619dd9aSJacopo Mondi 		if (block->size != info->size &&
1093cb6de6fSJacopo Mondi 		    (!(block->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) ||
1103cb6de6fSJacopo Mondi 		    block->size != sizeof(*block))) {
1113cb6de6fSJacopo Mondi 			dev_dbg(dev,
1123cb6de6fSJacopo Mondi 				"Invalid block size %u (expected %zu) at offset %zu\n",
113*d619dd9aSJacopo Mondi 				block->size, info->size, block_offset);
1143cb6de6fSJacopo Mondi 			return -EINVAL;
1153cb6de6fSJacopo Mondi 		}
1163cb6de6fSJacopo Mondi 
1173cb6de6fSJacopo Mondi 		block_offset += block->size;
1183cb6de6fSJacopo Mondi 		buffer_size -= block->size;
1193cb6de6fSJacopo Mondi 	}
1203cb6de6fSJacopo Mondi 
1213cb6de6fSJacopo Mondi 	if (buffer_size) {
1223cb6de6fSJacopo Mondi 		dev_dbg(dev, "Unexpected data after the parameters buffer end\n");
1233cb6de6fSJacopo Mondi 		return -EINVAL;
1243cb6de6fSJacopo Mondi 	}
1253cb6de6fSJacopo Mondi 
1263cb6de6fSJacopo Mondi 	return 0;
1273cb6de6fSJacopo Mondi }
1283cb6de6fSJacopo Mondi EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer);
1293cb6de6fSJacopo Mondi 
1303cb6de6fSJacopo Mondi MODULE_LICENSE("GPL");
1313cb6de6fSJacopo Mondi MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com");
1323cb6de6fSJacopo Mondi MODULE_DESCRIPTION("V4L2 generic ISP parameters and statistics helpers");
133