On 08/17/17 09:59, Hans Verkuil wrote: > Hi Sergei, > > A quick review. I'm concentrating on the mesh ioctl, since that's what sets this > driver apart. > > On 08/04/2017 08:03 PM, Sergei Shtylyov wrote: > > <snip> > >> Index: media_tree/Documentation/media/v4l-drivers/rcar_imr.rst >> =================================================================== >> --- /dev/null >> +++ media_tree/Documentation/media/v4l-drivers/rcar_imr.rst >> @@ -0,0 +1,372 @@ >> +Renesas R-Car Image Renderer (IMR) Driver >> +========================================= >> + >> +This file documents some driver-specific aspects of the IMR driver, such as >> +the driver-specific ioctl. > > Just drop the part ', such as...'. > > Can you add a high-level description of the functionality here? Similar to what > you did in the bindings document. > >> + >> +The ioctl reference >> +~~~~~~~~~~~~~~~~~~~ >> + >> +See ``include/uapi/linux/rcar_imr.h`` for the data structures used. >> + >> +VIDIOC_IMR_MESH - Set mapping data >> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >> + >> +Argument: ``struct imr_map_desc`` >> + >> +**Description**: >> + >> +This ioctl sets up the mesh through which the input frames will be transformed >> +into the output frames. The mesh can be strictly rectangular (when >> +``IMR_MAP_MESH`` bit is set in ``imr_map_desc::type``) or arbitrary (when that >> +bit is not set). > > Wouldn't something like 'IMR_MAP_RECTANGULAR' be much more descriptive than _MESH? > There is nothing in the name _MESH to indicate that this switches the data to > rectangles. > >> + >> +A rectangular mesh consists of the imr_mesh structure followed by M*N vertex >> +objects (where M is ``imr_mesh::rows`` and N is ``imr_mesh::columns``). >> +In case either ``IMR_MAP_AUTOSG`` or ``IMR_MAP_AUTODG`` (not both) bits are >> +set in ``imr_map_desc::type``, ``imr_mesh::{x|y}0`` specify the coordinates >> +of the top left corner of the auto-generated mesh and ``imr_mesh::d{x|y}`` >> +specify the mesh's X/Y steps. > > So if the auto bits are set, then there are no vertex objects? Since it's auto > generated by the hardware? > > I believe we discussed in the past whether 'type' should be split in a 'type' > and 'flags' field. > >> + >> +An arbitrary mesh consists of the imr_vbo structure followed by N triangle >> +objects (where N is ``imr_vbo::num``), consisting of 3 vertex objects each. >> +Setting ``IMR_MAP_AUTODG`` and ``IMR_MAP_AUTOSG`` bits in >> +``imr_map_desc::type``) isn't allowed for this type of mesh. >> + >> +The vertex object has a complex structure depending on some of the bits in >> +``imr_map_desc::type``: >> + >> +============ ============ ============== ============== =========================== >> +IMR_MAP_CLCE IMR_MAP_LUCE IMR_MAP_AUTODG IMR_MAP_AUTOSG Vertex structure variant > > You should explain the meaning of these bits in this section. I.e., what does > CLCE or AUTODG stand for? > >> +============ ============ ============== ============== =========================== >> +\ ``imr_full_coord`` >> +\ X ``imr_dst_coord`` >> +\ X ``imr_src_coord`` >> +\ X ``imr_full_coord_any_correct`` >> +\ X X ``imr_auto_coord_any_correct`` >> +\ X X ``imr_auto_coord_any_crrect`` > > crrect -> correct > >> +X ``imr_full_coord_any_correct`` >> +X X ``imr_auto_coord_any_correct`` >> +X X ``imr_auto_coord_any_correct`` >> +X X ``imr_full_coord_both_correct`` >> +X X X ``imr_auto_coord_both_correct`` >> +X X X ``imr_auto_coord_both_correct`` >> +============ ============ ============== ============== =========================== >> + >> +The luma correction is calculated according to the following formula (where >> +``Y`` is the luma value after texture mapping, ``Y'`` is the luma value after >> +luma correction, ``lscal`` and ``lofst`` are the luma correction scale and >> +offset taken from ``struct imr_luma_correct``, ``YLDPO`` is a luma correction >> +scale decimal point position specified by ``IMR_MAP_YLDPO(n)``): :: >> + >> + Y' = ((Y * lscal) >> YLDPO) + lofst >> + >> +The chroma correction is calculated according to the following formula (where >> +``U/V`` are the chroma values after texture mapping, ``U'/V'`` are the chroma >> +values after chroma correction, ``ubscl/vrscl`` and ``ubofs/vrofs`` are the >> +U/V value chroma correction scales and offsets taken from >> +``struct imr_chroma_correct``, ``UBDPO/VRDPO`` are the chroma correction scale >> +decimal point positions specified by ``IMR_MAP_{UBDPO|VRDPO}(n)``): :: >> + >> + U' = ((U + ubofs) * ubscl) >> UBDPO >> + V' = ((V + vrofs) * vrscl) >> VRDPO >> + >> +**Return value**: >> + >> +On success 0 is returned. On error -1 is returned and ``errno`` is set >> +appropriately. >> + >> +**Example code**: >> + >> +Below is an example code for constructing the meshes: ``imr_map_create()`` >> +constructs an arbitraty mesh, ``imr_map_mesh_src()`` function constructs > > arbitrary > >> +a rectangular mesh with the auto-generated destination coordinates. >> + >> +.. code-block:: C >> + >> + #include <malloc.h> >> + #include <math.h> >> + #include <linux/rcar_imr.h> >> + >> + /* IMR device data */ >> + struct imr_device { >> + /* V4L2 file decriptor */ > > descriptor > >> + int vfd; >> + >> + /* input/output buffers dimensions */ >> + int w, h, W, H; >> + }; >> + >> + #define IMR_SRC_SUBSAMPLE 5 >> + #define IMR_DST_SUBSAMPLE 2 >> + #define IMR_COORD_THRESHOLD (128 * 128 * (1 << 2 * IMR_DST_SUBSAMPLE)) >> + >> + /* find the longest side (index) */ >> + static void find_longest_side(int n, __s16 xy0, __s16 xy1, int *max, int *side) >> + { >> + int t = xy1 - xy0; >> + >> + t *= t; >> + if (*max < t) { >> + *max = t; >> + *side = n; >> + } >> + } >> + >> + /* recursively split a triangle until it can be passed to IMR */ >> + static int split_triangle(struct imr_full_coord *coord, >> + __s16 *xy0, __s16 *xy1, __s16 *xy2, >> + __u16 *uv0, __u16 *uv1, __u16 *uv2, int avail) >> + { >> + int max = 0, k = 0; >> + >> + /* we need to have at least one available triangle */ >> + if (avail < 1) >> + return 0; >> + >> + find_longest_side(0, xy0[0], xy1[0], &max, &k); >> + find_longest_side(0, xy0[1], xy1[1], &max, &k); >> + find_longest_side(1, xy1[0], xy2[0], &max, &k); >> + find_longest_side(1, xy1[1], xy2[1], &max, &k); >> + find_longest_side(2, xy2[0], xy0[0], &max, &k); >> + find_longest_side(2, xy2[1], xy0[1], &max, &k); >> + >> + /* if value exceeds the threshold, do splitting */ >> + if (max >= IMR_COORD_THRESHOLD) { >> + __s16 XY[2]; >> + __u16 UV[2]; >> + int n; >> + >> + switch (k) { >> + case 0: >> + /* split triangle along edge 0 - 1 */ >> + XY[0] = (xy0[0] + xy1[0]) / 2; >> + XY[1] = (xy0[1] + xy1[1]) / 2; >> + UV[0] = (uv0[0] + uv1[0]) / 2; >> + UV[1] = (uv0[1] + uv1[1]) / 2; >> + n = split_triangle(coord, xy0, XY, xy2, uv0, UV, uv2, >> + avail); >> + n += split_triangle(coord + 3 * n, XY, xy1, xy2, >> + UV, uv1, uv2, avail - n); >> + break; >> + case 1: >> + /* split triangle along edge 1 - 2 */ >> + XY[0] = (xy1[0] + xy2[0]) / 2; >> + XY[1] = (xy1[1] + xy2[1]) / 2; >> + UV[0] = (uv1[0] + uv2[0]) / 2; >> + UV[1] = (uv1[1] + uv2[1]) / 2; >> + n = split_triangle(coord, xy1, XY, xy0, uv1, UV, uv0, >> + avail); >> + n += split_triangle(coord + 3 * n, XY, xy2, xy0, >> + UV, uv2, uv0, avail - n); >> + break; >> + default: >> + /* split triangle along edge 2 - 0 */ >> + XY[0] = (xy2[0] + xy0[0]) / 2; >> + XY[1] = (xy2[1] + xy0[1]) / 2; >> + UV[0] = (uv2[0] + uv0[0]) / 2; >> + UV[1] = (uv2[1] + uv0[1]) / 2; >> + n = split_triangle(coord, xy2, XY, xy1, uv2, UV, uv1, >> + avail); >> + n += split_triangle(coord + 3 * n, XY, xy0, xy1, >> + UV, uv0, uv1, avail - n); >> + } >> + >> + /* return number of triangles added */ >> + return n; >> + } else { >> + /* no need to split a rectangle; save coordinates */ >> + coord[0].src.u = uv0[0]; >> + coord[0].src.v = uv0[1]; >> + coord[0].dst.x = xy0[0]; >> + coord[0].dst.y = xy0[1]; >> + coord[1].src.u = uv1[0]; >> + coord[1].src.v = uv1[1]; >> + coord[1].dst.x = xy1[0]; >> + coord[1].dst.y = xy1[1]; >> + coord[2].src.u = uv2[0]; >> + coord[2].src.v = uv2[1]; >> + coord[2].dst.x = xy2[0]; >> + coord[2].dst.y = xy2[1]; >> + >> + /* single triangle is created */ >> + return 1; >> + } >> + } >> + >> + /* process single triangle */ >> + static int process_triangle(struct imr_full_coord *coord, __u16 *uv, __s16 *xy, >> + int avail) >> + { >> + /* cull invisible triangle first */ >> + if ((xy[2] - xy[0]) * (xy[5] - xy[3]) >= >> + (xy[3] - xy[1]) * (xy[4] - xy[2])) { >> + return 0; >> + } else { >> + /* recursively split triangle into smaller ones */ >> + return split_triangle(coord, xy + 0, xy + 2, xy + 4, >> + uv + 0, uv + 2, uv + 4, avail); >> + } >> + } >> + >> + /* clamp texture coordinates to not exceed input dimensions */ >> + static void clamp_texture(__u16 *uv, float *UV, int w, int h) >> + { >> + float t; >> + >> + t = UV[0]; >> + if (t < 0) >> + uv[0] = 0; >> + t *= w; >> + if (t > w - 1) >> + uv[0] = w - 1; >> + else >> + uv[0] = round(t); >> + >> + t = UV[1]; >> + if (t < 0) >> + uv[1] = 0; >> + t *= h; >> + if (t > h - 1) >> + uv[1] = h - 1; >> + else >> + uv[1] = round(t); >> + } >> + >> + /* clamp vertex coordinates */ >> + static int clamp_vertex(__s16 *xy, float *XY, int W, int H) >> + { >> + float x = round(XY[0] * W), y = round(XY[1] * H), z = XY[2]; >> + >> + if (z < 0.1) >> + return 0; >> + if (x < -(256 << IMR_DST_SUBSAMPLE) || x >= W + (256 << IMR_DST_SUBSAMPLE)) >> + return 0; >> + if (y < -(256 << IMR_DST_SUBSAMPLE) || y >= H + (256 << IMR_DST_SUBSAMPLE)) >> + return 0; >> + >> + xy[0] = (__s16)x; >> + xy[1] = (__s16)y; >> + >> + return 1; >> + } >> + >> + /* create arbitrary mesh */ >> + struct imr_map_desc *imr_map_create(struct imr_device *dev, >> + float *uv, float *xy, int n) >> + { >> + struct imr_map_desc *desc; >> + struct imr_vbo *vbo; >> + struct imr_full_coord *coord; >> + int j, m, w, h, W, H; >> + >> + /* create a configuration structure */ >> + desc = malloc(sizeof(*desc) + sizeof(*vbo) + 3 * n * sizeof(*coord)); >> + if (!desc) >> + return NULL; >> + >> + /* fill-in VBO coordinates */ >> + vbo = (void *)(desc + 1); >> + coord = (void *)(vbo + 1); >> + >> + /* calculate source/destination dimensions in subpixel coordinates */ >> + w = dev->w << IMR_SRC_SUBSAMPLE; >> + h = dev->h << IMR_SRC_SUBSAMPLE; >> + W = dev->W << IMR_DST_SUBSAMPLE; >> + H = dev->H << IMR_DST_SUBSAMPLE; >> + >> + /* put at most N triangles into mesh descriptor */ >> + for (j = 0, m = 0; j < n && m < n; j++, xy += 9, uv += 6) { >> + __u16 UV[6]; >> + __s16 XY[6]; >> + int k; >> + >> + /* translate model coordinates to fixed-point */ >> + if (!clamp_vertex(XY + 0, xy + 0, W, H)) >> + continue; >> + if (!clamp_vertex(XY + 2, xy + 3, W, H)) >> + continue; >> + if (!clamp_vertex(XY + 4, xy + 6, W, H)) >> + continue; >> + >> + /* translate source coordinates */ >> + clamp_texture(UV + 0, uv + 0, w, h); >> + clamp_texture(UV + 2, uv + 2, w, h); >> + clamp_texture(UV + 4, uv + 4, w, h); >> + >> + /* process single triangle */ >> + k = process_triangle(coord, UV, XY, n - m); >> + if (k != 0) { >> + /* advance vertex coordinates pointer */ >> + coord += 3 * k; >> + m += k; >> + } >> + } >> + >> + /* put number of triangles in VBO */ >> + vbo->num = m; >> + >> + /* fill-in descriptor */ >> + desc->type = IMR_MAP_UVDPOR(IMR_SRC_SUBSAMPLE) | >> + (IMR_DST_SUBSAMPLE ? IMR_MAP_DDP : 0); >> + desc->size = (void *)coord - (void *)vbo; >> + desc->data = (__u64)vbo; >> + >> + return desc; >> + } >> + >> + /* create rectangular mesh */ >> + struct imr_map_desc *imr_map_mesh_src(struct imr_device *dev, float *uv, >> + int rows, int columns, >> + float x0, float y0, float dx, float dy) >> + { >> + struct imr_map_desc *desc; >> + struct imr_mesh *mesh; >> + struct imr_src_coord *coord; >> + int k, w, h, W, H; >> + >> + /* create a configuration structure */ >> + desc = malloc(sizeof(*desc) + sizeof(*mesh) + rows * columns * >> + sizeof(*coord)); >> + if (!desc) >> + return NULL; >> + >> + /* fill-in rectangular mesh coordinates */ >> + mesh = (void *)(desc + 1); >> + coord = (void *)(mesh + 1); >> + >> + /* calculate source/destination dimensions in subpixel coordinates */ >> + w = dev->w << IMR_SRC_SUBSAMPLE; >> + h = dev->h << IMR_SRC_SUBSAMPLE; >> + W = dev->W << IMR_DST_SUBSAMPLE; >> + H = dev->H << IMR_DST_SUBSAMPLE; >> + >> + /* set mesh parameters */ >> + mesh->rows = rows; >> + mesh->columns = columns; >> + mesh->x0 = (__u16)round(x0 * W); >> + mesh->y0 = (__u16)round(y0 * H); >> + mesh->dx = (__u16)round(dx * W); >> + mesh->dy = (__u16)round(dy * H); >> + >> + /* put mesh coordinates */ >> + for (k = 0; k < rows * columns; k++, uv += 2, coord++) { >> + __u16 UV[2]; >> + >> + /* transform point into texture coordinates */ >> + clamp_texture(UV, uv, w, h); >> + >> + /* fill the mesh */ >> + coord->u = UV[0]; >> + coord->v = UV[1]; >> + } >> + >> + /* fill-in descriptor */ >> + desc->type = IMR_MAP_MESH | IMR_MAP_AUTODG | >> + IMR_MAP_UVDPOR(IMR_SRC_SUBSAMPLE) | >> + (IMR_DST_SUBSAMPLE ? IMR_MAP_DDP : 0); >> + desc->size = (void *)coord - (void *)mesh; >> + desc->data = (__u64)mesh; >> + >> + return desc; >> + } > > <snip> > >> Index: media_tree/include/uapi/linux/rcar_imr.h >> =================================================================== >> --- /dev/null >> +++ media_tree/include/uapi/linux/rcar_imr.h >> @@ -0,0 +1,182 @@ >> +/* >> + * rcar_imr.h -- R-Car IMR-LX4 Driver UAPI >> + * >> + * Copyright (C) 2016-2017 Cogent Embedded, Inc. <source@xxxxxxxxxxxxxxxxxx> >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + */ >> + >> +#ifndef __RCAR_IMR_H >> +#define __RCAR_IMR_H >> + >> +#include <linux/videodev2.h> >> + >> +/******************************************************************************* >> + * Mapping specification descriptor >> + ******************************************************************************/ >> + >> +struct imr_map_desc { >> + /* bitmask of the mapping type (see below) */ >> + __u32 type; >> + >> + /* total data size */ >> + __u32 size; >> + >> + /* data user-pointer */ >> + __u64 data; >> +} __attribute__((packed)); >> + >> +/* regular mesh specification */ >> +#define IMR_MAP_MESH (1 << 0) >> + >> +/* auto-generated source coordinates */ >> +#define IMR_MAP_AUTOSG (1 << 1) >> + >> +/* auto-generated destination coordinates */ >> +#define IMR_MAP_AUTODG (1 << 2) >> + >> +/* luma correction flag */ >> +#define IMR_MAP_LUCE (1 << 3) >> + >> +/* chroma correction flag */ >> +#define IMR_MAP_CLCE (1 << 4) >> + >> +/* vertex clockwise-mode order */ >> +#define IMR_MAP_TCM (1 << 5) >> + >> +/* source coordinate decimal point position */ >> +#define __IMR_MAP_UVDPOR_SHIFT 8 >> +#define __IMR_MAP_UVDPOR(v) (((v) >> __IMR_MAP_UVDPOR_SHIFT) & 0x7) >> +#define IMR_MAP_UVDPOR(n) (((n) & 0x7) << __IMR_MAP_UVDPOR_SHIFT) >> + >> +/* destination coordinate sub-pixel mode */ >> +#define IMR_MAP_DDP (1 << 11) >> + >> +/* luminance correction scale decimal point position */ >> +#define __IMR_MAP_YLDPO_SHIFT 12 >> +#define __IMR_MAP_YLDPO(v) (((v) >> __IMR_MAP_YLDPO_SHIFT) & 0x7) >> +#define IMR_MAP_YLDPO(n) (((n) & 0x7) << __IMR_MAP_YLDPO_SHIFT) >> + >> +/* chroma (U) correction scale decimal point position */ >> +#define __IMR_MAP_UBDPO_SHIFT 15 >> +#define __IMR_MAP_UBDPO(v) (((v) >> __IMR_MAP_UBDPO_SHIFT) & 0x7) >> +#define IMR_MAP_UBDPO(n) (((n) & 0x7) << __IMR_MAP_UBDPO_SHIFT) >> + >> +/* chroma (V) correction scale decimal point position */ >> +#define __IMR_MAP_VRDPO_SHIFT 18 >> +#define __IMR_MAP_VRDPO(v) (((v) >> __IMR_MAP_VRDPO_SHIFT) & 0x7) >> +#define IMR_MAP_VRDPO(n) (((n) & 0x7) << __IMR_MAP_VRDPO_SHIFT) > > You need to document all these bits in the documentation. I.e. TCM and DDP > are not currently documented. > > It helps a lot to understand these defines if the documentation and the > comments explain what the abbreviations mean. E.g. what does DDP stand > for, or YLDPO? It's an alphabet soup right now. > >> + >> +/* regular mesh specification */ >> +struct imr_mesh { > > How about imr_rectangles? There is no indication in the struct name that this > describes a rectangle. > >> + /* rectangular mesh size */ >> + __u16 rows, columns; >> + >> + /* auto-generated mesh parameters */ >> + __u16 x0, y0, dx, dy; >> +} __attribute__((packed)); >> + >> +/* vertex-buffer-object (VBO) descriptor */ >> +struct imr_vbo { >> + /* number of triangles */ >> + __u16 num; >> +} __attribute__((packed)); >> + >> +/******************************************************************************* >> + * Vertex-related structures >> + ******************************************************************************/ >> + >> +/* source coordinates */ >> +struct imr_src_coord { >> + /* vertical, horizontal */ >> + __u16 v, u; > > Confusing: why isn't this 'v, h;' given the comment? Or does this refer to > chroma (U and V)? And what does 'vertical, horizontal' mean anyway? Vertical > what? > >> +} __attribute__((packed)); >> + >> +/* destination coordinates */ >> +struct imr_dst_coord { >> + /* vertical, horizontal */ > > Confusing comment as well. I assume y and x are simply coordinates of a vertex? > Just say so. This comment doesn't mean anything. > >> + __u16 y, x; >> +} __attribute__((packed)); >> + >> +/* luma correction parameters */ >> +struct imr_luma_correct { >> + /* offset */ >> + __s8 lofst; >> + >> + /* scale */ >> + __u8 lscal; >> + >> + __u16 reserved; > > Why is this reserved? Is that for padding? If so, then add a comment that mentions > that. > >> +} __attribute__((packed)); >> + >> +/* chroma correction parameters */ >> +struct imr_chroma_correct { >> + /* V value offset */ >> + __s8 vrofs; >> + >> + /* V value scale */ >> + __u8 vrscl; >> + >> + /* U value offset */ >> + __s8 ubofs; >> + >> + /* V value scale */ >> + __u8 ubscl; >> +} __attribute__((packed)); >> + >> +/* fully specified source/destination coordinates */ >> +struct imr_full_coord { >> + struct imr_src_coord src; >> + struct imr_dst_coord dst; >> +} __attribute__((packed)); >> + >> +/* auto-generated coordinates with luma or chroma correction */ >> +struct imr_auto_coord_any_correct { >> + union { >> + struct imr_src_coord src; >> + struct imr_dst_coord dst; > > Why have separate imr_src_coord and imr_dst_coord structs? Why not just > call it imr_coord? I think that is part of the reason of my confusion > regarding understanding those structs. > > The field name here indicates whether it is a source or destination, > the coordinate information is the same for both. > >> + }; >> + union { >> + struct imr_luma_correct luma; >> + struct imr_chroma_correct chroma; >> + }; >> +} __attribute__((packed)); >> + >> +/* auto-generated coordinates with both luma and chroma correction */ >> +struct imr_auto_coord_both_correct { >> + union { >> + struct imr_src_coord src; >> + struct imr_dst_coord dst; >> + }; >> + struct imr_luma_correct luma; >> + struct imr_chroma_correct chroma; >> +} __attribute__((packed)); >> + >> +/* fully specified coordinates with luma or chroma correction */ >> +struct imr_full_coord_any_correct { >> + struct imr_src_coord src; >> + struct imr_dst_coord dst; >> + union { >> + struct imr_luma_correct luma; >> + struct imr_chroma_correct chroma; >> + }; >> +} __attribute__((packed)); >> + >> +/* fully specified coordinates with both luma and chroma correction */ >> +struct imr_full_coord_both_correct { >> + struct imr_src_coord src; >> + struct imr_dst_coord dst; >> + struct imr_luma_correct luma; >> + struct imr_chroma_correct chroma; >> +} __attribute__((packed)); >> + >> +/******************************************************************************* >> + * Private IOCTL codes >> + ******************************************************************************/ >> + >> +#define VIDIOC_IMR_MESH _IOW('V', BASE_VIDIOC_PRIVATE + 0, struct imr_map_desc) >> + >> +#endif /* __RCAR_IMR_H */ >> > > Do you know the typical number of rectangles or triangles that are passed to this > function? Is there an upper hardware limit? > > I ask, because I wonder whether using a fixed vertex struct like imr_full_coord_both_correct > for all variations isn't much simpler. The driver just ignores the fields it doesn't > need in that case. > > Yes, you get some memory overhead, but the code for both userspace and kernelspace > will be much simpler. I just had a brainwave: rather than making these complicated structure variants, why not just do this (just thrown together, I didn't look at alignment etc.): struct imr_map_desc { /* bitmask of the mapping type (see below) */ __u32 type; /* total data size */ __u32 size; /* number of vertices */ __u32 vertices; /* rectangular mesh size (ignored for triangles) */ __u16 rows, columns; /* auto-generated mesh parameters (ignored if not auto-generated) */ __u16 x0, y0, dx, dy; /* data user-pointer */ __u64 src_coords; __u64 dst_coords; __u64 luma_corr; __u64 chroma_corr; } __attribute__((packed)); Just leave the corresponding data pointer 0 when not needed, and otherwise these data pointers point to a struct imr_coord, imr_luma_corr or imr_chroma_corr arrays. Much simpler and still memory efficient. BTW, I prefer either _corr or _correction over _correct. 'correct' is confusing since it is also a normal english word and it is not obvious that it is an abbreviation for 'correction'. Regards, Hans