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