get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2220298/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2220298,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2220298/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260406-gmsl2-3_serdes-v10-13-645560fedca5@analog.com/",
    "project": {
        "id": 42,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/42/?format=api",
        "name": "Linux GPIO development",
        "link_name": "linux-gpio",
        "list_id": "linux-gpio.vger.kernel.org",
        "list_email": "linux-gpio@vger.kernel.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260406-gmsl2-3_serdes-v10-13-645560fedca5@analog.com>",
    "date": "2026-04-06T20:14:52",
    "name": "[v10,13/22] media: i2c: add Maxim GMSL2/3 serializer and deserializer framework",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "5a3515d195002d0d2d6aaad0443d39c79adb1ec9",
    "submitter": {
        "id": 88270,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/88270/?format=api",
        "name": "Dumitru Ceclan via B4 Relay",
        "email": "devnull+dumitru.ceclan.analog.com@kernel.org"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260406-gmsl2-3_serdes-v10-13-645560fedca5@analog.com/mbox/",
    "series": [
        {
            "id": 498894,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/498894/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/list/?series=498894",
            "date": "2026-04-06T20:14:42",
            "name": "media: i2c: add Maxim GMSL2/3 serializer and deserializer drivers",
            "version": 10,
            "mbox": "http://patchwork.ozlabs.org/series/498894/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2220298/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2220298/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <linux-gpio+bounces-34734-incoming=patchwork.ozlabs.org@vger.kernel.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "linux-gpio@vger.kernel.org"
        ],
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=oI7P+ebv;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34734-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)",
            "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"oI7P+ebv\"",
            "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201"
        ],
        "Received": [
            "from tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fqLJT3zCbz1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 07 Apr 2026 06:19:05 +1000 (AEST)",
            "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 7E95E3098B96\n\tfor <incoming@patchwork.ozlabs.org>; Mon,  6 Apr 2026 20:15:51 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 508933A2540;\n\tMon,  6 Apr 2026 20:15:23 +0000 (UTC)",
            "from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 4441339E6C9;\n\tMon,  6 Apr 2026 20:15:22 +0000 (UTC)",
            "by smtp.kernel.org (Postfix) with ESMTPS id 297DBC19425;\n\tMon,  6 Apr 2026 20:15:22 +0000 (UTC)",
            "from aws-us-west-2-korg-lkml-1.web.codeaurora.org\n (localhost.localdomain [127.0.0.1])\n\tby smtp.lore.kernel.org (Postfix) with ESMTP id 20C94EF4ECE;\n\tMon,  6 Apr 2026 20:15:22 +0000 (UTC)"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775506522; cv=none;\n b=iJqG9d5xzmf6/BvkUQuqD6zs+BNHRLasNhPHDJIOcRz14mdbSSI8RyzGefWyXbE+o53Ad2gevYw+XY2kHtZjtqc9fGz7B7huP57jNLVCBdCgQBFyo9mYzDbmZzgeaoF+d+vuGeJOVSZfvtynz0u9l7o63m+9uo2lL18FgXYpZFA=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775506522; c=relaxed/simple;\n\tbh=VDql+Hfavigfh9ovTYOy1u+FLCxCrHF81DbvX/UyK60=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=ed+qSGv7x+n8zGQqhb+otdVEOpuCSypBwPf18HkXlNf1PXP7i0H22ljmI+WXhqmwLtziVhM+VDVNXIG6pcCQ4bXt/8n2PltP8b9UqbK9AK2MUCvE8ZWAAfRzbRKN5ebhk2qHeL2ZgpDQ69MPiInMj6+3geSz+zDQrB5FISv6zkc=",
        "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=oI7P+ebv; arc=none smtp.client-ip=10.30.226.201",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1775506522;\n\tbh=VDql+Hfavigfh9ovTYOy1u+FLCxCrHF81DbvX/UyK60=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From;\n\tb=oI7P+ebvHwheuv+n0CVgAYXRWsZZ08lYCgCfP6YKhdIaLHPY/IFOovvfJLWUvA+/H\n\t ogT1zfrO1mjRH/c2rth1IkodTAfEZuLrVJMRWPvAPMhSrPLM32tcb5uO9iNrMvkfaV\n\t JoGXKcvxwxWEdjYubupW39dOpc1fRVbe6fEysqa1Ay95+4JdrJeMh9DC3GsvJMAoAs\n\t 3quy9ixpJWamWwGx1lz/ZehxNso0Lqu/0k2RKQPnMJvZfihRT1bCn7Vy21+j85wKhh\n\t 1bY25T9zfbT/PPeyr+AYpdQbA7LtfITQ2QXSB3nKnlWQ9O5yXjDaiuqY/jSjab0KwJ\n\t fFzAj4sw7aTlQ==",
        "From": "Dumitru Ceclan via B4 Relay\n <devnull+dumitru.ceclan.analog.com@kernel.org>",
        "Date": "Mon, 06 Apr 2026 23:14:52 +0300",
        "Subject": "[PATCH v10 13/22] media: i2c: add Maxim GMSL2/3 serializer and\n deserializer framework",
        "Precedence": "bulk",
        "X-Mailing-List": "linux-gpio@vger.kernel.org",
        "List-Id": "<linux-gpio.vger.kernel.org>",
        "List-Subscribe": "<mailto:linux-gpio+subscribe@vger.kernel.org>",
        "List-Unsubscribe": "<mailto:linux-gpio+unsubscribe@vger.kernel.org>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "7bit",
        "Message-Id": "<20260406-gmsl2-3_serdes-v10-13-645560fedca5@analog.com>",
        "References": "<20260406-gmsl2-3_serdes-v10-0-645560fedca5@analog.com>",
        "In-Reply-To": "<20260406-gmsl2-3_serdes-v10-0-645560fedca5@analog.com>",
        "To": "Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>,\n  Mauro Carvalho Chehab <mchehab@kernel.org>,\n  Sakari Ailus <sakari.ailus@linux.intel.com>,\n  Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n  Julien Massot <julien.massot@collabora.com>, Rob Herring <robh@kernel.org>,\n\t=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>,\n  Greg Kroah-Hartman <gregkh@linuxfoundation.org>,\n  Cosmin Tanislav <cosmin.tanislav@analog.com>",
        "Cc": "mitrutzceclan@gmail.com, linux-media@vger.kernel.org,\n  linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,\n  linux-staging@lists.linux.dev, linux-gpio@vger.kernel.org, =?utf-8?q?Nikla?=\n\t=?utf-8?q?s_S=C3=B6derlund?= <niklas.soderlund+renesas@ragnatech.se>,\n  Martin Hecht <Martin.Hecht@avnet.eu>,\n  Cosmin Tanislav <demonsingur@gmail.com>",
        "X-Mailer": "b4 0.14.3",
        "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1775506518; l=19564;\n i=dumitru.ceclan@analog.com; s=20240313; h=from:subject:message-id;\n bh=22zVA0S1E2kxUS8lsopB3O7rVP2X5lwDlJqeQf9rdVA=;\n b=uKWr89dUcsgNqhn3XJlQ3JrbytvD6DLVy84Ze/CoysIJ9XMmTqvSNRSsW+mQVXjMOfPg+0y+4\n Q8xLPKKZFgfD+kcirIsnMMqywfwbhXma8EcGxrDLXWHQF34xEFaUW84",
        "X-Developer-Key": "i=dumitru.ceclan@analog.com; a=ed25519;\n pk=HdqMlVyrcazwoiai7oN6ghU+Bj1pusGUFRl30jhS7Bo=",
        "X-Endpoint-Received": "by B4 Relay for dumitru.ceclan@analog.com/20240313\n with auth_id=140",
        "X-Original-From": "Dumitru Ceclan <dumitru.ceclan@analog.com>",
        "Reply-To": "dumitru.ceclan@analog.com"
    },
    "content": "From: Cosmin Tanislav <demonsingur@gmail.com>\n\nThese drivers are meant to be used as a common framework for Maxim\nGMSL2/3 serializers and deserializers.\n\nThis framework enables support for the following new features across\nall the chips:\n * Full Streams API support\n * .get_frame_desc()\n * .get_mbus_config()\n * I2C ATR\n * automatic GMSL link version negotiation\n * automatic stream id selection\n * automatic VC remapping\n * automatic pixel mode / tunnel mode selection\n * automatic double mode selection / data padding\n * logging of internal state and chip status registers via .log_status()\n * PHY modes\n * serializer pinctrl\n * TPG\n\nSigned-off-by: Cosmin Tanislav <demonsingur@gmail.com>\n---\n MAINTAINERS                                 |   1 +\n drivers/media/i2c/Kconfig                   |   2 +\n drivers/media/i2c/Makefile                  |   1 +\n drivers/media/i2c/maxim-serdes/Kconfig      |  17 ++\n drivers/media/i2c/maxim-serdes/Makefile     |   3 +\n drivers/media/i2c/maxim-serdes/max_serdes.c | 413 ++++++++++++++++++++++++++++\n drivers/media/i2c/maxim-serdes/max_serdes.h | 183 ++++++++++++\n 7 files changed, 620 insertions(+)",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 5ae68688008d..70d3eeef8bfe 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -15257,6 +15257,7 @@ M:\tCosmin Tanislav <cosmin.tanislav@analog.com>\n L:\tlinux-media@vger.kernel.org\n S:\tMaintained\n F:\tDocumentation/devicetree/bindings/media/i2c/maxim,max9296a.yaml\n+F:\tdrivers/media/i2c/maxim-serdes/\n \n MAXIM MAX11205 DRIVER\n M:\tRamona Bolboaca <ramona.bolboaca@analog.com>\ndiff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig\nindex cdd7ba5da0d5..37f86e6de969 100644\n--- a/drivers/media/i2c/Kconfig\n+++ b/drivers/media/i2c/Kconfig\n@@ -1718,6 +1718,8 @@ config VIDEO_MAX96717\n \t  To compile this driver as a module, choose M here: the\n \t  module will be called max96717.\n \n+source \"drivers/media/i2c/maxim-serdes/Kconfig\"\n+\n endmenu\n \n endif # VIDEO_DEV\ndiff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile\nindex 57cdd8dc96f6..be3200b23819 100644\n--- a/drivers/media/i2c/Makefile\n+++ b/drivers/media/i2c/Makefile\n@@ -71,6 +71,7 @@ obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o\n obj-$(CONFIG_VIDEO_MAX9286) += max9286.o\n obj-$(CONFIG_VIDEO_MAX96714) += max96714.o\n obj-$(CONFIG_VIDEO_MAX96717) += max96717.o\n+obj-$(CONFIG_VIDEO_MAXIM_SERDES) += maxim-serdes/\n obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o\n obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o\n obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o\ndiff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig\nnew file mode 100644\nindex 000000000000..f5a4ca80a263\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/Kconfig\n@@ -0,0 +1,17 @@\n+# SPDX-License-Identifier: GPL-2.0\n+\n+config VIDEO_MAXIM_SERDES\n+\ttristate \"Maxim GMSL2/3 Serializer and Deserializer support\"\n+\tdepends on VIDEO_DEV\n+\tdepends on I2C\n+\tselect I2C_ATR\n+\tselect I2C_MUX\n+\tselect MEDIA_CONTROLLER\n+\tselect V4L2_FWNODE\n+\tselect VIDEO_V4L2_SUBDEV_API\n+\thelp\n+\t  This driver supports the Maxim GMSL2/3 common Serializer and\n+\t  Deserializer framework.\n+\n+\t  To compile this driver as a module, choose M here: the module\n+\t  will be called max_serdes.\ndiff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile\nnew file mode 100644\nindex 000000000000..630fbb486bab\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/Makefile\n@@ -0,0 +1,3 @@\n+# SPDX-License-Identifier: GPL-2.0\n+max-serdes-objs := max_serdes.o\n+obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o\ndiff --git a/drivers/media/i2c/maxim-serdes/max_serdes.c b/drivers/media/i2c/maxim-serdes/max_serdes.c\nnew file mode 100644\nindex 000000000000..bed70b8ce99a\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/max_serdes.c\n@@ -0,0 +1,413 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Copyright (C) 2025 Analog Devices Inc.\n+ */\n+\n+#include <linux/export.h>\n+#include <linux/kernel.h>\n+#include <linux/module.h>\n+#include <linux/stringify.h>\n+\n+#include <media/mipi-csi2.h>\n+\n+#include <video/videomode.h>\n+\n+#include <uapi/linux/media-bus-format.h>\n+\n+#include \"max_serdes.h\"\n+\n+const char * const max_serdes_tpg_patterns[] = {\n+\t[MAX_SERDES_TPG_PATTERN_GRADIENT] = \"Gradient\",\n+\t[MAX_SERDES_TPG_PATTERN_CHECKERBOARD] = \"Checkerboard\",\n+};\n+\n+static const char * const max_gmsl_versions[] = {\n+\t[MAX_SERDES_GMSL_2_3GBPS] = \"GMSL2 3Gbps\",\n+\t[MAX_SERDES_GMSL_2_6GBPS] = \"GMSL2 6Gbps\",\n+\t[MAX_SERDES_GMSL_3_12GBPS] = \"GMSL3 12Gbps\",\n+};\n+\n+const char *max_serdes_gmsl_version_str(enum max_serdes_gmsl_version version)\n+{\n+\tif (version > MAX_SERDES_GMSL_3_12GBPS)\n+\t\treturn NULL;\n+\n+\treturn max_gmsl_versions[version];\n+}\n+\n+static const char * const max_gmsl_mode[] = {\n+\t[MAX_SERDES_GMSL_PIXEL_MODE] = \"pixel\",\n+\t[MAX_SERDES_GMSL_TUNNEL_MODE] = \"tunnel\",\n+};\n+\n+const char *max_serdes_gmsl_mode_str(enum max_serdes_gmsl_mode mode)\n+{\n+\tif (mode > MAX_SERDES_GMSL_TUNNEL_MODE)\n+\t\treturn NULL;\n+\n+\treturn max_gmsl_mode[mode];\n+}\n+\n+static const struct max_serdes_mipi_format max_serdes_mipi_formats[] = {\n+\t{ MIPI_CSI2_DT_EMBEDDED_8B, 8 },\n+\t{ MIPI_CSI2_DT_YUV422_8B, 16 },\n+\t{ MIPI_CSI2_DT_YUV422_10B, 20 },\n+\t{ MIPI_CSI2_DT_RGB565, 16 },\n+\t{ MIPI_CSI2_DT_RGB666, 18 },\n+\t{ MIPI_CSI2_DT_RGB888, 24 },\n+\t{ MIPI_CSI2_DT_RAW8, 8 },\n+\t{ MIPI_CSI2_DT_RAW10, 10 },\n+\t{ MIPI_CSI2_DT_RAW12, 12 },\n+\t{ MIPI_CSI2_DT_RAW14, 14 },\n+\t{ MIPI_CSI2_DT_RAW16, 16 },\n+};\n+\n+const struct max_serdes_mipi_format *max_serdes_mipi_format_by_dt(u8 dt)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(max_serdes_mipi_formats); i++)\n+\t\tif (max_serdes_mipi_formats[i].dt == dt)\n+\t\t\treturn &max_serdes_mipi_formats[i];\n+\n+\treturn NULL;\n+}\n+\n+int max_serdes_get_fd_stream_entry(struct v4l2_subdev *sd, u32 pad, u32 stream,\n+\t\t\t\t   struct v4l2_mbus_frame_desc_entry *entry)\n+{\n+\tstruct v4l2_mbus_frame_desc fd;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tret = v4l2_subdev_call(sd, pad, get_frame_desc, pad, &fd);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tfor (i = 0; i < fd.num_entries; i++) {\n+\t\tif (fd.entry[i].stream == stream) {\n+\t\t\t*entry = fd.entry[i];\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\treturn -ENOENT;\n+}\n+\n+int max_serdes_get_fd_bpp(struct v4l2_mbus_frame_desc_entry *entry,\n+\t\t\t  unsigned int *bpp)\n+{\n+\tconst struct max_serdes_mipi_format *format;\n+\n+\tformat = max_serdes_mipi_format_by_dt(entry->bus.csi2.dt);\n+\tif (!format)\n+\t\treturn -ENOENT;\n+\n+\t*bpp = format->bpp;\n+\n+\treturn 0;\n+}\n+\n+int max_serdes_process_bpps(struct device *dev, u32 bpps,\n+\t\t\t    u32 allowed_double_bpps, unsigned int *doubled_bpp)\n+{\n+\tunsigned int min_bpp;\n+\tunsigned int max_bpp;\n+\tbool doubled = false;\n+\n+\tif (!bpps)\n+\t\treturn 0;\n+\n+\t*doubled_bpp = 0;\n+\n+\t/*\n+\t * Hardware can double bpps 8, 10, 12, and it can pad bpps < 16\n+\t * to another bpp <= 16:\n+\t * Hardware can only stream a single constant bpp up to 24.\n+\t *\n+\t * From these features and limitations, the following rules\n+\t * can be deduced:\n+\t *\n+\t * A bpp of 8 can always be doubled if present.\n+\t * A bpp of 10 can be doubled only if there are no other bpps or the\n+\t * only other bpp is 20.\n+\t * A bpp of 12 can be doubled only if there are no other bpps or the\n+\t * only other bpp is 24.\n+\t * Bpps <= 16 cannot coexist with bpps > 16.\n+\t * Bpps <= 16 need to be padded to the biggest bpp.\n+\t */\n+\n+\tmin_bpp = __ffs(bpps);\n+\tmax_bpp = __fls(bpps);\n+\n+\tif (min_bpp == 8) {\n+\t\tdoubled = true;\n+\t} else if (min_bpp == 10 || min_bpp == 12) {\n+\t\tu32 bpp_or_double = BIT(min_bpp) | BIT(min_bpp * 2);\n+\t\tu32 other_bpps = bpps & ~bpp_or_double;\n+\n+\t\tif (!other_bpps)\n+\t\t\tdoubled = true;\n+\t}\n+\n+\tif (doubled && (allowed_double_bpps & BIT(min_bpp))) {\n+\t\t*doubled_bpp = min_bpp;\n+\t\tbpps &= ~BIT(min_bpp);\n+\t\tbpps |= BIT(min_bpp * 2);\n+\t}\n+\n+\tmin_bpp = __ffs(bpps);\n+\tmax_bpp = __fls(bpps);\n+\n+\tif (max_bpp > 24) {\n+\t\tdev_err(dev, \"Cannot stream bpps > 24\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (min_bpp <= 16 && max_bpp > 16) {\n+\t\tdev_err(dev, \"Cannot stream bpps <= 16 with bpps > 16\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (max_bpp > 16 && min_bpp != max_bpp) {\n+\t\tdev_err(dev, \"Cannot stream multiple bpps when one is > 16\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int max_serdes_xlate_enable_disable_streams(struct max_serdes_source *sources,\n+\t\t\t\t\t    u32 source_sink_pad_offset,\n+\t\t\t\t\t    const struct v4l2_subdev_state *state,\n+\t\t\t\t\t    u32 pad, u64 updated_streams_mask,\n+\t\t\t\t\t    u32 sink_pad_start, u32 num_sink_pads,\n+\t\t\t\t\t    bool enable)\n+{\n+\tu32 failed_sink_pad;\n+\tint ret;\n+\tu32 i;\n+\n+\tfor (i = sink_pad_start; i < sink_pad_start + num_sink_pads; i++) {\n+\t\tu64 matched_streams_mask = updated_streams_mask;\n+\t\tu64 updated_sink_streams_mask;\n+\t\tstruct max_serdes_source *source;\n+\n+\t\tupdated_sink_streams_mask =\n+\t\t\tv4l2_subdev_state_xlate_streams(state, pad, i,\n+\t\t\t\t\t\t\t&matched_streams_mask);\n+\t\tif (!updated_sink_streams_mask)\n+\t\t\tcontinue;\n+\n+\t\tsource = &sources[i + source_sink_pad_offset];\n+\t\tif (!source)\n+\t\t\tcontinue;\n+\n+\t\tif (enable)\n+\t\t\tret = v4l2_subdev_enable_streams(source->sd, source->pad,\n+\t\t\t\t\t\t\t updated_sink_streams_mask);\n+\t\telse\n+\t\t\tret = v4l2_subdev_disable_streams(source->sd, source->pad,\n+\t\t\t\t\t\t\t  updated_sink_streams_mask);\n+\t\tif (ret) {\n+\t\t\tfailed_sink_pad = i;\n+\t\t\tgoto err;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+\n+err:\n+\tfor (i = sink_pad_start; i < failed_sink_pad; i++) {\n+\t\tu64 matched_streams_mask = updated_streams_mask;\n+\t\tu64 updated_sink_streams_mask;\n+\t\tstruct max_serdes_source *source;\n+\n+\t\tupdated_sink_streams_mask =\n+\t\t\tv4l2_subdev_state_xlate_streams(state, pad, i,\n+\t\t\t\t\t\t\t&matched_streams_mask);\n+\t\tif (!updated_sink_streams_mask)\n+\t\t\tcontinue;\n+\n+\t\tsource = &sources[i + source_sink_pad_offset];\n+\t\tif (!source)\n+\t\t\tcontinue;\n+\n+\t\tif (!enable)\n+\t\t\tv4l2_subdev_enable_streams(source->sd, source->pad,\n+\t\t\t\t\t\t   updated_sink_streams_mask);\n+\t\telse\n+\t\t\tv4l2_subdev_disable_streams(source->sd, source->pad,\n+\t\t\t\t\t\t    updated_sink_streams_mask);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int max_serdes_get_streams_masks(struct device *dev,\n+\t\t\t\t const struct v4l2_subdev_state *state,\n+\t\t\t\t u32 pad, u64 updated_streams_mask,\n+\t\t\t\t u32 num_pads, u64 *old_streams_masks,\n+\t\t\t\t u64 **new_streams_masks, bool enable)\n+{\n+\tu64 *streams_masks;\n+\tunsigned int i;\n+\n+\tstreams_masks = devm_kcalloc(dev, num_pads, sizeof(*streams_masks), GFP_KERNEL);\n+\tif (!streams_masks)\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = 0; i < num_pads; i++) {\n+\t\tu64 matched_streams_mask = updated_streams_mask;\n+\t\tu64 updated_sink_streams_mask;\n+\n+\t\tupdated_sink_streams_mask =\n+\t\t\tv4l2_subdev_state_xlate_streams(state, pad, i,\n+\t\t\t\t\t\t\t&matched_streams_mask);\n+\t\tif (!updated_sink_streams_mask)\n+\t\t\tcontinue;\n+\n+\t\tstreams_masks[i] = old_streams_masks[i];\n+\t\tif (enable)\n+\t\t\tstreams_masks[i] |= updated_sink_streams_mask;\n+\t\telse\n+\t\t\tstreams_masks[i] &= ~updated_sink_streams_mask;\n+\t}\n+\n+\tif (enable)\n+\t\tstreams_masks[pad] |= updated_streams_mask;\n+\telse\n+\t\tstreams_masks[pad] &= ~updated_streams_mask;\n+\n+\t*new_streams_masks = streams_masks;\n+\n+\treturn 0;\n+}\n+\n+static const struct videomode max_serdes_tpg_pixel_videomodes[] = {\n+\t{\n+\t\t.pixelclock = 25000000,\n+\t\t.hactive = 640,\n+\t\t.hfront_porch = 10,\n+\t\t.hsync_len = 96,\n+\t\t.hback_porch = 40,\n+\t\t.vactive = 480,\n+\t\t.vfront_porch = 2,\n+\t\t.vsync_len = 24,\n+\t\t.vback_porch = 24,\n+\t},\n+\t{\n+\t\t.pixelclock = 75000000,\n+\t\t.hactive = 1920,\n+\t\t.hfront_porch = 88,\n+\t\t.hsync_len = 44,\n+\t\t.hback_porch = 148,\n+\t\t.vactive = 1080,\n+\t\t.vfront_porch = 4,\n+\t\t.vsync_len = 16,\n+\t\t.vback_porch = 36,\n+\t},\n+\t{\n+\t\t.pixelclock = 150000000,\n+\t\t.hactive = 1920,\n+\t\t.hfront_porch = 88,\n+\t\t.hsync_len = 44,\n+\t\t.hback_porch = 148,\n+\t\t.vactive = 1080,\n+\t\t.vfront_porch = 4,\n+\t\t.vsync_len = 16,\n+\t\t.vback_porch = 36,\n+\t},\n+};\n+\n+static void max_serdes_get_vm_timings(const struct videomode *vm,\n+\t\t\t\t      struct max_serdes_tpg_timings *timings)\n+{\n+\tu32 hact = vm->hactive;\n+\tu32 hfp = vm->hfront_porch;\n+\tu32 hsync = vm->hsync_len;\n+\tu32 hbp = vm->hback_porch;\n+\tu32 htot = hact + hfp + hbp + hsync;\n+\n+\tu32 vact = vm->vactive;\n+\tu32 vfp = vm->vfront_porch;\n+\tu32 vsync = vm->vsync_len;\n+\tu32 vbp = vm->vback_porch;\n+\tu32 vtot = vact + vfp + vbp + vsync;\n+\n+\t*timings = (struct max_serdes_tpg_timings) {\n+\t\t.gen_vs = true,\n+\t\t.gen_hs = true,\n+\t\t.gen_de = true,\n+\t\t.vs_inv = true,\n+\t\t.vs_dly = 0,\n+\t\t.vs_high = vsync * htot,\n+\t\t.vs_low = (vact + vfp + vbp) * htot,\n+\t\t.v2h = 0,\n+\t\t.hs_high = hsync,\n+\t\t.hs_low = hact + hfp + hbp,\n+\t\t.hs_cnt = vact + vfp + vbp + vsync,\n+\t\t.v2d = htot * (vsync + vbp) + (hsync + hbp),\n+\t\t.de_high = hact,\n+\t\t.de_low = hfp + hsync + hbp,\n+\t\t.de_cnt = vact,\n+\t\t.clock = vm->pixelclock,\n+\t\t.fps = DIV_ROUND_CLOSEST(vm->pixelclock, vtot * htot),\n+\t};\n+}\n+\n+int max_serdes_get_tpg_timings(const struct max_serdes_tpg_entry *entry,\n+\t\t\t       struct max_serdes_tpg_timings *timings)\n+{\n+\tu32 fps;\n+\n+\tif (!entry)\n+\t\treturn 0;\n+\n+\tfps = DIV_ROUND_CLOSEST(1 * entry->interval.denominator,\n+\t\t\t\tentry->interval.numerator);\n+\n+\tfor (unsigned int i = 0; i < ARRAY_SIZE(max_serdes_tpg_pixel_videomodes); i++) {\n+\t\tstruct max_serdes_tpg_timings vm_timings;\n+\t\tconst struct videomode *vm;\n+\n+\t\tvm = &max_serdes_tpg_pixel_videomodes[i];\n+\n+\t\tmax_serdes_get_vm_timings(vm, &vm_timings);\n+\n+\t\tif (vm->hactive == entry->width &&\n+\t\t    vm->vactive == entry->height &&\n+\t\t    vm_timings.fps == fps) {\n+\t\t\t*timings = vm_timings;\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+EXPORT_SYMBOL_NS_GPL(max_serdes_get_tpg_timings, \"MAX_SERDES\");\n+\n+int max_serdes_validate_tpg_routing(struct v4l2_subdev_krouting *routing)\n+{\n+\tconst struct v4l2_subdev_route *route;\n+\n+\tif (routing->num_routes != 1)\n+\t\treturn -EINVAL;\n+\n+\troute = &routing->routes[0];\n+\n+\tif (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))\n+\t\treturn -EINVAL;\n+\n+\tif (route->sink_stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+MODULE_DESCRIPTION(\"Maxim GMSL2 Serializer/Deserializer Driver\");\n+MODULE_AUTHOR(\"Cosmin Tanislav <cosmin.tanislav@analog.com>\");\n+MODULE_LICENSE(\"GPL\");\ndiff --git a/drivers/media/i2c/maxim-serdes/max_serdes.h b/drivers/media/i2c/maxim-serdes/max_serdes.h\nnew file mode 100644\nindex 000000000000..d1d513e464d6\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/max_serdes.h\n@@ -0,0 +1,183 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2025 Analog Devices Inc.\n+ */\n+\n+#ifndef MAX_SERDES_H\n+#define MAX_SERDES_H\n+\n+#include <linux/types.h>\n+\n+#include <media/mipi-csi2.h>\n+#include <media/v4l2-subdev.h>\n+\n+#define REG_SEQUENCE_2(reg, val) \\\n+\t{ (reg),     ((val) >> 8) & 0xff }, \\\n+\t{ (reg) + 1, ((val) >> 0) & 0xff }\n+\n+#define REG_SEQUENCE_3(reg, val) \\\n+\t{ (reg),     ((val) >> 16) & 0xff }, \\\n+\t{ (reg) + 1, ((val) >> 8)  & 0xff }, \\\n+\t{ (reg) + 2, ((val) >> 0)  & 0xff }\n+\n+#define REG_SEQUENCE_3_LE(reg, val) \\\n+\t{ (reg),     ((val) >> 0) & 0xff }, \\\n+\t{ (reg) + 1, ((val) >> 8)  & 0xff }, \\\n+\t{ (reg) + 2, ((val) >> 16)  & 0xff }\n+\n+#define field_get(mask, val) (((val) & (mask)) >> __ffs(mask))\n+#define field_prep(mask, val) (((val) << __ffs(mask)) & (mask))\n+\n+#define MAX_SERDES_PHYS_MAX\t\t4\n+#define MAX_SERDES_STREAMS_NUM\t\t4\n+#define MAX_SERDES_VC_ID_NUM\t\t4\n+#define MAX_SERDES_TPG_STREAM\t\t0\n+\n+#define MAX_SERDES_GRAD_INCR\t\t4\n+#define MAX_SERDES_CHECKER_COLOR_A\t0x00ccfe\n+#define MAX_SERDES_CHECKER_COLOR_B\t0xa76a00\n+#define MAX_SERDES_CHECKER_SIZE\t\t60\n+\n+extern const char * const max_serdes_tpg_patterns[];\n+\n+enum max_serdes_gmsl_version {\n+\tMAX_SERDES_GMSL_MIN,\n+\tMAX_SERDES_GMSL_2_3GBPS = MAX_SERDES_GMSL_MIN,\n+\tMAX_SERDES_GMSL_2_6GBPS,\n+\tMAX_SERDES_GMSL_3_12GBPS,\n+\tMAX_SERDES_GMSL_MAX = MAX_SERDES_GMSL_3_12GBPS,\n+};\n+\n+enum max_serdes_gmsl_mode {\n+\tMAX_SERDES_GMSL_PIXEL_MODE,\n+\tMAX_SERDES_GMSL_TUNNEL_MODE,\n+};\n+\n+enum max_serdes_tpg_pattern {\n+\tMAX_SERDES_TPG_PATTERN_MIN,\n+\tMAX_SERDES_TPG_PATTERN_CHECKERBOARD = MAX_SERDES_TPG_PATTERN_MIN,\n+\tMAX_SERDES_TPG_PATTERN_GRADIENT,\n+\tMAX_SERDES_TPG_PATTERN_MAX = MAX_SERDES_TPG_PATTERN_GRADIENT,\n+};\n+\n+struct max_serdes_phys_config {\n+\tunsigned int lanes[MAX_SERDES_PHYS_MAX];\n+\tunsigned int clock_lane[MAX_SERDES_PHYS_MAX];\n+};\n+\n+struct max_serdes_phys_configs {\n+\tconst struct max_serdes_phys_config *configs;\n+\tunsigned int num_configs;\n+};\n+\n+struct max_serdes_i2c_xlate {\n+\tu8 src;\n+\tu8 dst;\n+\tbool en;\n+};\n+\n+struct max_serdes_mipi_format {\n+\tu8 dt;\n+\tu8 bpp;\n+};\n+\n+struct max_serdes_vc_remap {\n+\tu8 src;\n+\tu8 dst;\n+};\n+\n+struct max_serdes_source {\n+\tstruct v4l2_subdev *sd;\n+\tu16 pad;\n+\tstruct fwnode_handle *ep_fwnode;\n+\n+\tunsigned int index;\n+};\n+\n+struct max_serdes_asc {\n+\tstruct v4l2_async_connection base;\n+\tstruct max_serdes_source *source;\n+};\n+\n+struct max_serdes_tpg_entry {\n+\tu32 width;\n+\tu32 height;\n+\tstruct v4l2_fract interval;\n+\tu32 code;\n+\tu8 dt;\n+\tu8 bpp;\n+};\n+\n+#define MAX_TPG_ENTRY_640X480P60_RGB888 \\\n+\t{ 640, 480, { 1, 60 }, MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888, 24 }\n+\n+#define MAX_TPG_ENTRY_1920X1080P30_RGB888 \\\n+\t{ 1920, 1080, { 1, 30 }, MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888, 24 }\n+\n+#define MAX_TPG_ENTRY_1920X1080P60_RGB888 \\\n+\t{ 1920, 1080, { 1, 60 }, MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888, 24 }\n+\n+struct max_serdes_tpg_entries {\n+\tconst struct max_serdes_tpg_entry *entries;\n+\tunsigned int num_entries;\n+};\n+\n+struct max_serdes_tpg_timings {\n+\tbool gen_vs;\n+\tbool gen_hs;\n+\tbool gen_de;\n+\tbool vs_inv;\n+\tbool hs_inv;\n+\tbool de_inv;\n+\tu32 vs_dly;\n+\tu32 vs_high;\n+\tu32 vs_low;\n+\tu32 v2h;\n+\tu32 hs_high;\n+\tu32 hs_low;\n+\tu32 hs_cnt;\n+\tu32 v2d;\n+\tu32 de_high;\n+\tu32 de_low;\n+\tu32 de_cnt;\n+\tu32 clock;\n+\tu32 fps;\n+};\n+\n+static inline struct max_serdes_asc *asc_to_max(struct v4l2_async_connection *asc)\n+{\n+\treturn container_of(asc, struct max_serdes_asc, base);\n+}\n+\n+const char *max_serdes_gmsl_version_str(enum max_serdes_gmsl_version version);\n+const char *max_serdes_gmsl_mode_str(enum max_serdes_gmsl_mode mode);\n+\n+const struct max_serdes_mipi_format *max_serdes_mipi_format_by_dt(u8 dt);\n+\n+int max_serdes_get_fd_stream_entry(struct v4l2_subdev *sd, u32 pad, u32 stream,\n+\t\t\t\t   struct v4l2_mbus_frame_desc_entry *entry);\n+\n+int max_serdes_get_fd_bpp(struct v4l2_mbus_frame_desc_entry *entry,\n+\t\t\t  unsigned int *bpp);\n+int max_serdes_process_bpps(struct device *dev, u32 bpps,\n+\t\t\t    u32 allowed_double_bpps, unsigned int *doubled_bpp);\n+\n+int max_serdes_xlate_enable_disable_streams(struct max_serdes_source *sources,\n+\t\t\t\t\t    u32 source_sink_pad_offset,\n+\t\t\t\t\t    const struct v4l2_subdev_state *state,\n+\t\t\t\t\t    u32 pad, u64 updated_streams_mask,\n+\t\t\t\t\t    u32 sink_pad_start, u32 num_sink_pads,\n+\t\t\t\t\t    bool enable);\n+\n+int max_serdes_get_streams_masks(struct device *dev,\n+\t\t\t\t const struct v4l2_subdev_state *state,\n+\t\t\t\t u32 pad, u64 updated_streams_mask,\n+\t\t\t\t u32 num_pads, u64 *old_streams_masks,\n+\t\t\t\t u64 **new_streams_masks, bool enable);\n+\n+int max_serdes_get_tpg_timings(const struct max_serdes_tpg_entry *entry,\n+\t\t\t       struct max_serdes_tpg_timings *timings);\n+\n+int max_serdes_validate_tpg_routing(struct v4l2_subdev_krouting *routing);\n+\n+#endif // MAX_SERDES_H\n",
    "prefixes": [
        "v10",
        "13/22"
    ]
}