get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2220309,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2220309/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260406-gmsl2-3_serdes-v10-14-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-14-645560fedca5@analog.com>",
    "date": "2026-04-06T20:14:53",
    "name": "[v10,14/22] media: i2c: add Maxim GMSL2/3 serializer framework",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "d73443a0dffbcf14385c0c0a9a875eed86fbaa54",
    "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-14-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/2220309/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2220309/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <linux-gpio+bounces-34735-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=C0LxJo2g;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c0a:e001:db::12fc:5321; helo=sea.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34735-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=\"C0LxJo2g\"",
            "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201"
        ],
        "Received": [
            "from sea.lore.kernel.org (sea.lore.kernel.org\n [IPv6:2600:3c0a:e001:db::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fqLML67Ztz1yFt\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 07 Apr 2026 06:21:34 +1000 (AEST)",
            "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 7EF4F303AAA0\n\tfor <incoming@patchwork.ozlabs.org>; Mon,  6 Apr 2026 20:15:50 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 5A1913A254B;\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 5DEA139F190;\n\tMon,  6 Apr 2026 20:15:22 +0000 (UTC)",
            "by smtp.kernel.org (Postfix) with ESMTPS id 416A9C2BCB5;\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 38FDFF3D5E1;\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=n955dE5sfX79UyXIouuigtTlH0RC2sopws/t5kaVkKWpgE8hJh6VGbx74bBXCZ/LE4L8cM2j6hAWarSMDlJV4sWpsXGeiMraeDSNFZgCy+2SoCVsv185WmAV5L/FDb295P0XwoNwtPQz92yEtBpKcEkflmScRdB4Q4i6vTKsfCM=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775506522; c=relaxed/simple;\n\tbh=slDj7+aaFOlx8LzQGIopaZSQfsEYl/av3iMgff0fUDg=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=fgiP7enYgKT1RVwlDzxBCADcPFe/2JjcvhymN4gKQr1e0wz4mIz1LV+JQNP9n7gJZ8ICHyhql+x8eMO871Lxqede2g6cQrVmcjKsBpWjCICV97Fy3B36UvCufaBVh+kgKeWcVD1aGZ4DC2IKkBzVXb5t9bH5MBseh+BNLlpsJO8=",
        "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=C0LxJo2g; 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=slDj7+aaFOlx8LzQGIopaZSQfsEYl/av3iMgff0fUDg=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From;\n\tb=C0LxJo2g7rDqAbeA5rsdf+MhhFi6SCxPzmfbx2GyvcEwTs4x7XEP2oKE5qkeu449l\n\t REEe6MvqIMTT0yTO3eUB/Ophu6Qw+xFaonDUn35p0Zmxx5P4ZhYRMDDTdDIoOe93H5\n\t eADGJNZdfHKJZocRKbavPdaM306L6zTjpxh+HhN2KYSMytVBDsJGgD+4Hhj1s6BLmX\n\t hvVlIsuy9FYhi/tb/KeXXC3YHxl5znXgXA+kH/fHhqZIvygNnJ1pGCiIS/+QOJ4/5h\n\t geRQ0sWFH59/slUv/uQW9652yY9RIJaRAEBXTXZTdwgCU7FhkzXjkxAddy7xF6qNBQ\n\t gGr2b/GlIM1hw==",
        "From": "Dumitru Ceclan via B4 Relay\n <devnull+dumitru.ceclan.analog.com@kernel.org>",
        "Date": "Mon, 06 Apr 2026 23:14:53 +0300",
        "Subject": "[PATCH v10 14/22] media: i2c: add Maxim GMSL2/3 serializer\n 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-14-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=60763;\n i=dumitru.ceclan@analog.com; s=20240313; h=from:subject:message-id;\n bh=Ju5t03vxYJAlDMcQqQNRUFe0YV2C6p8Rbo17z7We0L4=;\n b=cSICkD4bmB9yPEPmhRyBxkaZE7NsATiprYPLKcZKtd6esJ48sSrEDYwEAF1PfTld4HrcyKzQI\n SVUsrpJYqaQCS+mLc8XrKLsNYSKMIE/SkvXX7SDjYT7dlLkO0z5DRNI",
        "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.\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 drivers/media/i2c/maxim-serdes/Makefile  |    2 +-\n drivers/media/i2c/maxim-serdes/max_ser.c | 2155 ++++++++++++++++++++++++++++++\n drivers/media/i2c/maxim-serdes/max_ser.h |  147 ++\n 3 files changed, 2303 insertions(+), 1 deletion(-)",
    "diff": "diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile\nindex 630fbb486bab..17511cb03369 100644\n--- a/drivers/media/i2c/maxim-serdes/Makefile\n+++ b/drivers/media/i2c/maxim-serdes/Makefile\n@@ -1,3 +1,3 @@\n # SPDX-License-Identifier: GPL-2.0\n-max-serdes-objs := max_serdes.o\n+max-serdes-objs := max_serdes.o max_ser.o\n obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o\ndiff --git a/drivers/media/i2c/maxim-serdes/max_ser.c b/drivers/media/i2c/maxim-serdes/max_ser.c\nnew file mode 100644\nindex 000000000000..9eb33cf83c07\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/max_ser.c\n@@ -0,0 +1,2155 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Maxim GMSL2 Serializer Driver\n+ *\n+ * Copyright (C) 2025 Analog Devices Inc.\n+ */\n+\n+#include <linux/delay.h>\n+#include <linux/i2c-atr.h>\n+#include <linux/i2c-mux.h>\n+#include <linux/module.h>\n+\n+#include <media/mipi-csi2.h>\n+#include <media/v4l2-ctrls.h>\n+#include <media/v4l2-fwnode.h>\n+#include <media/v4l2-subdev.h>\n+\n+#include \"max_ser.h\"\n+#include \"max_serdes.h\"\n+\n+#define MAX_SER_NUM_LINKS\t1\n+#define MAX_SER_NUM_PHYS\t1\n+\n+struct max_ser_priv {\n+\tstruct max_ser *ser;\n+\tstruct device *dev;\n+\tstruct i2c_client *client;\n+\n+\tstruct i2c_atr *atr;\n+\tstruct i2c_mux_core *mux;\n+\n+\tstruct media_pad *pads;\n+\tstruct max_serdes_source *sources;\n+\tu64 *streams_masks;\n+\tu32 double_bpps;\n+\n+\tstruct v4l2_subdev sd;\n+\tstruct v4l2_async_notifier nf;\n+\tstruct v4l2_ctrl_handler ctrl_handler;\n+};\n+\n+struct max_ser_route_hw {\n+\tstruct max_serdes_source *source;\n+\tstruct max_ser_pipe *pipe;\n+\tstruct v4l2_mbus_frame_desc_entry entry;\n+\tbool is_tpg;\n+};\n+\n+static inline struct max_ser_priv *sd_to_priv(struct v4l2_subdev *sd)\n+{\n+\treturn container_of(sd, struct max_ser_priv, sd);\n+}\n+\n+static inline struct max_ser_priv *nf_to_priv(struct v4l2_async_notifier *nf)\n+{\n+\treturn container_of(nf, struct max_ser_priv, nf);\n+}\n+\n+static inline struct max_ser_priv *ctrl_to_priv(struct v4l2_ctrl_handler *handler)\n+{\n+\treturn container_of(handler, struct max_ser_priv, ctrl_handler);\n+}\n+\n+static inline bool max_ser_pad_is_sink(struct max_ser *ser, u32 pad)\n+{\n+\treturn pad < ser->ops->num_phys;\n+}\n+\n+static inline bool max_ser_pad_is_source(struct max_ser *ser, u32 pad)\n+{\n+\treturn pad >= ser->ops->num_phys &&\n+\t       pad < ser->ops->num_phys + MAX_SER_NUM_LINKS;\n+}\n+\n+static inline u32 max_ser_source_pad(struct max_ser *ser)\n+{\n+\treturn ser->ops->num_phys;\n+}\n+\n+static inline bool max_ser_pad_is_tpg(struct max_ser *ser, u32 pad)\n+{\n+\treturn pad >= ser->ops->num_phys + MAX_SER_NUM_LINKS;\n+}\n+\n+static inline unsigned int max_ser_phy_to_pad(struct max_ser *ser,\n+\t\t\t\t\t      struct max_ser_phy *phy)\n+{\n+\treturn phy->index;\n+}\n+\n+static inline unsigned int max_ser_num_pads(struct max_ser *ser)\n+{\n+\treturn ser->ops->num_phys + MAX_SER_NUM_LINKS +\n+\t       (ser->ops->set_tpg ? 1 : 0);\n+}\n+\n+static struct max_ser_phy *max_ser_pad_to_phy(struct max_ser *ser, u32 pad)\n+{\n+\tif (!max_ser_pad_is_sink(ser, pad))\n+\t\treturn NULL;\n+\n+\treturn &ser->phys[pad];\n+}\n+\n+static struct max_ser_pipe *\n+max_ser_find_phy_pipe(struct max_ser *ser, struct max_ser_phy *phy)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < ser->ops->num_pipes; i++) {\n+\t\tstruct max_ser_pipe *pipe = &ser->pipes[i];\n+\n+\t\tif (pipe->phy_id == phy->index)\n+\t\t\treturn pipe;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct max_serdes_source *\n+max_ser_get_phy_source(struct max_ser_priv *priv, struct max_ser_phy *phy)\n+{\n+\treturn &priv->sources[phy->index];\n+}\n+\n+static const struct max_serdes_tpg_entry *\n+max_ser_find_tpg_entry(struct max_ser *ser, u32 target_index,\n+\t\t       u32 width, u32 height, u32 code,\n+\t\t       u32 numerator, u32 denominator)\n+{\n+\tconst struct max_serdes_tpg_entry *entry;\n+\tunsigned int index = 0;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < ser->ops->tpg_entries.num_entries; i++) {\n+\t\tentry = &ser->ops->tpg_entries.entries[i];\n+\n+\t\tif ((width != 0 && width != entry->width) ||\n+\t\t    (height != 0 && height != entry->height) ||\n+\t\t    (code != 0 && code != entry->code) ||\n+\t\t    (numerator != 0 && numerator != entry->interval.numerator) ||\n+\t\t    (denominator != 0 && denominator != entry->interval.denominator))\n+\t\t\tcontinue;\n+\n+\t\tif (index == target_index)\n+\t\t\tbreak;\n+\n+\t\tindex++;\n+\t}\n+\n+\tif (i == ser->ops->tpg_entries.num_entries)\n+\t\treturn NULL;\n+\n+\treturn &ser->ops->tpg_entries.entries[i];\n+}\n+\n+static const struct max_serdes_tpg_entry *\n+max_ser_find_state_tpg_entry(struct max_ser *ser, struct v4l2_subdev_state *state,\n+\t\t\t     unsigned int pad)\n+{\n+\tstruct v4l2_mbus_framefmt *fmt;\n+\tstruct v4l2_fract *in;\n+\n+\tfmt = v4l2_subdev_state_get_format(state, pad, MAX_SERDES_TPG_STREAM);\n+\tif (!fmt)\n+\t\treturn NULL;\n+\n+\tin = v4l2_subdev_state_get_interval(state, pad, MAX_SERDES_TPG_STREAM);\n+\tif (!in)\n+\t\treturn NULL;\n+\n+\treturn max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height, fmt->code,\n+\t\t\t\t      in->numerator, in->denominator);\n+}\n+\n+static int max_ser_get_tpg_fd_entry_state(struct max_ser *ser,\n+\t\t\t\t\t  struct v4l2_subdev_state *state,\n+\t\t\t\t\t  struct v4l2_mbus_frame_desc_entry *fd_entry,\n+\t\t\t\t\t  unsigned int pad)\n+{\n+\tconst struct max_serdes_tpg_entry *entry;\n+\n+\tentry = max_ser_find_state_tpg_entry(ser, state, pad);\n+\tif (!entry)\n+\t\treturn -EINVAL;\n+\n+\tfd_entry->stream = MAX_SERDES_TPG_STREAM;\n+\tfd_entry->flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;\n+\tfd_entry->length = entry->width * entry->height * entry->bpp / 8;\n+\tfd_entry->pixelcode = entry->code;\n+\tfd_entry->bus.csi2.vc = 0;\n+\tfd_entry->bus.csi2.dt = entry->dt;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_tpg_route_to_hw(struct max_ser_priv *priv,\n+\t\t\t\t   struct v4l2_subdev_state *state,\n+\t\t\t\t   struct v4l2_subdev_route *route,\n+\t\t\t\t   struct max_ser_route_hw *hw)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\n+\thw->pipe = &ser->pipes[0];\n+\n+\treturn max_ser_get_tpg_fd_entry_state(ser, state, &hw->entry,\n+\t\t\t\t\t      route->sink_pad);\n+}\n+\n+static int max_ser_route_to_hw(struct max_ser_priv *priv,\n+\t\t\t       struct v4l2_subdev_state *state,\n+\t\t\t       struct v4l2_subdev_route *route,\n+\t\t\t       struct max_ser_route_hw *hw)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_mbus_frame_desc fd;\n+\tstruct max_ser_phy *phy;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tmemset(hw, 0, sizeof(*hw));\n+\n+\thw->is_tpg = max_ser_pad_is_tpg(ser, route->sink_pad);\n+\tif (hw->is_tpg)\n+\t\treturn max_ser_tpg_route_to_hw(priv, state, route, hw);\n+\n+\tphy = max_ser_pad_to_phy(ser, route->sink_pad);\n+\tif (!phy)\n+\t\treturn -ENOENT;\n+\n+\thw->pipe = max_ser_find_phy_pipe(ser, phy);\n+\tif (!hw->pipe)\n+\t\treturn -ENOENT;\n+\n+\thw->source = max_ser_get_phy_source(priv, phy);\n+\tif (!hw->source->sd)\n+\t\treturn 0;\n+\n+\tret = v4l2_subdev_call(hw->source->sd, pad, get_frame_desc,\n+\t\t\t       hw->source->pad, &fd);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i < fd.num_entries; i++)\n+\t\tif (fd.entry[i].stream == route->sink_stream)\n+\t\t\tbreak;\n+\n+\tif (i == fd.num_entries)\n+\t\treturn -ENOENT;\n+\n+\thw->entry = fd.entry[i];\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_phy_set_active(struct max_ser *ser, struct max_ser_phy *phy,\n+\t\t\t\t  bool active)\n+{\n+\tint ret;\n+\n+\tif (ser->ops->set_phy_active) {\n+\t\tret = ser->ops->set_phy_active(ser, phy, active);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tphy->active = active;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_set_pipe_dts(struct max_ser_priv *priv, struct max_ser_pipe *pipe,\n+\t\t\t\tunsigned int *dts, unsigned int num_dts)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (!ser->ops->set_pipe_dt || !ser->ops->set_pipe_dt_en)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < num_dts; i++) {\n+\t\tret = ser->ops->set_pipe_dt(ser, pipe, i, dts[i]);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = ser->ops->set_pipe_dt_en(ser, pipe, i, true);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (num_dts == pipe->num_dts)\n+\t\treturn 0;\n+\n+\tfor (i = num_dts; i < ser->ops->num_dts_per_pipe; i++) {\n+\t\tret = ser->ops->set_pipe_dt_en(ser, pipe, i, false);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_set_pipe_mode(struct max_ser_priv *priv, struct max_ser_pipe *pipe,\n+\t\t\t\t struct max_ser_pipe_mode *mode)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\n+\tif (mode->bpp == pipe->mode.bpp &&\n+\t    mode->soft_bpp == pipe->mode.soft_bpp &&\n+\t    mode->dbl8 == pipe->mode.dbl8 &&\n+\t    mode->dbl10 == pipe->mode.dbl10 &&\n+\t    mode->dbl12 == pipe->mode.dbl12)\n+\t\treturn 0;\n+\n+\tif (!ser->ops->set_pipe_mode)\n+\t\treturn 0;\n+\n+\treturn ser->ops->set_pipe_mode(ser, pipe, mode);\n+}\n+\n+static int max_ser_i2c_atr_attach_addr(struct i2c_atr *atr, u32 chan_id,\n+\t\t\t\t       u16 addr, u16 alias)\n+{\n+\tstruct max_serdes_i2c_xlate xlate = {\n+\t\t.src = alias,\n+\t\t.dst = addr,\n+\t\t.en = true,\n+\t};\n+\tstruct max_ser_priv *priv = i2c_atr_get_driver_data(atr);\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < ser->ops->num_i2c_xlates; i++)\n+\t\tif (!ser->i2c_xlates[i].en)\n+\t\t\tbreak;\n+\n+\tif (i == ser->ops->num_i2c_xlates) {\n+\t\tdev_err(priv->dev,\n+\t\t\t\"Reached maximum number of I2C translations\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = ser->ops->set_i2c_xlate(ser, i, &xlate);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tser->i2c_xlates[i] = xlate;\n+\n+\treturn 0;\n+}\n+\n+static void max_ser_i2c_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr)\n+{\n+\tstruct max_ser_priv *priv = i2c_atr_get_driver_data(atr);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct max_serdes_i2c_xlate xlate = { 0 };\n+\tunsigned int i;\n+\n+\t/* Find index of matching I2C translation. */\n+\tfor (i = 0; i < ser->ops->num_i2c_xlates; i++)\n+\t\tif (ser->i2c_xlates[i].dst == addr)\n+\t\t\tbreak;\n+\n+\tWARN_ON(i == ser->ops->num_i2c_xlates);\n+\n+\tser->ops->set_i2c_xlate(ser, i, &xlate);\n+\tser->i2c_xlates[i] = xlate;\n+}\n+\n+static const struct i2c_atr_ops max_ser_i2c_atr_ops = {\n+\t.attach_addr = max_ser_i2c_atr_attach_addr,\n+\t.detach_addr = max_ser_i2c_atr_detach_addr,\n+};\n+\n+static void max_ser_i2c_atr_deinit(struct max_ser_priv *priv)\n+{\n+\t/* Deleting adapters that haven't been added does no harm. */\n+\ti2c_atr_del_adapter(priv->atr, 0);\n+\n+\ti2c_atr_delete(priv->atr);\n+}\n+\n+static int max_ser_i2c_atr_init(struct max_ser_priv *priv)\n+{\n+\tstruct i2c_atr_adap_desc desc = {\n+\t\t.chan_id = 0,\n+\t};\n+\n+\tif (!i2c_check_functionality(priv->client->adapter,\n+\t\t\t\t     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))\n+\t\treturn -ENODEV;\n+\n+\tpriv->atr = i2c_atr_new(priv->client->adapter, priv->dev,\n+\t\t\t\t&max_ser_i2c_atr_ops, 1, 0);\n+\tif (IS_ERR(priv->atr))\n+\t\treturn PTR_ERR(priv->atr);\n+\n+\ti2c_atr_set_driver_data(priv->atr, priv);\n+\n+\treturn i2c_atr_add_adapter(priv->atr, &desc);\n+}\n+\n+static int max_ser_i2c_mux_select(struct i2c_mux_core *mux, u32 chan)\n+{\n+\treturn 0;\n+}\n+\n+static void max_ser_i2c_mux_deinit(struct max_ser_priv *priv)\n+{\n+\ti2c_mux_del_adapters(priv->mux);\n+}\n+\n+static int max_ser_i2c_mux_init(struct max_ser_priv *priv)\n+{\n+\tpriv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,\n+\t\t\t\t  1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,\n+\t\t\t\t  max_ser_i2c_mux_select, NULL);\n+\tif (!priv->mux)\n+\t\treturn -ENOMEM;\n+\n+\treturn i2c_mux_add_adapter(priv->mux, 0, 0);\n+}\n+\n+static int max_ser_i2c_adapter_init(struct max_ser_priv *priv)\n+{\n+\tif (device_get_named_child_node(priv->dev, \"i2c-gate\"))\n+\t\treturn max_ser_i2c_mux_init(priv);\n+\telse\n+\t\treturn max_ser_i2c_atr_init(priv);\n+}\n+\n+static void max_ser_i2c_adapter_deinit(struct max_ser_priv *priv)\n+{\n+\tif (device_get_named_child_node(priv->dev, \"i2c-gate\"))\n+\t\tmax_ser_i2c_mux_deinit(priv);\n+\telse\n+\t\tmax_ser_i2c_atr_deinit(priv);\n+}\n+\n+static int max_ser_set_tpg_fmt(struct v4l2_subdev *sd,\n+\t\t\t       struct v4l2_subdev_state *state,\n+\t\t\t       struct v4l2_subdev_format *format)\n+{\n+\tstruct v4l2_mbus_framefmt *fmt = &format->format;\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tconst struct max_serdes_tpg_entry *entry;\n+\tstruct v4l2_fract *in;\n+\n+\tif (format->stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -EINVAL;\n+\n+\tentry = max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height,\n+\t\t\t\t       fmt->code, 0, 0);\n+\tif (!entry)\n+\t\treturn -EINVAL;\n+\n+\tin = v4l2_subdev_state_get_interval(state, format->pad, format->stream);\n+\tif (!in)\n+\t\treturn -EINVAL;\n+\n+\tin->numerator = entry->interval.numerator;\n+\tin->denominator = entry->interval.denominator;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_set_fmt(struct v4l2_subdev *sd,\n+\t\t\t   struct v4l2_subdev_state *state,\n+\t\t\t   struct v4l2_subdev_format *format)\n+{\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_mbus_framefmt *fmt;\n+\tint ret;\n+\n+\tif (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active)\n+\t\treturn -EBUSY;\n+\n+\t/* No transcoding, source and sink formats must match. */\n+\tif (max_ser_pad_is_source(ser, format->pad))\n+\t\treturn v4l2_subdev_get_fmt(sd, state, format);\n+\n+\tif (max_ser_pad_is_tpg(ser, format->pad)) {\n+\t\tret = max_ser_set_tpg_fmt(sd, state, format);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (format->format.code == ~0U)\n+\t\tformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16;\n+\n+\tfmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);\n+\tif (!fmt)\n+\t\treturn -EINVAL;\n+\n+\t*fmt = format->format;\n+\n+\tfmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,\n+\t\t\t\t\t\t\t   format->stream);\n+\tif (!fmt)\n+\t\treturn -EINVAL;\n+\n+\t*fmt = format->format;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_log_status(struct v4l2_subdev *sd)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int i, j;\n+\tint ret;\n+\n+\tv4l2_info(sd, \"mode: %s\\n\", max_serdes_gmsl_mode_str(ser->mode));\n+\tif (ser->ops->set_tpg) {\n+\t\tconst struct max_serdes_tpg_entry *entry = ser->tpg_entry;\n+\n+\t\tif (entry) {\n+\t\t\tv4l2_info(sd, \"tpg: %ux%u@%u/%u, code: %u, dt: %u, bpp: %u\\n\",\n+\t\t\t\t  entry->width, entry->height,\n+\t\t\t\t  entry->interval.numerator,\n+\t\t\t\t  entry->interval.denominator,\n+\t\t\t\t  entry->code, entry->dt,  entry->bpp);\n+\t\t} else {\n+\t\t\tv4l2_info(sd, \"tpg: disabled\\n\");\n+\t\t}\n+\t}\n+\tif (ser->ops->log_status) {\n+\t\tret = ser->ops->log_status(ser);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\tv4l2_info(sd, \"i2c_xlates:\\n\");\n+\tfor (i = 0; i < ser->ops->num_i2c_xlates; i++) {\n+\t\tv4l2_info(sd, \"\\ten: %u, src: 0x%02x dst: 0x%02x\\n\",\n+\t\t\t  ser->i2c_xlates[i].en, ser->i2c_xlates[i].src,\n+\t\t\t  ser->i2c_xlates[i].dst);\n+\t\tif (!ser->i2c_xlates[i].en)\n+\t\t\tbreak;\n+\t}\n+\tv4l2_info(sd, \"\\n\");\n+\tif (ser->ops->set_vc_remap) {\n+\t\tv4l2_info(sd, \"vc_remaps: %u\\n\", ser->num_vc_remaps);\n+\t\tfor (j = 0; j < ser->num_vc_remaps; j++) {\n+\t\t\tv4l2_info(sd, \"\\tvc_remap: src: %u, dst: %u\\n\",\n+\t\t\t\t  ser->vc_remaps[j].src, ser->vc_remaps[j].dst);\n+\t\t}\n+\t}\n+\tv4l2_info(sd, \"\\n\");\n+\n+\tfor (i = 0; i < ser->ops->num_pipes; i++) {\n+\t\tstruct max_ser_pipe *pipe = &ser->pipes[i];\n+\n+\t\tv4l2_info(sd, \"pipe: %u\\n\", pipe->index);\n+\t\tv4l2_info(sd, \"\\tenabled: %u\\n\", pipe->enabled);\n+\n+\t\tif (!pipe->enabled) {\n+\t\t\tv4l2_info(sd, \"\\n\");\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tv4l2_info(sd, \"\\tphy_id: %u\\n\", pipe->phy_id);\n+\t\tv4l2_info(sd, \"\\tstream_id: %u\\n\", pipe->stream_id);\n+\t\tif (ser->ops->set_pipe_phy)\n+\t\t\tv4l2_info(sd, \"\\tphy_id: %u\\n\", pipe->phy_id);\n+\t\tif (ser->ops->set_pipe_dt) {\n+\t\t\tv4l2_info(sd, \"\\tdts: %u\\n\", pipe->num_dts);\n+\t\t\tfor (j = 0; j < pipe->num_dts; j++)\n+\t\t\t\tv4l2_info(sd, \"\\t\\tdt: 0x%02x\\n\", pipe->dts[j]);\n+\t\t}\n+\t\tif (ser->ops->set_pipe_vcs)\n+\t\t\tv4l2_info(sd, \"\\tvcs: 0x%08x\\n\", pipe->vcs);\n+\t\tif (ser->ops->set_pipe_mode) {\n+\t\t\tv4l2_info(sd, \"\\tdbl8: %u\\n\", pipe->mode.dbl8);\n+\t\t\tv4l2_info(sd, \"\\tdbl10: %u\\n\", pipe->mode.dbl10);\n+\t\t\tv4l2_info(sd, \"\\tdbl12: %u\\n\", pipe->mode.dbl12);\n+\t\t\tv4l2_info(sd, \"\\tsoft_bpp: %u\\n\", pipe->mode.soft_bpp);\n+\t\t\tv4l2_info(sd, \"\\tbpp: %u\\n\", pipe->mode.bpp);\n+\t\t}\n+\t\tif (ser->ops->log_pipe_status) {\n+\t\t\tret = ser->ops->log_pipe_status(ser, pipe);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t\tv4l2_info(sd, \"\\n\");\n+\t}\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\n+\t\tv4l2_info(sd, \"phy: %u\\n\", phy->index);\n+\t\tv4l2_info(sd, \"\\tenabled: %u\\n\", phy->enabled);\n+\n+\t\tif (!phy->enabled) {\n+\t\t\tv4l2_info(sd, \"\\n\");\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tv4l2_info(sd, \"\\tactive: %u\\n\", phy->active);\n+\t\tv4l2_info(sd, \"\\tnum_data_lanes: %u\\n\", phy->mipi.num_data_lanes);\n+\t\tv4l2_info(sd, \"\\tclock_lane: %u\\n\", phy->mipi.clock_lane);\n+\t\tv4l2_info(sd, \"\\tnoncontinuous_clock: %u\\n\",\n+\t\t\t  !!(phy->mipi.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK));\n+\t\tif (ser->ops->log_phy_status) {\n+\t\t\tret = ser->ops->log_phy_status(ser, phy);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t\tv4l2_info(sd, \"\\n\");\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_s_ctrl(struct v4l2_ctrl *ctrl)\n+{\n+\tstruct max_ser_priv *priv = ctrl_to_priv(ctrl->handler);\n+\tstruct max_ser *ser = priv->ser;\n+\n+\tswitch (ctrl->id) {\n+\tcase V4L2_CID_TEST_PATTERN:\n+\t\tser->tpg_pattern = ctrl->val;\n+\t\treturn 0;\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+static int max_ser_enum_frame_interval(struct v4l2_subdev *sd,\n+\t\t\t\t       struct v4l2_subdev_state *state,\n+\t\t\t\t       struct v4l2_subdev_frame_interval_enum *fie)\n+{\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tconst struct max_serdes_tpg_entry *entry;\n+\n+\tif (!max_ser_pad_is_tpg(ser, fie->pad) ||\n+\t    fie->stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -ENOTTY;\n+\n+\tentry = max_ser_find_tpg_entry(ser, fie->index, fie->width, fie->height,\n+\t\t\t\t       fie->code, fie->interval.denominator,\n+\t\t\t\t       fie->interval.numerator);\n+\tif (!entry)\n+\t\treturn -EINVAL;\n+\n+\tfie->interval.numerator = entry->interval.numerator;\n+\tfie->interval.denominator = entry->interval.denominator;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_set_frame_interval(struct v4l2_subdev *sd,\n+\t\t\t\t      struct v4l2_subdev_state *state,\n+\t\t\t\t      struct v4l2_subdev_frame_interval *fi)\n+{\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tconst struct max_serdes_tpg_entry *entry;\n+\tstruct v4l2_mbus_framefmt *fmt;\n+\tstruct v4l2_fract *in;\n+\n+\tif (!max_ser_pad_is_tpg(ser, fi->pad) ||\n+\t    fi->stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -ENOTTY;\n+\n+\tif (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active)\n+\t\treturn -EBUSY;\n+\n+\tfmt = v4l2_subdev_state_get_format(state, fi->pad, fi->stream);\n+\tif (!fmt)\n+\t\treturn -EINVAL;\n+\n+\tentry = max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height,\n+\t\t\t\t       fmt->code, fi->interval.denominator,\n+\t\t\t\t       fi->interval.numerator);\n+\tif (!entry)\n+\t\treturn -EINVAL;\n+\n+\tin = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream);\n+\tif (!in)\n+\t\treturn -EINVAL;\n+\n+\tin->numerator = fi->interval.numerator;\n+\tin->denominator = fi->interval.denominator;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_get_frame_interval(struct v4l2_subdev *sd,\n+\t\t\t\t      struct v4l2_subdev_state *state,\n+\t\t\t\t      struct v4l2_subdev_frame_interval *fi)\n+{\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\n+\tif (!max_ser_pad_is_tpg(ser, fi->pad) ||\n+\t    fi->stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -ENOTTY;\n+\n+\treturn v4l2_subdev_get_frame_interval(sd, state, fi);\n+}\n+\n+static int max_ser_get_frame_desc_state(struct v4l2_subdev *sd,\n+\t\t\t\t\tstruct v4l2_subdev_state *state,\n+\t\t\t\t\tstruct v4l2_mbus_frame_desc *fd,\n+\t\t\t\t\tunsigned int pad)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_subdev_route *route;\n+\tint ret;\n+\n+\tif (!max_ser_pad_is_source(ser, pad))\n+\t\treturn -ENOENT;\n+\n+\tfd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\n+\t\tif (pad != route->source_pad)\n+\t\t\tcontinue;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\thw.entry.stream = route->source_stream;\n+\n+\t\tfd->entry[fd->num_entries++] = hw.entry;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,\n+\t\t\t\t  struct v4l2_mbus_frame_desc *fd)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct v4l2_subdev_state *state;\n+\tint ret;\n+\n+\tstate = v4l2_subdev_lock_and_get_active_state(&priv->sd);\n+\n+\tret = max_ser_get_frame_desc_state(sd, state, fd, pad);\n+\n+\tv4l2_subdev_unlock_state(state);\n+\n+\treturn ret;\n+}\n+\n+static int max_ser_set_tpg_routing(struct v4l2_subdev *sd,\n+\t\t\t\t   struct v4l2_subdev_state *state,\n+\t\t\t\t   struct v4l2_subdev_krouting *routing)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tconst struct max_serdes_tpg_entry *entry;\n+\tstruct v4l2_mbus_framefmt fmt = { 0 };\n+\tint ret;\n+\n+\tret = max_serdes_validate_tpg_routing(routing);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tentry = &ser->ops->tpg_entries.entries[0];\n+\n+\tfmt.width = entry->width;\n+\tfmt.height = entry->height;\n+\tfmt.code = entry->code;\n+\n+\treturn v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);\n+}\n+\n+static int __max_ser_set_routing(struct v4l2_subdev *sd,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t struct v4l2_subdev_krouting *routing)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_subdev_route *route;\n+\tbool is_tpg = false;\n+\tint ret;\n+\n+\tret = v4l2_subdev_routing_validate(sd, routing,\n+\t\t\t\t\t   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |\n+\t\t\t\t\t   V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor_each_active_route(routing, route) {\n+\t\tif (max_ser_pad_is_tpg(ser, route->sink_pad)) {\n+\t\t\tis_tpg = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (is_tpg)\n+\t\treturn max_ser_set_tpg_routing(sd, state, routing);\n+\n+\tstatic const struct v4l2_mbus_framefmt format = {\n+\t\t\t.code = MEDIA_BUS_FMT_Y8_1X8,\n+\t\t\t.field = V4L2_FIELD_NONE,\n+\t\t\t.width = 640,\n+\t\t\t.height = 480,\n+\t\t};\n+\n+\treturn v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);\n+}\n+\n+static int max_ser_set_routing(struct v4l2_subdev *sd,\n+\t\t\t       struct v4l2_subdev_state *state,\n+\t\t\t       enum v4l2_subdev_format_whence which,\n+\t\t\t       struct v4l2_subdev_krouting *routing)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\n+\tif (which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active)\n+\t\treturn -EBUSY;\n+\n+\treturn __max_ser_set_routing(sd, state, routing);\n+}\n+\n+static int max_ser_get_pipe_vcs_dts(struct max_ser_priv *priv,\n+\t\t\t\t    struct v4l2_subdev_state *state,\n+\t\t\t\t    struct max_ser_pipe *pipe,\n+\t\t\t\t    unsigned int *vcs,\n+\t\t\t\t    unsigned int *dts, unsigned int *num_dts,\n+\t\t\t\t    u64 *streams_masks)\n+{\n+\tstruct v4l2_subdev_route *route;\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int i;\n+\tint ret;\n+\n+\t*vcs = 0;\n+\t*num_dts = 0;\n+\n+\tif (ser->mode != MAX_SERDES_GMSL_PIXEL_MODE)\n+\t\treturn 0;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\t\tunsigned int vc, dt;\n+\n+\t\tif (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))\n+\t\t\tcontinue;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (hw.pipe != pipe)\n+\t\t\tcontinue;\n+\n+\t\tvc = hw.entry.bus.csi2.vc;\n+\t\tdt = hw.entry.bus.csi2.dt;\n+\n+\t\tif (vc >= MAX_SERDES_VC_ID_NUM)\n+\t\t\treturn -E2BIG;\n+\n+\t\t*vcs |= BIT(vc);\n+\n+\t\t/* Skip already added DT. */\n+\t\tfor (i = 0; i < *num_dts; i++)\n+\t\t\tif (dts[i] == dt)\n+\t\t\t\tbreak;\n+\n+\t\tif (i < *num_dts)\n+\t\t\tcontinue;\n+\n+\t\tdts[*num_dts] = dt;\n+\t\t(*num_dts)++;\n+\t}\n+\n+\t/*\n+\t * Hardware cannot distinguish between different pairs of VC and DT,\n+\t * issue a warning.\n+\t */\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\t\tunsigned int vc, dt;\n+\n+\t\t/*\n+\t\t * Skip enabled streams, we only want to check for leaks\n+\t\t * among the disabled streams.\n+\t\t */\n+\t\tif ((BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))\n+\t\t\tcontinue;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (hw.pipe != pipe)\n+\t\t\tcontinue;\n+\n+\t\tvc = hw.entry.bus.csi2.vc;\n+\t\tdt = hw.entry.bus.csi2.dt;\n+\n+\t\tif (vc >= MAX_SERDES_VC_ID_NUM)\n+\t\t\treturn -E2BIG;\n+\n+\t\tif (!(*vcs & BIT(vc)))\n+\t\t\tcontinue;\n+\n+\t\tfor (i = 0; i < *num_dts; i++)\n+\t\t\tif (dts[i] == dt)\n+\t\t\t\tbreak;\n+\n+\t\tif (i == *num_dts)\n+\t\t\tcontinue;\n+\n+\t\tdev_warn(priv->dev, \"Leaked disabled stream %u:%u with VC: %u, DT: %u\",\n+\t\t\t route->source_pad, route->source_stream, vc, dt);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_get_pipe_mode(struct max_ser_priv *priv,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t struct max_ser_pipe *pipe,\n+\t\t\t\t struct max_ser_pipe_mode *mode)\n+{\n+\tstruct v4l2_subdev_route *route;\n+\tstruct max_ser *ser = priv->ser;\n+\tbool force_set_bpp = false;\n+\tunsigned int doubled_bpp;\n+\tunsigned int min_bpp;\n+\tunsigned int max_bpp;\n+\tu32 bpps = 0;\n+\tint ret;\n+\n+\tif (ser->mode != MAX_SERDES_GMSL_PIXEL_MODE)\n+\t\treturn 0;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\t\tunsigned int bpp;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (hw.pipe != pipe)\n+\t\t\tcontinue;\n+\n+\t\tif (hw.is_tpg)\n+\t\t\tforce_set_bpp = true;\n+\n+\t\tret = max_serdes_get_fd_bpp(&hw.entry, &bpp);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tbpps |= BIT(bpp);\n+\t}\n+\n+\tret = max_serdes_process_bpps(priv->dev, bpps, priv->double_bpps, &doubled_bpp);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (doubled_bpp == 8)\n+\t\tmode->dbl8 = true;\n+\telse if (doubled_bpp == 10)\n+\t\tmode->dbl10 = true;\n+\telse if (doubled_bpp == 12)\n+\t\tmode->dbl12 = true;\n+\n+\tif (doubled_bpp) {\n+\t\tbpps &= ~BIT(doubled_bpp);\n+\t\tbpps |= BIT(doubled_bpp * 2);\n+\t}\n+\n+\tmin_bpp = __ffs(bpps);\n+\tmax_bpp = __fls(bpps);\n+\n+\tif (doubled_bpp)\n+\t\tmode->soft_bpp = min_bpp;\n+\n+\tif (min_bpp != max_bpp || force_set_bpp)\n+\t\tmode->bpp = max_bpp;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_update_pipe_enable(struct max_ser_priv *priv,\n+\t\t\t\t      struct max_ser_pipe *pipe,\n+\t\t\t\t      struct v4l2_subdev_state *state,\n+\t\t\t\t      u64 *streams_masks)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_subdev_route *route;\n+\tbool enable = false;\n+\tint ret;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\n+\t\tif (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))\n+\t\t\tcontinue;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (hw.pipe != pipe)\n+\t\t\tcontinue;\n+\n+\t\tenable = true;\n+\t\tbreak;\n+\t}\n+\n+\tif (enable == pipe->enabled)\n+\t\treturn 0;\n+\n+\tret = ser->ops->set_pipe_enable(ser, pipe, enable);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tpipe->enabled = enable;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_update_pipe(struct max_ser_priv *priv,\n+\t\t\t       struct max_ser_pipe *pipe,\n+\t\t\t       struct v4l2_subdev_state *state,\n+\t\t\t       u64 *streams_masks)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct max_ser_pipe_mode mode = { 0 };\n+\tunsigned int num_dts;\n+\tunsigned int *dts;\n+\tunsigned int vcs;\n+\tint ret;\n+\n+\tif (!ser->ops->num_dts_per_pipe)\n+\t\treturn 0;\n+\n+\tdts = devm_kcalloc(priv->dev, ser->ops->num_dts_per_pipe, sizeof(*dts),\n+\t\t\t   GFP_KERNEL);\n+\tif (!dts)\n+\t\treturn -ENOMEM;\n+\n+\tret = max_ser_get_pipe_vcs_dts(priv, state, pipe, &vcs, dts, &num_dts,\n+\t\t\t\t       streams_masks);\n+\tif (ret)\n+\t\tgoto err_free_dts;\n+\n+\tret = max_ser_get_pipe_mode(priv, state, pipe, &mode);\n+\tif (ret)\n+\t\tgoto err_free_dts;\n+\n+\tif (ser->ops->set_pipe_vcs) {\n+\t\tret = ser->ops->set_pipe_vcs(ser, pipe, vcs);\n+\t\tif (ret)\n+\t\t\tgoto err_free_dts;\n+\t}\n+\n+\tret = max_ser_set_pipe_mode(priv, pipe, &mode);\n+\tif (ret)\n+\t\tgoto err_revert_vcs;\n+\n+\tret = max_ser_set_pipe_dts(priv, pipe, dts, num_dts);\n+\tif (ret)\n+\t\tgoto err_revert_mode;\n+\n+\tpipe->vcs = vcs;\n+\tpipe->mode = mode;\n+\n+\tif (pipe->dts)\n+\t\tdevm_kfree(priv->dev, pipe->dts);\n+\n+\tpipe->dts = dts;\n+\tpipe->num_dts = num_dts;\n+\n+\treturn 0;\n+\n+err_revert_mode:\n+\tmax_ser_set_pipe_mode(priv, pipe, &pipe->mode);\n+\n+err_revert_vcs:\n+\tif (ser->ops->set_pipe_vcs)\n+\t\tser->ops->set_pipe_vcs(ser, pipe, pipe->vcs);\n+\n+err_free_dts:\n+\tdevm_kfree(priv->dev, dts);\n+\n+\treturn ret;\n+}\n+\n+static int max_ser_update_phy(struct max_ser_priv *priv,\n+\t\t\t      struct v4l2_subdev_state *state,\n+\t\t\t      struct max_ser_phy *phy, u64 *streams_masks)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tu32 pad = max_ser_phy_to_pad(ser, phy);\n+\tbool enable_changed = !streams_masks[pad] != !priv->streams_masks[pad];\n+\tbool enable = !!streams_masks[pad];\n+\tstruct max_ser_pipe *pipe;\n+\tint ret;\n+\n+\tpipe = max_ser_find_phy_pipe(ser, phy);\n+\tif (!pipe)\n+\t\treturn -ENOENT;\n+\n+\tif (!enable && enable_changed) {\n+\t\tret = max_ser_phy_set_active(ser, phy, enable);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = max_ser_update_pipe(priv, pipe, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_phy_disable;\n+\n+\tret = max_ser_update_pipe_enable(priv, pipe, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_pipe_update;\n+\n+\tif (enable && enable_changed) {\n+\t\tret = max_ser_phy_set_active(ser, phy, enable);\n+\t\tif (ret)\n+\t\t\tgoto err_revert_update_pipe_enable;\n+\t}\n+\n+\treturn 0;\n+\n+err_revert_update_pipe_enable:\n+\tmax_ser_update_pipe_enable(priv, pipe, state, priv->streams_masks);\n+\n+err_revert_pipe_update:\n+\tmax_ser_update_pipe(priv, pipe, state, priv->streams_masks);\n+\n+err_revert_phy_disable:\n+\tif (!enable && enable_changed)\n+\t\tmax_ser_phy_set_active(ser, phy, !enable);\n+\n+\treturn ret;\n+}\n+\n+static int max_ser_update_phys(struct max_ser_priv *priv,\n+\t\t\t       struct v4l2_subdev_state *state,\n+\t\t\t       u64 *streams_masks)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int failed_update_phy_id = ser->ops->num_phys;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\n+\t\tret = max_ser_update_phy(priv, state, phy, streams_masks);\n+\t\tif (ret) {\n+\t\t\tfailed_update_phy_id = i;\n+\t\t\tgoto err;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+\n+err:\n+\tfor (i = 0; i < failed_update_phy_id; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\n+\t\tmax_ser_update_phy(priv, state, phy, priv->streams_masks);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int max_ser_enable_disable_streams(struct max_ser_priv *priv,\n+\t\t\t\t\t  struct v4l2_subdev_state *state,\n+\t\t\t\t\t  u32 pad, u64 updated_streams_mask,\n+\t\t\t\t\t  bool enable)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\n+\treturn max_serdes_xlate_enable_disable_streams(priv->sources, 0, state,\n+\t\t\t\t\t\t       pad, updated_streams_mask, 0,\n+\t\t\t\t\t\t       ser->ops->num_phys, enable);\n+}\n+\n+static bool max_ser_is_tpg_routed(struct max_ser_priv *priv,\n+\t\t\t\t  struct v4l2_subdev_state *state)\n+{\n+\tstruct v4l2_subdev_route *route;\n+\tint ret;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn false;\n+\n+\t\tif (hw.is_tpg)\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+static int max_ser_update_tpg(struct max_ser_priv *priv,\n+\t\t\t      struct v4l2_subdev_state *state,\n+\t\t\t      u64 *streams_masks)\n+{\n+\tconst struct max_serdes_tpg_entry *entry = NULL;\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_subdev_route *route;\n+\tint ret;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_ser_route_hw hw;\n+\n+\t\tif (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))\n+\t\t\tcontinue;\n+\n+\t\tret = max_ser_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.is_tpg)\n+\t\t\tcontinue;\n+\n+\t\tentry = max_ser_find_state_tpg_entry(ser, state, route->sink_pad);\n+\t\tbreak;\n+\t}\n+\n+\tif (entry == ser->tpg_entry)\n+\t\treturn 0;\n+\n+\tret = ser->ops->set_tpg(ser, entry);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tser->tpg_entry = entry;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_update_streams(struct v4l2_subdev *sd,\n+\t\t\t\t  struct v4l2_subdev_state *state,\n+\t\t\t\t  u32 pad, u64 updated_streams_mask, bool enable)\n+{\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int num_pads = max_ser_num_pads(ser);\n+\tu64 *streams_masks;\n+\tint ret;\n+\n+\tret = max_serdes_get_streams_masks(priv->dev, state, pad, updated_streams_mask,\n+\t\t\t\t\t   num_pads, priv->streams_masks, &streams_masks,\n+\t\t\t\t\t   enable);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!enable) {\n+\t\tret = max_ser_enable_disable_streams(priv, state, pad,\n+\t\t\t\t\t\t     updated_streams_mask, enable);\n+\t\tif (ret)\n+\t\t\tgoto err_free_streams_masks;\n+\t}\n+\n+\tret = max_ser_update_tpg(priv, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_streams_disable;\n+\n+\tret = max_ser_update_phys(priv, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_update_tpg;\n+\n+\tif (enable) {\n+\t\tret = max_ser_enable_disable_streams(priv, state, pad,\n+\t\t\t\t\t\t     updated_streams_mask, enable);\n+\t\tif (ret)\n+\t\t\tgoto err_revert_phys_update;\n+\t}\n+\n+\tdevm_kfree(priv->dev, priv->streams_masks);\n+\tpriv->streams_masks = streams_masks;\n+\tser->active = !!streams_masks[pad];\n+\n+\treturn 0;\n+\n+err_revert_phys_update:\n+\tmax_ser_update_phys(priv, state, priv->streams_masks);\n+\n+err_revert_update_tpg:\n+\tmax_ser_update_tpg(priv, state, priv->streams_masks);\n+\n+err_revert_streams_disable:\n+\tif (!enable)\n+\t\tmax_ser_enable_disable_streams(priv, state, pad,\n+\t\t\t\t\t       updated_streams_mask, !enable);\n+\n+err_free_streams_masks:\n+\tdevm_kfree(priv->dev, streams_masks);\n+\n+\treturn ret;\n+}\n+\n+static int max_ser_enable_streams(struct v4l2_subdev *sd,\n+\t\t\t\t  struct v4l2_subdev_state *state,\n+\t\t\t\t  u32 pad, u64 streams_mask)\n+{\n+\treturn max_ser_update_streams(sd, state, pad, streams_mask, true);\n+}\n+\n+static int max_ser_disable_streams(struct v4l2_subdev *sd,\n+\t\t\t\t   struct v4l2_subdev_state *state,\n+\t\t\t\t   u32 pad, u64 streams_mask)\n+{\n+\treturn max_ser_update_streams(sd, state, pad, streams_mask, false);\n+}\n+\n+static int max_ser_init_state(struct v4l2_subdev *sd,\n+\t\t\t      struct v4l2_subdev_state *state)\n+{\n+\tstruct v4l2_subdev_route routes[MAX_SER_NUM_PHYS] = { 0 };\n+\tstruct v4l2_subdev_krouting routing = {\n+\t\t.routes = routes,\n+\t};\n+\tstruct max_ser_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int stream = 0;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\n+\t\tif (!phy->enabled)\n+\t\t\tcontinue;\n+\n+\t\trouting.routes[routing.num_routes++] = (struct v4l2_subdev_route) {\n+\t\t\t.sink_pad = max_ser_phy_to_pad(ser, phy),\n+\t\t\t.sink_stream = 0,\n+\t\t\t.source_pad = max_ser_source_pad(ser),\n+\t\t\t.source_stream = stream,\n+\t\t\t.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,\n+\t\t};\n+\t\tstream++;\n+\n+\t\t/*\n+\t\t * The Streams API is an experimental feature.\n+\t\t * If multiple routes are provided here, userspace will not be\n+\t\t * able to configure them unless the Streams API is enabled.\n+\t\t * Provide a single route until it is enabled.\n+\t\t */\n+\t\tbreak;\n+\t}\n+\n+\treturn __max_ser_set_routing(sd, state, &routing);\n+}\n+\n+#ifdef CONFIG_VIDEO_ADV_DEBUG\n+static int max_ser_g_register(struct v4l2_subdev *sd,\n+\t\t\t      struct v4l2_dbg_register *reg)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int val;\n+\tint ret;\n+\n+\tret = ser->ops->reg_read(ser, reg->reg, &val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treg->val = val;\n+\treg->size = 1;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_s_register(struct v4l2_subdev *sd,\n+\t\t\t      const struct v4l2_dbg_register *reg)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\n+\treturn ser->ops->reg_write(ser, reg->reg, reg->val);\n+}\n+#endif\n+\n+static const struct v4l2_subdev_core_ops max_ser_core_ops = {\n+\t.log_status = max_ser_log_status,\n+#ifdef CONFIG_VIDEO_ADV_DEBUG\n+\t.g_register = max_ser_g_register,\n+\t.s_register = max_ser_s_register,\n+#endif\n+};\n+\n+static const struct v4l2_ctrl_ops max_ser_ctrl_ops = {\n+\t.s_ctrl = max_ser_s_ctrl,\n+};\n+\n+static const struct v4l2_subdev_pad_ops max_ser_pad_ops = {\n+\t.enable_streams = max_ser_enable_streams,\n+\t.disable_streams = max_ser_disable_streams,\n+\n+\t.set_routing = max_ser_set_routing,\n+\t.get_frame_desc = max_ser_get_frame_desc,\n+\n+\t.get_fmt = v4l2_subdev_get_fmt,\n+\t.set_fmt = max_ser_set_fmt,\n+\n+\t.enum_frame_interval = max_ser_enum_frame_interval,\n+\t.get_frame_interval = max_ser_get_frame_interval,\n+\t.set_frame_interval = max_ser_set_frame_interval,\n+};\n+\n+static const struct v4l2_subdev_ops max_ser_subdev_ops = {\n+\t.core = &max_ser_core_ops,\n+\t.pad = &max_ser_pad_ops,\n+};\n+\n+static const struct v4l2_subdev_internal_ops max_ser_internal_ops = {\n+\t.init_state = &max_ser_init_state,\n+};\n+\n+static const struct media_entity_operations max_ser_media_ops = {\n+\t.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,\n+\t.has_pad_interdep = v4l2_subdev_has_pad_interdep,\n+\t.link_validate = v4l2_subdev_link_validate,\n+};\n+\n+static int max_ser_init(struct max_ser_priv *priv)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (ser->ops->init) {\n+\t\tret = ser->ops->init(ser);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (ser->ops->set_tunnel_enable) {\n+\t\tret = ser->ops->set_tunnel_enable(ser, false);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\n+\t\tif (phy->enabled) {\n+\t\t\tret = ser->ops->init_phy(ser, phy);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (ser->ops->set_phy_active) {\n+\t\t\tret = ser->ops->set_phy_active(ser, phy, false);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < ser->ops->num_pipes; i++) {\n+\t\tstruct max_ser_pipe *pipe = &ser->pipes[i];\n+\t\tstruct max_ser_phy *phy = &ser->phys[pipe->phy_id];\n+\n+\t\tret = ser->ops->set_pipe_enable(ser, pipe, false);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (ser->ops->set_pipe_stream_id) {\n+\t\t\tret = ser->ops->set_pipe_stream_id(ser, pipe, pipe->stream_id);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (ser->ops->set_pipe_phy) {\n+\t\t\tret = ser->ops->set_pipe_phy(ser, pipe, phy);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (ser->ops->set_pipe_vcs) {\n+\t\t\tret = ser->ops->set_pipe_vcs(ser, pipe, 0);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (ser->ops->set_pipe_mode) {\n+\t\t\tret = ser->ops->set_pipe_mode(ser, pipe, &pipe->mode);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = max_ser_set_pipe_dts(priv, pipe, NULL, 0);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_notify_bound(struct v4l2_async_notifier *nf,\n+\t\t\t\tstruct v4l2_subdev *subdev,\n+\t\t\t\tstruct v4l2_async_connection *base_asc)\n+{\n+\tstruct max_ser_priv *priv = nf_to_priv(nf);\n+\tstruct max_serdes_asc *asc = asc_to_max(base_asc);\n+\tstruct max_serdes_source *source = asc->source;\n+\tu32 pad = source->index;\n+\tint ret;\n+\n+\tret = media_entity_get_fwnode_pad(&subdev->entity,\n+\t\t\t\t\t  source->ep_fwnode,\n+\t\t\t\t\t  MEDIA_PAD_FL_SOURCE);\n+\tif (ret < 0) {\n+\t\tdev_err(priv->dev, \"Failed to find pad for %s\\n\", subdev->name);\n+\t\treturn ret;\n+\t}\n+\n+\tsource->sd = subdev;\n+\tsource->pad = ret;\n+\n+\tret = media_create_pad_link(&source->sd->entity, source->pad,\n+\t\t\t\t    &priv->sd.entity, pad,\n+\t\t\t\t    MEDIA_LNK_FL_ENABLED |\n+\t\t\t\t    MEDIA_LNK_FL_IMMUTABLE);\n+\tif (ret) {\n+\t\tdev_err(priv->dev, \"Unable to link %s:%u -> %s:%u\\n\",\n+\t\t\tsource->sd->name, source->pad, priv->sd.name, pad);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void max_ser_notify_unbind(struct v4l2_async_notifier *nf,\n+\t\t\t\t  struct v4l2_subdev *subdev,\n+\t\t\t\t  struct v4l2_async_connection *base_asc)\n+{\n+\tstruct max_serdes_asc *asc = asc_to_max(base_asc);\n+\tstruct max_serdes_source *source = asc->source;\n+\n+\tsource->sd = NULL;\n+}\n+\n+static const struct v4l2_async_notifier_operations max_ser_notify_ops = {\n+\t.bound = max_ser_notify_bound,\n+\t.unbind = max_ser_notify_unbind,\n+};\n+\n+static int max_ser_v4l2_notifier_register(struct max_ser_priv *priv)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tv4l2_async_subdev_nf_init(&priv->nf, &priv->sd);\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\t\tstruct max_serdes_source *source;\n+\t\tstruct max_serdes_asc *asc;\n+\n+\t\tsource = max_ser_get_phy_source(priv, phy);\n+\t\tif (!source->ep_fwnode)\n+\t\t\tcontinue;\n+\n+\t\tasc = v4l2_async_nf_add_fwnode(&priv->nf, source->ep_fwnode,\n+\t\t\t\t\t       struct max_serdes_asc);\n+\t\tif (IS_ERR(asc)) {\n+\t\t\tdev_err(priv->dev,\n+\t\t\t\t\"Failed to add subdev for source %u: %pe\", i,\n+\t\t\t\tasc);\n+\n+\t\t\tv4l2_async_nf_cleanup(&priv->nf);\n+\n+\t\t\treturn PTR_ERR(asc);\n+\t\t}\n+\n+\t\tasc->source = source;\n+\t}\n+\n+\tpriv->nf.ops = &max_ser_notify_ops;\n+\n+\tret = v4l2_async_nf_register(&priv->nf);\n+\tif (ret) {\n+\t\tdev_err(priv->dev, \"Failed to register subdev notifier\");\n+\t\tv4l2_async_nf_cleanup(&priv->nf);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void max_ser_v4l2_notifier_unregister(struct max_ser_priv *priv)\n+{\n+\tv4l2_async_nf_unregister(&priv->nf);\n+\tv4l2_async_nf_cleanup(&priv->nf);\n+}\n+\n+static int max_ser_v4l2_register(struct max_ser_priv *priv)\n+{\n+\tstruct v4l2_subdev *sd = &priv->sd;\n+\tstruct max_ser *ser = priv->ser;\n+\tvoid *data = i2c_get_clientdata(priv->client);\n+\tunsigned int num_pads = max_ser_num_pads(ser);\n+\tunsigned int i;\n+\tint ret;\n+\n+\tv4l2_i2c_subdev_init(sd, priv->client, &max_ser_subdev_ops);\n+\ti2c_set_clientdata(priv->client, data);\n+\tsd->internal_ops = &max_ser_internal_ops;\n+\tsd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;\n+\tsd->entity.ops = &max_ser_media_ops;\n+\tsd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;\n+\n+\tpriv->pads = devm_kcalloc(priv->dev, num_pads, sizeof(*priv->pads), GFP_KERNEL);\n+\tif (!priv->pads)\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = 0; i < num_pads; i++) {\n+\t\tif (max_ser_pad_is_sink(ser, i))\n+\t\t\tpriv->pads[i].flags = MEDIA_PAD_FL_SINK;\n+\t\telse if (max_ser_pad_is_source(ser, i))\n+\t\t\tpriv->pads[i].flags = MEDIA_PAD_FL_SOURCE;\n+\t\telse if (max_ser_pad_is_tpg(ser, i))\n+\t\t\tpriv->pads[i].flags = MEDIA_PAD_FL_SINK |\n+\t\t\t\t\t      MEDIA_PAD_FL_INTERNAL;\n+\t\telse\n+\t\t\treturn -EINVAL;\n+\t}\n+\n+\tv4l2_set_subdevdata(sd, priv);\n+\n+\tif (ser->ops->tpg_patterns) {\n+\t\tv4l2_ctrl_handler_init(&priv->ctrl_handler, 1);\n+\t\tpriv->sd.ctrl_handler = &priv->ctrl_handler;\n+\n+\t\tv4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,\n+\t\t\t\t\t     &max_ser_ctrl_ops,\n+\t\t\t\t\t     V4L2_CID_TEST_PATTERN,\n+\t\t\t\t\t     MAX_SERDES_TPG_PATTERN_MAX,\n+\t\t\t\t\t     ~ser->ops->tpg_patterns,\n+\t\t\t\t\t     __ffs(ser->ops->tpg_patterns),\n+\t\t\t\t\t     max_serdes_tpg_patterns);\n+\t\tif (priv->ctrl_handler.error) {\n+\t\t\tret = priv->ctrl_handler.error;\n+\t\t\tgoto err_free_ctrl;\n+\t\t}\n+\t}\n+\n+\tret = media_entity_pads_init(&sd->entity, num_pads, priv->pads);\n+\tif (ret)\n+\t\tgoto err_free_ctrl;\n+\n+\tret = max_ser_v4l2_notifier_register(priv);\n+\tif (ret)\n+\t\tgoto err_media_entity_cleanup;\n+\n+\tret = v4l2_subdev_init_finalize(sd);\n+\tif (ret)\n+\t\tgoto err_nf_cleanup;\n+\n+\tret = v4l2_async_register_subdev(sd);\n+\tif (ret)\n+\t\tgoto err_sd_cleanup;\n+\n+\treturn 0;\n+\n+err_sd_cleanup:\n+\tv4l2_subdev_cleanup(sd);\n+err_nf_cleanup:\n+\tmax_ser_v4l2_notifier_unregister(priv);\n+err_media_entity_cleanup:\n+\tmedia_entity_cleanup(&sd->entity);\n+err_free_ctrl:\n+\tv4l2_ctrl_handler_free(&priv->ctrl_handler);\n+\n+\treturn ret;\n+}\n+\n+static void max_ser_v4l2_unregister(struct max_ser_priv *priv)\n+{\n+\tstruct v4l2_subdev *sd = &priv->sd;\n+\n+\tmax_ser_v4l2_notifier_unregister(priv);\n+\tv4l2_async_unregister_subdev(sd);\n+\tv4l2_subdev_cleanup(sd);\n+\tmedia_entity_cleanup(&sd->entity);\n+}\n+\n+static int max_ser_parse_sink_dt_endpoint(struct max_ser_priv *priv,\n+\t\t\t\t\t  struct max_ser_phy *phy,\n+\t\t\t\t\t  struct max_serdes_source *source,\n+\t\t\t\t\t  struct fwnode_handle *fwnode)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tu32 pad = max_ser_phy_to_pad(ser, phy);\n+\tstruct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };\n+\tstruct fwnode_handle *ep;\n+\tint ret;\n+\n+\tep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0);\n+\tif (!ep)\n+\t\treturn 0;\n+\n+\tsource->ep_fwnode = fwnode_graph_get_remote_endpoint(ep);\n+\tif (!source->ep_fwnode) {\n+\t\tdev_err(priv->dev,\n+\t\t\t\"Failed to get remote endpoint on port %u\\n\", pad);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);\n+\tfwnode_handle_put(ep);\n+\tif (ret) {\n+\t\tdev_err(priv->dev, \"Could not parse endpoint on port %u\\n\", pad);\n+\t\treturn ret;\n+\t}\n+\n+\tphy->mipi = v4l2_ep.bus.mipi_csi2;\n+\tphy->enabled = true;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_find_phys_config(struct max_ser_priv *priv)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tconst struct max_serdes_phys_configs *configs = &ser->ops->phys_configs;\n+\tstruct max_ser_phy *phy;\n+\tunsigned int i, j;\n+\n+\tif (!configs->num_configs)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < configs->num_configs; i++) {\n+\t\tconst struct max_serdes_phys_config *config = &configs->configs[i];\n+\t\tbool matching = true;\n+\n+\t\tfor (j = 0; j < ser->ops->num_phys; j++) {\n+\t\t\tphy = &ser->phys[j];\n+\n+\t\t\tif (!phy->enabled)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (phy->mipi.num_data_lanes <= config->lanes[j])\n+\t\t\t\tcontinue;\n+\n+\t\t\tmatching = false;\n+\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (matching)\n+\t\t\tbreak;\n+\t}\n+\n+\tif (i == configs->num_configs) {\n+\t\tdev_err(priv->dev, \"Invalid lane configuration\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tser->phys_config = i;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_parse_dt(struct max_ser_priv *priv)\n+{\n+\tstruct fwnode_handle *fwnode = dev_fwnode(priv->dev);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct max_ser_pipe *pipe;\n+\tstruct max_ser_phy *phy;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tphy = &ser->phys[i];\n+\t\tphy->index = i;\n+\t}\n+\n+\tfor (i = 0; i < ser->ops->num_pipes; i++) {\n+\t\tpipe = &ser->pipes[i];\n+\t\tpipe->index = i;\n+\t\tpipe->phy_id = i % ser->ops->num_phys;\n+\t\tpipe->stream_id = i % MAX_SERDES_STREAMS_NUM;\n+\t}\n+\n+\tfor (i = 0; i < ser->ops->num_phys; i++) {\n+\t\tstruct max_ser_phy *phy = &ser->phys[i];\n+\t\tstruct max_serdes_source *source;\n+\n+\t\tsource = max_ser_get_phy_source(priv, phy);\n+\t\tsource->index = i;\n+\n+\t\tret = max_ser_parse_sink_dt_endpoint(priv, phy, source, fwnode);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn max_ser_find_phys_config(priv);\n+}\n+\n+static int max_ser_allocate(struct max_ser_priv *priv)\n+{\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int num_pads = max_ser_num_pads(ser);\n+\n+\tser->phys = devm_kcalloc(priv->dev, ser->ops->num_phys,\n+\t\t\t\t sizeof(*ser->phys), GFP_KERNEL);\n+\tif (!ser->phys)\n+\t\treturn -ENOMEM;\n+\n+\tser->pipes = devm_kcalloc(priv->dev, ser->ops->num_pipes,\n+\t\t\t\t  sizeof(*ser->pipes), GFP_KERNEL);\n+\tif (!ser->pipes)\n+\t\treturn -ENOMEM;\n+\n+\tser->vc_remaps = devm_kcalloc(priv->dev, ser->ops->num_vc_remaps,\n+\t\t\t\t      sizeof(*ser->vc_remaps), GFP_KERNEL);\n+\tif (!ser->vc_remaps)\n+\t\treturn -ENOMEM;\n+\n+\tser->i2c_xlates = devm_kcalloc(priv->dev, ser->ops->num_i2c_xlates,\n+\t\t\t\t       sizeof(*ser->i2c_xlates), GFP_KERNEL);\n+\tif (!ser->i2c_xlates)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->sources = devm_kcalloc(priv->dev, ser->ops->num_phys,\n+\t\t\t\t     sizeof(*priv->sources), GFP_KERNEL);\n+\tif (!priv->sources)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->streams_masks = devm_kcalloc(priv->dev, num_pads,\n+\t\t\t\t\t   sizeof(*priv->streams_masks),\n+\t\t\t\t\t   GFP_KERNEL);\n+\tif (!priv->streams_masks)\n+\t\treturn -ENOMEM;\n+\n+\treturn 0;\n+}\n+\n+int max_ser_probe(struct i2c_client *client, struct max_ser *ser)\n+{\n+\tstruct device *dev = &client->dev;\n+\tstruct max_ser_priv *priv;\n+\tint ret;\n+\n+\tif (ser->ops->num_phys > MAX_SER_NUM_PHYS)\n+\t\treturn -E2BIG;\n+\n+\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->client = client;\n+\tpriv->dev = dev;\n+\tpriv->ser = ser;\n+\tser->priv = priv;\n+\tser->mode = __ffs(ser->ops->modes);\n+\n+\tret = max_ser_allocate(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_ser_parse_dt(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_ser_init(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_ser_i2c_adapter_init(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_ser_v4l2_register(priv);\n+\tif (ret)\n+\t\tgoto err_i2c_adapter_deinit;\n+\n+\treturn 0;\n+\n+err_i2c_adapter_deinit:\n+\tmax_ser_i2c_adapter_deinit(priv);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_NS_GPL(max_ser_probe, \"MAX_SERDES\");\n+\n+int max_ser_remove(struct max_ser *ser)\n+{\n+\tstruct max_ser_priv *priv = ser->priv;\n+\n+\tmax_ser_v4l2_unregister(priv);\n+\n+\tmax_ser_i2c_adapter_deinit(priv);\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_NS_GPL(max_ser_remove, \"MAX_SERDES\");\n+\n+int max_ser_set_double_bpps(struct v4l2_subdev *sd, u32 double_bpps)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\n+\tpriv->double_bpps = double_bpps;\n+\n+\treturn 0;\n+}\n+\n+int max_ser_set_stream_id(struct v4l2_subdev *sd, unsigned int stream_id)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct max_ser_pipe *pipe = &ser->pipes[0];\n+\n+\tif (!ser->ops->set_pipe_stream_id)\n+\t\treturn -EOPNOTSUPP;\n+\n+\treturn ser->ops->set_pipe_stream_id(ser, pipe, stream_id);\n+}\n+\n+int max_ser_get_stream_id(struct v4l2_subdev *sd, unsigned int *stream_id)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct max_ser_pipe *pipe = &ser->pipes[0];\n+\n+\tif (!ser->ops->get_pipe_stream_id)\n+\t\treturn -EOPNOTSUPP;\n+\n+\t*stream_id = ser->ops->get_pipe_stream_id(ser, pipe);\n+\n+\treturn 0;\n+}\n+\n+unsigned int max_ser_get_supported_modes(struct v4l2_subdev *sd)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tstruct v4l2_subdev_state *state;\n+\tunsigned int modes = ser->ops->modes;\n+\n+\tstate = v4l2_subdev_lock_and_get_active_state(&priv->sd);\n+\n+\tif (max_ser_is_tpg_routed(priv, state))\n+\t\tmodes = BIT(ser->ops->tpg_mode);\n+\n+\tv4l2_subdev_unlock_state(state);\n+\n+\treturn modes;\n+}\n+\n+bool max_ser_supports_vc_remap(struct v4l2_subdev *sd)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\n+\treturn !!ser->ops->set_vc_remap;\n+}\n+\n+int max_ser_set_mode(struct v4l2_subdev *sd, enum max_serdes_gmsl_mode mode)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tint ret;\n+\n+\tif (!(ser->ops->modes & BIT(mode)))\n+\t\treturn -EINVAL;\n+\n+\tif (ser->mode == mode)\n+\t\treturn 0;\n+\n+\tif (ser->ops->set_tunnel_enable) {\n+\t\tbool tunnel_enable = mode == MAX_SERDES_GMSL_TUNNEL_MODE;\n+\n+\t\tret = ser->ops->set_tunnel_enable(ser, tunnel_enable);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tser->mode = mode;\n+\n+\treturn 0;\n+}\n+\n+int max_ser_set_vc_remaps(struct v4l2_subdev *sd,\n+\t\t\t  struct max_serdes_vc_remap *vc_remaps,\n+\t\t\t  int num_vc_remaps)\n+{\n+\tstruct max_ser_priv *priv = sd_to_priv(sd);\n+\tstruct max_ser *ser = priv->ser;\n+\tunsigned int mask = 0;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (!ser->ops->set_vc_remap)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tif (num_vc_remaps > ser->ops->num_vc_remaps)\n+\t\treturn -E2BIG;\n+\n+\tfor (i = 0; i < num_vc_remaps; i++) {\n+\t\tret = ser->ops->set_vc_remap(ser, i, &vc_remaps[i]);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tmask |= BIT(i);\n+\t}\n+\n+\tret = ser->ops->set_vc_remaps_enable(ser, mask);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i < num_vc_remaps; i++)\n+\t\tser->vc_remaps[i] = vc_remaps[i];\n+\n+\tser->num_vc_remaps = num_vc_remaps;\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_read_reg(struct i2c_adapter *adapter, u8 addr,\n+\t\t\t    u16 reg, u8 *val)\n+{\n+\tu8 buf[2] = { reg >> 8, reg & 0xff };\n+\tstruct i2c_msg msg[2] = {\n+\t\t{\n+\t\t\t.addr = addr,\n+\t\t\t.flags = 0,\n+\t\t\t.buf = buf,\n+\t\t\t.len = sizeof(buf),\n+\t\t},\n+\t\t{\n+\t\t\t.addr = addr,\n+\t\t\t.flags = I2C_M_RD,\n+\t\t\t.buf = buf,\n+\t\t\t.len = 1,\n+\t\t},\n+\t};\n+\tint ret;\n+\n+\tret = i2c_transfer(adapter, msg, ARRAY_SIZE(msg));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\t*val = buf[0];\n+\n+\treturn 0;\n+}\n+\n+static int max_ser_write_reg(struct i2c_adapter *adapter, u8 addr,\n+\t\t\t     u16 reg, u8 val)\n+{\n+\tu8 buf[3] = { reg >> 8, reg & 0xff, val };\n+\tstruct i2c_msg msg[1] = {\n+\t\t{\n+\t\t\t.addr = addr,\n+\t\t\t.flags = 0,\n+\t\t\t.buf = buf,\n+\t\t\t.len = sizeof(buf),\n+\t\t},\n+\t};\n+\tint ret;\n+\n+\tret = i2c_transfer(adapter, msg, ARRAY_SIZE(msg));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+int max_ser_reset(struct i2c_adapter *adapter, u8 addr)\n+{\n+\tint ret;\n+\tu8 val;\n+\n+\tret = max_ser_read_reg(adapter, addr, MAX_SER_CTRL0, &val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tval |= MAX_SER_CTRL0_RESET_ALL;\n+\n+\treturn max_ser_write_reg(adapter, addr, MAX_SER_CTRL0, val);\n+}\n+\n+int max_ser_wait_for_multiple(struct i2c_adapter *adapter, u8 *addrs,\n+\t\t\t      unsigned int num_addrs, u8 *current_addr)\n+{\n+\tunsigned int i, j;\n+\tint ret = 0;\n+\tu8 val;\n+\n+\tfor (i = 0; i < 10; i++) {\n+\t\tfor (j = 0; j < num_addrs; j++) {\n+\t\t\tret = max_ser_read_reg(adapter, addrs[j], MAX_SER_REG0, &val);\n+\t\t\tif (!ret && val) {\n+\t\t\t\t*current_addr = addrs[j];\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\n+\t\t\tmsleep(100);\n+\t\t}\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int max_ser_wait(struct i2c_adapter *adapter, u8 addr)\n+{\n+\tu8 current_addr;\n+\n+\treturn max_ser_wait_for_multiple(adapter, &addr, 1, &current_addr);\n+}\n+\n+int max_ser_fix_tx_ids(struct i2c_adapter *adapter, u8 addr)\n+{\n+\tunsigned int addr_regs[] = {\n+\t\tMAX_SER_CFGI_INFOFR_TR3,\n+\t\tMAX_SER_CFGL_SPI_TR3,\n+\t\tMAX_SER_CFGC_CC_TR3,\n+\t\tMAX_SER_CFGC_GPIO_TR3,\n+\t\tMAX_SER_CFGL_IIC_X_TR3,\n+\t\tMAX_SER_CFGL_IIC_Y_TR3,\n+\t};\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(addr_regs); i++) {\n+\t\tret = max_ser_write_reg(adapter, addr, addr_regs[i], addr);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int max_ser_change_address(struct i2c_adapter *adapter, u8 addr, u8 new_addr)\n+{\n+\tu8 val = FIELD_PREP(MAX_SER_REG0_DEV_ADDR, new_addr);\n+\n+\treturn max_ser_write_reg(adapter, addr, MAX_SER_REG0, val);\n+}\n+\n+MODULE_LICENSE(\"GPL\");\n+MODULE_IMPORT_NS(\"I2C_ATR\");\ndiff --git a/drivers/media/i2c/maxim-serdes/max_ser.h b/drivers/media/i2c/maxim-serdes/max_ser.h\nnew file mode 100644\nindex 000000000000..a9365be5e62d\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/max_ser.h\n@@ -0,0 +1,147 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2025 Analog Devices Inc.\n+ */\n+\n+#ifndef MAX_SER_H\n+#define MAX_SER_H\n+\n+#include <linux/i2c.h>\n+\n+#include <media/v4l2-mediabus.h>\n+\n+#include \"max_serdes.h\"\n+\n+#define MAX_SER_REG0\t\t\t\t0x0\n+#define MAX_SER_REG0_DEV_ADDR\t\t\tGENMASK(7, 1)\n+\n+#define MAX_SER_CTRL0\t\t\t\t0x10\n+#define MAX_SER_CTRL0_RESET_ALL\t\t\tBIT(7)\n+\n+#define MAX_SER_CFGI_INFOFR_TR3\t\t\t0x7b\n+#define MAX_SER_CFGL_SPI_TR3\t\t\t0x83\n+#define MAX_SER_CFGC_CC_TR3\t\t\t0x8b\n+#define MAX_SER_CFGC_GPIO_TR3\t\t\t0x93\n+#define MAX_SER_CFGL_IIC_X_TR3\t\t\t0xa3\n+#define MAX_SER_CFGL_IIC_Y_TR3\t\t\t0xab\n+\n+struct max_ser_phy {\n+\tunsigned int index;\n+\tstruct v4l2_mbus_config_mipi_csi2 mipi;\n+\tbool enabled;\n+\tbool active;\n+};\n+\n+struct max_ser_pipe_mode {\n+\tunsigned int soft_bpp;\n+\tunsigned int bpp;\n+\tbool dbl8;\n+\tbool dbl10;\n+\tbool dbl12;\n+};\n+\n+struct max_ser_pipe {\n+\tunsigned int index;\n+\tunsigned int phy_id;\n+\tunsigned int stream_id;\n+\tunsigned int *dts;\n+\tunsigned int num_dts;\n+\tunsigned int vcs;\n+\tstruct max_ser_pipe_mode mode;\n+\tbool enabled;\n+};\n+\n+struct max_ser;\n+\n+struct max_ser_ops {\n+\tunsigned int modes;\n+\tunsigned int num_pipes;\n+\tunsigned int num_dts_per_pipe;\n+\tunsigned int num_phys;\n+\tunsigned int num_i2c_xlates;\n+\tunsigned int num_vc_remaps;\n+\n+\tstruct max_serdes_phys_configs phys_configs;\n+\tstruct max_serdes_tpg_entries tpg_entries;\n+\tenum max_serdes_gmsl_mode tpg_mode;\n+\tunsigned int tpg_patterns;\n+\n+#ifdef CONFIG_VIDEO_ADV_DEBUG\n+\tint (*reg_read)(struct max_ser *ser, unsigned int reg, unsigned int *val);\n+\tint (*reg_write)(struct max_ser *ser, unsigned int reg, unsigned int val);\n+#endif\n+\tint (*log_status)(struct max_ser *ser);\n+\tint (*log_pipe_status)(struct max_ser *ser, struct max_ser_pipe *pipe);\n+\tint (*log_phy_status)(struct max_ser *ser, struct max_ser_phy *phy);\n+\tint (*init)(struct max_ser *ser);\n+\tint (*set_i2c_xlate)(struct max_ser *ser, unsigned int i,\n+\t\t\t     struct max_serdes_i2c_xlate *i2c_xlate);\n+\tint (*set_tunnel_enable)(struct max_ser *ser, bool enable);\n+\tint (*set_tpg)(struct max_ser *ser, const struct max_serdes_tpg_entry *entry);\n+\tint (*init_phy)(struct max_ser *ser, struct max_ser_phy *phy);\n+\tint (*set_phy_active)(struct max_ser *ser, struct max_ser_phy *phy,\n+\t\t\t      bool enable);\n+\tint (*set_pipe_enable)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t       bool enable);\n+\tint (*set_pipe_dt)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t   unsigned int i, unsigned int dt);\n+\tint (*set_pipe_dt_en)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t      unsigned int i, bool enable);\n+\tint (*set_pipe_vcs)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t    unsigned int vcs);\n+\tint (*set_pipe_mode)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t     struct max_ser_pipe_mode *mode);\n+\tint (*set_vc_remap)(struct max_ser *ser, unsigned int i,\n+\t\t\t    struct max_serdes_vc_remap *vc_remap);\n+\tint (*set_vc_remaps_enable)(struct max_ser *ser, unsigned int mask);\n+\tint (*set_pipe_stream_id)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t\t  unsigned int stream_id);\n+\tunsigned int (*get_pipe_stream_id)(struct max_ser *ser, struct max_ser_pipe *pipe);\n+\tint (*set_pipe_phy)(struct max_ser *ser, struct max_ser_pipe *pipe,\n+\t\t\t    struct max_ser_phy *phy);\n+};\n+\n+struct max_ser_priv;\n+\n+struct max_ser {\n+\tstruct max_ser_priv *priv;\n+\n+\tconst struct max_ser_ops *ops;\n+\n+\tstruct max_serdes_i2c_xlate *i2c_xlates;\n+\n+\tstruct max_ser_phy *phys;\n+\tstruct max_ser_pipe *pipes;\n+\tconst struct max_serdes_tpg_entry *tpg_entry;\n+\tenum max_serdes_tpg_pattern tpg_pattern;\n+\n+\tstruct max_serdes_vc_remap *vc_remaps;\n+\tunsigned int num_vc_remaps;\n+\n+\tunsigned int phys_config;\n+\tunsigned int active;\n+\tenum max_serdes_gmsl_mode mode;\n+};\n+\n+int max_ser_probe(struct i2c_client *client, struct max_ser *ser);\n+\n+int max_ser_remove(struct max_ser *ser);\n+\n+int max_ser_set_double_bpps(struct v4l2_subdev *sd, u32 double_bpps);\n+unsigned int max_ser_get_supported_modes(struct v4l2_subdev *sd);\n+int max_ser_set_mode(struct v4l2_subdev *sd, enum max_serdes_gmsl_mode mode);\n+bool max_ser_supports_vc_remap(struct v4l2_subdev *sd);\n+int max_ser_set_stream_id(struct v4l2_subdev *sd, unsigned int stream_id);\n+int max_ser_get_stream_id(struct v4l2_subdev *sd, unsigned int *stream_id);\n+int max_ser_set_vc_remaps(struct v4l2_subdev *sd, struct max_serdes_vc_remap *vc_remaps,\n+\t\t\t  int num_vc_remaps);\n+\n+int max_ser_reset(struct i2c_adapter *adapter, u8 addr);\n+int max_ser_wait(struct i2c_adapter *adapter, u8 addr);\n+int max_ser_wait_for_multiple(struct i2c_adapter *adapter, u8 *addrs,\n+\t\t\t      unsigned int num_addrs, u8 *current_addr);\n+\n+int max_ser_change_address(struct i2c_adapter *adapter, u8 addr, u8 new_addr);\n+int max_ser_fix_tx_ids(struct i2c_adapter *adapter, u8 addr);\n+\n+#endif // MAX_SER_H\n",
    "prefixes": [
        "v10",
        "14/22"
    ]
}