lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <716bf11b-b1fc-4706-965f-d3975c317c57@gmail.com>
Date: Thu, 3 Jul 2025 15:21:08 +0300
From: Cosmin Tanislav <demonsingur@...il.com>
To: Julien Massot <julien.massot@...labora.com>,
 Cosmin Tanislav <cosmin.tanislav@...log.com>,
 Tomi Valkeinen <tomi.valkeinen+renesas@...asonboard.com>,
 Mauro Carvalho Chehab <mchehab@...nel.org>, Rob Herring <robh@...nel.org>,
 Niklas Söderlund <niklas.soderlund@...natech.se>,
 Sakari Ailus <sakari.ailus@...ux.intel.com>,
 Laurent Pinchart <laurent.pinchart@...asonboard.com>,
 Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
 Linus Walleij <linus.walleij@...aro.org>
Cc: linux-media@...r.kernel.org, devicetree@...r.kernel.org,
 linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
 linux-staging@...ts.linux.dev, linux-gpio@...r.kernel.org
Subject: Re: [PATCH v5 15/24] media: i2c: add Maxim GMSL2/3 serializer and
 deserializer framework



On 7/3/25 3:08 PM, Julien Massot wrote:
> Hi Cosmin,
> 
> On Wed, 2025-07-02 at 16:20 +0300, Cosmin Tanislav wrote:
>> These drivers are meant to be used as a common framework for Maxim
>> GMSL2/3 serializers and deserializers.
>>
>> This framework enables support for the following new features across
>> all the chips:
>>   * Full Streams API support
>>   * .get_frame_desc()
>>   * .get_mbus_config()
>>   * I2C ATR
>>   * automatic GMSL link version negotiation
>>   * automatic stream id selection
>>   * automatic VC remapping
>>   * automatic pixel mode / tunnel mode selection
>>   * automatic double mode selection / data padding
>>   * logging of internal state and chip status registers via .log_status()
>>   * PHY modes
>>   * serializer pinctrl
>>   * TPG
>>
>> Signed-off-by: Cosmin Tanislav <demonsingur@...il.com>
>> ---
>>   MAINTAINERS                                 |   1 +
>>   drivers/media/i2c/Kconfig                   |   2 +
>>   drivers/media/i2c/Makefile                  |   1 +
>>   drivers/media/i2c/maxim-serdes/Kconfig      |  16 +
>>   drivers/media/i2c/maxim-serdes/Makefile     |   3 +
>>   drivers/media/i2c/maxim-serdes/max_serdes.c | 413 ++++++++++++++++++++
>>   drivers/media/i2c/maxim-serdes/max_serdes.h | 183 +++++++++
>>   7 files changed, 619 insertions(+)
>>   create mode 100644 drivers/media/i2c/maxim-serdes/Kconfig
>>   create mode 100644 drivers/media/i2c/maxim-serdes/Makefile
>>   create mode 100644 drivers/media/i2c/maxim-serdes/max_serdes.c
>>   create mode 100644 drivers/media/i2c/maxim-serdes/max_serdes.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 334195989c00..8cd57b66afe3 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -14769,6 +14769,7 @@ M:	Cosmin Tanislav <cosmin.tanislav@...log.com>
>>   L:	linux-media@...r.kernel.org
>>   S:	Maintained
>>   F:	Documentation/devicetree/bindings/media/i2c/maxim,max9296a.yaml
>> +F:	drivers/media/i2c/maxim-serdes/
>>   
>>   MAXIM MAX11205 DRIVER
>>   M:	Ramona Bolboaca <ramona.bolboaca@...log.com>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 4b4c199da6ea..f504016aadfd 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -1700,6 +1700,8 @@ config VIDEO_MAX96717
>>   	  To compile this driver as a module, choose M here: the
>>   	  module will be called max96717.
>>   
>> +source "drivers/media/i2c/maxim-serdes/Kconfig"
>> +
>>   endmenu
>>   
>>   endif # VIDEO_DEV
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 5873d29433ee..25a0093d40ec 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o
>>   obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
>>   obj-$(CONFIG_VIDEO_MAX96714) += max96714.o
>>   obj-$(CONFIG_VIDEO_MAX96717) += max96717.o
>> +obj-$(CONFIG_VIDEO_MAXIM_SERDES) += maxim-serdes/
>>   obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
>>   obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
>>   obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o
>> diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig
>> new file mode 100644
>> index 000000000000..cae1d5a1293e
>> --- /dev/null
>> +++ b/drivers/media/i2c/maxim-serdes/Kconfig
>> @@ -0,0 +1,16 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +
>> +config VIDEO_MAXIM_SERDES
>> +	tristate "Maxim GMSL2/3 Serializer and Deserializer support"
>> +	depends on VIDEO_DEV
>> +	select I2C_ATR
>> +	select I2C_MUX
>> +	select MEDIA_CONTROLLER
>> +	select V4L2_FWNODE
>> +	select VIDEO_V4L2_SUBDEV_API
>> +	help
>> +	  This driver supports the Maxim GMSL2/3 common Serializer and
>> +	  Deserializer framework.
>> +
>> +	  To compile this driver as a module, choose M here: the module
>> +	  will be called max_serdes.
>> diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile
>> new file mode 100644
>> index 000000000000..630fbb486bab
>> --- /dev/null
>> +++ b/drivers/media/i2c/maxim-serdes/Makefile
>> @@ -0,0 +1,3 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +max-serdes-objs := max_serdes.o
>> +obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o
>> diff --git a/drivers/media/i2c/maxim-serdes/max_serdes.c b/drivers/media/i2c/maxim-
>> serdes/max_serdes.c
>> new file mode 100644
>> index 000000000000..73e018d1f0d2
>> --- /dev/null
>> +++ b/drivers/media/i2c/maxim-serdes/max_serdes.c
>> @@ -0,0 +1,413 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2025 Analog Devices Inc.
>> + */
>> +
>> +#include <linux/export.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/stringify.h>
>> +
>> +#include <media/mipi-csi2.h>
>> +
>> +#include <video/videomode.h>
>> +
>> +#include <uapi/linux/media-bus-format.h>
>> +
>> +#include "max_serdes.h"
>> +
>> +const char * const max_serdes_tpg_patterns[] = {
>> +	[MAX_SERDES_TPG_PATTERN_GRADIENT] = "Gradient",
>> +	[MAX_SERDES_TPG_PATTERN_CHECKERBOARD] = "Checkerboard",
>> +};
>> +
>> +static const char * const max_gmsl_versions[] = {
>> +	[MAX_SERDES_GMSL_2_3GBPS] = "GMSL2 3Gbps",
>> +	[MAX_SERDES_GMSL_2_6GBPS] = "GMSL2 6Gbps",
>> +	[MAX_SERDES_GMSL_3] = "GMSL3",
>> +};
> GMSL3 12Gbps for consistency ?
> 

Sure.

>>
>> +
>> +const char *max_serdes_gmsl_version_str(enum max_serdes_gmsl_version version)
>> +{
>> +	if (version > MAX_SERDES_GMSL_3)
>> +		return NULL;
>> +
>> +	return max_gmsl_versions[version];
>> +}
>> +
>> +static const char * const max_gmsl_mode[] = {
>> +	[MAX_SERDES_GMSL_PIXEL_MODE] = "pixel",
>> +	[MAX_SERDES_GMSL_TUNNEL_MODE] = "tunnel",
>> +};
>> +
>> +const char *max_serdes_gmsl_mode_str(enum max_serdes_gmsl_mode mode)
>> +{
>> +	if (mode > MAX_SERDES_GMSL_TUNNEL_MODE)
>> +		return NULL;
>> +
>> +	return max_gmsl_mode[mode];
>> +}
>> +
>> +static const struct max_serdes_mipi_format max_serdes_mipi_formats[] = {
>> +	{ MIPI_CSI2_DT_EMBEDDED_8B, 8 },
>> +	{ MIPI_CSI2_DT_YUV422_8B, 16 },
>> +	{ MIPI_CSI2_DT_YUV422_10B, 20 },
>> +	{ MIPI_CSI2_DT_RGB565, 16 },
>> +	{ MIPI_CSI2_DT_RGB666, 18 },
>> +	{ MIPI_CSI2_DT_RGB888, 24 },
>> +	{ MIPI_CSI2_DT_RAW8, 8 },
>> +	{ MIPI_CSI2_DT_RAW10, 10 },
>> +	{ MIPI_CSI2_DT_RAW12, 12 },
>> +	{ MIPI_CSI2_DT_RAW14, 14 },
>> +	{ MIPI_CSI2_DT_RAW16, 16 },
>> +};
>> +
>> +const struct max_serdes_mipi_format *max_serdes_mipi_format_by_dt(u8 dt)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(max_serdes_mipi_formats); i++)
>> +		if (max_serdes_mipi_formats[i].dt == dt)
>> +			return &max_serdes_mipi_formats[i];
>> +
>> +	return NULL;
>> +}
>> +
>> +int max_serdes_get_fd_stream_entry(struct v4l2_subdev *sd, u32 pad, u32 stream,
>> +				   struct v4l2_mbus_frame_desc_entry *entry)
>> +{
>> +	struct v4l2_mbus_frame_desc fd;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	ret = v4l2_subdev_call(sd, pad, get_frame_desc, pad, &fd);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
>> +		return -EOPNOTSUPP;
>> +
>> +	for (i = 0; i < fd.num_entries; i++) {
>> +		if (fd.entry[i].stream == stream) {
>> +			*entry = fd.entry[i];
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	return -ENOENT;
>> +}
>> +
>> +int max_serdes_get_fd_bpp(struct v4l2_mbus_frame_desc_entry *entry,
>> +			  unsigned int *bpp)
>> +{
>> +	const struct max_serdes_mipi_format *format;
>> +
>> +	format = max_serdes_mipi_format_by_dt(entry->bus.csi2.dt);
>> +	if (!format)
>> +		return -ENOENT;
>> +
>> +	*bpp = format->bpp;
>> +
>> +	return 0;
>> +}
>> +
>> +int max_serdes_process_bpps(struct device *dev, u32 bpps,
>> +			    u32 allowed_double_bpps, unsigned int *doubled_bpp)
>> +{
>> +	unsigned int min_bpp;
>> +	unsigned int max_bpp;
>> +	bool doubled = false;
>> +
>> +	if (!bpps)
>> +		return 0;
>> +
>> +	*doubled_bpp = 0;
>> +
>> +	/*
>> +	 * Hardware can double bpps 8, 10, 12, and it can pad bpps < 16
>> +	 * to another bpp <= 16:
>> +	 * Hardware can only stream a single constant bpp up to 24.
>> +	 *
>> +	 * From these features and limitations, the following rules
>> +	 * can be deduced:
>> +	 *
>> +	 * A bpp of 8 can always be doubled if present.
>> +	 * A bpp of 10 can be doubled only if there are no other bpps or the
>> +	 * only other bpp is 20.
>> +	 * A bpp of 12 can be doubled only if there are no other bpps or the
>> +	 * only other bpp is 24.
>> +	 * Bpps <= 16 cannot coexist with bpps > 16.
>> +	 * Bpps <= 16 need to be padded to the biggest bpp.
>> +	 */
>> +
>> +	min_bpp = __ffs(bpps);
>> +	max_bpp = __fls(bpps);
>> +
>> +	if (min_bpp == 8) {
>> +		doubled = true;
>> +	} else if (min_bpp == 10 || min_bpp == 12) {
>> +		u32 bpp_or_double = BIT(min_bpp) | BIT(min_bpp * 2);
>> +		u32 other_bpps = bpps & ~bpp_or_double;
>> +
>> +		if (!other_bpps)
>> +			doubled = true;
>> +	}
>> +
>> +	if (doubled && (allowed_double_bpps & BIT(min_bpp))) {
>> +		*doubled_bpp = min_bpp;
>> +		bpps &= ~BIT(min_bpp);
>> +		bpps |= BIT(min_bpp * 2);
>> +	}
>> +
>> +	min_bpp = __ffs(bpps);
>> +	max_bpp = __fls(bpps);
>> +
>> +	if (max_bpp > 24) {
>> +		dev_err(dev, "Cannot stream bpps > 24\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (min_bpp <= 16 && max_bpp > 16) {
>> +		dev_err(dev, "Cannot stream bpps <= 16 with bpps > 16\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (max_bpp > 16 && min_bpp != max_bpp) {
>> +		dev_err(dev, "Cannot stream multiple bpps when one is > 16\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int max_serdes_xlate_enable_disable_streams(struct max_serdes_source *sources,
>> +					    u32 source_sink_pad_offset,
>> +					    const struct v4l2_subdev_state *state,
>> +					    u32 pad, u64 updated_streams_mask,
>> +					    u32 sink_pad_start, u32 num_sink_pads,
>> +					    bool enable)
>> +{
>> +	u32 failed_sink_pad;
>> +	int ret;
>> +	u32 i;
>> +
>> +	for (i = sink_pad_start; i < sink_pad_start + num_sink_pads; i++) {
>> +		u64 matched_streams_mask = updated_streams_mask;
>> +		u64 updated_sink_streams_mask;
>> +		struct max_serdes_source *source;
>> +
>> +		updated_sink_streams_mask =
>> +			v4l2_subdev_state_xlate_streams(state, pad, i,
>> +							&matched_streams_mask);
>> +		if (!updated_sink_streams_mask)
>> +			continue;
>> +
>> +		source = &sources[i + source_sink_pad_offset];
>> +		if (!source)
>> +			continue;
>> +
>> +		if (enable)
>> +			ret = v4l2_subdev_enable_streams(source->sd, source->pad,
>> +							 updated_sink_streams_mask);
>> +		else
>> +			ret = v4l2_subdev_disable_streams(source->sd, source->pad,
>> +							  updated_sink_streams_mask);
>> +		if (ret) {
>> +			failed_sink_pad = i;
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	for (i = sink_pad_start; i < failed_sink_pad; i++) {
>> +		u64 matched_streams_mask = updated_streams_mask;
>> +		u64 updated_sink_streams_mask;
>> +		struct max_serdes_source *source;
>> +
>> +		updated_sink_streams_mask =
>> +			v4l2_subdev_state_xlate_streams(state, pad, i,
>> +							&matched_streams_mask);
>> +		if (!updated_sink_streams_mask)
>> +			continue;
>> +
>> +		source = &sources[i + source_sink_pad_offset];
>> +		if (!source)
>> +			continue;
>> +
>> +		if (!enable)
>> +			v4l2_subdev_enable_streams(source->sd, source->pad,
>> +						   updated_sink_streams_mask);
>> +		else
>> +			v4l2_subdev_disable_streams(source->sd, source->pad,
>> +						    updated_sink_streams_mask);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +int max_serdes_get_streams_masks(struct device *dev,
>> +				 const struct v4l2_subdev_state *state,
>> +				 u32 pad, u64 updated_streams_mask,
>> +				 u32 num_pads, u64 *old_streams_masks,
>> +				 u64 **new_streams_masks, bool enable)
>> +{
>> +	u64 *streams_masks;
>> +	unsigned int i;
>> +
>> +	streams_masks = devm_kcalloc(dev, num_pads, sizeof(*streams_masks), GFP_KERNEL);
>> +	if (!streams_masks)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < num_pads; i++) {
>> +		u64 matched_streams_mask = updated_streams_mask;
>> +		u64 updated_sink_streams_mask;
>> +
>> +		updated_sink_streams_mask =
>> +			v4l2_subdev_state_xlate_streams(state, pad, i,
>> +							&matched_streams_mask);
>> +		if (!updated_sink_streams_mask)
>> +			continue;
>> +
>> +		streams_masks[i] = old_streams_masks[i];
>> +		if (enable)
>> +			streams_masks[i] |= updated_sink_streams_mask;
>> +		else
>> +			streams_masks[i] &= ~updated_sink_streams_mask;
>> +	}
>> +
>> +	if (enable)
>> +		streams_masks[pad] |= updated_streams_mask;
>> +	else
>> +		streams_masks[pad] &= ~updated_streams_mask;
>> +
>> +	*new_streams_masks = streams_masks;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct videomode max_serdes_tpg_pixel_videomodes[] = {
>> +	{
>> +		.pixelclock = 25000000,
>> +		.hactive = 640,
>> +		.hfront_porch = 10,
>> +		.hsync_len = 96,
>> +		.hback_porch = 40,
>> +		.vactive = 480,
>> +		.vfront_porch = 2,
>> +		.vsync_len = 24,
>> +		.vback_porch = 24,
>> +	},
>> +	{
>> +		.pixelclock = 75000000,
>> +		.hactive = 1920,
>> +		.hfront_porch = 88,
>> +		.hsync_len = 44,
>> +		.hback_porch = 148,
>> +		.vactive = 1080,
>> +		.vfront_porch = 4,
>> +		.vsync_len = 16,
>> +		.vback_porch = 36,
>> +	},
>> +	{
>> +		.pixelclock = 150000000,
>> +		.hactive = 1920,
>> +		.hfront_porch = 88,
>> +		.hsync_len = 44,
>> +		.hback_porch = 148,
>> +		.vactive = 1080,
>> +		.vfront_porch = 4,
>> +		.vsync_len = 16,
>> +		.vback_porch = 36,
>> +	},
>> +};
>> +
>> +static void max_serdes_get_vm_timings(const struct videomode *vm,
>> +				      struct max_serdes_tpg_timings *timings)
>> +{
>> +	u32 hact = vm->hactive;
>> +	u32 hfp = vm->hfront_porch;
>> +	u32 hsync = vm->hsync_len;
>> +	u32 hbp = vm->hback_porch;
>> +	u32 htot = hact + hfp + hbp + hsync;
>> +
>> +	u32 vact = vm->vactive;
>> +	u32 vfp = vm->vfront_porch;
>> +	u32 vsync = vm->vsync_len;
>> +	u32 vbp = vm->vback_porch;
>> +	u32 vtot = vact + vfp + vbp + vsync;
>> +
>> +	*timings = (struct max_serdes_tpg_timings) {
>> +		.gen_vs = true,
>> +		.gen_hs = true,
>> +		.gen_de = true,
>> +		.vs_inv = true,
>> +		.vs_dly = 0,
>> +		.vs_high = vsync * htot,
>> +		.vs_low = (vact + vfp + vbp) * htot,
>> +		.v2h = 0,
>> +		.hs_high = hsync,
>> +		.hs_low = hact + hfp + hbp,
>> +		.hs_cnt = vact + vfp + vbp + vsync,
>> +		.v2d = htot * (vsync + vbp) + (hsync + hbp),
>> +		.de_high = hact,
>> +		.de_low = hfp + hsync + hbp,
>> +		.de_cnt = vact,
>> +		.clock = vm->pixelclock,
>> +		.fps = DIV_ROUND_CLOSEST(vm->pixelclock, vtot * htot),
>> +	};
>> +}
>> +
>> +int max_serdes_get_tpg_timings(const struct max_serdes_tpg_entry *entry,
>> +			       struct max_serdes_tpg_timings *timings)
>> +{
>> +	u32 fps;
>> +
>> +	if (!entry)
>> +		return 0;
>> +
>> +	fps = DIV_ROUND_CLOSEST(1 * entry->interval.denominator,
>> +				entry->interval.numerator);
>> +
>> +	for (unsigned int i = 0; i < ARRAY_SIZE(max_serdes_tpg_pixel_videomodes); i++) {
>> +		struct max_serdes_tpg_timings vm_timings;
>> +		const struct videomode *vm;
>> +
>> +		vm = &max_serdes_tpg_pixel_videomodes[i];
>> +
>> +		max_serdes_get_vm_timings(vm, &vm_timings);
>> +
>> +		if (vm->hactive == entry->width &&
>> +		    vm->vactive == entry->height &&
>> +		    vm_timings.fps == fps) {
>> +			*timings = vm_timings;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +EXPORT_SYMBOL_GPL(max_serdes_get_tpg_timings);
>> +
>> +int max_serdes_validate_tpg_routing(struct v4l2_subdev_krouting *routing)
>> +{
>> +	const struct v4l2_subdev_route *route;
>> +
>> +	if (routing->num_routes != 1)
>> +		return -EINVAL;
>> +
>> +	route = &routing->routes[0];
>> +
>> +	if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
>> +		return -EINVAL;
>> +
>> +	if (route->sink_stream != MAX_SERDES_TPG_STREAM)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +MODULE_DESCRIPTION("Maxim GMSL2 Serializer/Deserializer Driver");
>> +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@...log.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/i2c/maxim-serdes/max_serdes.h b/drivers/media/i2c/maxim-
>> serdes/max_serdes.h
>> new file mode 100644
>> index 000000000000..fdd761fcd8b2
>> --- /dev/null
>> +++ b/drivers/media/i2c/maxim-serdes/max_serdes.h
>> @@ -0,0 +1,183 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2025 Analog Devices Inc.
>> + */
>> +
>> +#ifndef MAX_SERDES_H
>> +#define MAX_SERDES_H
>> +
>> +#include <linux/types.h>
>> +
>> +#include <media/mipi-csi2.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define REG_SEQUENCE_2(reg, val) \
>> +	{ (reg),     ((val) >> 8) & 0xff }, \
>> +	{ (reg) + 1, ((val) >> 0) & 0xff }
>> +
>> +#define REG_SEQUENCE_3(reg, val) \
>> +	{ (reg),     ((val) >> 16) & 0xff }, \
>> +	{ (reg) + 1, ((val) >> 8)  & 0xff }, \
>> +	{ (reg) + 2, ((val) >> 0)  & 0xff }
>> +
>> +#define REG_SEQUENCE_3_LE(reg, val) \
>> +	{ (reg),     ((val) >> 0) & 0xff }, \
>> +	{ (reg) + 1, ((val) >> 8)  & 0xff }, \
>> +	{ (reg) + 2, ((val) >> 16)  & 0xff }
>> +
>>
>> cci helper handle that nicely.

I don't really want to change to CCI helpers for a handful of registers
when the rest have 8-bit values. And the LE ones would still need to
have their bytes flipped.

>>
>> +#define field_get(mask, val) (((val) & (mask)) >> __ffs(mask))
>> +#define field_prep(mask, val) (((val) << __ffs(mask)) & (mask))
>> +
> At some place you use FIELD_PREP some other field_prep, this is confusing
> can you use only the FIELD_GET/PREP ?
> 

FIELD_GET/PREP require constant masks, sometimes we don't have that.
These helpers are present in other files too.

rg "#define field_get" -l | wc -l
9

> 
>> +#define MAX_SERDES_PHYS_MAX		4
>> +#define MAX_SERDES_STREAMS_NUM		4
>> +#define MAX_SERDES_VC_ID_NUM		4
>> +#define MAX_SERDES_TPG_STREAM		0
>> +
>> +#define MAX_SERDES_GRAD_INCR		4
>> +#define MAX_SERDES_CHECKER_COLOR_A	0x00ccfe
>> +#define MAX_SERDES_CHECKER_COLOR_B	0xa76a00
>> +#define MAX_SERDES_CHECKER_SIZE		60
>> +
>> +extern const char * const max_serdes_tpg_patterns[];
>> +
>> +enum max_serdes_gmsl_version {
>> +	MAX_SERDES_GMSL_MIN,
>> +	MAX_SERDES_GMSL_2_3GBPS = MAX_SERDES_GMSL_MIN,
>> +	MAX_SERDES_GMSL_2_6GBPS,
>> +	MAX_SERDES_GMSL_3,
>> +	MAX_SERDES_GMSL_MAX = MAX_SERDES_GMSL_3,
>> +};
>> +
>> +enum max_serdes_gmsl_mode {
>> +	MAX_SERDES_GMSL_PIXEL_MODE,
>> +	MAX_SERDES_GMSL_TUNNEL_MODE,
>> +};
>> +
>> +enum max_serdes_tpg_pattern {
>> +	MAX_SERDES_TPG_PATTERN_MIN,
>> +	MAX_SERDES_TPG_PATTERN_CHECKERBOARD = MAX_SERDES_TPG_PATTERN_MIN,
>> +	MAX_SERDES_TPG_PATTERN_GRADIENT,
>> +	MAX_SERDES_TPG_PATTERN_MAX = MAX_SERDES_TPG_PATTERN_GRADIENT,
>> +};
>> +
>> +struct max_serdes_phys_config {
>> +	unsigned int lanes[MAX_SERDES_PHYS_MAX];
>> +	unsigned int clock_lane[MAX_SERDES_PHYS_MAX];
>> +};
>> +
>> +struct max_serdes_phys_configs {
>> +	const struct max_serdes_phys_config *configs;
>> +	unsigned int num_configs;
>> +};
>> +
>> +struct max_serdes_i2c_xlate {
>> +	u8 src;
>> +	u8 dst;
>> +	bool en;
>> +};
>> +
>> +struct max_serdes_mipi_format {
>> +	u8 dt;
>> +	u8 bpp;
>> +};
>> +
>> +struct max_serdes_vc_remap {
>> +	u8 src;
>> +	u8 dst;
>> +};
>> +
>> +struct max_serdes_source {
>> +	struct v4l2_subdev *sd;
>> +	u16 pad;
>> +	struct fwnode_handle *ep_fwnode;
>> +
>> +	unsigned int index;
>> +};
>> +
>> +struct max_serdes_asc {
>> +	struct v4l2_async_connection base;
>> +	struct max_serdes_source *source;
>> +};
>> +
>> +struct max_serdes_tpg_entry {
>> +	u32 width;
>> +	u32 height;
>> +	struct v4l2_fract interval;
>> +	u32 code;
>> +	u8 dt;
>> +	u8 bpp;
>> +};
>> +
>> +#define MAX_TPG_ENTRY_640X480P60_RGB888 \
>> +	{ 640, 480, { 1, 60 }, MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888, 24 }
>> +
>> +#define MAX_TPG_ENTRY_1920X1080P30_RGB888 \
>> +	{ 1920, 1080, { 1, 30 }, MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888, 24 }
>> +
>> +#define MAX_TPG_ENTRY_1920X1080P60_RGB888 \
>> +	{ 1920, 1080, { 1, 60 }, MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888, 24 }
>> +
>> +struct max_serdes_tpg_entries {
>> +	const struct max_serdes_tpg_entry *entries;
>> +	unsigned int num_entries;
>> +};
>> +
>> +struct max_serdes_tpg_timings {
>> +	bool gen_vs;
>> +	bool gen_hs;
>> +	bool gen_de;
>> +	bool vs_inv;
>> +	bool hs_inv;
>> +	bool de_inv;
>> +	u32 vs_dly;
>> +	u32 vs_high;
>> +	u32 vs_low;
>> +	u32 v2h;
>> +	u32 hs_high;
>> +	u32 hs_low;
>> +	u32 hs_cnt;
>> +	u32 v2d;
>> +	u32 de_high;
>> +	u32 de_low;
>> +	u32 de_cnt;
>> +	u32 clock;
>> +	u32 fps;
>> +};
>> +
>> +static inline struct max_serdes_asc *asc_to_max(struct v4l2_async_connection *asc)
>> +{
>> +	return container_of(asc, struct max_serdes_asc, base);
>> +}
>> +
>> +const char *max_serdes_gmsl_version_str(enum max_serdes_gmsl_version version);
>> +const char *max_serdes_gmsl_mode_str(enum max_serdes_gmsl_mode mode);
>> +
>> +const struct max_serdes_mipi_format *max_serdes_mipi_format_by_dt(u8 dt);
>> +
>> +int max_serdes_get_fd_stream_entry(struct v4l2_subdev *sd, u32 pad, u32 stream,
>> +				   struct v4l2_mbus_frame_desc_entry *entry);
>> +
>> +int max_serdes_get_fd_bpp(struct v4l2_mbus_frame_desc_entry *entry,
>> +			  unsigned int *bpp);
>> +int max_serdes_process_bpps(struct device *dev, u32 bpps,
>> +			    u32 allowed_double_bpps, unsigned int *doubled_bpp);
>> +
>> +int max_serdes_xlate_enable_disable_streams(struct max_serdes_source *sources,
>> +					    u32 source_sink_pad_offset,
>> +					    const struct v4l2_subdev_state *state,
>> +					    u32 pad, u64 updated_streams_mask,
>> +					    u32 sink_pad_start, u32 num_sink_pads,
>> +					    bool enable);
>> +
>> +int max_serdes_get_streams_masks(struct device *dev,
>> +				 const struct v4l2_subdev_state *state,
>> +				 u32 pad, u64 updated_streams_mask,
>> +				 u32 num_pads, u64 *old_streams_masks,
>> +				 u64 **new_streams_masks, bool enable);
>> +
>> +int max_serdes_get_tpg_timings(const struct max_serdes_tpg_entry *entry,
>> +			       struct max_serdes_tpg_timings *timings);
>> +
>> +int max_serdes_validate_tpg_routing(struct v4l2_subdev_krouting *routing);
>> +
>> +#endif // MAX_SERDES_H
> 


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ