xref: /linux/drivers/media/v4l2-core/v4l2-isp.c (revision 3cb6de6fafb8fca55b14313e63f13ce10ecc6fc4)
1*3cb6de6fSJacopo Mondi // SPDX-License-Identifier: GPL-2.0-or-later
2*3cb6de6fSJacopo Mondi /*
3*3cb6de6fSJacopo Mondi  * Video4Linux2 generic ISP parameters and statistics support
4*3cb6de6fSJacopo Mondi  *
5*3cb6de6fSJacopo Mondi  * Copyright (C) 2025 Ideas On Board Oy
6*3cb6de6fSJacopo Mondi  * Author: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
7*3cb6de6fSJacopo Mondi  */
8*3cb6de6fSJacopo Mondi 
9*3cb6de6fSJacopo Mondi #include <media/v4l2-isp.h>
10*3cb6de6fSJacopo Mondi 
11*3cb6de6fSJacopo Mondi #include <linux/bitops.h>
12*3cb6de6fSJacopo Mondi #include <linux/device.h>
13*3cb6de6fSJacopo Mondi 
14*3cb6de6fSJacopo Mondi #include <media/videobuf2-core.h>
15*3cb6de6fSJacopo Mondi 
16*3cb6de6fSJacopo Mondi int v4l2_isp_params_validate_buffer_size(struct device *dev,
17*3cb6de6fSJacopo Mondi 					 struct vb2_buffer *vb,
18*3cb6de6fSJacopo Mondi 					 size_t max_size)
19*3cb6de6fSJacopo Mondi {
20*3cb6de6fSJacopo Mondi 	size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
21*3cb6de6fSJacopo Mondi 	size_t payload_size = vb2_get_plane_payload(vb, 0);
22*3cb6de6fSJacopo Mondi 
23*3cb6de6fSJacopo Mondi 	/* Payload size can't be greater than the destination buffer size */
24*3cb6de6fSJacopo Mondi 	if (payload_size > max_size) {
25*3cb6de6fSJacopo Mondi 		dev_dbg(dev, "Payload size is too large: %zu\n", payload_size);
26*3cb6de6fSJacopo Mondi 		return -EINVAL;
27*3cb6de6fSJacopo Mondi 	}
28*3cb6de6fSJacopo Mondi 
29*3cb6de6fSJacopo Mondi 	/* Payload size can't be smaller than the header size */
30*3cb6de6fSJacopo Mondi 	if (payload_size < header_size) {
31*3cb6de6fSJacopo Mondi 		dev_dbg(dev, "Payload size is too small: %zu\n", payload_size);
32*3cb6de6fSJacopo Mondi 		return -EINVAL;
33*3cb6de6fSJacopo Mondi 	}
34*3cb6de6fSJacopo Mondi 
35*3cb6de6fSJacopo Mondi 	return 0;
36*3cb6de6fSJacopo Mondi }
37*3cb6de6fSJacopo Mondi EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer_size);
38*3cb6de6fSJacopo Mondi 
39*3cb6de6fSJacopo Mondi int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb,
40*3cb6de6fSJacopo Mondi 				    const struct v4l2_isp_params_buffer *buffer,
41*3cb6de6fSJacopo Mondi 				    const struct v4l2_isp_params_block_info *info,
42*3cb6de6fSJacopo Mondi 				    size_t num_blocks)
43*3cb6de6fSJacopo Mondi {
44*3cb6de6fSJacopo Mondi 	size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
45*3cb6de6fSJacopo Mondi 	size_t payload_size = vb2_get_plane_payload(vb, 0);
46*3cb6de6fSJacopo Mondi 	size_t block_offset = 0;
47*3cb6de6fSJacopo Mondi 	size_t buffer_size;
48*3cb6de6fSJacopo Mondi 
49*3cb6de6fSJacopo Mondi 	/*
50*3cb6de6fSJacopo Mondi 	 * Currently only the first version of the V4L2 ISP parameters format is
51*3cb6de6fSJacopo Mondi 	 * supported. We accept both V0 and V1 to support existing drivers
52*3cb6de6fSJacopo Mondi 	 * compatible with V4L2 ISP that use either 0 or 1 as their "first
53*3cb6de6fSJacopo Mondi 	 * version" identifiers.
54*3cb6de6fSJacopo Mondi 	 */
55*3cb6de6fSJacopo Mondi 	if (buffer->version != V4L2_ISP_PARAMS_VERSION_V0 &&
56*3cb6de6fSJacopo Mondi 	    buffer->version != V4L2_ISP_PARAMS_VERSION_V1) {
57*3cb6de6fSJacopo Mondi 		dev_dbg(dev,
58*3cb6de6fSJacopo Mondi 			"Unsupported V4L2 ISP parameters format version: %u\n",
59*3cb6de6fSJacopo Mondi 			buffer->version);
60*3cb6de6fSJacopo Mondi 		return -EINVAL;
61*3cb6de6fSJacopo Mondi 	}
62*3cb6de6fSJacopo Mondi 
63*3cb6de6fSJacopo Mondi 	/* Validate the size reported in the header */
64*3cb6de6fSJacopo Mondi 	buffer_size = header_size + buffer->data_size;
65*3cb6de6fSJacopo Mondi 	if (buffer_size != payload_size) {
66*3cb6de6fSJacopo Mondi 		dev_dbg(dev, "Data size %zu and payload size %zu are different\n",
67*3cb6de6fSJacopo Mondi 			buffer_size, payload_size);
68*3cb6de6fSJacopo Mondi 		return -EINVAL;
69*3cb6de6fSJacopo Mondi 	}
70*3cb6de6fSJacopo Mondi 
71*3cb6de6fSJacopo Mondi 	/* Walk the list of ISP configuration blocks and validate them. */
72*3cb6de6fSJacopo Mondi 	buffer_size = buffer->data_size;
73*3cb6de6fSJacopo Mondi 	while (buffer_size >= sizeof(struct v4l2_isp_params_block_header)) {
74*3cb6de6fSJacopo Mondi 		const struct v4l2_isp_params_block_info *block_info;
75*3cb6de6fSJacopo Mondi 		const struct v4l2_isp_params_block_header *block;
76*3cb6de6fSJacopo Mondi 
77*3cb6de6fSJacopo Mondi 		block = (const struct v4l2_isp_params_block_header *)
78*3cb6de6fSJacopo Mondi 			(buffer->data + block_offset);
79*3cb6de6fSJacopo Mondi 
80*3cb6de6fSJacopo Mondi 		if (block->type >= num_blocks) {
81*3cb6de6fSJacopo Mondi 			dev_dbg(dev,
82*3cb6de6fSJacopo Mondi 				"Invalid block type %u at offset %zu\n",
83*3cb6de6fSJacopo Mondi 				block->type, block_offset);
84*3cb6de6fSJacopo Mondi 			return -EINVAL;
85*3cb6de6fSJacopo Mondi 		}
86*3cb6de6fSJacopo Mondi 
87*3cb6de6fSJacopo Mondi 		if (block->size > buffer_size) {
88*3cb6de6fSJacopo Mondi 			dev_dbg(dev, "Premature end of parameters data\n");
89*3cb6de6fSJacopo Mondi 			return -EINVAL;
90*3cb6de6fSJacopo Mondi 		}
91*3cb6de6fSJacopo Mondi 
92*3cb6de6fSJacopo Mondi 		/* It's invalid to specify both ENABLE and DISABLE. */
93*3cb6de6fSJacopo Mondi 		if ((block->flags & (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
94*3cb6de6fSJacopo Mondi 				     V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) ==
95*3cb6de6fSJacopo Mondi 		     (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
96*3cb6de6fSJacopo Mondi 		     V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) {
97*3cb6de6fSJacopo Mondi 			dev_dbg(dev, "Invalid block flags %x at offset %zu\n",
98*3cb6de6fSJacopo Mondi 				block->flags, block_offset);
99*3cb6de6fSJacopo Mondi 			return -EINVAL;
100*3cb6de6fSJacopo Mondi 		}
101*3cb6de6fSJacopo Mondi 
102*3cb6de6fSJacopo Mondi 		/*
103*3cb6de6fSJacopo Mondi 		 * Match the block reported size against the info provided
104*3cb6de6fSJacopo Mondi 		 * one, but allow the block to only contain the header in
105*3cb6de6fSJacopo Mondi 		 * case it is going to be disabled.
106*3cb6de6fSJacopo Mondi 		 */
107*3cb6de6fSJacopo Mondi 		block_info = &info[block->type];
108*3cb6de6fSJacopo Mondi 		if (block->size != block_info->size &&
109*3cb6de6fSJacopo Mondi 		    (!(block->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) ||
110*3cb6de6fSJacopo Mondi 		    block->size != sizeof(*block))) {
111*3cb6de6fSJacopo Mondi 			dev_dbg(dev,
112*3cb6de6fSJacopo Mondi 				"Invalid block size %u (expected %zu) at offset %zu\n",
113*3cb6de6fSJacopo Mondi 				block->size, block_info->size, block_offset);
114*3cb6de6fSJacopo Mondi 			return -EINVAL;
115*3cb6de6fSJacopo Mondi 		}
116*3cb6de6fSJacopo Mondi 
117*3cb6de6fSJacopo Mondi 		block_offset += block->size;
118*3cb6de6fSJacopo Mondi 		buffer_size -= block->size;
119*3cb6de6fSJacopo Mondi 	}
120*3cb6de6fSJacopo Mondi 
121*3cb6de6fSJacopo Mondi 	if (buffer_size) {
122*3cb6de6fSJacopo Mondi 		dev_dbg(dev, "Unexpected data after the parameters buffer end\n");
123*3cb6de6fSJacopo Mondi 		return -EINVAL;
124*3cb6de6fSJacopo Mondi 	}
125*3cb6de6fSJacopo Mondi 
126*3cb6de6fSJacopo Mondi 	return 0;
127*3cb6de6fSJacopo Mondi }
128*3cb6de6fSJacopo Mondi EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer);
129*3cb6de6fSJacopo Mondi 
130*3cb6de6fSJacopo Mondi MODULE_LICENSE("GPL");
131*3cb6de6fSJacopo Mondi MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com");
132*3cb6de6fSJacopo Mondi MODULE_DESCRIPTION("V4L2 generic ISP parameters and statistics helpers");
133