diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-common.c')
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-common.c | 899 |
1 files changed, 650 insertions, 249 deletions
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index a5ea1f517291..554c591e1113 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Video for Linux Two * @@ -7,14 +8,8 @@ * This file replaces the videodev.c file that comes with the * regular kernel distribution. * - * 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. - * * Author: Bill Dirks <bill@thedirks.org> * based on code by Alan Cox, <alan@cymru.net> - * */ /* @@ -23,11 +18,6 @@ * A generic video device interface for the LINUX operating system * using a set of device structures/vectors for low level operations. * - * 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. - * * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk> * * Fixes: @@ -44,18 +34,16 @@ * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman) */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/errno.h> -#include <linux/i2c.h> -#if defined(CONFIG_SPI) -#include <linux/spi/spi.h> -#endif #include <linux/uaccess.h> -#include <asm/pgtable.h> #include <asm/io.h> #include <asm/div64.h> #include <media/v4l2-common.h> @@ -64,10 +52,6 @@ #include <linux/videodev2.h> -MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr"); -MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers"); -MODULE_LICENSE("GPL"); - /* * * V 4 L 2 D R I V E R H E L P E R A P I @@ -100,308 +84,725 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 _min, s32 _max, s32 _ qctrl->step = step; qctrl->default_value = def; qctrl->reserved[0] = qctrl->reserved[1] = 0; - strlcpy(qctrl->name, name, sizeof(qctrl->name)); + strscpy(qctrl->name, name, sizeof(qctrl->name)); return 0; } EXPORT_SYMBOL(v4l2_ctrl_query_fill); -/* I2C Helper functions */ +/* Clamp x to be between min and max, aligned to a multiple of 2^align. min + * and max don't have to be aligned, but there must be at least one valid + * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples + * of 16 between 17 and 31. */ +static unsigned int clamp_align(unsigned int x, unsigned int min, + unsigned int max, unsigned int align) +{ + /* Bits that must be zero to be aligned */ + unsigned int mask = ~((1 << align) - 1); + + /* Clamp to aligned min and max */ + x = clamp(x, (min + ~mask) & mask, max & mask); + + /* Round to nearest aligned value */ + if (align) + x = (x + (1 << (align - 1))) & mask; -#if IS_ENABLED(CONFIG_I2C) + return x; +} -void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, - const struct v4l2_subdev_ops *ops) +static unsigned int clamp_roundup(unsigned int x, unsigned int min, + unsigned int max, unsigned int alignment) { - v4l2_subdev_init(sd, ops); - sd->flags |= V4L2_SUBDEV_FL_IS_I2C; - /* the owner is the same as the i2c_client's driver owner */ - sd->owner = client->dev.driver->owner; - sd->dev = &client->dev; - /* i2c_client and v4l2_subdev point to one another */ - v4l2_set_subdevdata(sd, client); - i2c_set_clientdata(client, sd); - /* initialize name */ - snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", - client->dev.driver->name, i2c_adapter_id(client->adapter), - client->addr); + x = clamp(x, min, max); + if (alignment) + x = round_up(x, alignment); + + return x; } -EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); -/* Load an i2c sub-device. */ -struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, struct i2c_board_info *info, - const unsigned short *probe_addrs) +void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, + unsigned int walign, + u32 *h, unsigned int hmin, unsigned int hmax, + unsigned int halign, unsigned int salign) { - struct v4l2_subdev *sd = NULL; - struct i2c_client *client; + *w = clamp_align(*w, wmin, wmax, walign); + *h = clamp_align(*h, hmin, hmax, halign); - BUG_ON(!v4l2_dev); + /* Usually we don't need to align the size and are done now. */ + if (!salign) + return; - request_module(I2C_MODULE_PREFIX "%s", info->type); + /* How much alignment do we have? */ + walign = __ffs(*w); + halign = __ffs(*h); + /* Enough to satisfy the image alignment? */ + if (walign + halign < salign) { + /* Max walign where there is still a valid width */ + unsigned int wmaxa = __fls(wmax ^ (wmin - 1)); + /* Max halign where there is still a valid height */ + unsigned int hmaxa = __fls(hmax ^ (hmin - 1)); - /* Create the i2c client */ - if (info->addr == 0 && probe_addrs) - client = i2c_new_probed_device(adapter, info, probe_addrs, - NULL); - else - client = i2c_new_device(adapter, info); - - /* Note: by loading the module first we are certain that c->driver - will be set if the driver was found. If the module was not loaded - first, then the i2c core tries to delay-load the module for us, - and then c->driver is still NULL until the module is finally - loaded. This delay-load mechanism doesn't work if other drivers - want to use the i2c device, so explicitly loading the module - is the best alternative. */ - if (client == NULL || client->dev.driver == NULL) - goto error; - - /* Lock the module so we can safely get the v4l2_subdev pointer */ - if (!try_module_get(client->dev.driver->owner)) - goto error; - sd = i2c_get_clientdata(client); - - /* Register with the v4l2_device which increases the module's - use count as well. */ - if (v4l2_device_register_subdev(v4l2_dev, sd)) - sd = NULL; - /* Decrease the module use count to match the first try_module_get. */ - module_put(client->dev.driver->owner); - -error: - /* If we have a client but no subdev, then something went wrong and - we must unregister the client. */ - if (client && sd == NULL) - i2c_unregister_device(client); - return sd; + /* up the smaller alignment until we have enough */ + do { + if (halign >= hmaxa || + (walign <= halign && walign < wmaxa)) { + *w = clamp_align(*w, wmin, wmax, walign + 1); + walign = __ffs(*w); + } else { + *h = clamp_align(*h, hmin, hmax, halign + 1); + halign = __ffs(*h); + } + } while (halign + walign < salign); + } } -EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); +EXPORT_SYMBOL_GPL(v4l_bound_align_image); -struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, const char *client_type, - u8 addr, const unsigned short *probe_addrs) +const void * +__v4l2_find_nearest_size_conditional(const void *array, size_t array_size, + size_t entry_size, size_t width_offset, + size_t height_offset, s32 width, + s32 height, + bool (*func)(const void *array, + size_t index, + const void *context), + const void *context) { - struct i2c_board_info info; + u32 error, min_error = U32_MAX; + const void *best = NULL; + size_t i; - /* Setup the i2c board info with the device type and - the device address. */ - memset(&info, 0, sizeof(info)); - strlcpy(info.type, client_type, sizeof(info.type)); - info.addr = addr; + if (!array) + return NULL; - return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs); + for (i = 0; i < array_size; i++, array += entry_size) { + const u32 *entry_width = array + width_offset; + const u32 *entry_height = array + height_offset; + + if (func && !func(array, i, context)) + continue; + + error = abs(*entry_width - width) + abs(*entry_height - height); + if (error > min_error) + continue; + + min_error = error; + best = array; + if (!error) + break; + } + + return best; } -EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); +EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size_conditional); -/* Return i2c client address of v4l2_subdev. */ -unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) +int v4l2_g_parm_cap(struct video_device *vdev, + struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_subdev_frame_interval ival = { 0 }; + int ret; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; - return client ? client->addr : I2C_CLIENT_END; + if (vdev->device_caps & V4L2_CAP_READWRITE) + a->parm.capture.readbuffers = 2; + if (v4l2_subdev_has_op(sd, pad, get_frame_interval)) + a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + ret = v4l2_subdev_call_state_active(sd, pad, get_frame_interval, &ival); + if (!ret) + a->parm.capture.timeperframe = ival.interval; + return ret; } -EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); +EXPORT_SYMBOL_GPL(v4l2_g_parm_cap); -/* Return a list of I2C tuner addresses to probe. Use only if the tuner - addresses are unknown. */ -const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) +int v4l2_s_parm_cap(struct video_device *vdev, + struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - static const unsigned short radio_addrs[] = { -#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) - 0x10, -#endif - 0x60, - I2C_CLIENT_END - }; - static const unsigned short demod_addrs[] = { - 0x42, 0x43, 0x4a, 0x4b, - I2C_CLIENT_END + struct v4l2_subdev_frame_interval ival = { + .interval = a->parm.capture.timeperframe }; - static const unsigned short tv_addrs[] = { - 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ - 0x60, 0x61, 0x62, 0x63, 0x64, - I2C_CLIENT_END + int ret; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + memset(&a->parm, 0, sizeof(a->parm)); + if (vdev->device_caps & V4L2_CAP_READWRITE) + a->parm.capture.readbuffers = 2; + else + a->parm.capture.readbuffers = 0; + + if (v4l2_subdev_has_op(sd, pad, get_frame_interval)) + a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + ret = v4l2_subdev_call_state_active(sd, pad, set_frame_interval, &ival); + if (!ret) + a->parm.capture.timeperframe = ival.interval; + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_s_parm_cap); + +const struct v4l2_format_info *v4l2_format_info(u32 format) +{ + static const struct v4l2_format_info formats[] = { + /* RGB formats */ + { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* YUV packed formats */ + { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_UYVY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_Y210, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, + { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, + + /* YUV planar formats */ + { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV21, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV15, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_P012, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + + { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 4 }, + { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 4 }, + { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* Tiled YUV formats */ + { .format = V4L2_PIX_FMT_NV12_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV15_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 4, 2, 0, 0 }, .block_h = { 1, 1, 0, 0 }}, + { .format = V4L2_PIX_FMT_P010_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + + /* YUV planar formats, non contiguous variant */ + { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + { .format = V4L2_PIX_FMT_NV12M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV21M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV16M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_P012M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + + /* Tiled YUV formats, non contiguous variant */ + { .format = V4L2_PIX_FMT_NV12MT, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 64, 32, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, + { .format = V4L2_PIX_FMT_NV12MT_16X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 16, 8, 0, 0 }, .block_h = { 16, 8, 0, 0 }}, + + /* Bayer RGB formats */ + { .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* Renesas Camera Data Receiver Unit formats, bayer order agnostic */ + { .format = V4L2_PIX_FMT_RAW_CRU10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 6, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RAW_CRU12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 5, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RAW_CRU14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RAW_CRU20, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 3, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, }; + unsigned int i; - switch (type) { - case ADDRS_RADIO: - return radio_addrs; - case ADDRS_DEMOD: - return demod_addrs; - case ADDRS_TV: - return tv_addrs; - case ADDRS_TV_WITH_DEMOD: - return tv_addrs + 4; - } + for (i = 0; i < ARRAY_SIZE(formats); ++i) + if (formats[i].format == format) + return &formats[i]; return NULL; } -EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); +EXPORT_SYMBOL(v4l2_format_info); -#endif /* defined(CONFIG_I2C) */ +static inline unsigned int v4l2_format_block_width(const struct v4l2_format_info *info, int plane) +{ + if (!info->block_w[plane]) + return 1; + return info->block_w[plane]; +} -#if defined(CONFIG_SPI) +static inline unsigned int v4l2_format_block_height(const struct v4l2_format_info *info, int plane) +{ + if (!info->block_h[plane]) + return 1; + return info->block_h[plane]; +} -/* Load an spi sub-device. */ +static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane, + unsigned int width) +{ + unsigned int hdiv = plane ? info->hdiv : 1; + unsigned int aligned_width = + ALIGN(width, v4l2_format_block_width(info, plane)); -void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, - const struct v4l2_subdev_ops *ops) + return DIV_ROUND_UP(aligned_width, hdiv) * + info->bpp[plane] / info->bpp_div[plane]; +} + +static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane, + unsigned int height) { - v4l2_subdev_init(sd, ops); - sd->flags |= V4L2_SUBDEV_FL_IS_SPI; - /* the owner is the same as the spi_device's driver owner */ - sd->owner = spi->dev.driver->owner; - sd->dev = &spi->dev; - /* spi_device and v4l2_subdev point to one another */ - v4l2_set_subdevdata(sd, spi); - spi_set_drvdata(spi, sd); - /* initialize name */ - strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name)); + unsigned int vdiv = plane ? info->vdiv : 1; + unsigned int aligned_height = + ALIGN(height, v4l2_format_block_height(info, plane)); + + return DIV_ROUND_UP(aligned_height, vdiv); } -EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init); -struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, - struct spi_master *master, struct spi_board_info *info) +static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane, + unsigned int width, unsigned int height) { - struct v4l2_subdev *sd = NULL; - struct spi_device *spi = NULL; + return v4l2_format_plane_stride(info, plane, width) * + v4l2_format_plane_height(info, plane, height); +} - BUG_ON(!v4l2_dev); +void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, + const struct v4l2_frmsize_stepwise *frmsize) +{ + if (!frmsize) + return; - if (info->modalias[0]) - request_module(info->modalias); + /* + * Clamp width/height to meet min/max constraints and round it up to + * macroblock alignment. + */ + *width = clamp_roundup(*width, frmsize->min_width, frmsize->max_width, + frmsize->step_width); + *height = clamp_roundup(*height, frmsize->min_height, frmsize->max_height, + frmsize->step_height); +} +EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints); - spi = spi_new_device(master, info); +int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height) +{ + const struct v4l2_format_info *info; + struct v4l2_plane_pix_format *plane; + int i; - if (spi == NULL || spi->dev.driver == NULL) - goto error; + info = v4l2_format_info(pixelformat); + if (!info) + return -EINVAL; - if (!try_module_get(spi->dev.driver->owner)) - goto error; + pixfmt->width = width; + pixfmt->height = height; + pixfmt->pixelformat = pixelformat; + pixfmt->num_planes = info->mem_planes; + + if (info->mem_planes == 1) { + plane = &pixfmt->plane_fmt[0]; + plane->bytesperline = v4l2_format_plane_stride(info, 0, width); + plane->sizeimage = 0; + + for (i = 0; i < info->comp_planes; i++) + plane->sizeimage += + v4l2_format_plane_size(info, i, width, height); + } else { + for (i = 0; i < info->comp_planes; i++) { + plane = &pixfmt->plane_fmt[i]; + plane->bytesperline = + v4l2_format_plane_stride(info, i, width); + plane->sizeimage = plane->bytesperline * + v4l2_format_plane_height(info, i, height); + } + } + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp); - sd = spi_get_drvdata(spi); +int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, + u32 width, u32 height) +{ + const struct v4l2_format_info *info; + int i; - /* Register with the v4l2_device which increases the module's - use count as well. */ - if (v4l2_device_register_subdev(v4l2_dev, sd)) - sd = NULL; + info = v4l2_format_info(pixelformat); + if (!info) + return -EINVAL; - /* Decrease the module use count to match the first try_module_get. */ - module_put(spi->dev.driver->owner); + /* Single planar API cannot be used for multi plane formats. */ + if (info->mem_planes > 1) + return -EINVAL; -error: - /* If we have a client but no subdev, then something went wrong and - we must unregister the client. */ - if (!sd) - spi_unregister_device(spi); + pixfmt->width = width; + pixfmt->height = height; + pixfmt->pixelformat = pixelformat; + pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width); + pixfmt->sizeimage = 0; - return sd; + for (i = 0; i < info->comp_planes; i++) + pixfmt->sizeimage += + v4l2_format_plane_size(info, i, width, height); + return 0; } -EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev); - -#endif /* defined(CONFIG_SPI) */ +EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); -/* Clamp x to be between min and max, aligned to a multiple of 2^align. min - * and max don't have to be aligned, but there must be at least one valid - * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples - * of 16 between 17 and 31. */ -static unsigned int clamp_align(unsigned int x, unsigned int min, - unsigned int max, unsigned int align) +#ifdef CONFIG_MEDIA_CONTROLLER +static s64 v4l2_get_link_freq_ctrl(struct v4l2_ctrl_handler *handler, + unsigned int mul, unsigned int div) { - /* Bits that must be zero to be aligned */ - unsigned int mask = ~((1 << align) - 1); + struct v4l2_ctrl *ctrl; + s64 freq; - /* Clamp to aligned min and max */ - x = clamp(x, (min + ~mask) & mask, max & mask); + ctrl = v4l2_ctrl_find(handler, V4L2_CID_LINK_FREQ); + if (ctrl) { + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; + int ret; - /* Round to nearest aligned value */ - if (align) - x = (x + (1 << (align - 1))) & mask; + qm.index = v4l2_ctrl_g_ctrl(ctrl); - return x; + ret = v4l2_querymenu(handler, &qm); + if (ret) + return -ENOENT; + + freq = qm.value; + } else { + if (!mul || !div) + return -ENOENT; + + ctrl = v4l2_ctrl_find(handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) + return -ENOENT; + + freq = div_u64(v4l2_ctrl_g_ctrl_int64(ctrl) * mul, div); + + pr_warn_once("%s: Link frequency estimated using pixel rate: result might be inaccurate\n", + __func__); + pr_warn_once("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n", + __func__); + } + + return freq > 0 ? freq : -EINVAL; } -/* Bound an image to have a width between wmin and wmax, and height between - * hmin and hmax, inclusive. Additionally, the width will be a multiple of - * 2^walign, the height will be a multiple of 2^halign, and the overall size - * (width*height) will be a multiple of 2^salign. The image may be shrunk - * or enlarged to fit the alignment constraints. - * - * The width or height maximum must not be smaller than the corresponding - * minimum. The alignments must not be so high there are no possible image - * sizes within the allowed bounds. wmin and hmin must be at least 1 - * (don't use 0). If you don't care about a certain alignment, specify 0, - * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If - * you only want to adjust downward, specify a maximum that's the same as - * the initial value. +s64 v4l2_get_link_freq(const struct media_pad *pad, unsigned int mul, + unsigned int div) +{ + struct v4l2_mbus_config mbus_config = {}; + struct v4l2_subdev *sd; + int ret; + + sd = media_entity_to_v4l2_subdev(pad->entity); + ret = v4l2_subdev_call(sd, pad, get_mbus_config, pad->index, + &mbus_config); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + if (mbus_config.link_freq) + return mbus_config.link_freq; + + /* + * Fall back to using the link frequency control if the media bus config + * doesn't provide a link frequency. + */ + return v4l2_get_link_freq_ctrl(sd->ctrl_handler, mul, div); +} +EXPORT_SYMBOL_GPL(v4l2_get_link_freq); + +int v4l2_get_active_data_lanes(const struct media_pad *pad, + unsigned int max_data_lanes) +{ + struct v4l2_mbus_config mbus_config = {}; + struct v4l2_subdev *sd; + unsigned int lanes; + int ret; + + sd = media_entity_to_v4l2_subdev(pad->entity); + ret = v4l2_subdev_call(sd, pad, get_mbus_config, pad->index, + &mbus_config); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* This relies on the mbus_config being zeroed at init time */ + lanes = mbus_config.bus.mipi_csi2.num_data_lanes; + if (!lanes) + return max_data_lanes; + + if (lanes > max_data_lanes) { + dev_dbg(sd->dev, "Active data lanes (%u) exceeds max (%u)\n", + lanes, max_data_lanes); + return -EINVAL; + } + + return lanes; +} +EXPORT_SYMBOL_GPL(v4l2_get_active_data_lanes); +#endif + +/* + * Simplify a fraction using a simple continued fraction decomposition. The + * idea here is to convert fractions such as 333333/10000000 to 1/30 using + * 32 bit arithmetic only. The algorithm is not perfect and relies upon two + * arbitrary parameters to remove non-significative terms from the simple + * continued fraction decomposition. Using 8 and 333 for n_terms and threshold + * respectively seems to give nice results. */ -void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, - unsigned int walign, - u32 *h, unsigned int hmin, unsigned int hmax, - unsigned int halign, unsigned int salign) +void v4l2_simplify_fraction(u32 *numerator, u32 *denominator, + unsigned int n_terms, unsigned int threshold) { - *w = clamp_align(*w, wmin, wmax, walign); - *h = clamp_align(*h, hmin, hmax, halign); + u32 *an; + u32 x, y, r; + unsigned int i, n; - /* Usually we don't need to align the size and are done now. */ - if (!salign) + an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL); + if (an == NULL) return; - /* How much alignment do we have? */ - walign = __ffs(*w); - halign = __ffs(*h); - /* Enough to satisfy the image alignment? */ - if (walign + halign < salign) { - /* Max walign where there is still a valid width */ - unsigned int wmaxa = __fls(wmax ^ (wmin - 1)); - /* Max halign where there is still a valid height */ - unsigned int hmaxa = __fls(hmax ^ (hmin - 1)); + /* + * Convert the fraction to a simple continued fraction. See + * https://en.wikipedia.org/wiki/Continued_fraction + * Stop if the current term is bigger than or equal to the given + * threshold. + */ + x = *numerator; + y = *denominator; + + for (n = 0; n < n_terms && y != 0; ++n) { + an[n] = x / y; + if (an[n] >= threshold) { + if (n < 2) + n++; + break; + } - /* up the smaller alignment until we have enough */ - do { - if (halign >= hmaxa || - (walign <= halign && walign < wmaxa)) { - *w = clamp_align(*w, wmin, wmax, walign + 1); - walign = __ffs(*w); - } else { - *h = clamp_align(*h, hmin, hmax, halign + 1); - halign = __ffs(*h); - } - } while (halign + walign < salign); + r = x - an[n] * y; + x = y; + y = r; + } + + /* Expand the simple continued fraction back to an integer fraction. */ + x = 0; + y = 1; + + for (i = n; i > 0; --i) { + r = y; + y = an[i-1] * y + x; + x = r; } + + *numerator = y; + *denominator = x; + kfree(an); } -EXPORT_SYMBOL_GPL(v4l_bound_align_image); +EXPORT_SYMBOL_GPL(v4l2_simplify_fraction); -const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( - const struct v4l2_discrete_probe *probe, - s32 width, s32 height) +/* + * Convert a fraction to a frame interval in 100ns multiples. The idea here is + * to compute numerator / denominator * 10000000 using 32 bit fixed point + * arithmetic only. + */ +u32 v4l2_fraction_to_interval(u32 numerator, u32 denominator) { - int i; - u32 error, min_error = UINT_MAX; - const struct v4l2_frmsize_discrete *size, *best = NULL; + u32 multiplier; + + /* Saturate the result if the operation would overflow. */ + if (denominator == 0 || + numerator/denominator >= ((u32)-1)/10000000) + return (u32)-1; + + /* + * Divide both the denominator and the multiplier by two until + * numerator * multiplier doesn't overflow. If anyone knows a better + * algorithm please let me know. + */ + multiplier = 10000000; + while (numerator > ((u32)-1)/multiplier) { + multiplier /= 2; + denominator /= 2; + } - if (!probe) - return best; + return denominator ? numerator * multiplier / denominator : 0; +} +EXPORT_SYMBOL_GPL(v4l2_fraction_to_interval); - for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) { - error = abs(size->width - width) + abs(size->height - height); - if (error < min_error) { - min_error = error; - best = size; - } - if (!error) +int v4l2_link_freq_to_bitmap(struct device *dev, const u64 *fw_link_freqs, + unsigned int num_of_fw_link_freqs, + const s64 *driver_link_freqs, + unsigned int num_of_driver_link_freqs, + unsigned long *bitmap) +{ + unsigned int i; + + *bitmap = 0; + + if (!num_of_fw_link_freqs) { + dev_err(dev, "no link frequencies in firmware\n"); + return -ENODATA; + } + + for (i = 0; i < num_of_fw_link_freqs; i++) { + unsigned int j; + + for (j = 0; j < num_of_driver_link_freqs; j++) { + if (fw_link_freqs[i] != driver_link_freqs[j]) + continue; + + dev_dbg(dev, "enabling link frequency %lld Hz\n", + driver_link_freqs[j]); + *bitmap |= BIT(j); break; + } } - return best; + if (!*bitmap) { + dev_err(dev, "no matching link frequencies found\n"); + + dev_dbg(dev, "specified in firmware:\n"); + for (i = 0; i < num_of_fw_link_freqs; i++) + dev_dbg(dev, "\t%llu Hz\n", fw_link_freqs[i]); + + dev_dbg(dev, "driver supported:\n"); + for (i = 0; i < num_of_driver_link_freqs; i++) + dev_dbg(dev, "\t%lld Hz\n", driver_link_freqs[i]); + + return -ENOENT; + } + + return 0; } -EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); +EXPORT_SYMBOL_GPL(v4l2_link_freq_to_bitmap); -void v4l2_get_timestamp(struct timeval *tv) +struct clk *__devm_v4l2_sensor_clk_get(struct device *dev, const char *id, + bool legacy, bool fixed_rate, + unsigned long clk_rate) { - struct timespec ts; + bool of_node = is_of_node(dev_fwnode(dev)); + const char *clk_id __free(kfree) = NULL; + struct clk_hw *clk_hw; + struct clk *clk; + u32 rate = clk_rate; + int ret = 0; + + clk = devm_clk_get_optional(dev, id); + if (IS_ERR(clk)) + return clk; + + /* + * If the caller didn't request a fixed rate, retrieve it from the + * clock-frequency property. -EINVAL indicates the property is absent, + * and is not a failure. Other errors, or success with a clock-frequency + * value of 0, are hard failures. + */ + if (!fixed_rate || !clk_rate) { + ret = device_property_read_u32(dev, "clock-frequency", &rate); + if ((ret && ret != -EINVAL) || (!ret && !rate)) + return ERR_PTR(-EINVAL); + } + + if (clk) { + /* + * On non-OF platforms, or when legacy behaviour is requested, + * set the clock rate if a rate has been specified by the caller + * or by the clock-frequency property. + */ + if (rate && (!of_node || legacy)) { + ret = clk_set_rate(clk, rate); + if (ret) { + dev_err(dev, "Failed to set clock rate: %u\n", + rate); + return ERR_PTR(ret); + } + } + return clk; + } + + /* + * Register a dummy fixed clock on non-OF platforms or when legacy + * behaviour is requested. This required the common clock framework. + */ + if (!IS_ENABLED(CONFIG_COMMON_CLK) || (of_node && !legacy)) + return ERR_PTR(-ENOENT); + + /* We need a rate to create a clock. */ + if (ret) + return ERR_PTR(ret == -EINVAL ? -EPROBE_DEFER : ret); + + if (!id) { + clk_id = kasprintf(GFP_KERNEL, "clk-%s", dev_name(dev)); + if (!clk_id) + return ERR_PTR(-ENOMEM); + id = clk_id; + } + + clk_hw = devm_clk_hw_register_fixed_rate(dev, id, NULL, 0, rate); + if (IS_ERR(clk_hw)) + return ERR_CAST(clk_hw); - ktime_get_ts(&ts); - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + return clk_hw->clk; } -EXPORT_SYMBOL_GPL(v4l2_get_timestamp); +EXPORT_SYMBOL_GPL(__devm_v4l2_sensor_clk_get); |
