From nobody Tue Feb 10 01:15:26 2026 Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010061.outbound.protection.outlook.com [52.101.56.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1606D30FF2E; Tue, 30 Dec 2025 08:33:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.56.61 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767083635; cv=fail; b=mdaIyPLA47HiRwcOEztbOz5ifTm7e2blRPL/KoE6UNzb7Qzz1lgzr1qs8fm6E8b23DN0LfX/yxnM+Kl/4dy4jFk8TL2Tlx60pEx0Fd5PBvAefpRZG405URdBfoOXjQgJVXx4QWtoiR5JiO5CYRetzd/oGUXtkhdmZORwjtD9lDQ= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767083635; c=relaxed/simple; bh=Po9ovcxpCXOTSCAUhisXUKaBexfM4box2SUbIF3F720=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MLWQaz84dFTba/5ZmpYVojycOmSjn0wmJYNAyt4Twj2Jm35HYGnGSH594zjgHIRHnG1QSvVyiKo0BmNZLNdvqMaYjMhK81DETwILP2emz7CqlUlO+OUgp9Cj0xTY0oyGGVQjs2YiXQSlK8DwpVrOxbORsvFdNlP/pbmQPwnfsEU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=l+dUXqGq; arc=fail smtp.client-ip=52.101.56.61 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="l+dUXqGq" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=nTpk7IOcDmkj0RELhnHZve8haGh2wMtalb5wZ9G0wKpJBk3YhnKLh8GTlOe7A5yuio5QiwccTW+sbGXcuMF1YCGVmODsMs7WC+QS/swizdDK0WMoGfSOl2ZgpQoU3XvP7Kyboipuvo0vyI74g/LX7OJC+DZqm6VFWhF3Ws/JbGIVUx3I3Jl97dVx3V28vuoSzCQwk/4tQ5+uwwoA36+BIrd5s3U54AWYZ4jRwjoYp5/N0VH6vd572rOPwaMe6kDVXGM4Fn6bkPxxuFZX/KbZ3+Q5oKTERJDADcXPFwQvxs0whSgGCI1IhUkCgYlRlL2Kumt9+7YquNPFQ1CGVZDxfQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Fm9+2GoQfT3pZ+X8GdESP4za2przGy6XnK1Hji/lSsI=; b=trNrEgugzGINzMRWU9POI5/FWRoLW9fEqnTdvAP1am6A1aMpCww2E1ip9VQBSl6QZ5voKm+pwmTLmDw3tc/ZujNf5t8+AyV5NDgpMKxXUBPGixQ/+I4z2FyqodCC0Gyzlk60haoaD84kmYG53isCxXWmymuUuW9XM6Fx62xQJIWSm6zpNCkJJMCgzAtT9xf3VjlEOLC14twUPF4EPPJ3ynNPGyEM6x/3YdN8UOZFfYmUzh5S4FWPHhB+VcXXUR6gmE4Qgk5HXwovb+KBtF5ei0HX3bDyd17b4EHOvsbPDNGKZz7KUhTTrmWUtFuqamV0XJLie97Uz/ExGETUheMKYQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 198.47.23.195) smtp.rcpttodomain=vger.kernel.org smtp.mailfrom=ti.com; dmarc=pass (p=quarantine sp=none pct=100) action=none header.from=ti.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Fm9+2GoQfT3pZ+X8GdESP4za2przGy6XnK1Hji/lSsI=; b=l+dUXqGqdBXw2addN/4q6ssawsImOP1B+CWTUHEkskF6TkrJaSrm3DHoLMDhlCUDKUrqWDPiHtNsUu5RWXTfrxu66CEEdScoRJdBsw0htbFalFBlnxij+P3sWcl4ey+CghIeTyvXkEQ9VjqR4K2L5v3m85LjY2mDowS/KPM+81Y= Received: from CH0PR03CA0243.namprd03.prod.outlook.com (2603:10b6:610:e5::8) by SJ0PR10MB4717.namprd10.prod.outlook.com (2603:10b6:a03:2ac::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9456.14; Tue, 30 Dec 2025 08:33:50 +0000 Received: from CH2PEPF0000009A.namprd02.prod.outlook.com (2603:10b6:610:e5:cafe::7) by CH0PR03CA0243.outlook.office365.com (2603:10b6:610:e5::8) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9456.14 via Frontend Transport; Tue, 30 Dec 2025 08:33:50 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 198.47.23.195) smtp.mailfrom=ti.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=ti.com; Received-SPF: Pass (protection.outlook.com: domain of ti.com designates 198.47.23.195 as permitted sender) receiver=protection.outlook.com; client-ip=198.47.23.195; helo=lewvzet201.ext.ti.com; pr=C Received: from lewvzet201.ext.ti.com (198.47.23.195) by CH2PEPF0000009A.mail.protection.outlook.com (10.167.244.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9456.9 via Frontend Transport; Tue, 30 Dec 2025 08:33:49 +0000 Received: from DLEE213.ent.ti.com (157.170.170.116) by lewvzet201.ext.ti.com (10.4.14.104) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Tue, 30 Dec 2025 02:33:47 -0600 Received: from DLEE215.ent.ti.com (157.170.170.118) by DLEE213.ent.ti.com (157.170.170.116) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Tue, 30 Dec 2025 02:33:47 -0600 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DLEE215.ent.ti.com (157.170.170.118) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20 via Frontend Transport; Tue, 30 Dec 2025 02:33:47 -0600 Received: from ws.dhcp.ti.com (ws.dhcp.ti.com [172.24.233.149]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 5BU8WL9f579464; Tue, 30 Dec 2025 02:33:41 -0600 From: Rishikesh Donadkar To: , , CC: , , , , , , , , , , , , , , , , , , , , , Subject: [PATCH v9 12/19] media: cadence: csi2rx: add multistream support Date: Tue, 30 Dec 2025 14:02:13 +0530 Message-ID: <20251230083220.2405247-13-r-donadkar@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251230083220.2405247-1-r-donadkar@ti.com> References: <20251230083220.2405247-1-r-donadkar@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH2PEPF0000009A:EE_|SJ0PR10MB4717:EE_ X-MS-Office365-Filtering-Correlation-Id: b5c7b2c3-e861-4cef-aab7-08de477e284b X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|82310400026|7416014|376014|36860700013|1800799024; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?JifZ/Z3B8UuVTmx4j0s4WBwal+TfYUJPVoeW1qTrjU2oR6yN9vv72UuPdydm?= =?us-ascii?Q?AVaTqsP/h++Z+4HWO2OdpjLiv5oVMNUZ0+lhG9MVdG5rw6PKkyHBoC4FLVFW?= =?us-ascii?Q?X+AEjvGXTz7V1qlHW/1Lqh2w+T1qpBAV5GHTELzjRvAOk/rBMxJuhVv8K0xn?= =?us-ascii?Q?LrGGCDS2u8LyuOsAyLldjSTXouy7gMMjCFDYq/UwKAxvPmOZHDjbweSaJ8N6?= =?us-ascii?Q?JxDX3KsWq4dpPqR05iEtUoVFsK+EKLL7SCr6YqFRpMnez/V4KqfdajoTjS2J?= =?us-ascii?Q?1BHwAmHDyfVOeju8TJ7h4dOoBPKwXBauR8tlSpTILcgNtFBqFPQyrYg2GtqH?= =?us-ascii?Q?FErtkmMkPu4EVOGxp1egK0mL0Qhf7vJ4N5eztckq+eX+rz8u5ElvZd+4Y9ZY?= =?us-ascii?Q?DadlTwSKhEY/W8jlZ1LufW+47SqcZQbVi3Ai9ByQTwI7VQnrqd7e8laI+DoA?= =?us-ascii?Q?mKBETEbJHxH+OLpv0LXQoJd7V6HAKExqNaY20vxcmlIZu2MYB7L8VftWPwT5?= =?us-ascii?Q?QZg3wU+3rb6n6kUXhx9VWchi72oDkEMKmSwMBMsYTAh80WzfJ8fBbTStE5+1?= =?us-ascii?Q?MavZ9SbPGaVE4iY6t8IkX5OfcXuElPCubRm06cvVRx9DfN/YQjmCKu3btKAi?= =?us-ascii?Q?M9k9mCyCmGcqGmb29A623LQsZLKOweTgDqpf1R8rm1PFb4evt0JihB9cnRka?= =?us-ascii?Q?lBl+l7huWeVic6cbrgrhEw5meqrRYW7zd0tggc1aDginYZiL5t4zJQ87syTP?= =?us-ascii?Q?Hki/9l6EUflz2WyFvplZLfFmBXmrg0eNiR9X4VFSJ3ZRmQR8Sg7jxYHqfggg?= =?us-ascii?Q?bLCdmdbNVVcOrCTCOTTIRxRm/GzrGOP5LBL6MBvjTKE0DRLZ2ISHG33qc+8l?= =?us-ascii?Q?G8EMGODPFKGg1BmywadkB9rjElBNk0iYISSf/G6iIRy10O1vjc7MD2K3McFE?= =?us-ascii?Q?JhV7UYesdeHK7R4682Pm1NDN5TjIR0up0eQGJeHTnsOswu8MHw3WSzHsaxj5?= =?us-ascii?Q?xot8PJdJvdQYU+RCEyTsfTDiz1rMxe4h8A9wzkxV26T/VIw2k9ZdrsUlXtlH?= =?us-ascii?Q?C+kMBiR3BFBKhMKKZWljiVivqBjXD2GdFLAvLhyNBDpzU34C5JUq7TlK2loR?= =?us-ascii?Q?05ZUrOuctC4aciTtdDDxp7BiNbpkMc+1JFye9DpJEXP1m7DIrkcbg1z+evZj?= =?us-ascii?Q?s22d8Py4EAq/4eO41gf4itsPMHpBZTVajhs9J7BULqX92XPhJC1cxuvhOz7d?= =?us-ascii?Q?EpPx4EuNWZfZ8zFoGeUNVHmU+ML7zZ+txSfinffLLCmWX/BfOiVoffDonkeW?= =?us-ascii?Q?ttNbkXPiuTHCdiMWL7gf7pnzyYJDxTlMMt+TN9bUTk4lIRdpfMBHlmS3cGLl?= =?us-ascii?Q?HKWrrSLhtCP2Li4UbGBR7hkc1k8/N2ZFNowa3LVEUSV+6kb49mM39iHtkZJz?= =?us-ascii?Q?DCVG8LSLl43A9SWu+uM1z+aa/TGAxfxcwmTDy2UuHkwArGZ1qxdovzhBrlCH?= =?us-ascii?Q?w6PWM6krcXAI77luNmXDITtYmKJRLAD//t8ZIKWQn/Q7Zv/dTCQHk7k5f4qV?= =?us-ascii?Q?hVYZnMAuvgSfpwNjX64=3D?= X-Forefront-Antispam-Report: CIP:198.47.23.195;CTRY:US;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:lewvzet201.ext.ti.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230040)(82310400026)(7416014)(376014)(36860700013)(1800799024);DIR:OUT;SFP:1101; X-OriginatorOrg: ti.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Dec 2025 08:33:49.9724 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: b5c7b2c3-e861-4cef-aab7-08de477e284b X-MS-Exchange-CrossTenant-Id: e5b49634-450b-4709-8abb-1e2b19b982b7 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=e5b49634-450b-4709-8abb-1e2b19b982b7;Ip=[198.47.23.195];Helo=[lewvzet201.ext.ti.com] X-MS-Exchange-CrossTenant-AuthSource: CH2PEPF0000009A.namprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ0PR10MB4717 Content-Type: text/plain; charset="utf-8" From: Jai Luthra Cadence CSI-2 bridge IP supports capturing multiple virtual "streams" of data over the same physical interface using MIPI Virtual Channels. While the hardware IP supports usecases where streams coming in the sink pad can be broadcasted to multiple source pads, the driver will need significant re-architecture to make that possible. The two users of this IP in mainline linux are TI Shim and StarFive JH7110 CAMSS, and both have only integrated the first source pad i.e stream0 of this IP. So for now keep it simple and only allow 1-to-1 mapping of streams from sink to source, without any broadcasting. Signed-off-by: Jai Luthra Reviewed-by: Changhuang Liang Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen --- drivers/media/platform/cadence/cdns-csi2rx.c | 248 +++++++++++++++---- 1 file changed, 201 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/p= latform/cadence/cdns-csi2rx.c index 65c6acb02f85b..5c16a2e509136 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -135,6 +135,7 @@ struct csi2rx_priv { struct phy *dphy; =20 u8 num_pixels[CSI2RX_STREAMS_MAX]; + u32 vc_select[CSI2RX_STREAMS_MAX]; u8 lanes[CSI2RX_LANES_MAX]; u8 num_lanes; u8 max_lanes; @@ -273,30 +274,43 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) =20 static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) { - struct media_pad *src_pad =3D - &csi2rx->source_subdev->entity.pads[csi2rx->source_pad]; union phy_configure_opts opts =3D { }; struct phy_configure_opts_mipi_dphy *cfg =3D &opts.mipi_dphy; - struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev_state *state; const struct csi2rx_fmt *fmt; + int source_pad =3D csi2rx->source_pad; + struct media_pad *pad =3D &csi2rx->source_subdev->entity.pads[source_pad]; s64 link_freq; int ret; + u32 bpp; =20 state =3D v4l2_subdev_get_locked_active_state(&csi2rx->subdev); =20 - framefmt =3D v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, 0); - if (!framefmt) { - dev_err(csi2rx->dev, "Did not find active sink format\n"); - return -EINVAL; - } + /* + * For multi-stream transmitters there is no single pixel rate. + * + * In multistream usecase pass bpp as 0 so that v4l2_get_link_freq() + * returns an error if it falls back to V4L2_CID_PIXEL_RATE. + */ + if (state->routing.num_routes > 1) { + bpp =3D 0; + } else { + framefmt =3D v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, 0); + if (!framefmt) { + dev_err(csi2rx->dev, "Did not find active sink format\n"); + return -EINVAL; + } =20 - fmt =3D csi2rx_get_fmt_by_code(framefmt->code); + fmt =3D csi2rx_get_fmt_by_code(framefmt->code); + bpp =3D fmt->bpp; + } =20 - link_freq =3D v4l2_get_link_freq(src_pad, - fmt->bpp, 2 * csi2rx->num_lanes); - if (link_freq < 0) + link_freq =3D v4l2_get_link_freq(pad, bpp, 2 * csi2rx->num_lanes); + if (link_freq < 0) { + dev_err(csi2rx->dev, "Unable to calculate link frequency\n"); return link_freq; + } =20 ret =3D phy_mipi_dphy_get_default_config_for_hsclk(link_freq, csi2rx->num_lanes, cfg); @@ -394,11 +408,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) csi2rx->num_pixels[i]), csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); =20 - /* - * Enable one virtual channel. When multiple virtual channels - * are supported this will have to be changed. - */ - writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0), + writel(csi2rx->vc_select[i], csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); =20 writel(CSI2RX_STREAM_CTRL_START, @@ -486,18 +496,59 @@ static int csi2rx_log_status(struct v4l2_subdev *sd) return 0; } =20 +static void csi2rx_update_vc_select(struct csi2rx_priv *csi2rx, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_frame_desc fd =3D {0}; + struct v4l2_subdev_route *route; + unsigned int i; + int ret; + + /* Capture VC=3D0 by default */ + for (i =3D 0; i < CSI2RX_STREAMS_MAX; i++) + csi2rx->vc_select[i] =3D CSI2RX_STREAM_DATA_CFG_VC_SELECT(0); + + ret =3D csi2rx_get_frame_desc_from_source(csi2rx, &fd); + if (ret || fd.type !=3D V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_dbg(csi2rx->dev, + "Failed to get source frame desc, allowing only VC=3D0\n"); + return; + } + + /* If source provides per-stream VC info, use it to filter by VC */ + memset(csi2rx->vc_select, 0, sizeof(csi2rx->vc_select)); + + for_each_active_route(&state->routing, route) { + u32 cdns_stream =3D route->source_pad - CSI2RX_PAD_SOURCE_STREAM0; + + for (i =3D 0; i < fd.num_entries; i++) { + if (fd.entry[i].stream !=3D route->sink_stream) + continue; + + csi2rx->vc_select[cdns_stream] |=3D + CSI2RX_STREAM_DATA_CFG_VC_SELECT(fd.entry[i].bus.csi2.vc); + } + } +} + static int csi2rx_enable_streams(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) { struct csi2rx_priv *csi2rx =3D v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; int ret; =20 + sink_streams =3D v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); + /* * If we're not the first users, there's no need to * enable the whole controller. */ if (!csi2rx->count) { + csi2rx_update_vc_select(csi2rx, state); ret =3D csi2rx_start(csi2rx); if (ret) return ret; @@ -505,10 +556,11 @@ static int csi2rx_enable_streams(struct v4l2_subdev *= subdev, =20 /* Start streaming on the source */ ret =3D v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_= pad, - BIT_U64(0)); + sink_streams); if (ret) { dev_err(csi2rx->dev, - "Failed to start streams %d on subdev\n", 0); + "Failed to start streams %#llx on subdev\n", + sink_streams); if (!csi2rx->count) csi2rx_stop(csi2rx); return ret; @@ -523,9 +575,14 @@ static int csi2rx_disable_streams(struct v4l2_subdev *= subdev, u64 streams_mask) { struct csi2rx_priv *csi2rx =3D v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; + + sink_streams =3D v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); =20 if (v4l2_subdev_disable_streams(csi2rx->source_subdev, - csi2rx->source_pad, BIT_U64(0))) { + csi2rx->source_pad, sink_streams)) { dev_err(csi2rx->dev, "Couldn't disable our subdev\n"); } =20 @@ -550,12 +607,56 @@ static int csi2rx_enum_mbus_code(struct v4l2_subdev *= subdev, return 0; } =20 +static int _csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format =3D { + .width =3D 640, + .height =3D 480, + .code =3D MEDIA_BUS_FMT_UYVY8_1X16, + .field =3D V4L2_FIELD_NONE, + .colorspace =3D V4L2_COLORSPACE_SRGB, + .ycbcr_enc =3D V4L2_YCBCR_ENC_601, + .quantization =3D V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func =3D V4L2_XFER_FUNC_SRGB, + }; + int ret; + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret =3D v4l2_subdev_routing_validate(subdev, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(subdev, state, routing, &format); +} + +static int csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct csi2rx_priv *csi2rx =3D v4l2_subdev_to_csi2rx(subdev); + int ret; + + if (which =3D=3D V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count) + return -EBUSY; + + ret =3D _csi2rx_set_routing(subdev, state, routing); + if (ret) + return ret; + + return 0; +} + static int csi2rx_set_fmt(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt; - unsigned int i; =20 /* No transcoding, source and sink formats must match. */ if (format->pad !=3D CSI2RX_PAD_SINK) @@ -567,14 +668,16 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field =3D V4L2_FIELD_NONE; =20 /* Set sink format */ - fmt =3D v4l2_subdev_state_get_format(state, format->pad); + fmt =3D v4l2_subdev_state_get_format(state, format->pad, format->stream); *fmt =3D format->format; =20 - /* Propagate to source formats */ - for (i =3D CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt =3D v4l2_subdev_state_get_format(state, i); - *fmt =3D format->format; - } + /* Propagate to source format */ + fmt =3D v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt =3D format->format; =20 return 0; } @@ -582,21 +685,22 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, static int csi2rx_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state) { - struct v4l2_subdev_format format =3D { - .pad =3D CSI2RX_PAD_SINK, - .format =3D { - .width =3D 640, - .height =3D 480, - .code =3D MEDIA_BUS_FMT_UYVY8_1X16, - .field =3D V4L2_FIELD_NONE, - .colorspace =3D V4L2_COLORSPACE_SRGB, - .ycbcr_enc =3D V4L2_YCBCR_ENC_601, - .quantization =3D V4L2_QUANTIZATION_LIM_RANGE, - .xfer_func =3D V4L2_XFER_FUNC_SRGB, + struct v4l2_subdev_route routes[] =3D { + { + .sink_pad =3D CSI2RX_PAD_SINK, + .sink_stream =3D 0, + .source_pad =3D CSI2RX_PAD_SOURCE_STREAM0, + .source_stream =3D 0, + .flags =3D V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, }; =20 - return csi2rx_set_fmt(subdev, state, &format); + struct v4l2_subdev_krouting routing =3D { + .num_routes =3D ARRAY_SIZE(routes), + .routes =3D routes, + }; + + return _csi2rx_set_routing(subdev, state, &routing); } =20 int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, @@ -630,17 +734,66 @@ static int csi2rx_get_frame_desc(struct v4l2_subdev *= subdev, unsigned int pad, struct v4l2_mbus_frame_desc *fd) { struct csi2rx_priv *csi2rx =3D v4l2_subdev_to_csi2rx(subdev); + struct v4l2_mbus_frame_desc source_fd =3D {0}; + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret; =20 - return csi2rx_get_frame_desc_from_source(csi2rx, fd); + ret =3D csi2rx_get_frame_desc_from_source(csi2rx, &source_fd); + if (ret) + return ret; + + fd->type =3D V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + state =3D v4l2_subdev_lock_and_get_active_state(subdev); + + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *source_entry =3D NULL; + unsigned int i; + + if (route->source_pad !=3D pad) + continue; + + for (i =3D 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream =3D=3D route->sink_stream) { + source_entry =3D &source_fd.entry[i]; + break; + } + } + + if (!source_entry) { + dev_err(csi2rx->dev, + "Failed to find stream from source frame desc\n"); + ret =3D -EPIPE; + goto err_missing_stream; + } + + fd->entry[fd->num_entries].stream =3D route->source_stream; + fd->entry[fd->num_entries].flags =3D source_entry->flags; + fd->entry[fd->num_entries].length =3D source_entry->length; + fd->entry[fd->num_entries].pixelcode =3D source_entry->pixelcode; + fd->entry[fd->num_entries].bus.csi2.vc =3D + source_entry->bus.csi2.vc; + fd->entry[fd->num_entries].bus.csi2.dt =3D + source_entry->bus.csi2.dt; + + fd->num_entries++; + } + +err_missing_stream: + v4l2_subdev_unlock_state(state); + + return ret; } =20 static const struct v4l2_subdev_pad_ops csi2rx_pad_ops =3D { - .enum_mbus_code =3D csi2rx_enum_mbus_code, - .get_fmt =3D v4l2_subdev_get_fmt, - .set_fmt =3D csi2rx_set_fmt, - .enable_streams =3D csi2rx_enable_streams, - .disable_streams =3D csi2rx_disable_streams, - .get_frame_desc =3D csi2rx_get_frame_desc, + .enum_mbus_code =3D csi2rx_enum_mbus_code, + .get_fmt =3D v4l2_subdev_get_fmt, + .set_fmt =3D csi2rx_set_fmt, + .get_frame_desc =3D csi2rx_get_frame_desc, + .set_routing =3D csi2rx_set_routing, + .enable_streams =3D csi2rx_enable_streams, + .disable_streams =3D csi2rx_disable_streams, }; =20 static const struct v4l2_subdev_core_ops csi2rx_core_ops =3D { @@ -876,7 +1029,8 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->pads[CSI2RX_PAD_SINK].flags =3D MEDIA_PAD_FL_SINK; for (i =3D CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) csi2rx->pads[i].flags =3D MEDIA_PAD_FL_SOURCE; - csi2rx->subdev.flags |=3D V4L2_SUBDEV_FL_HAS_DEVNODE; + csi2rx->subdev.flags =3D V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_STREAMS; csi2rx->subdev.entity.ops =3D &csi2rx_media_ops; =20 ret =3D media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, --=20 2.34.1