Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2220299/?format=api
{ "id": 2220299, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2220299/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260406-gmsl2-3_serdes-v10-15-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-15-645560fedca5@analog.com>", "date": "2026-04-06T20:14:54", "name": "[v10,15/22] media: i2c: add Maxim GMSL2/3 deserializer framework", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "31487db6f0faf2ee6a693190be18ef084237ab7a", "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-15-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/2220299/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2220299/checks/", "tags": {}, "headers": { "Return-Path": "\n <linux-gpio+bounces-34736-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=OVr2QCea;\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-34736-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=\"OVr2QCea\"", "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)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fqLKB5my7z1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 07 Apr 2026 06:19:42 +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 09CCC30B438F\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 6 Apr 2026 20:16:03 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 831873A2575;\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 7890A39FCA8;\n\tMon, 6 Apr 2026 20:15:22 +0000 (UTC)", "by smtp.kernel.org (Postfix) with ESMTPS id 5B96FC2BCB3;\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 52D0FFB5161;\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=pmkNc9KVQC7fAfS4GFR5bUsBzsAkHscOCM1TIcC12u5MD0+9MGViZ/hmT9iYYCgNO1RMnfYhStczsdXON0BGZCI+JtEKxiicxF8jtaVI0myiCzq9FukftCHO180bjcYIhSgnq40R85wqxcDwIikqW+eLZ4JhpNAoftnn8a9HBbQ=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775506522; c=relaxed/simple;\n\tbh=zbHtxqoYj2l63qpQyJwe/GGUbgm72v8z8B8wMlgl2z0=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=Wg+eyrtKNZLYxZLO/laQ3xKqIc9nm0YiQ/yIvUaxYyGQAwchoW1fUycZgBqox/9f9uQeQaSFWrLG6at1UZ8gYBYJ32CEiE6q5kLR8Fvo672YuSneQyPtwKVucoP40SJ25aHJqoXFNZPT5hRoy231+zHyT0wjl8QOHhNTVjH/0ug=", "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=OVr2QCea; 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=zbHtxqoYj2l63qpQyJwe/GGUbgm72v8z8B8wMlgl2z0=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From;\n\tb=OVr2QCeaqtPovl1nDJhvuoMNJu4pj+jUaaqk39udRKpuTPAvMeF+r4lE8SpTgxqBN\n\t Vnz/ZErsAFZOoY9i6v6zlerYYoKBcsEf1zJSTrY+bogr4NQBlRZS4AedPZiH6VFeHf\n\t r2GC3QEmLEwFV84YxFE2QdqevGD7ScRTUbzYhNLYdvFeegMLxiX4q+99rN4SnfFL5Y\n\t Fan4hCt3nFin+Fp7P0c5xDcnE3kYBTATncmrhhMMr+OdbH7FEpElAv1r4cGXukLIva\n\t rDDLsSORbtqn5UjUnQl5Z4Rn7z3P/G44t4+rjvDuh5dtkC0vYXbsDTjo6qGXMbCoju\n\t yeW9JmrUUpuMg==", "From": "Dumitru Ceclan via B4 Relay\n <devnull+dumitru.ceclan.analog.com@kernel.org>", "Date": "Mon, 06 Apr 2026 23:14:54 +0300", "Subject": "[PATCH v10 15/22] media: i2c: add Maxim GMSL2/3 deserializer\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-15-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=90113;\n i=dumitru.ceclan@analog.com; s=20240313; h=from:subject:message-id;\n bh=Hn9YIxQeO5NhcPqdG21IFaygB0OuJLqOJXUZMXeRnrE=;\n b=ljoZtLbgJ05eYRkjtPTiuHM+rV/p2giFbKohJ9elJQWGMuTdpRhoGAkr+WsZrLwBLOXOYMTRm\n djW4ZmeNEORCtDvNPFa7A0zVhZ3Mw61o99jy6blFuP+fZl2/GWH+/2I", "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 deserializer.\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_des.c | 3205 ++++++++++++++++++++++++++++++\n drivers/media/i2c/maxim-serdes/max_des.h | 156 ++\n 3 files changed, 3362 insertions(+), 1 deletion(-)", "diff": "diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile\nindex 17511cb03369..b54326a5c81b 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 max_ser.o\n+max-serdes-objs := max_serdes.o max_ser.o max_des.o\n obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o\ndiff --git a/drivers/media/i2c/maxim-serdes/max_des.c b/drivers/media/i2c/maxim-serdes/max_des.c\nnew file mode 100644\nindex 000000000000..a03101678360\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/max_des.c\n@@ -0,0 +1,3205 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Maxim GMSL2 Deserializer 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+#include <linux/regulator/consumer.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_des.h\"\n+#include \"max_ser.h\"\n+#include \"max_serdes.h\"\n+\n+#define MAX_DES_LINK_FREQUENCY_MIN\t\t100000000ull\n+#define MAX_DES_LINK_FREQUENCY_DEFAULT\t\t750000000ull\n+#define MAX_DES_LINK_FREQUENCY_MAX\t\t1250000000ull\n+\n+#define MAX_DES_NUM_PHYS\t\t\t4\n+#define MAX_DES_NUM_LINKS\t\t\t4\n+#define MAX_DES_NUM_PIPES\t\t\t8\n+\n+struct max_des_priv {\n+\tstruct max_des *des;\n+\n+\tstruct device *dev;\n+\tstruct i2c_client *client;\n+\tstruct i2c_atr *atr;\n+\tstruct i2c_mux_core *mux;\n+\n+\tstruct media_pad *pads;\n+\tstruct regulator **pocs;\n+\tstruct max_serdes_source *sources;\n+\tu64 *streams_masks;\n+\n+\tstruct notifier_block i2c_nb;\n+\tstruct v4l2_subdev sd;\n+\tstruct v4l2_async_notifier nf;\n+\tstruct v4l2_ctrl_handler ctrl_handler;\n+\n+\tstruct max_des_phy *unused_phy;\n+};\n+\n+struct max_des_remap_context {\n+\tenum max_serdes_gmsl_mode mode;\n+\t/* Mark whether TPG is enabled */\n+\tbool tpg;\n+\t/* Mark the PHYs to which each pipe is mapped. */\n+\tunsigned long pipe_phy_masks[MAX_DES_NUM_PIPES];\n+\t/* Mark the pipes in use. */\n+\tbool pipe_in_use[MAX_DES_NUM_PIPES];\n+\t/* Mark whether pipe has remapped VC ids. */\n+\tbool vc_ids_remapped[MAX_DES_NUM_PIPES];\n+\t/* Map between pipe VC ids and PHY VC ids. */\n+\tunsigned int vc_ids_map[MAX_DES_NUM_PIPES][MAX_DES_NUM_PHYS][MAX_SERDES_VC_ID_NUM];\n+\t/* Mark whether a pipe VC id has been mapped to a PHY VC id. */\n+\tunsigned long vc_ids_masks[MAX_DES_NUM_PIPES][MAX_DES_NUM_PHYS];\n+\t/* Mark whether a PHY VC id has been mapped. */\n+\tunsigned long dst_vc_ids_masks[MAX_DES_NUM_PHYS];\n+};\n+\n+struct max_des_mode_context {\n+\tbool phys_bpp8_shared_with_16[MAX_DES_NUM_PHYS];\n+\tbool pipes_bpp8_shared_with_16[MAX_DES_NUM_PIPES];\n+\tu32 phys_double_bpps[MAX_DES_NUM_PHYS];\n+\tu32 pipes_double_bpps[MAX_DES_NUM_PIPES];\n+};\n+\n+struct max_des_route_hw {\n+\tstruct max_serdes_source *source;\n+\tstruct max_des_pipe *pipe;\n+\tstruct max_des_phy *phy;\n+\tstruct v4l2_mbus_frame_desc_entry entry;\n+\tbool is_tpg;\n+};\n+\n+struct max_des_link_hw {\n+\tstruct max_serdes_source *source;\n+\tstruct max_des_link *link;\n+\tstruct max_des_pipe *pipe;\n+};\n+\n+static inline struct max_des_priv *sd_to_priv(struct v4l2_subdev *sd)\n+{\n+\treturn container_of(sd, struct max_des_priv, sd);\n+}\n+\n+static inline struct max_des_priv *nf_to_priv(struct v4l2_async_notifier *nf)\n+{\n+\treturn container_of(nf, struct max_des_priv, nf);\n+}\n+\n+static inline struct max_des_priv *ctrl_to_priv(struct v4l2_ctrl_handler *handler)\n+{\n+\treturn container_of(handler, struct max_des_priv, ctrl_handler);\n+}\n+\n+static inline bool max_des_pad_is_sink(struct max_des *des, u32 pad)\n+{\n+\treturn pad < des->info->num_links;\n+}\n+\n+static inline bool max_des_pad_is_source(struct max_des *des, u32 pad)\n+{\n+\treturn pad >= des->info->num_links &&\n+\t pad < des->info->num_links + des->info->num_phys;\n+}\n+\n+static inline bool max_des_pad_is_tpg(struct max_des *des, u32 pad)\n+{\n+\treturn pad == des->info->num_links + des->info->num_phys;\n+}\n+\n+static inline unsigned int max_des_link_to_pad(struct max_des *des,\n+\t\t\t\t\t struct max_des_link *link)\n+{\n+\treturn link->index;\n+}\n+\n+static inline unsigned int max_des_phy_to_pad(struct max_des *des,\n+\t\t\t\t\t struct max_des_phy *phy)\n+{\n+\treturn phy->index + des->info->num_links;\n+}\n+\n+static inline unsigned int max_des_num_pads(struct max_des *des)\n+{\n+\treturn des->info->num_links + des->info->num_phys +\n+\t (des->ops->set_tpg ? 1 : 0);\n+}\n+\n+static struct max_des_phy *max_des_pad_to_phy(struct max_des *des, u32 pad)\n+{\n+\tif (!max_des_pad_is_source(des, pad))\n+\t\treturn NULL;\n+\n+\treturn &des->phys[pad - des->info->num_links];\n+}\n+\n+static struct max_des_link *max_des_pad_to_link(struct max_des *des, u32 pad)\n+{\n+\tif (!max_des_pad_is_sink(des, pad))\n+\t\treturn NULL;\n+\n+\treturn &des->links[pad];\n+}\n+\n+static struct max_des_pipe *\n+max_des_find_link_pipe(struct max_des *des, struct max_des_link *link)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\tstruct max_des_pipe *pipe = &des->pipes[i];\n+\n+\t\tif (pipe->link_id == link->index)\n+\t\t\treturn pipe;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct max_serdes_source *\n+max_des_get_link_source(struct max_des_priv *priv, struct max_des_link *link)\n+{\n+\treturn &priv->sources[link->index];\n+}\n+\n+static const struct max_serdes_tpg_entry *\n+max_des_find_tpg_entry(struct max_des *des, 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 < des->info->tpg_entries.num_entries; i++) {\n+\t\tentry = &des->info->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 == des->info->tpg_entries.num_entries)\n+\t\treturn NULL;\n+\n+\treturn &des->info->tpg_entries.entries[i];\n+}\n+\n+static const struct max_serdes_tpg_entry *\n+max_des_find_state_tpg_entry(struct max_des *des, 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_des_find_tpg_entry(des, 0, fmt->width, fmt->height, fmt->code,\n+\t\t\t\t in->numerator, in->denominator);\n+}\n+\n+static int max_des_get_tpg_fd_entry_state(struct max_des *des,\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_des_find_state_tpg_entry(des, 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_des_tpg_route_to_hw(struct max_des_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_des_route_hw *hw)\n+{\n+\tstruct max_des *des = priv->des;\n+\n+\t/* TPG injects its data into all pipes, but use pipe 0 for simplicity. */\n+\thw->pipe = &des->pipes[0];\n+\n+\thw->phy = max_des_pad_to_phy(des, route->source_pad);\n+\tif (!hw->phy)\n+\t\treturn -ENOENT;\n+\n+\treturn max_des_get_tpg_fd_entry_state(des, state, &hw->entry,\n+\t\t\t\t\t route->sink_pad);\n+}\n+\n+static int max_des_route_to_hw(struct max_des_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_des_route_hw *hw)\n+{\n+\tstruct max_des *des = priv->des;\n+\tstruct v4l2_mbus_frame_desc fd;\n+\tstruct max_des_link *link;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tmemset(hw, 0, sizeof(*hw));\n+\n+\thw->is_tpg = max_des_pad_is_tpg(des, route->sink_pad);\n+\tif (hw->is_tpg)\n+\t\treturn max_des_tpg_route_to_hw(priv, state, route, hw);\n+\n+\tlink = max_des_pad_to_link(des, route->sink_pad);\n+\tif (!link)\n+\t\treturn -ENOENT;\n+\n+\thw->phy = max_des_pad_to_phy(des, route->source_pad);\n+\tif (!hw->phy)\n+\t\treturn -ENOENT;\n+\n+\thw->pipe = max_des_find_link_pipe(des, link);\n+\tif (!hw->pipe)\n+\t\treturn -ENOENT;\n+\n+\thw->source = max_des_get_link_source(priv, link);\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_des_link_to_hw(struct max_des_priv *priv,\n+\t\t\t struct max_des_link *link,\n+\t\t\t struct max_des_link_hw *hw)\n+{\n+\tstruct max_des *des = priv->des;\n+\n+\tmemset(hw, 0, sizeof(*hw));\n+\n+\thw->link = link;\n+\n+\thw->pipe = max_des_find_link_pipe(des, hw->link);\n+\tif (!hw->pipe)\n+\t\treturn -ENOENT;\n+\n+\thw->source = max_des_get_link_source(priv, hw->link);\n+\n+\treturn 0;\n+}\n+\n+static int max_des_link_index_to_hw(struct max_des_priv *priv, unsigned int i,\n+\t\t\t\t struct max_des_link_hw *hw)\n+{\n+\treturn max_des_link_to_hw(priv, &priv->des->links[i], hw);\n+}\n+\n+static int max_des_set_pipe_remaps(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct max_des_remap *remaps,\n+\t\t\t\t unsigned int num_remaps)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int mask = 0;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (!des->ops->set_pipe_remap)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < num_remaps; i++) {\n+\t\tret = des->ops->set_pipe_remap(des, pipe, i, &remaps[i]);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tmask |= BIT(i);\n+\t}\n+\n+\treturn des->ops->set_pipe_remaps_enable(des, pipe, mask);\n+}\n+\n+static int max_des_set_pipe_vc_remaps(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct max_serdes_vc_remap *vc_remaps,\n+\t\t\t\t unsigned int num_vc_remaps)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int mask = 0;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < num_vc_remaps; i++) {\n+\t\tret = des->ops->set_pipe_vc_remap(des, pipe, i, &vc_remaps[i]);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tmask |= BIT(i);\n+\t}\n+\n+\treturn des->ops->set_pipe_vc_remaps_enable(des, pipe, mask);\n+}\n+\n+static int max_des_map_src_dst_vc_id(struct max_des_remap_context *context,\n+\t\t\t\t unsigned int pipe_id, unsigned int phy_id,\n+\t\t\t\t unsigned int src_vc_id, bool keep_vc)\n+{\n+\tunsigned int vc_id;\n+\n+\tif (src_vc_id >= MAX_SERDES_VC_ID_NUM)\n+\t\treturn -E2BIG;\n+\n+\tif (context->vc_ids_masks[pipe_id][phy_id] & BIT(src_vc_id))\n+\t\treturn 0;\n+\n+\tif (keep_vc && !(context->dst_vc_ids_masks[phy_id] & BIT(src_vc_id)))\n+\t\tvc_id = src_vc_id;\n+\telse\n+\t\tvc_id = ffz(context->dst_vc_ids_masks[phy_id]);\n+\n+\tif (vc_id != src_vc_id)\n+\t\tcontext->vc_ids_remapped[pipe_id] = true;\n+\n+\tif (vc_id >= MAX_SERDES_VC_ID_NUM)\n+\t\treturn -E2BIG;\n+\n+\tcontext->pipe_phy_masks[pipe_id] |= BIT(phy_id);\n+\tcontext->dst_vc_ids_masks[phy_id] |= BIT(vc_id);\n+\n+\tcontext->vc_ids_map[pipe_id][phy_id][src_vc_id] = vc_id;\n+\tcontext->vc_ids_masks[pipe_id][phy_id] |= BIT(src_vc_id);\n+\n+\treturn 0;\n+}\n+\n+static int max_des_get_src_dst_vc_id(struct max_des_remap_context *context,\n+\t\t\t\t unsigned int pipe_id, unsigned int phy_id,\n+\t\t\t\t unsigned int src_vc_id, unsigned int *dst_vc_id)\n+{\n+\tif (!(context->vc_ids_masks[pipe_id][phy_id] & BIT(src_vc_id)))\n+\t\treturn -ENOENT;\n+\n+\t*dst_vc_id = context->vc_ids_map[pipe_id][phy_id][src_vc_id];\n+\n+\treturn 0;\n+}\n+\n+static int max_des_populate_remap_usage(struct max_des_priv *priv,\n+\t\t\t\t\tstruct max_des_remap_context *context,\n+\t\t\t\t\tstruct 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_des_route_hw hw;\n+\n+\t\tret = max_des_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\tcontext->tpg = true;\n+\n+\t\tcontext->pipe_in_use[hw.pipe->index] = true;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_get_supported_modes(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t unsigned int *modes)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\t*modes = des->info->modes;\n+\n+\tif (context->tpg)\n+\t\t*modes = BIT(des->info->tpg_mode);\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link_hw hw;\n+\n+\t\tret = max_des_link_index_to_hw(priv, i, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!hw.source->sd)\n+\t\t\tcontinue;\n+\n+\t\tif (!context->pipe_in_use[hw.pipe->index])\n+\t\t\tcontinue;\n+\n+\t\t*modes &= max_ser_get_supported_modes(hw.source->sd);\n+\t}\n+\n+\t/*\n+\t * Serializers need to all be in the same mode because of hardware\n+\t * issues when running them in mixed modes.\n+\t */\n+\tif (!*modes)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_populate_remap_context_mode(struct max_des_priv *priv,\n+\t\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t\t unsigned int modes)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tcontext->mode = MAX_SERDES_GMSL_PIXEL_MODE;\n+\n+\t/*\n+\t * If pixel mode is the only supported mode, do not try to see if\n+\t * tunnel mode can be used.\n+\t */\n+\tif (modes == BIT(MAX_SERDES_GMSL_PIXEL_MODE))\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link_hw hw;\n+\n+\t\tret = max_des_link_index_to_hw(priv, i, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!hw.source->sd)\n+\t\t\tcontinue;\n+\n+\t\tif (!context->pipe_in_use[hw.pipe->index])\n+\t\t\tcontinue;\n+\n+\t\tif (hweight_long(context->pipe_phy_masks[hw.pipe->index]) == 1 &&\n+\t\t (!context->vc_ids_remapped[hw.pipe->index] ||\n+\t\t max_ser_supports_vc_remap(hw.source->sd) ||\n+\t\t des->ops->set_pipe_vc_remap))\n+\t\t\tcontinue;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tcontext->mode = MAX_SERDES_GMSL_TUNNEL_MODE;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_should_keep_vc(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_route_hw *hw,\n+\t\t\t\t unsigned int modes)\n+{\n+\tstruct max_des *des = priv->des;\n+\n+\t/* Pixel mode deserializers always have the ability to remap VCs. */\n+\tif (modes == BIT(MAX_SERDES_GMSL_PIXEL_MODE))\n+\t\treturn false;\n+\n+\tif (des->ops->set_pipe_vc_remap)\n+\t\treturn false;\n+\n+\tif (!hw->is_tpg && hw->source && hw->source->sd &&\n+\t max_ser_supports_vc_remap(hw->source->sd))\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static int max_des_populate_remap_context(struct max_des_priv *priv,\n+\t\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t\t struct v4l2_subdev_state *state)\n+{\n+\tstruct v4l2_subdev_route *route;\n+\tunsigned int modes;\n+\tint ret;\n+\n+\tret = max_des_populate_remap_usage(priv, context, state);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_get_supported_modes(priv, context, &modes);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_des_route_hw hw;\n+\t\tbool keep_vc;\n+\n+\t\tret = max_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tkeep_vc = max_des_should_keep_vc(priv, &hw, modes);\n+\n+\t\tret = max_des_map_src_dst_vc_id(context, hw.pipe->index, hw.phy->index,\n+\t\t\t\t\t\thw.entry.bus.csi2.vc, keep_vc);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn max_des_populate_remap_context_mode(priv, context, modes);\n+}\n+\n+static int max_des_populate_mode_context(struct max_des_priv *priv,\n+\t\t\t\t\t struct max_des_mode_context *context,\n+\t\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t\t enum max_serdes_gmsl_mode mode)\n+{\n+\tbool bpp8_not_shared_with_16_phys[MAX_DES_NUM_PHYS] = { 0 };\n+\tu32 undoubled_bpps_phys[MAX_DES_NUM_PHYS] = { 0 };\n+\tu32 bpps_pipes[MAX_DES_NUM_PIPES] = { 0 };\n+\tstruct max_des *des = priv->des;\n+\tstruct v4l2_subdev_route *route;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (mode != MAX_SERDES_GMSL_PIXEL_MODE)\n+\t\treturn 0;\n+\n+\t/*\n+\t * Go over all streams and gather the bpps for all pipes.\n+\t *\n+\t * Then, go over all the streams again and check if the\n+\t * current stream is doubled.\n+\t *\n+\t * If the current stream is doubled, add it to a doubled mask for both\n+\t * the pipe and the PHY.\n+\t *\n+\t * If the current stream is not doubled, add it to a local undoubled\n+\t * mask for the PHY.\n+\t *\n+\t * Also, track whether an 8bpp stream is shared with any bpp > 8 on both\n+\t * the PHYs and the pipes, since that needs to be special cased.\n+\t *\n+\t * After going over all the streams, remove the undoubled streams from\n+\t * the doubled ones. Doubled and undoubled streams cannot be streamed\n+\t * over the same PHY.\n+\t *\n+\t * Then, do a second pass to remove the undoubled streams from the pipes.\n+\t *\n+\t * This operation cannot be done in a single pass because any pipe might\n+\t * generate an undoubled stream for a specific bpp, causing already\n+\t * processed pipes to need to have their doubled bpps updated.\n+\t */\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_des_route_hw hw;\n+\t\tunsigned int bpp;\n+\n+\t\tret = max_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\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_pipes[hw.pipe->index] |= BIT(bpp);\n+\t}\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tunsigned int bpp, min_bpp, max_bpp, doubled_bpp;\n+\t\tunsigned int pipe_id, phy_id;\n+\t\tstruct max_des_route_hw hw;\n+\t\tu32 sink_bpps;\n+\n+\t\tret = max_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = max_serdes_get_fd_bpp(&hw.entry, &bpp);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tsink_bpps = bpps_pipes[hw.pipe->index];\n+\n+\t\tret = max_serdes_process_bpps(priv->dev, sink_bpps, ~0U, &doubled_bpp);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tmin_bpp = __ffs(sink_bpps);\n+\t\tmax_bpp = __fls(sink_bpps);\n+\t\tpipe_id = hw.pipe->index;\n+\t\tphy_id = hw.phy->index;\n+\n+\t\tif (bpp == doubled_bpp) {\n+\t\t\tcontext->phys_double_bpps[phy_id] |= BIT(bpp);\n+\t\t\tcontext->pipes_double_bpps[pipe_id] |= BIT(bpp);\n+\t\t} else {\n+\t\t\tundoubled_bpps_phys[phy_id] |= BIT(bpp);\n+\t\t}\n+\n+\t\tif (min_bpp == 8 && max_bpp > 8) {\n+\t\t\tcontext->phys_bpp8_shared_with_16[phy_id] = true;\n+\t\t\tcontext->pipes_bpp8_shared_with_16[pipe_id] = true;\n+\t\t} else if (min_bpp == 8 && max_bpp == 8) {\n+\t\t\tbpp8_not_shared_with_16_phys[phy_id] = true;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tif (context->phys_bpp8_shared_with_16[i] && bpp8_not_shared_with_16_phys[i]) {\n+\t\t\tdev_err(priv->dev,\n+\t\t\t\t\"Cannot stream 8bpp coming from pipes padded to 16bpp \"\n+\t\t\t\t\"and pipes not padded to 16bpp on the same PHY\\n\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_phys; i++)\n+\t\tcontext->phys_double_bpps[i] &= ~undoubled_bpps_phys[i];\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_des_route_hw hw;\n+\n+\t\tret = max_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tcontext->pipes_double_bpps[hw.pipe->index] &=\n+\t\t\tcontext->phys_double_bpps[hw.phy->index];\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_add_vc_remap(struct max_des *des, struct max_serdes_vc_remap *vc_remaps,\n+\t\t\t\tunsigned int *num_vc_remaps, unsigned int src_vc_id,\n+\t\t\t\tunsigned int dst_vc_id)\n+{\n+\tstruct max_serdes_vc_remap *vc_remap;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < *num_vc_remaps; i++) {\n+\t\tvc_remap = &vc_remaps[i];\n+\n+\t\tif (vc_remap->src == src_vc_id && vc_remap->dst == dst_vc_id)\n+\t\t\treturn 0;\n+\t}\n+\n+\tif (*num_vc_remaps == MAX_SERDES_VC_ID_NUM)\n+\t\treturn -E2BIG;\n+\n+\tvc_remaps[*num_vc_remaps].src = src_vc_id;\n+\tvc_remaps[*num_vc_remaps].dst = dst_vc_id;\n+\n+\t(*num_vc_remaps)++;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_get_pipe_vc_remaps(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct max_serdes_vc_remap *vc_remaps,\n+\t\t\t\t unsigned int *num_vc_remaps,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t u64 *streams_masks, bool with_tpg)\n+{\n+\tstruct max_des *des = priv->des;\n+\tstruct v4l2_subdev_route *route;\n+\tint ret;\n+\n+\t*num_vc_remaps = 0;\n+\n+\tif (context->mode != MAX_SERDES_GMSL_TUNNEL_MODE)\n+\t\treturn 0;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tunsigned int src_vc_id, dst_vc_id;\n+\t\tstruct max_des_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_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!with_tpg && hw.is_tpg)\n+\t\t\tcontinue;\n+\n+\t\tif (hw.pipe != pipe)\n+\t\t\tcontinue;\n+\n+\t\tsrc_vc_id = hw.entry.bus.csi2.vc;\n+\n+\t\tret = max_des_get_src_dst_vc_id(context, pipe->index, hw.phy->index,\n+\t\t\t\t\t\tsrc_vc_id, &dst_vc_id);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = max_des_add_vc_remap(des, vc_remaps, num_vc_remaps,\n+\t\t\t\t\t src_vc_id, dst_vc_id);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void max_des_get_pipe_mode(struct max_des_mode_context *context,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct max_des_pipe_mode *mode)\n+{\n+\tu32 double_bpps = context->pipes_double_bpps[pipe->index];\n+\n+\tif ((double_bpps & BIT(8)) &&\n+\t !context->pipes_bpp8_shared_with_16[pipe->index]) {\n+\t\tmode->dbl8 = true;\n+\t\tmode->dbl8mode = true;\n+\t}\n+}\n+\n+static void max_des_get_phy_mode(struct max_des_mode_context *context,\n+\t\t\t\t struct max_des_phy *phy,\n+\t\t\t\t struct max_des_phy_mode *mode)\n+{\n+\tbool bpp8_pipe_shared_with_16 = context->phys_bpp8_shared_with_16[phy->index];\n+\tu32 double_bpps = context->phys_double_bpps[phy->index];\n+\n+\tif (BIT(8) & double_bpps) {\n+\t\tif (bpp8_pipe_shared_with_16)\n+\t\t\tmode->alt2_mem_map8 = true;\n+\t\telse\n+\t\t\tmode->alt_mem_map8 = true;\n+\t}\n+\n+\tif (BIT(10) & double_bpps)\n+\t\tmode->alt_mem_map10 = true;\n+\n+\tif (BIT(12) & double_bpps)\n+\t\tmode->alt_mem_map12 = true;\n+}\n+\n+static int max_des_set_modes(struct max_des_priv *priv,\n+\t\t\t struct max_des_mode_context *context)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tstruct max_des_phy *phy = &des->phys[i];\n+\t\tstruct max_des_phy_mode mode = { 0 };\n+\n+\t\tmax_des_get_phy_mode(context, phy, &mode);\n+\n+\t\tif (phy->mode.alt_mem_map8 == mode.alt_mem_map8 &&\n+\t\t phy->mode.alt_mem_map10 == mode.alt_mem_map10 &&\n+\t\t phy->mode.alt_mem_map12 == mode.alt_mem_map12 &&\n+\t\t phy->mode.alt2_mem_map8 == mode.alt2_mem_map8)\n+\t\t\tcontinue;\n+\n+\t\tif (des->ops->set_phy_mode) {\n+\t\t\tret = des->ops->set_phy_mode(des, phy, &mode);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tphy->mode = mode;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\tstruct max_des_pipe *pipe = &des->pipes[i];\n+\t\tstruct max_des_pipe_mode mode = { 0 };\n+\n+\t\tmax_des_get_pipe_mode(context, pipe, &mode);\n+\n+\t\tif (pipe->mode.dbl8 == mode.dbl8 &&\n+\t\t pipe->mode.dbl10 == mode.dbl10 &&\n+\t\t pipe->mode.dbl12 == mode.dbl12 &&\n+\t\t pipe->mode.dbl8mode == mode.dbl8mode &&\n+\t\t pipe->mode.dbl10mode == mode.dbl10mode)\n+\t\t\tcontinue;\n+\n+\t\tif (des->ops->set_pipe_mode) {\n+\t\t\tret = des->ops->set_pipe_mode(des, pipe, &mode);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tpipe->mode = mode;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link_hw hw;\n+\t\tu32 pipe_double_bpps = 0;\n+\n+\t\tret = max_des_link_index_to_hw(priv, i, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!hw.source->sd)\n+\t\t\tcontinue;\n+\n+\t\tpipe_double_bpps = context->pipes_double_bpps[hw.pipe->index];\n+\n+\t\tret = max_ser_set_double_bpps(hw.source->sd, pipe_double_bpps);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_set_tunnel(struct max_des_priv *priv,\n+\t\t\t struct max_des_remap_context *context)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (des->ops->set_pipe_tunnel_enable) {\n+\t\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\t\tstruct max_des_pipe *pipe = &des->pipes[i];\n+\t\t\tbool tunnel_mode = context->mode == MAX_SERDES_GMSL_TUNNEL_MODE;\n+\n+\t\t\tret = des->ops->set_pipe_tunnel_enable(des, pipe, tunnel_mode);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link_hw hw;\n+\n+\t\tret = max_des_link_index_to_hw(priv, i, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!hw.source->sd)\n+\t\t\tcontinue;\n+\n+\t\tif (!context->pipe_in_use[hw.pipe->index])\n+\t\t\tcontinue;\n+\n+\t\tret = max_ser_set_mode(hw.source->sd, context->mode);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tdes->mode = context->mode;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_set_vc_remaps(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t u64 *streams_masks)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (des->ops->set_pipe_vc_remap)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_serdes_vc_remap vc_remaps[MAX_SERDES_VC_ID_NUM];\n+\t\tstruct max_des_link_hw hw;\n+\t\tunsigned int num_vc_remaps;\n+\n+\t\tret = max_des_link_index_to_hw(priv, i, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!hw.source->sd)\n+\t\t\tcontinue;\n+\n+\t\tif (!max_ser_supports_vc_remap(hw.source->sd))\n+\t\t\tcontinue;\n+\n+\t\tret = max_des_get_pipe_vc_remaps(priv, context, hw.pipe,\n+\t\t\t\t\t\t vc_remaps, &num_vc_remaps,\n+\t\t\t\t\t\t state, streams_masks, false);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = max_ser_set_vc_remaps(hw.source->sd, vc_remaps, num_vc_remaps);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_set_pipes_stream_id(struct max_des_priv *priv)\n+{\n+\tbool stream_id_usage[MAX_SERDES_STREAMS_NUM] = { 0 };\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link_hw hw;\n+\t\tunsigned int stream_id;\n+\n+\t\tret = max_des_link_index_to_hw(priv, i, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (!hw.link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!hw.source->sd)\n+\t\t\tcontinue;\n+\n+\t\tstream_id = hw.pipe->stream_id;\n+\n+\t\tret = max_ser_set_stream_id(hw.source->sd, stream_id);\n+\t\tif (ret == -EOPNOTSUPP) {\n+\t\t\t/*\n+\t\t\t * Serializer does not support setting the stream id,\n+\t\t\t * retrieve its hardcoded stream id.\n+\t\t\t */\n+\t\t\tret = max_ser_get_stream_id(hw.source->sd, &stream_id);\n+\t\t}\n+\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (stream_id_usage[stream_id] && des->info->needs_unique_stream_id) {\n+\t\t\tdev_err(priv->dev, \"Duplicate stream id %u\\n\", stream_id);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tret = des->ops->set_pipe_stream_id(des, hw.pipe, stream_id);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tstream_id_usage[stream_id] = true;\n+\t\thw.pipe->stream_id = stream_id;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_set_pipes_phy(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_remap_context *context)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (!des->ops->set_pipe_phy && !des->ops->set_pipe_tunnel_phy)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\tstruct max_des_pipe *pipe = &des->pipes[i];\n+\t\tstruct max_des_phy *phy;\n+\t\tunsigned int phy_id;\n+\n+\t\tphy_id = find_first_bit(&context->pipe_phy_masks[pipe->index],\n+\t\t\t\t\tdes->info->num_phys);\n+\n+\t\tif (priv->unused_phy &&\n+\t\t (context->mode != MAX_SERDES_GMSL_TUNNEL_MODE ||\n+\t\t phy_id == des->info->num_phys))\n+\t\t\tphy_id = priv->unused_phy->index;\n+\n+\t\tif (phy_id != des->info->num_phys) {\n+\t\t\tphy = &des->phys[phy_id];\n+\n+\t\t\tif (context->mode == MAX_SERDES_GMSL_PIXEL_MODE &&\n+\t\t\t des->ops->set_pipe_phy)\n+\t\t\t\tret = des->ops->set_pipe_phy(des, pipe, phy);\n+\t\t\telse if (context->mode == MAX_SERDES_GMSL_TUNNEL_MODE &&\n+\t\t\t\t des->ops->set_pipe_tunnel_phy)\n+\t\t\t\tret = des->ops->set_pipe_tunnel_phy(des, pipe, phy);\n+\t\t\telse\n+\t\t\t\tret = 0;\n+\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tpipe->phy_id = phy_id;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_add_remap(struct max_des *des, struct max_des_remap *remaps,\n+\t\t\t unsigned int *num_remaps, unsigned int phy_id,\n+\t\t\t unsigned int src_vc_id, unsigned int dst_vc_id,\n+\t\t\t unsigned int dt)\n+{\n+\tstruct max_des_remap *remap;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < *num_remaps; i++) {\n+\t\tremap = &remaps[i];\n+\n+\t\tif (remap->from_dt == dt && remap->to_dt == dt &&\n+\t\t remap->from_vc == src_vc_id && remap->to_vc == dst_vc_id &&\n+\t\t remap->phy == phy_id)\n+\t\t\treturn 0;\n+\t}\n+\n+\tif (*num_remaps == des->info->num_remaps_per_pipe)\n+\t\treturn -E2BIG;\n+\n+\tremap = &remaps[*num_remaps];\n+\tremap->from_dt = dt;\n+\tremap->from_vc = src_vc_id;\n+\tremap->to_dt = dt;\n+\tremap->to_vc = dst_vc_id;\n+\tremap->phy = phy_id;\n+\n+\t(*num_remaps)++;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_add_remaps(struct max_des *des, struct max_des_remap *remaps,\n+\t\t\t unsigned int *num_remaps, unsigned int phy_id,\n+\t\t\t unsigned int src_vc_id, unsigned int dst_vc_id,\n+\t\t\t unsigned int dt)\n+{\n+\tint ret;\n+\n+\tret = max_des_add_remap(des, remaps, num_remaps, phy_id,\n+\t\t\t\tsrc_vc_id, dst_vc_id, dt);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_add_remap(des, remaps, num_remaps, phy_id,\n+\t\t\t\tsrc_vc_id, dst_vc_id, MIPI_CSI2_DT_FS);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_add_remap(des, remaps, num_remaps, phy_id,\n+\t\t\t\tsrc_vc_id, dst_vc_id, MIPI_CSI2_DT_FE);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_get_pipe_remaps(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct max_des_remap *remaps,\n+\t\t\t\t unsigned int *num_remaps,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t u64 *streams_masks)\n+{\n+\tstruct v4l2_mbus_frame_desc_entry tpg_entry = { 0 };\n+\tstruct max_des *des = priv->des;\n+\tstruct v4l2_subdev_route *route;\n+\tbool is_tpg_pipe = true;\n+\tint ret;\n+\n+\t*num_remaps = 0;\n+\n+\tif (context->mode != MAX_SERDES_GMSL_PIXEL_MODE)\n+\t\treturn 0;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_des_route_hw hw;\n+\t\tunsigned int src_vc_id, dst_vc_id;\n+\n+\t\tif (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))\n+\t\t\tcontinue;\n+\n+\t\tret = max_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (hw.is_tpg && hw.pipe != pipe) {\n+\t\t\tis_tpg_pipe = false;\n+\t\t\ttpg_entry = hw.entry;\n+\t\t}\n+\n+\t\tif (hw.pipe != pipe)\n+\t\t\tcontinue;\n+\n+\t\tsrc_vc_id = hw.entry.bus.csi2.vc;\n+\n+\t\tret = max_des_get_src_dst_vc_id(context, pipe->index, hw.phy->index,\n+\t\t\t\t\t\tsrc_vc_id, &dst_vc_id);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = max_des_add_remaps(des, remaps, num_remaps, hw.phy->index,\n+\t\t\t\t\t src_vc_id, dst_vc_id,\n+\t\t\t\t\t hw.entry.bus.csi2.dt);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * TPG mode is only handled on pipe 0, but the TPG pollutes other pipes\n+\t * with the same data.\n+\t * For devices that do not support setting the default PHY of a pipe,\n+\t * we want to filter out this data so it does not end up on the wrong\n+\t * PHY.\n+\t * Devices that support setting the default PHY of a pipe already use it\n+\t * to route unused pipes to an unused PHY.\n+\t */\n+\tif (context->tpg && !is_tpg_pipe && !des->ops->set_pipe_phy &&\n+\t priv->unused_phy) {\n+\t\tret = max_des_add_remaps(des, remaps, num_remaps,\n+\t\t\t\t\t priv->unused_phy->index,\n+\t\t\t\t\t tpg_entry.bus.csi2.vc,\n+\t\t\t\t\t tpg_entry.bus.csi2.vc,\n+\t\t\t\t\t tpg_entry.bus.csi2.dt);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_update_pipe_vc_remaps(struct max_des_priv *priv,\n+\t\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t\t u64 *streams_masks)\n+{\n+\tstruct max_des *des = priv->des;\n+\tstruct max_serdes_vc_remap *vc_remaps;\n+\tunsigned int num_vc_remaps;\n+\tint ret;\n+\n+\tif (!des->ops->set_pipe_vc_remap)\n+\t\treturn 0;\n+\n+\tvc_remaps = devm_kcalloc(priv->dev, MAX_SERDES_VC_ID_NUM,\n+\t\t\t\t sizeof(*vc_remaps), GFP_KERNEL);\n+\tif (!vc_remaps)\n+\t\treturn -ENOMEM;\n+\n+\tret = max_des_get_pipe_vc_remaps(priv, context, pipe, vc_remaps, &num_vc_remaps,\n+\t\t\t\t\t state, streams_masks, true);\n+\tif (ret)\n+\t\tgoto err_free_new_vc_remaps;\n+\n+\tret = max_des_set_pipe_vc_remaps(priv, pipe, vc_remaps, num_vc_remaps);\n+\tif (ret)\n+\t\tgoto err_free_new_vc_remaps;\n+\n+\tif (pipe->num_vc_remaps)\n+\t\tdevm_kfree(priv->dev, pipe->vc_remaps);\n+\n+\tpipe->vc_remaps = vc_remaps;\n+\tpipe->num_vc_remaps = num_vc_remaps;\n+\n+\treturn 0;\n+\n+err_free_new_vc_remaps:\n+\tdevm_kfree(priv->dev, vc_remaps);\n+\n+\treturn ret;\n+}\n+\n+static int max_des_update_pipe_remaps(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_remap_context *context,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t u64 *streams_masks)\n+{\n+\tstruct max_des *des = priv->des;\n+\tstruct max_des_remap *remaps;\n+\tunsigned int num_remaps;\n+\tint ret;\n+\n+\tif (!des->ops->set_pipe_remap)\n+\t\treturn 0;\n+\n+\tremaps = devm_kcalloc(priv->dev, des->info->num_remaps_per_pipe,\n+\t\t\t sizeof(*remaps), GFP_KERNEL);\n+\tif (!remaps)\n+\t\treturn -ENOMEM;\n+\n+\tret = max_des_get_pipe_remaps(priv, context, pipe, remaps, &num_remaps,\n+\t\t\t\t state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_free_new_remaps;\n+\n+\tret = max_des_set_pipe_remaps(priv, pipe, remaps, num_remaps);\n+\tif (ret)\n+\t\tgoto err_free_new_remaps;\n+\n+\tif (pipe->remaps)\n+\t\tdevm_kfree(priv->dev, pipe->remaps);\n+\n+\tpipe->remaps = remaps;\n+\tpipe->num_remaps = num_remaps;\n+\n+\treturn 0;\n+\n+err_free_new_remaps:\n+\tdevm_kfree(priv->dev, remaps);\n+\n+\treturn ret;\n+}\n+\n+static int max_des_update_pipe_enable(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_pipe *pipe,\n+\t\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t\t u64 *streams_masks)\n+{\n+\tstruct max_des *des = priv->des;\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_des_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_des_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 = des->ops->set_pipe_enable(des, pipe, enable);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tpipe->enabled = enable;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_update_pipe(struct max_des_priv *priv,\n+\t\t\t struct max_des_remap_context *context,\n+\t\t\t struct max_des_pipe *pipe,\n+\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t u64 *streams_masks)\n+{\n+\tint ret;\n+\n+\tret = max_des_update_pipe_remaps(priv, context, pipe,\n+\t\t\t\t\t state, streams_masks);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_update_pipe_vc_remaps(priv, context, pipe, state,\n+\t\t\t\t\t streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_update_pipe_remaps;\n+\n+\tret = max_des_update_pipe_enable(priv, pipe, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_update_pipe_vc_remaps;\n+\n+\treturn 0;\n+\n+err_revert_update_pipe_vc_remaps:\n+\tmax_des_update_pipe_vc_remaps(priv, context, pipe, state,\n+\t\t\t\t priv->streams_masks);\n+\n+err_revert_update_pipe_remaps:\n+\tmax_des_update_pipe_remaps(priv, context, pipe, state,\n+\t\t\t\t priv->streams_masks);\n+\n+\treturn ret;\n+}\n+\n+static int max_des_init_link_ser_xlate(struct max_des_priv *priv,\n+\t\t\t\t struct max_des_link *link,\n+\t\t\t\t struct i2c_adapter *adapter,\n+\t\t\t\t u8 power_up_addr, u8 new_addr)\n+{\n+\tstruct max_des *des = priv->des;\n+\tu8 addrs[] = { power_up_addr, new_addr };\n+\tu8 current_addr;\n+\tint ret;\n+\n+\tret = des->ops->select_links(des, BIT(link->index));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_ser_wait_for_multiple(adapter, addrs, ARRAY_SIZE(addrs),\n+\t\t\t\t\t¤t_addr);\n+\tif (ret) {\n+\t\tdev_err(priv->dev,\n+\t\t\t\"Failed to wait for serializer at 0x%02x or 0x%02x: %d\\n\",\n+\t\t\tpower_up_addr, new_addr, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = max_ser_reset(adapter, current_addr);\n+\tif (ret) {\n+\t\tdev_err(priv->dev, \"Failed to reset serializer: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = max_ser_wait(adapter, power_up_addr);\n+\tif (ret) {\n+\t\tdev_err(priv->dev,\n+\t\t\t\"Failed to wait for serializer at 0x%02x: %d\\n\",\n+\t\t\tpower_up_addr, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = max_ser_change_address(adapter, power_up_addr, new_addr);\n+\tif (ret) {\n+\t\tdev_err(priv->dev,\n+\t\t\t\"Failed to change serializer from 0x%02x to 0x%02x: %d\\n\",\n+\t\t\tpower_up_addr, new_addr, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = max_ser_wait(adapter, new_addr);\n+\tif (ret) {\n+\t\tdev_err(priv->dev,\n+\t\t\t\"Failed to wait for serializer at 0x%02x: %d\\n\",\n+\t\t\tnew_addr, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (des->info->fix_tx_ids) {\n+\t\tret = max_ser_fix_tx_ids(adapter, new_addr);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int max_des_init(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (des->ops->init) {\n+\t\tret = des->ops->init(des);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (des->ops->set_enable) {\n+\t\tret = des->ops->set_enable(des, false);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tstruct max_des_phy *phy = &des->phys[i];\n+\n+\t\tif (phy->enabled) {\n+\t\t\tret = des->ops->init_phy(des, phy);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = des->ops->set_phy_enable(des, phy, phy->enabled);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\tstruct max_des_pipe *pipe = &des->pipes[i];\n+\t\tstruct max_des_link *link = &des->links[pipe->link_id];\n+\n+\t\tret = des->ops->set_pipe_enable(des, pipe, false);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (des->ops->set_pipe_tunnel_enable) {\n+\t\t\tret = des->ops->set_pipe_tunnel_enable(des, pipe, false);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (des->ops->set_pipe_stream_id) {\n+\t\t\tret = des->ops->set_pipe_stream_id(des, pipe, pipe->stream_id);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (des->ops->set_pipe_link) {\n+\t\t\tret = des->ops->set_pipe_link(des, pipe, link);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = max_des_set_pipe_remaps(priv, pipe, pipe->remaps,\n+\t\t\t\t\t pipe->num_remaps);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (!des->ops->init_link)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tret = des->ops->init_link(des, link);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void max_des_ser_find_version_range(struct max_des *des, int *min, int *max)\n+{\n+\tunsigned int i;\n+\n+\t*min = MAX_SERDES_GMSL_MIN;\n+\t*max = MAX_SERDES_GMSL_MAX;\n+\n+\tif (!des->info->needs_single_link_version)\n+\t\treturn;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!link->ser_xlate.en)\n+\t\t\tcontinue;\n+\n+\t\t*min = *max = link->version;\n+\n+\t\treturn;\n+\t}\n+}\n+\n+static int max_des_ser_attach_addr(struct max_des_priv *priv, u32 chan_id,\n+\t\t\t\t u16 addr, u16 alias)\n+{\n+\tstruct max_des *des = priv->des;\n+\tstruct max_des_link *link = &des->links[chan_id];\n+\tint i, min, max;\n+\tint ret = 0;\n+\n+\tmax_des_ser_find_version_range(des, &min, &max);\n+\n+\tif (link->ser_xlate.en) {\n+\t\tdev_err(priv->dev, \"Serializer for link %u already bound\\n\",\n+\t\t\tlink->index);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (i = max; i >= min; i--) {\n+\t\tif (!(des->info->versions & BIT(i)))\n+\t\t\tcontinue;\n+\n+\t\tif (des->ops->set_link_version) {\n+\t\t\tret = des->ops->set_link_version(des, link, i);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = max_des_init_link_ser_xlate(priv, link, priv->client->adapter,\n+\t\t\t\t\t\t addr, alias);\n+\t\tif (!ret)\n+\t\t\tbreak;\n+\t}\n+\n+\tif (ret) {\n+\t\tdev_err(priv->dev, \"Cannot find serializer for link %u\\n\",\n+\t\t\tlink->index);\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tlink->version = i;\n+\tlink->ser_xlate.src = alias;\n+\tlink->ser_xlate.dst = addr;\n+\tlink->ser_xlate.en = true;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_ser_atr_attach_addr(struct i2c_atr *atr, u32 chan_id,\n+\t\t\t\t u16 addr, u16 alias)\n+{\n+\tstruct max_des_priv *priv = i2c_atr_get_driver_data(atr);\n+\n+\treturn max_des_ser_attach_addr(priv, chan_id, addr, alias);\n+}\n+\n+static void max_des_ser_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr)\n+{\n+\t/* Don't do anything. */\n+}\n+\n+static const struct i2c_atr_ops max_des_i2c_atr_ops = {\n+\t.attach_addr = max_des_ser_atr_attach_addr,\n+\t.detach_addr = max_des_ser_atr_detach_addr,\n+};\n+\n+static void max_des_i2c_atr_deinit(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\t/* Deleting adapters that haven't been added does no harm. */\n+\t\ti2c_atr_del_adapter(priv->atr, link->index);\n+\t}\n+\n+\ti2c_atr_delete(priv->atr);\n+}\n+\n+static int max_des_i2c_atr_init(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int mask = 0;\n+\tunsigned int i;\n+\tint ret;\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_des_i2c_atr_ops, des->info->num_links,\n+\t\t\t\tI2C_ATR_F_STATIC | I2C_ATR_F_PASSTHROUGH);\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+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\t\tstruct i2c_atr_adap_desc desc = {\n+\t\t\t.chan_id = i,\n+\t\t};\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tret = i2c_atr_add_adapter(priv->atr, &desc);\n+\t\tif (ret)\n+\t\t\tgoto err_add_adapters;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tmask |= BIT(link->index);\n+\t}\n+\n+\treturn des->ops->select_links(des, mask);\n+\n+err_add_adapters:\n+\tmax_des_i2c_atr_deinit(priv);\n+\n+\treturn ret;\n+}\n+\n+static void max_des_i2c_mux_deinit(struct max_des_priv *priv)\n+{\n+\ti2c_mux_del_adapters(priv->mux);\n+\tbus_unregister_notifier(&i2c_bus_type, &priv->i2c_nb);\n+}\n+\n+static int max_des_i2c_mux_bus_notifier_call(struct notifier_block *nb,\n+\t\t\t\t\t unsigned long event, void *device)\n+{\n+\tstruct max_des_priv *priv = container_of(nb, struct max_des_priv, i2c_nb);\n+\tstruct device *dev = device;\n+\tstruct i2c_client *client;\n+\tu32 chan_id;\n+\n+\t/*\n+\t * Ideally, we would want to negotiate the GMSL version on\n+\t * BUS_NOTIFY_ADD_DEVICE, but the adapters list is only populated with\n+\t * the new adapter after BUS_NOTIFY_ADD_DEVICE is issued.\n+\t */\n+\tif (event != BUS_NOTIFY_BIND_DRIVER)\n+\t\treturn NOTIFY_DONE;\n+\n+\tclient = i2c_verify_client(dev);\n+\tif (!client)\n+\t\treturn NOTIFY_DONE;\n+\n+\tfor (chan_id = 0; chan_id < priv->mux->max_adapters; ++chan_id) {\n+\t\tif (client->adapter == priv->mux->adapter[chan_id])\n+\t\t\tbreak;\n+\t}\n+\n+\tif (chan_id == priv->mux->max_adapters)\n+\t\treturn NOTIFY_DONE;\n+\n+\tmax_des_ser_attach_addr(priv, chan_id, client->addr, client->addr);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+static int max_des_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)\n+{\n+\tstruct max_des_priv *priv = i2c_mux_priv(muxc);\n+\tstruct max_des *des = priv->des;\n+\n+\tif (!des->ops->select_links)\n+\t\treturn 0;\n+\n+\treturn des->ops->select_links(des, BIT(chan));\n+}\n+\n+static int max_des_i2c_mux_init(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tu32 flags = I2C_MUX_LOCKED;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (des->info->num_links == 1)\n+\t\tflags |= I2C_MUX_GATE;\n+\n+\tpriv->mux = i2c_mux_alloc(priv->client->adapter, priv->dev,\n+\t\t\t\t des->info->num_links, 0, flags,\n+\t\t\t\t max_des_i2c_mux_select, NULL);\n+\tif (!priv->mux)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->mux->priv = priv;\n+\n+\tpriv->i2c_nb.notifier_call = max_des_i2c_mux_bus_notifier_call;\n+\tret = bus_register_notifier(&i2c_bus_type, &priv->i2c_nb);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tret = i2c_mux_add_adapter(priv->mux, 0, i);\n+\t\tif (ret)\n+\t\t\tgoto err_add_adapters;\n+\t}\n+\n+\treturn 0;\n+\n+err_add_adapters:\n+\tmax_des_i2c_mux_deinit(priv);\n+\n+\treturn ret;\n+}\n+\n+static void max_des_i2c_adapter_deinit(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\n+\tif (des->info->use_atr)\n+\t\treturn max_des_i2c_atr_deinit(priv);\n+\telse\n+\t\treturn max_des_i2c_mux_deinit(priv);\n+}\n+\n+static int max_des_i2c_adapter_init(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\n+\tif (des->info->use_atr)\n+\t\treturn max_des_i2c_atr_init(priv);\n+\telse\n+\t\treturn max_des_i2c_mux_init(priv);\n+\n+\treturn 0;\n+}\n+\n+static int max_des_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_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\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_des_find_tpg_entry(des, 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_des_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_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\tstruct v4l2_mbus_framefmt *fmt;\n+\tint ret;\n+\n+\tif (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active)\n+\t\treturn -EBUSY;\n+\n+\t/* No transcoding, source and sink formats must match. */\n+\tif (max_des_pad_is_source(des, format->pad))\n+\t\treturn v4l2_subdev_get_fmt(sd, state, format);\n+\n+\tif (max_des_pad_is_tpg(des, format->pad)) {\n+\t\tret = max_des_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_des_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_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\tconst struct max_serdes_tpg_entry *entry;\n+\n+\tif (!max_des_pad_is_tpg(des, fie->pad) ||\n+\t fie->stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -ENOTTY;\n+\n+\tentry = max_des_find_tpg_entry(des, 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_des_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_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\n+\tif (!max_des_pad_is_tpg(des, 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_des_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_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\tconst struct max_serdes_tpg_entry *entry;\n+\tstruct v4l2_mbus_framefmt *fmt;\n+\tstruct v4l2_fract *in;\n+\n+\tif (!max_des_pad_is_tpg(des, fi->pad) ||\n+\t fi->stream != MAX_SERDES_TPG_STREAM)\n+\t\treturn -ENOTTY;\n+\n+\tif (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE && des->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_des_find_tpg_entry(des, 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_des_log_status(struct v4l2_subdev *sd)\n+{\n+\tstruct max_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i, j;\n+\tint ret;\n+\n+\tv4l2_info(sd, \"active: %u\\n\", des->active);\n+\tv4l2_info(sd, \"mode: %s\", max_serdes_gmsl_mode_str(des->mode));\n+\tif (des->ops->set_tpg) {\n+\t\tconst struct max_serdes_tpg_entry *entry = des->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 (des->ops->log_status) {\n+\t\tret = des->ops->log_status(des);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\tv4l2_info(sd, \"\\n\");\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tv4l2_info(sd, \"link: %u\\n\", link->index);\n+\t\tv4l2_info(sd, \"\\tenabled: %u\\n\", link->enabled);\n+\n+\t\tif (!link->enabled) {\n+\t\t\tv4l2_info(sd, \"\\n\");\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tv4l2_info(sd, \"\\tversion: %s\\n\", max_serdes_gmsl_version_str(link->version));\n+\t\tv4l2_info(sd, \"\\tser_xlate: en: %u, src: 0x%02x dst: 0x%02x\\n\",\n+\t\t\t link->ser_xlate.en, link->ser_xlate.src,\n+\t\t\t link->ser_xlate.dst);\n+\t\tv4l2_info(sd, \"\\n\");\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\tstruct max_des_pipe *pipe = &des->pipes[i];\n+\n+\t\tv4l2_info(sd, \"pipe: %u\\n\", pipe->index);\n+\t\tv4l2_info(sd, \"\\tenabled: %u\\n\", pipe->enabled);\n+\t\tif (pipe->phy_id == des->info->num_phys ||\n+\t\t (priv->unused_phy && pipe->phy_id == priv->unused_phy->index))\n+\t\t\tv4l2_info(sd, \"\\tphy_id: invalid\\n\");\n+\t\telse\n+\t\t\tv4l2_info(sd, \"\\tphy_id: %u\\n\", pipe->phy_id);\n+\t\tv4l2_info(sd, \"\\tlink_id: %u\\n\", pipe->link_id);\n+\t\tif (des->ops->set_pipe_stream_id)\n+\t\t\tv4l2_info(sd, \"\\tstream_id: %u\\n\", pipe->stream_id);\n+\t\tif (des->ops->set_pipe_mode) {\n+\t\t\tv4l2_info(sd, \"\\tdbl8: %u\\n\", pipe->mode.dbl8);\n+\t\t\tv4l2_info(sd, \"\\tdbl8mode: %u\\n\", pipe->mode.dbl8mode);\n+\t\t\tv4l2_info(sd, \"\\tdbl10: %u\\n\", pipe->mode.dbl10);\n+\t\t\tv4l2_info(sd, \"\\tdbl10mode: %u\\n\", pipe->mode.dbl10mode);\n+\t\t\tv4l2_info(sd, \"\\tdbl12: %u\\n\", pipe->mode.dbl12);\n+\t\t}\n+\t\tif (des->ops->set_pipe_remap) {\n+\t\t\tv4l2_info(sd, \"\\tremaps: %u\\n\", pipe->num_remaps);\n+\t\t\tfor (j = 0; j < pipe->num_remaps; j++) {\n+\t\t\t\tstruct max_des_remap *remap = &pipe->remaps[j];\n+\n+\t\t\t\tv4l2_info(sd, \"\\t\\tremap: from: vc: %u, dt: 0x%02x\\n\",\n+\t\t\t\t\t remap->from_vc, remap->from_dt);\n+\t\t\t\tv4l2_info(sd, \"\\t\\t to: vc: %u, dt: 0x%02x, phy: %u\\n\",\n+\t\t\t\t\t remap->to_vc, remap->to_dt, remap->phy);\n+\t\t\t}\n+\t\t}\n+\t\tif (des->ops->set_pipe_vc_remap) {\n+\t\t\tv4l2_info(sd, \"\\tvc_remaps: %u\\n\", pipe->num_vc_remaps);\n+\t\t\tfor (j = 0; j < pipe->num_vc_remaps; j++) {\n+\t\t\t\tv4l2_info(sd, \"\\t\\tvc_remap: src: %u, dst: %u\\n\",\n+\t\t\t\t\t pipe->vc_remaps[j].src, pipe->vc_remaps[j].dst);\n+\t\t\t}\n+\t\t}\n+\t\tif (des->ops->log_pipe_status) {\n+\t\t\tret = des->ops->log_pipe_status(des, 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 < des->info->num_phys; i++) {\n+\t\tstruct max_des_phy *phy = &des->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, \"\\tlink_frequency: %llu\\n\", phy->link_frequency);\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\tif (des->ops->set_phy_mode) {\n+\t\t\tv4l2_info(sd, \"\\talt_mem_map8: %u\\n\", phy->mode.alt_mem_map8);\n+\t\t\tv4l2_info(sd, \"\\talt2_mem_map8: %u\\n\", phy->mode.alt2_mem_map8);\n+\t\t\tv4l2_info(sd, \"\\talt_mem_map10: %u\\n\", phy->mode.alt_mem_map10);\n+\t\t\tv4l2_info(sd, \"\\talt_mem_map12: %u\\n\", phy->mode.alt_mem_map12);\n+\t\t}\n+\t\tif (des->ops->log_phy_status) {\n+\t\t\tret = des->ops->log_phy_status(des, 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_des_s_ctrl(struct v4l2_ctrl *ctrl)\n+{\n+\tstruct max_des_priv *priv = ctrl_to_priv(ctrl->handler);\n+\tstruct max_des *des = priv->des;\n+\n+\tswitch (ctrl->id) {\n+\tcase V4L2_CID_TEST_PATTERN:\n+\t\tdes->tpg_pattern = ctrl->val;\n+\t\treturn 0;\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+static int max_des_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_des_remap_context context = { 0 };\n+\tstruct max_des_priv *priv = sd_to_priv(sd);\n+\tstruct v4l2_subdev_route *route;\n+\tint ret;\n+\n+\tfd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;\n+\n+\tret = max_des_populate_remap_context(priv, &context, state);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_des_route_hw hw;\n+\t\tunsigned int dst_vc_id;\n+\n+\t\tif (pad != route->source_pad)\n+\t\t\tcontinue;\n+\n+\t\tret = max_des_route_to_hw(priv, state, route, &hw);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = max_des_get_src_dst_vc_id(&context, hw.pipe->index, hw.phy->index,\n+\t\t\t\t\t\thw.entry.bus.csi2.vc, &dst_vc_id);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\thw.entry.bus.csi2.vc = dst_vc_id;\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_des_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_des_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_des_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_des_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,\n+\t\t\t\t struct v4l2_mbus_config *cfg)\n+{\n+\tstruct max_des_priv *priv = sd_to_priv(sd);\n+\tstruct max_des *des = priv->des;\n+\tstruct max_des_phy *phy;\n+\n+\tphy = max_des_pad_to_phy(des, pad);\n+\tif (!phy)\n+\t\treturn -EINVAL;\n+\n+\tcfg->type = phy->bus_type;\n+\tcfg->bus.mipi_csi2 = phy->mipi;\n+\tcfg->link_freq = phy->link_frequency;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_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_des_priv *priv = sd_to_priv(sd);\n+\tstruct max_des *des = priv->des;\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 = &des->info->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_des_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_des_priv *priv = sd_to_priv(sd);\n+\tstruct max_des *des = priv->des;\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_des_pad_is_tpg(des, 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_des_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_des_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_des_priv *priv = sd_to_priv(sd);\n+\tstruct max_des *des = priv->des;\n+\n+\tif (which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active)\n+\t\treturn -EBUSY;\n+\n+\treturn __max_des_set_routing(sd, state, routing);\n+}\n+\n+static int max_des_update_link(struct max_des_priv *priv,\n+\t\t\t struct max_des_remap_context *context,\n+\t\t\t struct max_des_link *link,\n+\t\t\t struct v4l2_subdev_state *state,\n+\t\t\t u64 *streams_masks)\n+{\n+\tstruct max_des *des = priv->des;\n+\tstruct max_des_pipe *pipe;\n+\tint ret;\n+\n+\tpipe = max_des_find_link_pipe(des, link);\n+\tif (!pipe)\n+\t\treturn -ENOENT;\n+\n+\tret = max_des_update_pipe(priv, context, pipe, state, streams_masks);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_update_tpg(struct max_des_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_des *des = priv->des;\n+\tstruct v4l2_subdev_route *route;\n+\tint ret;\n+\n+\tfor_each_active_route(&state->routing, route) {\n+\t\tstruct max_des_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_des_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_des_find_state_tpg_entry(des, state, route->sink_pad);\n+\t\tbreak;\n+\t}\n+\n+\tif (entry == des->tpg_entry)\n+\t\treturn 0;\n+\n+\tret = des->ops->set_tpg(des, entry);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tdes->tpg_entry = entry;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_update_active(struct max_des_priv *priv, u64 *streams_masks,\n+\t\t\t\t bool expected_active)\n+{\n+\tstruct max_des *des = priv->des;\n+\tbool active = false;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tstruct max_des_phy *phy = &des->phys[i];\n+\t\tu32 pad = max_des_phy_to_pad(des, phy);\n+\n+\t\tif (streams_masks[pad]) {\n+\t\t\tactive = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (active != expected_active || des->active == active)\n+\t\treturn 0;\n+\n+\tif (des->ops->set_enable) {\n+\t\tret = des->ops->set_enable(des, active);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tdes->active = active;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_update_links(struct max_des_priv *priv,\n+\t\t\t\tstruct max_des_remap_context *context,\n+\t\t\t\tstruct v4l2_subdev_state *state,\n+\t\t\t\tu64 *streams_masks)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int failed_update_link_id = des->info->num_links;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tret = max_des_update_link(priv, context, link, state,\n+\t\t\t\t\t streams_masks);\n+\t\tif (ret) {\n+\t\t\tfailed_update_link_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_link_id; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tmax_des_update_link(priv, context, link, state,\n+\t\t\t\t priv->streams_masks);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int max_des_enable_disable_streams(struct max_des_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_des *des = priv->des;\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 des->info->num_links, enable);\n+}\n+\n+static int max_des_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_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des_remap_context context = { 0 };\n+\tstruct max_des_mode_context mode_context = { 0 };\n+\tstruct max_des *des = priv->des;\n+\tunsigned int num_pads = max_des_num_pads(des);\n+\tu64 *streams_masks;\n+\tint ret;\n+\n+\tret = max_des_populate_remap_context(priv, &context, state);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_populate_mode_context(priv, &mode_context, state, context.mode);\n+\tif (ret)\n+\t\treturn 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+\tret = max_des_set_pipes_phy(priv, &context);\n+\tif (ret)\n+\t\tgoto err_free_streams_masks;\n+\n+\tret = max_des_set_tunnel(priv, &context);\n+\tif (ret)\n+\t\tgoto err_free_streams_masks;\n+\n+\tret = max_des_set_modes(priv, &mode_context);\n+\tif (ret)\n+\t\tgoto err_free_streams_masks;\n+\n+\tret = max_des_set_vc_remaps(priv, &context, state, streams_masks);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_set_pipes_stream_id(priv);\n+\tif (ret)\n+\t\tgoto err_free_streams_masks;\n+\n+\tif (!enable) {\n+\t\tret = max_des_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_des_update_active(priv, streams_masks, false);\n+\tif (ret)\n+\t\tgoto err_revert_streams_disable;\n+\n+\tret = max_des_update_links(priv, &context, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_active_disable;\n+\n+\tret = max_des_update_tpg(priv, state, streams_masks);\n+\tif (ret)\n+\t\tgoto err_revert_links_update;\n+\n+\tret = max_des_update_active(priv, streams_masks, true);\n+\tif (ret)\n+\t\tgoto err_revert_tpg_update;\n+\n+\tif (enable) {\n+\t\tret = max_des_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_active_enable;\n+\t}\n+\n+\tdevm_kfree(priv->dev, priv->streams_masks);\n+\tpriv->streams_masks = streams_masks;\n+\n+\treturn 0;\n+\n+err_revert_active_enable:\n+\tmax_des_update_active(priv, priv->streams_masks, false);\n+\n+err_revert_tpg_update:\n+\tmax_des_update_tpg(priv, state, priv->streams_masks);\n+\n+err_revert_links_update:\n+\tmax_des_update_links(priv, &context, state, priv->streams_masks);\n+\n+err_revert_active_disable:\n+\tmax_des_update_active(priv, priv->streams_masks, true);\n+\n+err_revert_streams_disable:\n+\tif (!enable)\n+\t\tmax_des_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_des_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_des_update_streams(sd, state, pad, streams_mask, true);\n+}\n+\n+static int max_des_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_des_update_streams(sd, state, pad, streams_mask, false);\n+}\n+\n+static int max_des_init_state(struct v4l2_subdev *sd,\n+\t\t\t struct v4l2_subdev_state *state)\n+{\n+\tstruct v4l2_subdev_route routes[MAX_DES_NUM_LINKS] = { 0 };\n+\tstruct v4l2_subdev_krouting routing = {\n+\t\t.routes = routes,\n+\t};\n+\tstruct max_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\tstruct max_des_phy *phy = NULL;\n+\tunsigned int stream = 0;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tif (des->phys[i].enabled) {\n+\t\t\tphy = &des->phys[i];\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!phy)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\trouting.routes[routing.num_routes++] = (struct v4l2_subdev_route) {\n+\t\t\t.sink_pad = max_des_link_to_pad(des, link),\n+\t\t\t.sink_stream = 0,\n+\t\t\t.source_pad = max_des_phy_to_pad(des, phy),\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_des_set_routing(sd, state, &routing);\n+}\n+\n+#ifdef CONFIG_VIDEO_ADV_DEBUG\n+static int max_des_g_register(struct v4l2_subdev *sd,\n+\t\t\t struct v4l2_dbg_register *reg)\n+{\n+\tstruct max_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\tunsigned int val;\n+\tint ret;\n+\n+\tret = des->ops->reg_read(des, 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_des_s_register(struct v4l2_subdev *sd,\n+\t\t\t const struct v4l2_dbg_register *reg)\n+{\n+\tstruct max_des_priv *priv = v4l2_get_subdevdata(sd);\n+\tstruct max_des *des = priv->des;\n+\n+\treturn des->ops->reg_write(des, reg->reg, reg->val);\n+}\n+#endif\n+\n+static const struct v4l2_subdev_core_ops max_des_core_ops = {\n+\t.log_status = max_des_log_status,\n+#ifdef CONFIG_VIDEO_ADV_DEBUG\n+\t.g_register = max_des_g_register,\n+\t.s_register = max_des_s_register,\n+#endif\n+};\n+\n+static const struct v4l2_ctrl_ops max_des_ctrl_ops = {\n+\t.s_ctrl = max_des_s_ctrl,\n+};\n+\n+static const struct v4l2_subdev_pad_ops max_des_pad_ops = {\n+\t.enable_streams = max_des_enable_streams,\n+\t.disable_streams = max_des_disable_streams,\n+\n+\t.set_routing = max_des_set_routing,\n+\t.get_frame_desc = max_des_get_frame_desc,\n+\n+\t.get_mbus_config = max_des_get_mbus_config,\n+\n+\t.get_fmt = v4l2_subdev_get_fmt,\n+\t.set_fmt = max_des_set_fmt,\n+\n+\t.enum_frame_interval = max_des_enum_frame_interval,\n+\t.get_frame_interval = max_des_get_frame_interval,\n+\t.set_frame_interval = max_des_set_frame_interval,\n+};\n+\n+static const struct v4l2_subdev_ops max_des_subdev_ops = {\n+\t.core = &max_des_core_ops,\n+\t.pad = &max_des_pad_ops,\n+};\n+\n+static const struct v4l2_subdev_internal_ops max_des_internal_ops = {\n+\t.init_state = &max_des_init_state,\n+};\n+\n+static const struct media_entity_operations max_des_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_des_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_des_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+\tstruct max_des *des = priv->des;\n+\tstruct max_des_link *link = &des->links[source->index];\n+\tu32 pad = max_des_link_to_pad(des, link);\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_des_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_des_notify_ops = {\n+\t.bound = max_des_notify_bound,\n+\t.unbind = max_des_notify_unbind,\n+};\n+\n+static int max_des_v4l2_notifier_register(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tv4l2_async_subdev_nf_init(&priv->nf, &priv->sd);\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\t\tstruct max_serdes_source *source;\n+\t\tstruct max_serdes_asc *asc;\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tsource = max_des_get_link_source(priv, link);\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_des_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_des_v4l2_notifier_unregister(struct max_des_priv *priv)\n+{\n+\tv4l2_async_nf_unregister(&priv->nf);\n+\tv4l2_async_nf_cleanup(&priv->nf);\n+}\n+\n+static int max_des_v4l2_register(struct max_des_priv *priv)\n+{\n+\tstruct v4l2_subdev *sd = &priv->sd;\n+\tstruct max_des *des = priv->des;\n+\tvoid *data = i2c_get_clientdata(priv->client);\n+\tunsigned int num_pads = max_des_num_pads(des);\n+\tunsigned int i;\n+\tint ret;\n+\n+\tv4l2_i2c_subdev_init(sd, priv->client, &max_des_subdev_ops);\n+\ti2c_set_clientdata(priv->client, data);\n+\tsd->internal_ops = &max_des_internal_ops;\n+\tsd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;\n+\tsd->entity.ops = &max_des_media_ops;\n+\tsd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;\n+\n+\tfor (i = 0; i < num_pads; i++) {\n+\t\tif (max_des_pad_is_sink(des, i))\n+\t\t\tpriv->pads[i].flags = MEDIA_PAD_FL_SINK;\n+\t\telse if (max_des_pad_is_source(des, i))\n+\t\t\tpriv->pads[i].flags = MEDIA_PAD_FL_SOURCE;\n+\t\telse if (max_des_pad_is_tpg(des, 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 (des->info->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_des_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 ~des->info->tpg_patterns,\n+\t\t\t\t\t __ffs(des->info->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_des_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_des_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_des_v4l2_unregister(struct max_des_priv *priv)\n+{\n+\tstruct v4l2_subdev *sd = &priv->sd;\n+\n+\tv4l2_async_unregister_subdev(sd);\n+\tv4l2_subdev_cleanup(sd);\n+\tmax_des_v4l2_notifier_unregister(priv);\n+\tmedia_entity_cleanup(&sd->entity);\n+\tv4l2_ctrl_handler_free(&priv->ctrl_handler);\n+}\n+\n+static int max_des_update_pocs(struct max_des_priv *priv, bool enable)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\n+\t\tif (!link->enabled)\n+\t\t\tcontinue;\n+\n+\t\tif (!priv->pocs[i])\n+\t\t\tcontinue;\n+\n+\t\tif (enable)\n+\t\t\tret = regulator_enable(priv->pocs[i]);\n+\t\telse\n+\t\t\tret = regulator_disable(priv->pocs[i]);\n+\n+\t\tif (ret) {\n+\t\t\tdev_err(priv->dev,\n+\t\t\t\t\"Failed to set POC supply to %u: %u\\n\",\n+\t\t\t\tenable, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_parse_sink_dt_endpoint(struct max_des_priv *priv,\n+\t\t\t\t\t struct max_des_link *link,\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_des *des = priv->des;\n+\tu32 pad = max_des_link_to_pad(des, link);\n+\tunsigned int index = link->index;\n+\tstruct fwnode_handle *ep;\n+\tchar poc_name[10];\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+\tfwnode_handle_put(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 -ENODEV;\n+\t}\n+\n+\tsnprintf(poc_name, sizeof(poc_name), \"port%u-poc\", index);\n+\tpriv->pocs[index] = devm_regulator_get_optional(priv->dev, poc_name);\n+\tif (IS_ERR(priv->pocs[index])) {\n+\t\tret = PTR_ERR(priv->pocs[index]);\n+\t\tif (ret != -ENODEV) {\n+\t\t\tdev_err(priv->dev,\n+\t\t\t\t\"Failed to get POC supply on port %u: %d\\n\",\n+\t\t\t\tindex, ret);\n+\t\t\tgoto err_put_source_ep_fwnode;\n+\t\t}\n+\n+\t\tpriv->pocs[index] = NULL;\n+\t}\n+\n+\tlink->enabled = true;\n+\n+\treturn 0;\n+\n+err_put_source_ep_fwnode:\n+\tfwnode_handle_put(source->ep_fwnode);\n+\n+\treturn ret;\n+}\n+\n+static int max_des_parse_src_dt_endpoint(struct max_des_priv *priv,\n+\t\t\t\t\t struct max_des_phy *phy,\n+\t\t\t\t\t struct fwnode_handle *fwnode)\n+{\n+\tstruct max_des *des = priv->des;\n+\tu32 pad = max_des_phy_to_pad(des, phy);\n+\tstruct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_UNKNOWN };\n+\tstruct v4l2_mbus_config_mipi_csi2 *mipi = &v4l2_ep.bus.mipi_csi2;\n+\tenum v4l2_mbus_type bus_type;\n+\tstruct fwnode_handle *ep;\n+\tu64 link_frequency;\n+\tunsigned int i;\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+\tret = v4l2_fwnode_endpoint_alloc_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+\tbus_type = v4l2_ep.bus_type;\n+\tif (bus_type != V4L2_MBUS_CSI2_DPHY &&\n+\t bus_type != V4L2_MBUS_CSI2_CPHY) {\n+\t\tv4l2_fwnode_endpoint_free(&v4l2_ep);\n+\t\tdev_err(priv->dev, \"Unsupported bus-type %u on port %u\\n\",\n+\t\t\tpad, bus_type);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (v4l2_ep.nr_of_link_frequencies == 0)\n+\t\tlink_frequency = MAX_DES_LINK_FREQUENCY_DEFAULT;\n+\telse if (v4l2_ep.nr_of_link_frequencies == 1)\n+\t\tlink_frequency = v4l2_ep.link_frequencies[0];\n+\telse\n+\t\tret = -EINVAL;\n+\n+\tv4l2_fwnode_endpoint_free(&v4l2_ep);\n+\n+\tif (ret) {\n+\t\tdev_err(priv->dev, \"Invalid link frequencies %u on port %u\\n\",\n+\t\t\tv4l2_ep.nr_of_link_frequencies, pad);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (link_frequency < MAX_DES_LINK_FREQUENCY_MIN ||\n+\t link_frequency > MAX_DES_LINK_FREQUENCY_MAX) {\n+\t\tdev_err(priv->dev, \"Invalid link frequency %llu on port %u\\n\",\n+\t\t\tlink_frequency, pad);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (i = 0; i < mipi->num_data_lanes; i++) {\n+\t\tif (mipi->data_lanes[i] > mipi->num_data_lanes) {\n+\t\t\tdev_err(priv->dev, \"Invalid data lane %u on port %u\\n\",\n+\t\t\t\tmipi->data_lanes[i], pad);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\tphy->bus_type = bus_type;\n+\tphy->mipi = *mipi;\n+\tphy->link_frequency = link_frequency;\n+\tphy->enabled = true;\n+\n+\treturn 0;\n+}\n+\n+int max_des_phy_hw_data_lanes(struct max_des *des, struct max_des_phy *phy)\n+{\n+\tconst struct max_serdes_phys_configs *configs = &des->info->phys_configs;\n+\tconst struct max_serdes_phys_config *config =\n+\t\t&configs->configs[des->phys_config];\n+\n+\treturn config->lanes[phy->index];\n+}\n+EXPORT_SYMBOL_NS_GPL(max_des_phy_hw_data_lanes, \"MAX_SERDES\");\n+\n+static int max_des_find_phys_config(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tconst struct max_serdes_phys_configs *configs = &des->info->phys_configs;\n+\tstruct max_des_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 < des->info->num_phys; j++) {\n+\t\t\tphy = &des->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 phy->mipi.clock_lane == config->clock_lane[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+\tdes->phys_config = i;\n+\n+\treturn 0;\n+}\n+\n+static int max_des_parse_dt(struct max_des_priv *priv)\n+{\n+\tstruct fwnode_handle *fwnode = dev_fwnode(priv->dev);\n+\tstruct max_des *des = priv->des;\n+\tstruct max_des_link *link;\n+\tstruct max_des_pipe *pipe;\n+\tstruct max_des_phy *phy;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tphy = &des->phys[i];\n+\t\tphy->index = i;\n+\n+\t\tret = max_des_parse_src_dt_endpoint(priv, phy, fwnode);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = max_des_find_phys_config(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Find an unsed PHY to send unampped data to. */\n+\tfor (i = 0; i < des->info->num_phys; i++) {\n+\t\tphy = &des->phys[i];\n+\n+\t\tif (!phy->enabled) {\n+\t\t\tpriv->unused_phy = phy;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_pipes; i++) {\n+\t\tpipe = &des->pipes[i];\n+\t\tpipe->index = i;\n+\n+\t\t/*\n+\t\t * Serializers can send data on different stream ids over the\n+\t\t * same link, and some deserializers support stream id autoselect\n+\t\t * allowing them to receive data from all stream ids.\n+\t\t * Deserializers that support that feature should enable it.\n+\t\t * Deserializers that support per-link stream ids do not need\n+\t\t * to assign unique stream ids to each serializer.\n+\t\t */\n+\t\tif (des->info->needs_unique_stream_id)\n+\t\t\tpipe->stream_id = i;\n+\t\telse\n+\t\t\tpipe->stream_id = 0;\n+\n+\t\t/*\n+\t\t * We already checked that num_pipes >= num_links.\n+\t\t * Set up pipe to receive data from the link with the same index.\n+\t\t * This is already the default for most chips, and some of them\n+\t\t * don't even support receiving pipe data from a different link.\n+\t\t */\n+\t\tpipe->link_id = i % des->info->num_links;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tlink = &des->links[i];\n+\t\tlink->index = i;\n+\t}\n+\n+\tfor (i = 0; i < des->info->num_links; i++) {\n+\t\tstruct max_des_link *link = &des->links[i];\n+\t\tstruct max_serdes_source *source;\n+\n+\t\tsource = max_des_get_link_source(priv, link);\n+\t\tsource->index = i;\n+\n+\t\tret = max_des_parse_sink_dt_endpoint(priv, link, source, fwnode);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int max_des_allocate(struct max_des_priv *priv)\n+{\n+\tstruct max_des *des = priv->des;\n+\tunsigned int num_pads = max_des_num_pads(des);\n+\n+\tdes->phys = devm_kcalloc(priv->dev, des->info->num_phys,\n+\t\t\t\t sizeof(*des->phys), GFP_KERNEL);\n+\tif (!des->phys)\n+\t\treturn -ENOMEM;\n+\n+\tdes->pipes = devm_kcalloc(priv->dev, des->info->num_pipes,\n+\t\t\t\t sizeof(*des->pipes), GFP_KERNEL);\n+\tif (!des->pipes)\n+\t\treturn -ENOMEM;\n+\n+\tdes->links = devm_kcalloc(priv->dev, des->info->num_links,\n+\t\t\t\t sizeof(*des->links), GFP_KERNEL);\n+\tif (!des->links)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->sources = devm_kcalloc(priv->dev, des->info->num_links,\n+\t\t\t\t sizeof(*priv->sources), GFP_KERNEL);\n+\tif (!priv->sources)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->pocs = devm_kcalloc(priv->dev, des->info->num_links,\n+\t\t\t\t sizeof(*priv->pocs), GFP_KERNEL);\n+\tif (!priv->pocs)\n+\t\treturn -ENOMEM;\n+\n+\tpriv->pads = devm_kcalloc(priv->dev, num_pads,\n+\t\t\t\t sizeof(*priv->pads), GFP_KERNEL);\n+\tif (!priv->pads)\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_des_probe(struct i2c_client *client, struct max_des *des)\n+{\n+\tstruct device *dev = &client->dev;\n+\tstruct max_des_priv *priv;\n+\tint ret;\n+\n+\tif (des->info->num_phys > MAX_DES_NUM_PHYS)\n+\t\treturn -E2BIG;\n+\n+\tif (des->info->num_pipes > MAX_DES_NUM_PIPES)\n+\t\treturn -E2BIG;\n+\n+\tif (des->info->num_links > MAX_DES_NUM_LINKS)\n+\t\treturn -E2BIG;\n+\n+\tif (des->info->num_links > des->info->num_pipes)\n+\t\treturn -E2BIG;\n+\n+\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\n+\tif (des->ops->set_link_version && !des->ops->select_links) {\n+\t\tdev_err(dev,\n+\t\t\t\"Cannot implement .select_link_version() without .select_links()\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (hweight_long(des->info->versions) >= 1 &&\n+\t !des->ops->set_link_version) {\n+\t\tdev_err(dev, \"Multiple version without .select_link_version()\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tpriv->client = client;\n+\tpriv->dev = dev;\n+\tpriv->des = des;\n+\tdes->priv = priv;\n+\n+\tret = max_des_allocate(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_parse_dt(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_init(priv);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_update_pocs(priv, true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = max_des_i2c_adapter_init(priv);\n+\tif (ret)\n+\t\tgoto err_disable_pocs;\n+\n+\tret = max_des_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_des_i2c_adapter_deinit(priv);\n+\n+err_disable_pocs:\n+\tmax_des_update_pocs(priv, false);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_NS_GPL(max_des_probe, \"MAX_SERDES\");\n+\n+int max_des_remove(struct max_des *des)\n+{\n+\tstruct max_des_priv *priv = des->priv;\n+\n+\tmax_des_v4l2_unregister(priv);\n+\n+\tmax_des_i2c_adapter_deinit(priv);\n+\n+\tmax_des_update_pocs(priv, false);\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_NS_GPL(max_des_remove, \"MAX_SERDES\");\n+\n+MODULE_LICENSE(\"GPL\");\n+MODULE_IMPORT_NS(\"I2C_ATR\");\ndiff --git a/drivers/media/i2c/maxim-serdes/max_des.h b/drivers/media/i2c/maxim-serdes/max_des.h\nnew file mode 100644\nindex 000000000000..069a863e3da2\n--- /dev/null\n+++ b/drivers/media/i2c/maxim-serdes/max_des.h\n@@ -0,0 +1,156 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2025 Analog Devices Inc.\n+ */\n+\n+#ifndef MAX_DES_H\n+#define MAX_DES_H\n+\n+#include <media/v4l2-mediabus.h>\n+\n+#include \"max_serdes.h\"\n+\n+#define MAX_DES_DT_VC(dt, vc) (((vc) & 0x3) << 6 | ((dt) & 0x3f))\n+\n+struct max_des_remap {\n+\tu8 from_dt;\n+\tu8 from_vc;\n+\tu8 to_dt;\n+\tu8 to_vc;\n+\tu8 phy;\n+};\n+\n+struct max_des_link {\n+\tunsigned int index;\n+\tbool enabled;\n+\tenum max_serdes_gmsl_version version;\n+\tstruct max_serdes_i2c_xlate ser_xlate;\n+};\n+\n+struct max_des_pipe_mode {\n+\tbool dbl8;\n+\tbool dbl10;\n+\tbool dbl12;\n+\tbool dbl8mode;\n+\tbool dbl10mode;\n+};\n+\n+struct max_des_pipe {\n+\tunsigned int index;\n+\tunsigned int stream_id;\n+\tunsigned int link_id;\n+\tunsigned int phy_id;\n+\tstruct max_des_remap *remaps;\n+\tunsigned int num_remaps;\n+\tstruct max_serdes_vc_remap *vc_remaps;\n+\tunsigned int num_vc_remaps;\n+\tstruct max_des_pipe_mode mode;\n+\tbool enabled;\n+};\n+\n+struct max_des_phy_mode {\n+\tbool alt_mem_map8;\n+\tbool alt2_mem_map8;\n+\tbool alt_mem_map10;\n+\tbool alt_mem_map12;\n+};\n+\n+struct max_des_phy {\n+\tunsigned int index;\n+\ts64 link_frequency;\n+\tstruct v4l2_mbus_config_mipi_csi2 mipi;\n+\tenum v4l2_mbus_type bus_type;\n+\tstruct max_des_phy_mode mode;\n+\tbool enabled;\n+};\n+\n+struct max_des;\n+\n+struct max_des_info {\n+\tunsigned int num_phys;\n+\tunsigned int num_pipes;\n+\tunsigned int num_links;\n+\tunsigned int num_remaps_per_pipe;\n+\tunsigned int versions;\n+\tunsigned int modes;\n+\tbool fix_tx_ids;\n+\tbool use_atr;\n+\tbool needs_single_link_version;\n+\tbool needs_unique_stream_id;\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+\n+struct max_des_ops {\n+#ifdef CONFIG_VIDEO_ADV_DEBUG\n+\tint (*reg_read)(struct max_des *des, unsigned int reg, unsigned int *val);\n+\tint (*reg_write)(struct max_des *des, unsigned int reg, unsigned int val);\n+#endif\n+\tint (*log_status)(struct max_des *des);\n+\tint (*log_pipe_status)(struct max_des *des, struct max_des_pipe *pipe);\n+\tint (*log_phy_status)(struct max_des *des, struct max_des_phy *phy);\n+\tint (*set_enable)(struct max_des *des, bool enable);\n+\tint (*set_tpg)(struct max_des *des, const struct max_serdes_tpg_entry *entry);\n+\tint (*init)(struct max_des *des);\n+\tint (*init_phy)(struct max_des *des, struct max_des_phy *phy);\n+\tint (*set_phy_mode)(struct max_des *des, struct max_des_phy *phy,\n+\t\t\t struct max_des_phy_mode *mode);\n+\tint (*set_phy_enable)(struct max_des *des, struct max_des_phy *phy,\n+\t\t\t bool active);\n+\tint (*set_pipe_stream_id)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t\t unsigned int stream_id);\n+\tint (*set_pipe_link)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t struct max_des_link *link);\n+\tint (*set_pipe_phy)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t struct max_des_phy *phy);\n+\tint (*set_pipe_tunnel_phy)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t\t struct max_des_phy *phy);\n+\tint (*set_pipe_enable)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t bool enable);\n+\tint (*set_pipe_remap)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t unsigned int i, struct max_des_remap *remap);\n+\tint (*set_pipe_remaps_enable)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t\t unsigned int mask);\n+\tint (*set_pipe_vc_remap)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t\t unsigned int i, struct max_serdes_vc_remap *vc_remap);\n+\tint (*set_pipe_vc_remaps_enable)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t\t\t unsigned int mask);\n+\tint (*set_pipe_mode)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t struct max_des_pipe_mode *mode);\n+\tint (*set_pipe_tunnel_enable)(struct max_des *des, struct max_des_pipe *pipe,\n+\t\t\t\t bool enable);\n+\tint (*init_link)(struct max_des *des, struct max_des_link *link);\n+\tint (*select_links)(struct max_des *des, unsigned int mask);\n+\tint (*set_link_version)(struct max_des *des, struct max_des_link *link,\n+\t\t\t\tenum max_serdes_gmsl_version version);\n+};\n+\n+struct max_des_priv;\n+\n+struct max_des {\n+\tstruct max_des_priv *priv;\n+\n+\tconst struct max_des_info *info;\n+\tconst struct max_des_ops *ops;\n+\n+\tstruct max_des_phy *phys;\n+\tstruct max_des_pipe *pipes;\n+\tstruct max_des_link *links;\n+\tconst struct max_serdes_tpg_entry *tpg_entry;\n+\tenum max_serdes_tpg_pattern tpg_pattern;\n+\n+\tunsigned int phys_config;\n+\tenum max_serdes_gmsl_mode mode;\n+\tbool active;\n+};\n+\n+int max_des_probe(struct i2c_client *client, struct max_des *des);\n+\n+int max_des_remove(struct max_des *des);\n+\n+int max_des_phy_hw_data_lanes(struct max_des *des, struct max_des_phy *phy);\n+\n+#endif // MAX_DES_H\n", "prefixes": [ "v10", "15/22" ] }