From patchwork Sun Jun 8 14:49:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bin Du X-Patchwork-Id: 894861 Received: from NAM11-BN8-obe.outbound.protection.outlook.com (mail-bn8nam11on2066.outbound.protection.outlook.com [40.107.236.66]) (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 B67742505CE; Sun, 8 Jun 2025 14:49:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.236.66 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394192; cv=fail; b=J7dMyhAO18UO1cYS1U79mjZCXZH2X2viO6OHWECVG6KP2xxA63cRIEW8QhGIfR+GwOUcHff+Vodu/yGs6h9bLWCAi2v+4jf/Rt+PBXadBzDP/KcpxKM42fAOf8pYHtB66ObLaD1ERbJPKU9dukgErj9DtG40OYd+LsNyf2davrs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394192; c=relaxed/simple; bh=+A8RBM/d/2xC2zgJ2DqZ9kw4pm4p6SbOz+3TNManqR4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MlgsU6c76ARX3yMKDFVLkHQWe+VocWCR6pfvl3LMMP8u2z/WPhMyIejHulOHLOOg08iqNqvEWILf2EL4n9H7K0dW1y8C5VmCJf7Hij+T4y0ayObZGXkkmYxHvH3pfXCJrog8WTsn2xHxT0ChuRDTg4xmYc1tWtTLf3tJjPbwVxI= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=XjHynrLB; arc=fail smtp.client-ip=40.107.236.66 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="XjHynrLB" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=wKrT/Ogo8XmddXOKPGcNChT6YJj+57syPoeSTyCC9EZzkgIhLuDEqUwEyFR7rhvyI61CoRXojjpvVS+zaTdNb6oPAu5mvrGG7T6kuunVPMPTY+IYg74+RtKSUWRp/0QXxsn2klo5fw1GV+yDDjKrZcctGmIsnTof4KmU+yi6+y+utOMlMexWgLFQHBuBWZqwBNCl+UXaxMiMuxvCFzpu8zm4+1AxyqOcZC0uOq37vKcmcmRYXvb2XLa0GHF5KhlYWgbec5YFsxH17XqoR4Wg3fqjl5gXc5mKIS6HSruZgqM01NkPeMtq1jvtFfWVay4/S2BBfCsq0KRxzBBGKMwyKw== 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=3e7UX37afj2KB2OrCYyMt5UsXmehvgk81bicExSVcOw=; b=NOdMPKRYBGmoHwCKA1lPtS+1RgKoq/81KjezUBBmYy5bBrlOT0lsZvi41ABR2vOC0cO3fTsSZCV+M0NGHmndP1kvI4RBZOBfmkQZjuRhbBTHHt2mwqu4DG0Fu30LOQEi4ccWduuqBAl2G69yxsLUYl+HW7l7QTGUvP8pB/Zp/ZrkO+8B3hwbGDISaHchElfgKeI45LyHquDNMwYL6uWCdAlPza5Ai/USCGKSAeMdd8QHSVRuIlcPYJHMt8yBalFAvd+g2VHp3KDym+zIXSUt/U32DDYiopbt2AvfETgj0Wdcfy/g+beqlzDng/g2oSEaarq8ODQl6zvq3VEIfbreaQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=3e7UX37afj2KB2OrCYyMt5UsXmehvgk81bicExSVcOw=; b=XjHynrLB+/vS95gtEnMFuZ/906C4SwlitTFSEOOfWweYmIEtdkXpCO5vmet0EwlnP21gphBIHzKxU5OY4lK5ceB16PcloDPJwsDG/S66FRaPGfCYG4cp9Qt9K1hNhNyTGB1u9/+GsWPdgDNbj6lnfZewfaoc7zpWXwJwNguKwD8= Received: from BY5PR17CA0046.namprd17.prod.outlook.com (2603:10b6:a03:167::23) by MW4PR12MB6923.namprd12.prod.outlook.com (2603:10b6:303:208::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8792.38; Sun, 8 Jun 2025 14:49:41 +0000 Received: from SJ5PEPF000001D3.namprd05.prod.outlook.com (2603:10b6:a03:167:cafe::4f) by BY5PR17CA0046.outlook.office365.com (2603:10b6:a03:167::23) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8746.30 via Frontend Transport; Sun, 8 Jun 2025 14:49:41 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by SJ5PEPF000001D3.mail.protection.outlook.com (10.167.242.55) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.8835.15 via Frontend Transport; Sun, 8 Jun 2025 14:49:41 +0000 Received: from 555e2b870847.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Sun, 8 Jun 2025 09:49:36 -0500 From: Bin Du To: , , , , , , , CC: , , , , , , , , "Bin Du" Subject: [PATCH v1 1/9] media: platform: amd: Introduce amd isp4 capture driver Date: Sun, 8 Jun 2025 22:49:08 +0800 Message-ID: <20250608144916.222835-2-Bin.Du@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250608144916.222835-1-Bin.Du@amd.com> References: <20250608144916.222835-1-Bin.Du@amd.com> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: SATLEXMB04.amd.com (10.181.40.145) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ5PEPF000001D3:EE_|MW4PR12MB6923:EE_ X-MS-Office365-Filtering-Correlation-Id: e2912efc-be77-42b9-9f1c-08dda69bb35b X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|1800799024|36860700013|82310400026|376014; X-Microsoft-Antispam-Message-Info: HOa1NP74TARN9CqL/bVDfCNDHv7PMzeAZaxx5S7HYN/gRU1JX/5lzYjfhou1zvVmGUJWMbRAz4WMLSwRdInD+f63/PayCeLmpvGcAs6OD/IQ5tNBc9qzqvFi9/NwfQhlslYU+BfSQnQcLZc8GDO+dM5g9nalaT1xMzqxXg5XvnDG5uLrl3MmowHy+aU+x28XKXxfeKabOKEgO3hJdyjv9ptyyxjMMymt4bSJh8enkKiM0/dmZeK9Vk06kv9mpeiljXwgtWAOarwhzE2h0q78v41OJf3Q555Hmtvwrh5hZ4SIp599ldI2JAhPpNEVCV0yAnyiSKMFST3cfnXp5QPAN8rJvnL8aGBL1PpHgp+BIPg4Vs5MFDdF1Qldc6qkUD2kGCkBM3AMNZYVYn3ncexmXyWgoHvKzRSXpGfA7Z4INnZoB0bi3uBzqZJP9axllQRhhX/fvdkknFe1z9nNcRZW0tTa4rjZawFKxOV7Si7b+AkImNtfPEBIuGNIa0zNxn8olPge6MycSVGwSj85lFZmseOaXcw7NuG7iAWP9q2u8wk2tBT75oy+rnn1qH688PeFY1QVBM8u7LPbfgqfyI0X4MKWRAOpL+I67acNChlxOEU0es1dAMm+mAZIqIFmwmRHhLeiNLrGofvdOkn2Ag/OvWH68e3d9EKLThAbKZrTjoBDM3IowDlkAxNCzNkFARaU9w6DsSfrlRq0Xoji4qQbMvast8otmBtcefQNeOK2BMKB5MKndCcNW8nEfUeqXkxIuvotDzglNyAOzME+noRNISyrC4m5zQCkn9Mr7Y9FJL8f/H3uDTXPcb/Ns67z6y8rC9OPUk/1L/YHynHLUxykof1+53DvTBRzDrmS+b1AfHxo9CdLKuD8MHr49F5g6pdYmOa6H5vpbI9cyy4QnTnY6WR18ENJThsZDtFwzY+9rJL0lmmJZ2huUpCc5JYpfqaIojZ+5rfHx5I2Wpqo15qTQwiE3vtd3bz2NgbwEm6HGwe5My4RCk7qt4g9l3FgAgwt+jnlbXsYVThU7jwQXIwH5mT9AYnbljNphIzujrjyGEsLbD8Z8Tx/AYpUF270f5yn7UA3CRR90zo2MS1F4B8Ig8bagbHsVlneMyp+/nvW97PknfzlxKQtkPd0FVFQJ3+kCQr0QG3aanr5jTIOyqYep9M68ObsjlGS357WNfmMjXeodKouj5QB0/R2HREeTaSVFTFOfV5Qshpq2XtK6QLxSu1zV233uA+8NIx3ErwTuFtn0jJDtR/hLGRXcniU/esaETtrgWuXeja4VXMDLfoYlWeXKMs+hM7iAjkvdcNdeWWhgqceID/mKLKFkFa7ZH/E6UxPpORuSaF7ewpWUGIn3LlFvjc16s6JwWeGMB3P2Wl3rHmvZwFhcVbZ67ynHGU75Ql/+BYtzMgnUznk8KzZ8cEDDZ6Cfu2t3fnCxXFcUvOrJTL/TndPkZ3qe68IjYGTXsLrdfd9B/IpQAjuhIFxwBEkuf890i90boG0IcM8QJ2a8bwHTBDvj2DoIWZbv5db X-Forefront-Antispam-Report: CIP:165.204.84.17; CTRY:US; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:SATLEXMB04.amd.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(1800799024)(36860700013)(82310400026)(376014); DIR:OUT; SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Jun 2025 14:49:41.4488 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: e2912efc-be77-42b9-9f1c-08dda69bb35b X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d; Ip=[165.204.84.17]; Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: SJ5PEPF000001D3.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MW4PR12MB6923 Amd isp4 capture is a v4l2 media device which implements media controller interface. It has one sub-device (amd ISP4 sub-device) endpoint which can be connected to a remote CSI2 TX endpoint. It supports only one physical interface for now. Signed-off-by: Bin Du Signed-off-by: Svetoslav Stoilov Change-Id: I1a55f348db6a307797349203640132675aafbc0a --- drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/amd/Kconfig | 15 +++ drivers/media/platform/amd/Makefile | 5 + drivers/media/platform/amd/isp4/Makefile | 16 +++ drivers/media/platform/amd/isp4/isp4.c | 139 +++++++++++++++++++++++ drivers/media/platform/amd/isp4/isp4.h | 35 ++++++ 7 files changed, 212 insertions(+) create mode 100644 drivers/media/platform/amd/Kconfig create mode 100644 drivers/media/platform/amd/Makefile create mode 100644 drivers/media/platform/amd/isp4/Makefile create mode 100644 drivers/media/platform/amd/isp4/isp4.c create mode 100644 drivers/media/platform/amd/isp4/isp4.h diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 85d2627776b6..d525c2262a7d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -89,5 +89,6 @@ source "drivers/media/platform/ti/Kconfig" source "drivers/media/platform/verisilicon/Kconfig" source "drivers/media/platform/via/Kconfig" source "drivers/media/platform/xilinx/Kconfig" +source "drivers/media/platform/amd/Kconfig" endif # MEDIA_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index ace4e34483dd..9f3d1693868d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -32,6 +32,7 @@ obj-y += ti/ obj-y += verisilicon/ obj-y += via/ obj-y += xilinx/ +obj-y += amd/ # Please place here only ancillary drivers that aren't SoC-specific # Please keep it alphabetically sorted by Kconfig name diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig new file mode 100644 index 000000000000..361b3d687529 --- /dev/null +++ b/drivers/media/platform/amd/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MIT + +config AMD_ISP4 + tristate "AMD ISP4 and camera driver" + default y + depends on VIDEO_DEV && VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_CORE + select VIDEOBUF2_V4L2 + select VIDEOBUF2_MEMOPS + select VIDEOBUF2_VMALLOC + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG + help + This is support for AMD ISP4 and camera subsystem driver. + Say Y here to enable the ISP4 and camera device for video capture. diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile new file mode 100644 index 000000000000..76146efcd2bf --- /dev/null +++ b/drivers/media/platform/amd/Makefile @@ -0,0 +1,5 @@ +# Copyright 2024 Advanced Micro Devices, Inc. +# add isp block +ifneq ($(CONFIG_AMD_ISP4),) +obj-y += isp4/ +endif diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile new file mode 100644 index 000000000000..f2ac9b2a95f0 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 Advanced Micro Devices, Inc. + +obj-$(CONFIG_AMD_ISP4) += amd_capture.o +amd_capture-objs := isp4.o + +ccflags-y += -I$(srctree)/drivers/media/platform/amd/isp4 +ccflags-y += -I$(srctree)/include + +ifneq ($(call cc-option, -mpreferred-stack-boundary=4),) + cc_stack_align := -mpreferred-stack-boundary=4 +endif + +ccflags-y += $(cc_stack_align) +ccflags-y += -DCONFIG_COMPAT diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c new file mode 100644 index 000000000000..d0be90c5ec3b --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4.h" + +#define VIDEO_BUF_NUM 5 + +#define ISP4_DRV_NAME "amd_isp_capture" + +/* interrupt num */ +static const u32 isp4_ringbuf_interrupt_num[] = { + 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ + 1, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT10 */ + 3, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT11 */ + 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ +}; + +#define to_isp4_device(dev) \ + ((struct isp4_device *)container_of(dev, struct isp4_device, v4l2_dev)) + +static irqreturn_t isp4_irq_handler(int irq, void *arg) +{ + struct isp4_device *isp_dev = dev_get_drvdata((struct device *)arg); + + if (!isp_dev) + goto error_drv_data; + +error_drv_data: + return IRQ_HANDLED; +} + +/* + * amd capture module + */ +static int isp4_capture_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isp4_device *isp_dev; + + int i, irq, ret; + + isp_dev = devm_kzalloc(&pdev->dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + isp_dev->pdev = pdev; + dev->init_name = ISP4_DRV_NAME; + + for (i = 0; i < ARRAY_SIZE(isp4_ringbuf_interrupt_num); i++) { + irq = platform_get_irq(pdev, isp4_ringbuf_interrupt_num[i]); + if (irq < 0) + return dev_err_probe(dev, -ENODEV, + "fail to get irq %d\n", + isp4_ringbuf_interrupt_num[i]); + ret = devm_request_irq(&pdev->dev, irq, isp4_irq_handler, 0, + "ISP_IRQ", &pdev->dev); + if (ret) + return dev_err_probe(dev, ret, "fail to req irq %d\n", + irq); + } + + isp_dev->pltf_data = pdev->dev.platform_data; + + dev_dbg(dev, "isp irq registration successful\n"); + + /* Link the media device within the v4l2_device */ + isp_dev->v4l2_dev.mdev = &isp_dev->mdev; + + /* Initialize media device */ + strscpy(isp_dev->mdev.model, "amd_isp41_mdev", + sizeof(isp_dev->mdev.model)); + snprintf(isp_dev->mdev.bus_info, sizeof(isp_dev->mdev.bus_info), + "platform:%s", ISP4_DRV_NAME); + isp_dev->mdev.dev = &pdev->dev; + media_device_init(&isp_dev->mdev); + + /* register v4l2 device */ + snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name), + "AMD-V4L2-ROOT"); + ret = v4l2_device_register(&pdev->dev, &isp_dev->v4l2_dev); + if (ret) + return dev_err_probe(dev, ret, + "fail register v4l2 device\n"); + + dev_dbg(dev, "AMD ISP v4l2 device registered\n"); + + ret = media_device_register(&isp_dev->mdev); + if (ret) { + dev_err(dev, "fail to register media device %d\n", ret); + goto err_unreg_v4l2; + } + + platform_set_drvdata(pdev, isp_dev); + + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + return 0; + +err_unreg_v4l2: + v4l2_device_unregister(&isp_dev->v4l2_dev); + + return dev_err_probe(dev, ret, "isp probe fail\n"); +} + +static void isp4_capture_remove(struct platform_device *pdev) +{ + struct isp4_device *isp_dev = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + media_device_unregister(&isp_dev->mdev); + v4l2_device_unregister(&isp_dev->v4l2_dev); + dev_dbg(dev, "AMD ISP v4l2 device unregistered\n"); +} + +static struct platform_driver isp4_capture_drv = { + .probe = isp4_capture_probe, + .remove = isp4_capture_remove, + .driver = { + .name = ISP4_DRV_NAME, + .owner = THIS_MODULE, + } +}; + +module_platform_driver(isp4_capture_drv); + +MODULE_ALIAS("platform:" ISP4_DRV_NAME); +MODULE_IMPORT_NS("DMA_BUF"); + +MODULE_DESCRIPTION("AMD ISP4 Driver"); +MODULE_AUTHOR("Bin Du "); +MODULE_AUTHOR("Pratap Nirujogi "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h new file mode 100644 index 000000000000..27a7362ce6f9 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_H_ +#define _ISP4_H_ + +#include +#include +#include +#include + +#define ISP4_GET_ISP_REG_BASE(isp4sd) (((isp4sd))->mmio) + +struct isp4_platform_data { + void *adev; + void *bo; + void *cpu_ptr; + u64 gpu_addr; + u32 size; + u32 asic_type; + resource_size_t base_rmmio_size; +}; + +struct isp4_device { + struct v4l2_device v4l2_dev; + struct media_device mdev; + + struct isp4_platform_data *pltf_data; + struct platform_device *pdev; + struct notifier_block i2c_nb; +}; + +#endif /* isp4.h */ From patchwork Sun Jun 8 14:49:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bin Du X-Patchwork-Id: 894860 Received: from NAM02-SN1-obe.outbound.protection.outlook.com (mail-sn1nam02on2083.outbound.protection.outlook.com [40.107.96.83]) (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 A4A5B22A7EC; Sun, 8 Jun 2025 14:50:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.96.83 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394204; cv=fail; b=OjMibDliXab72bMjFUXy7ASYTIrk6c2AQlkdJkO6ZSqv75P2g/h+uEjdt62RHqngisUqLC9cPkosmu28LUGwd/nwWg/tZieGtZ3jFEORktqlx4p9Kyp+0pRfvPhyOQdLZARu08zopUy51YEB5ydRrU7GO58Q5bepdBe9NmPJeuw= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394204; c=relaxed/simple; bh=ZraS2G6Z0ayRdd6roYGpd1iO7lPO7RHq7UZfd2JKQ5Y=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=I94yRXpSyiy+s5dNccsqjMPAKHeINSmYBh491405lqfB3GunpuLMjintsseVV7YBQbDY6kkJC1/sBgZbDp2hsH8jzMHTkdPLgUAHejxgp2VPduwh9b9XtLmuQsCW/U+dqpCkKiiImAPKRmiIal42WzgF9Hf/rXYRHsTjB1vjRYc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=uDAj6hIo; arc=fail smtp.client-ip=40.107.96.83 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="uDAj6hIo" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=ZamEFXGAoT7lLzgpW1PzCrU7jlgjoFE468FoOrCFIxYzv6RCTUQcJA21xnRTkaXTw/M0Fz5Lh660q/ttN7RQKi2qOgJ+H89obw5MBEMn2jsaLBm14cLy/k7m6Bhomdsat637YW7bJY+MWvnq3RfbWPC8Nbq4xI9fmm5njyMhcvyjqV0bz9pTL9BfpmVvsa+mHgGog8WN+GERcaR+N0b2/mDfN1D+Wl5i3gvKMZLKhP1NKDP1WjVjSS88DPuJbktmKZtJlYfP7Dmb/JEEexRTJm6H2+8wbj4al1rCWSEMW63lwyxRxkrO+NaJ+CLNN+WB9YNt8jMZS7HjQ5nrTntS7g== 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=o7OQWvnef0sc5wRky3R37nIsw1QLY8MriPcN6FAPSrU=; b=oydIAdLVOeGervmEh+4Uk/fx1WKL59B4kmOEGYaEB4afhxgNCmo3GWD1zntsswG7FD1qh+pZTQTNUrGopPGVFbUTVoSeKM7xEyUru9GMXFeFYUf36gXFJ/tM0G89di1saX84qSvAQZt2wpjwqoTDQoSds3gZ7i5Z45SISZkuI7tmXR8WP8IMl3J9lpLbZ/v94c5k8Orjp2gCwW+sPjHhXbNqXDx2UheHNEsH1HLhiPIsyvNRs9T0bDR95U7gvo1Onm9BBaskcTls2PsMc0ij98d4rj1AUJ0pfnjOdLdbjyQTYqZCnKArU5LXLDafIO4/eHzsbO+Wx6HCBRIw9/AIyg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=o7OQWvnef0sc5wRky3R37nIsw1QLY8MriPcN6FAPSrU=; b=uDAj6hIoUzisx9UQ8m1bRViZ3RfR0zliKI9Rj/7jTJxfsdxz6Jn7VnZf+tAUhFi0j2QjJYH4N2lCT+5Jdrd2Wb30J/hcQbkQmf1JaMY+7NpA9QPDmujqc0Frn7G9ERUNLoyKzo8Xw4gGs2Ukh7JxCYilAnaQ5OK9JoUhsAx0q8g= Received: from SJ0PR05CA0124.namprd05.prod.outlook.com (2603:10b6:a03:33d::9) by IA1PR12MB8286.namprd12.prod.outlook.com (2603:10b6:208:3f8::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8792.34; Sun, 8 Jun 2025 14:49:55 +0000 Received: from SJ5PEPF000001D0.namprd05.prod.outlook.com (2603:10b6:a03:33d:cafe::25) by SJ0PR05CA0124.outlook.office365.com (2603:10b6:a03:33d::9) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8835.14 via Frontend Transport; Sun, 8 Jun 2025 14:49:54 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by SJ5PEPF000001D0.mail.protection.outlook.com (10.167.242.52) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.8835.15 via Frontend Transport; Sun, 8 Jun 2025 14:49:54 +0000 Received: from 555e2b870847.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Sun, 8 Jun 2025 09:49:49 -0500 From: Bin Du To: , , , , , , , CC: , , , , , , , , "Bin Du" Subject: [PATCH v1 4/9] media: platform: amd: Add isp4 fw and hw interface Date: Sun, 8 Jun 2025 22:49:11 +0800 Message-ID: <20250608144916.222835-5-Bin.Du@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250608144916.222835-1-Bin.Du@amd.com> References: <20250608144916.222835-1-Bin.Du@amd.com> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: SATLEXMB04.amd.com (10.181.40.145) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ5PEPF000001D0:EE_|IA1PR12MB8286:EE_ X-MS-Office365-Filtering-Correlation-Id: bbd3359f-e4c7-4d39-3546-08dda69bbb0f X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|1800799024|36860700013|376014|82310400026; X-Microsoft-Antispam-Message-Info: u0WOqgqX/y7y+zoZvK99ryAYCiPAALAPNrdOHdlLuOSGOAYPmd0XoU1iNDbx3viOEX2/4g24SQy9bxoAqFAJV1Ok9pUVlrsCjHt+d8e6gn0+wEWHAAKFPdyQKlRSq0TF6POAMdpJa6pVSzC3HZ9rqAZog0ZPk9St4EvrjRhm5ze1zv8o94o648MYR90J/D/BYeP623LAL9oc9bqUvWjCalaNJiwNldbEoG3gMZgQQTc9kc5pR0kbF7R2XhIwvjZEjSJpQgpFGhMZkIDCL16OljqSkqEa2KSmgkj9+ZJRs4GXYhki+rdIVVis+UW3VbhWZ+NQjjl3hjBqikdX/cFZQY2Ueer9zurnZFSE39YObobC8FI23CPJ90eqJMCp0/5+5hpvPJqGvNVDJhtNuArwfnmwWR/zjI6vxBK4D0sd6xVDJiInHOeYoMspGBS9O8DA09irRPAjz6tu1Ubkl6AkukykGDN3uuNnPfC9hFA18+T3vdsGWtXnuVlJZjuejbdvvMKZtHi+Ha5tiSsQPQf6moPr0BJUqV1PEspH37vQe+rtzfVjpv/n2ZARweCo0/fsSBplodAWEdJlNC2gL9c6olKnrDF5SHoy+D2p1nIo+hwWN+Gn/38aJVwbz6gW2e1mfuX8b53n2ZkkLxo2auwKhPZ612GLEr+YQlO7rbTgLcAY0XN7U6e5K1gWBSZXV21uDqwXvp1j2S5qX8Z0ripcVxtHDCi/QOU69wuuCZROXLfbxq7FDQeZlZ6w2am2tpLnQqdRJ8bEIh/XIGKxX4rYgmesYMmspYCtrV/RrFF34B0QyJ9adHmQWbAtoG9T/NvQHl+O1X/8TwLxZE2RvquIVNtaMvj1ftpICKSv+rtvgBiocgHWzYCpQAE7F7uTl2fEuL/H/sArO4VH456FKKaSUNsrLwa+7bZGJyT01R6pnVx2SWvuTJDYNx2m6/4HO6oGGom/ps0hVqJcXIvbLuYyKxtp1/MJGnGaoufZcKxQNknoL8tA32CqC+3a86QnSjGPlrXx5GAOND/6KiTwE5xvUQj3h6xnII/JXAHvsVE4QKdn0yhSQ9b5HAUW97bS0aLbocQE3HdQ2Dr0y2RYGsPMw+l6K4pfmUCu6lfmT62lGeNlAzbqhZ0s9gGWxjxc7borm6z90fSX6Lggptgcc1UTKJszCuSyIVmlV8/K4nSZllSDSN1/aTof/AlLMmL3eo69jTf7KwvZUuz4MS98sJL3KM44AQyL4DXehmtvv/j2VTJrn2d/NnkpNyDm7JSl1RpbSdi73ksWPbDF8Fgz6v28gQUEW5JpP06BV/j4a95NPnem74pkCyk4rAGsDBMBoYia4vQoWCDQXQIOMpMJbPzxbnCpDCNNxVosk9ckWZPFXdTW24CzOgTRMoZoUNMQ2VMjc7CQWZPI5/aXfqMTqtVpeAHV0ZtnwKGGJRNhJAdRwbm52BKjiSBQtoBUDh4DlfybTB62HfncOz7BAfIvgpPPTshfWZQV1tioNtmsq1q38gNrAQeL0w/GZ6QHz/IoT38m X-Forefront-Antispam-Report: CIP:165.204.84.17; CTRY:US; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:SATLEXMB04.amd.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(1800799024)(36860700013)(376014)(82310400026); DIR:OUT; SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Jun 2025 14:49:54.3683 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: bbd3359f-e4c7-4d39-3546-08dda69bbb0f X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d; Ip=[165.204.84.17]; Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: SJ5PEPF000001D0.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA1PR12MB8286 ISP firmware controls ISP HW pipeline using dedicated embedded processor called ccpu. The communication between ISP FW and driver is using commands and response messages sent through the ring buffer. Command buffers support either global setting that is not specific to the steam and support stream specific parameters. Response buffers contains ISP FW notification information such as frame buffer done and command done. IRQ is used for receiving response buffer from ISP firmware, which is handled in the main isp4 media device. ISP ccpu is booted up through the firmware loading helper function prior to stream start. Memory used for command buffer and response buffer needs to be allocated from amdgpu buffer manager because isp4 is a child device of amdgpu. Signed-off-by: Bin Du Signed-off-by: Svetoslav Stoilov Change-Id: Ib15ed5b21fba7686d174a1326e3a66573e93b0ea --- drivers/media/platform/amd/isp4/Makefile | 15 + .../platform/amd/isp4/isp4_fw_cmd_resp.h | 318 +++++ .../media/platform/amd/isp4/isp4_interface.c | 1058 +++++++++++++++++ .../media/platform/amd/isp4/isp4_interface.h | 164 +++ 4 files changed, 1555 insertions(+) create mode 100644 drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.c create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.h diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 7cb496a56353..c76b8a327be6 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -5,10 +5,25 @@ obj-$(CONFIG_AMD_ISP4) += amd_capture.o amd_capture-objs := isp4.o \ isp4_phy.o \ + isp4_interface.o \ isp4_hw.o \ ccflags-y += -I$(srctree)/drivers/media/platform/amd/isp4 +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/include +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/amdgpu +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/pm/inc +ccflags-y += -I$(srctree)/include/drm ccflags-y += -I$(srctree)/include +ccflags-y += -I$(srctree)/include/uapi/drm +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/scheduler +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/powerplay/inc +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/acp/include +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/display +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/display/include +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/display/modules/inc +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/display/dc +ccflags-y += -I$(srctree)/drivers/gpu/drm/amd/display/amdgpu_dm +ccflags-y += -I$(srctree)/../external/libdrm/amdgpu ifneq ($(call cc-option, -mpreferred-stack-boundary=4),) cc_stack_align := -mpreferred-stack-boundary=4 diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h new file mode 100644 index 000000000000..437d89469af2 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_CMD_RESP_H_ +#define _ISP4_CMD_RESP_H_ + +/* + * @brief Host and Firmware command & response channel. + * Two types of command/response channel. + * Type Global Command has one command/response channel. + * Type Stream Command has one command/response channel. + *----------- ------------ + *| | --------------------------- | | + *| | ---->| Global Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | --------------------------- | | + *| | ---->| Stream Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | | | + *| HOST | | Firmware | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Global Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Stream Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *----------- ------------ + */ + +/* + * @brief command ID format + * cmd_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define CMD_TYPE_SHIFT (24) +#define CMD_GROUP_SHIFT (16) +#define CMD_TYPE_STREAM_CTRL ((u32)0x2 << CMD_TYPE_SHIFT) + +#define CMD_GROUP_STREAM_CTRL ((u32)0x1 << CMD_GROUP_SHIFT) +#define CMD_GROUP_STREAM_BUFFER ((u32)0x4 << CMD_GROUP_SHIFT) + +/* Stream Command */ +#define CMD_ID_SET_STREAM_CONFIG \ + (CMD_TYPE_STREAM_CTRL | CMD_GROUP_STREAM_CTRL | 0x1) +#define CMD_ID_SET_OUT_CHAN_PROP \ + (CMD_TYPE_STREAM_CTRL | CMD_GROUP_STREAM_CTRL | 0x3) +#define CMD_ID_ENABLE_OUT_CHAN \ + (CMD_TYPE_STREAM_CTRL | CMD_GROUP_STREAM_CTRL | 0x5) +#define CMD_ID_START_STREAM \ + (CMD_TYPE_STREAM_CTRL | CMD_GROUP_STREAM_CTRL | 0x7) +#define CMD_ID_STOP_STREAM \ + (CMD_TYPE_STREAM_CTRL | CMD_GROUP_STREAM_CTRL | 0x8) + +/* Stream Buffer Command */ +#define CMD_ID_SEND_BUFFER \ + (CMD_TYPE_STREAM_CTRL | CMD_GROUP_STREAM_BUFFER | 0x1) + +/* + * @brief response ID format + * resp_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define RESP_GROUP_SHIFT (16) +#define RESP_GROUP_MASK (0xff << RESP_GROUP_SHIFT) + +#define GET_RESP_GROUP_VALUE(resp_id) \ + (((resp_id) & RESP_GROUP_MASK) >> \ + RESP_GROUP_SHIFT) +#define GET_RESP_ID_VALUE(resp_id) ((resp_id) & 0xffff) + +#define RESP_GROUP_GENERAL (0x1 << RESP_GROUP_SHIFT) +#define RESP_GROUP_NOTIFICATION (0x3 << RESP_GROUP_SHIFT) + +/* General Response */ +#define RESP_ID_CMD_DONE (RESP_GROUP_GENERAL | 0x1) + +/* Notification */ +#define RESP_ID_NOTI_FRAME_DONE (RESP_GROUP_NOTIFICATION | 0x1) + +#define CMD_STATUS_SUCCESS (0) +#define CMD_STATUS_FAIL (1) +#define CMD_STATUS_SKIPPED (2) + +#define ADDR_SPACE_TYPE_GPU_VA 4 + +#define FW_MEMORY_POOL_SIZE (200 * 1024 * 1024) + +/* + * standard ISP mipicsi=>isp + */ +#define MIPI0_ISP_PIPELINE_ID 0x5f91 + +enum isp4fw_sensor_id { + SENSOR_ID_ON_MIPI0 = 0, /* Sensor id for ISP input from MIPI port 0 */ +}; + +enum isp4fw_stream_id { + STREAM_ID_INVALID = -1, /* STREAM_ID_INVALID. */ + STREAM_ID_1 = 0, /* STREAM_ID_1. */ + STREAM_ID_2 = 1, /* STREAM_ID_2. */ + STREAM_ID_3 = 2, /* STREAM_ID_3. */ + STREAM_ID_MAXIMUM /* STREAM_ID_MAXIMUM. */ +}; + +enum isp4fw_image_format { + IMAGE_FORMAT_NV12 = 1, /* 4:2:0,semi-planar, 8-bit */ + IMAGE_FORMAT_YUV422INTERLEAVED = 7, /* interleave, 4:2:2, 8-bit */ +}; + +enum isp4fw_pipe_out_ch { + ISP_PIPE_OUT_CH_PREVIEW = 0, +}; + +enum isp4fw_yuv_range { + ISP_YUV_RANGE_FULL = 0, /* YUV value range in 0~255 */ + ISP_YUV_RANGE_NARROW = 1, /* YUV value range in 16~235 */ + ISP_YUV_RANGE_MAX +}; + +enum isp4fw_buffer_type { + BUFFER_TYPE_PREVIEW = 8, + BUFFER_TYPE_META_INFO = 10, + BUFFER_TYPE_MEM_POOL = 15, +}; + +enum isp4fw_buffer_status { + BUFFER_STATUS_INVALID, /* The buffer is INVALID */ + BUFFER_STATUS_SKIPPED, /* The buffer is not filled with image data */ + BUFFER_STATUS_EXIST, /* The buffer is exist and waiting for filled */ + BUFFER_STATUS_DONE, /* The buffer is filled with image data */ + BUFFER_STATUS_LACK, /* The buffer is unavailable */ + BUFFER_STATUS_DIRTY, /* The buffer is dirty, probably caused by + * LMI leakage + */ + BUFFER_STATUS_MAX /* The buffer STATUS_MAX */ +}; + +enum isp4fw_buffer_source { + /* The buffer is from the stream buffer queue */ + BUFFER_SOURCE_STREAM, +}; + +struct isp4fw_error_code { + u32 code1; + u32 code2; + u32 code3; + u32 code4; + u32 code5; +}; + +/* + * Command Structure for FW + */ + +struct isp4fw_cmd { + u32 cmd_seq_num; + u32 cmd_id; + u32 cmd_param[12]; + u16 cmd_stream_id; + u8 cmd_silent_resp; + u8 reserved; + u32 cmd_check_sum; +}; + +struct isp4fw_resp_cmd_done { + /* The host2fw command seqNum. + * To indicate which command this response refer to. + */ + u32 cmd_seq_num; + /* The host2fw command id for host double check. */ + u32 cmd_id; + /* Indicate the command process status. + * 0 means success. 1 means fail. 2 means skipped + */ + u16 cmd_status; + /* If the cmd_status is 1, that means the command is processed fail, */ + /* host can check the isp4fw_error_code to get the detail + * error information + */ + u16 isp4fw_error_code; + /* The response payload will be in different struct type */ + /* according to different cmd done response. */ + u8 payload[36]; +}; + +struct isp4fw_resp_param_package { + u32 package_addr_lo; /* The low 32 bit addr of the pkg address. */ + u32 package_addr_hi; /* The high 32 bit addr of the pkg address. */ + u32 package_size; /* The total pkg size in bytes. */ + u32 package_check_sum; /* The byte sum of the pkg. */ +}; + +struct isp4fw_resp { + u32 resp_seq_num; + u32 resp_id; + union { + struct isp4fw_resp_cmd_done cmd_done; + struct isp4fw_resp_param_package frame_done; + u32 resp_param[12]; + } param; + u8 reserved[4]; + u32 resp_check_sum; +}; + +struct isp4fw_mipi_pipe_path_cfg { + u32 b_enable; + enum isp4fw_sensor_id isp4fw_sensor_id; +}; + +struct isp4fw_isp_pipe_path_cfg { + u32 isp_pipe_id; /* pipe ids for pipeline construction */ +}; + +struct isp4fw_isp_stream_cfg { + /* Isp mipi path */ + struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg; + /* Isp pipe path */ + struct isp4fw_isp_pipe_path_cfg isp_pipe_path_cfg; + /* enable TNR */ + u32 b_enable_tnr; + /* number of frame rta per-processing, + * set to 0 to use fw default value + */ + u32 rta_frames_per_proc; +}; + +struct isp4fw_image_prop { + enum isp4fw_image_format image_format; /* Image format */ + u32 width; /* Width */ + u32 height; /* Height */ + u32 luma_pitch; /* Luma pitch */ + u32 chroma_pitch; /* Chrom pitch */ + enum isp4fw_yuv_range yuv_range; /* YUV value range */ +}; + +struct isp4fw_buffer { + /* A check num for debug usage, host need to */ + /* set the buf_tags to different number */ + u32 buf_tags; + union { + u32 value; + struct { + u32 space : 16; + u32 vmid : 16; + } bit; + } vmid_space; + u32 buf_base_a_lo; /* Low address of buffer A */ + u32 buf_base_a_hi; /* High address of buffer A */ + u32 buf_size_a; /* Buffer size of buffer A */ + + u32 buf_base_b_lo; /* Low address of buffer B */ + u32 buf_base_b_hi; /* High address of buffer B */ + u32 buf_size_b; /* Buffer size of buffer B */ + + u32 buf_base_c_lo; /* Low address of buffer C */ + u32 buf_base_c_hi; /* High address of buffer C */ + u32 buf_size_c; /* Buffer size of buffer C */ +}; + +struct isp4fw_buffer_meta_info { + u32 enabled; /* enabled flag */ + enum isp4fw_buffer_status status; /* BufferStatus */ + struct isp4fw_error_code err; /* err code */ + enum isp4fw_buffer_source source; /* BufferSource */ + struct isp4fw_image_prop image_prop; /* image_prop */ + struct isp4fw_buffer buffer; /* buffer */ +}; + +struct isp4fw_meta_info { + u32 poc; /* frame id */ + u32 fc_id; /* frame ctl id */ + u32 time_stamp_lo; /* time_stamp_lo */ + u32 time_stamp_hi; /* time_stamp_hi */ + struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */ +}; + +struct isp4fw_cmd_send_buffer { + enum isp4fw_buffer_type buffer_type; /* buffer Type */ + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_cmd_set_out_ch_prop { + enum isp4fw_pipe_out_ch ch; /* ISP pipe out channel */ + struct isp4fw_image_prop image_prop; /* image property */ +}; + +struct isp4fw_cmd_enable_out_ch { + enum isp4fw_pipe_out_ch ch; /* ISP pipe out channel */ + u32 is_enable; /* If enable channel or not */ +}; + +struct isp4fw_cmd_set_stream_cfg { + struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */ +}; + +#endif diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c new file mode 100644 index 000000000000..d46d7487a994 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -0,0 +1,1058 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include + +#include "amdgpu_object.h" + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \ + (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK | \ + ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK | \ + ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK | \ + ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK) + +struct isp4if_rb_config { + const char *name; + u32 index; + u32 reg_rptr; + u32 reg_wptr; + u32 reg_base_lo; + u32 reg_base_hi; + u32 reg_size; + u32 val_size; + u64 base_mc_addr; + void *base_sys_addr; +}; + +/* FW cmd ring buffer configuration */ +static struct isp4if_rb_config + isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "CMD_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR4, + .reg_wptr = ISP_RB_WPTR4, + .reg_base_lo = ISP_RB_BASE_LO4, + .reg_base_hi = ISP_RB_BASE_HI4, + .reg_size = ISP_RB_SIZE4, + }, + { + .name = "CMD_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR1, + .reg_wptr = ISP_RB_WPTR1, + .reg_base_lo = ISP_RB_BASE_LO1, + .reg_base_hi = ISP_RB_BASE_HI1, + .reg_size = ISP_RB_SIZE1, + }, + { + .name = "CMD_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR2, + .reg_wptr = ISP_RB_WPTR2, + .reg_base_lo = ISP_RB_BASE_LO2, + .reg_base_hi = ISP_RB_BASE_HI2, + .reg_size = ISP_RB_SIZE2, + }, + { + .name = "CMD_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR3, + .reg_wptr = ISP_RB_WPTR3, + .reg_base_lo = ISP_RB_BASE_LO3, + .reg_base_hi = ISP_RB_BASE_HI3, + .reg_size = ISP_RB_SIZE3, + }, +}; + +/* FW resp ring buffer configuration */ +static struct isp4if_rb_config + isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "RES_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR12, + .reg_wptr = ISP_RB_WPTR12, + .reg_base_lo = ISP_RB_BASE_LO12, + .reg_base_hi = ISP_RB_BASE_HI12, + .reg_size = ISP_RB_SIZE12, + }, + { + .name = "RES_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR9, + .reg_wptr = ISP_RB_WPTR9, + .reg_base_lo = ISP_RB_BASE_LO9, + .reg_base_hi = ISP_RB_BASE_HI9, + .reg_size = ISP_RB_SIZE9, + }, + { + .name = "RES_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR10, + .reg_wptr = ISP_RB_WPTR10, + .reg_base_lo = ISP_RB_BASE_LO10, + .reg_base_hi = ISP_RB_BASE_HI10, + .reg_size = ISP_RB_SIZE10, + }, + { + .name = "RES_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR11, + .reg_wptr = ISP_RB_WPTR11, + .reg_base_lo = ISP_RB_BASE_LO11, + .reg_base_hi = ISP_RB_BASE_HI11, + .reg_size = ISP_RB_SIZE11, + }, +}; + +/* FW log ring buffer configuration */ +static struct isp4if_rb_config isp4if_log_rb_config = { + .name = "LOG_RB", + .index = 0, + .reg_rptr = ISP_LOG_RB_RPTR0, + .reg_wptr = ISP_LOG_RB_WPTR0, + .reg_base_lo = ISP_LOG_RB_BASE_LO0, + .reg_base_hi = ISP_LOG_RB_BASE_HI0, + .reg_size = ISP_LOG_RB_SIZE0, +}; + +static struct isp4if_gpu_mem_info *isp4if_gpu_mem_alloc(struct isp4_interface + *ispif, + u32 mem_size) +{ + struct isp4if_gpu_mem_info *mem_info; + struct amdgpu_bo *bo = NULL; + struct amdgpu_device *adev; + struct device *dev; + + void *cpu_ptr; + u64 gpu_addr; + u32 ret; + + dev = ispif->dev; + + if (!mem_size) + return NULL; + + mem_info = kzalloc(sizeof(*mem_info), GFP_KERNEL); + if (!mem_info) + return NULL; + + adev = (struct amdgpu_device *)ispif->adev; + mem_info->mem_size = mem_size; + mem_info->mem_align = ISP4IF_ISP_MC_ADDR_ALIGN; + mem_info->mem_domain = AMDGPU_GEM_DOMAIN_GTT; + + ret = amdgpu_bo_create_kernel(adev, + mem_info->mem_size, + mem_info->mem_align, + mem_info->mem_domain, + &bo, + &gpu_addr, + &cpu_ptr); + + if (!cpu_ptr || ret) { + dev_err(dev, "gpuvm buffer alloc fail, size %u\n", mem_size); + kfree(mem_info); + return NULL; + } + + mem_info->sys_addr = cpu_ptr; + mem_info->gpu_mc_addr = gpu_addr; + mem_info->mem_handle = (void *)bo; + + return mem_info; +} + +static int isp4if_gpu_mem_free(struct isp4_interface *ispif, + struct isp4if_gpu_mem_info *mem_info) +{ + struct device *dev = ispif->dev; + struct amdgpu_bo *bo; + + if (!mem_info) { + dev_err(dev, "invalid mem_info\n"); + return -EINVAL; + } + + bo = (struct amdgpu_bo *)mem_info->mem_handle; + + amdgpu_bo_free_kernel(&bo, &mem_info->gpu_mc_addr, &mem_info->sys_addr); + + kfree(mem_info); + + return 0; +} + +static int isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif) +{ + int i; + + if (ispif->fw_mem_pool) { + isp4if_gpu_mem_free(ispif, ispif->fw_mem_pool); + ispif->fw_mem_pool = NULL; + } + + if (ispif->fw_cmd_resp_buf) { + isp4if_gpu_mem_free(ispif, ispif->fw_cmd_resp_buf); + ispif->fw_cmd_resp_buf = NULL; + } + + if (ispif->fw_log_buf) { + isp4if_gpu_mem_free(ispif, ispif->fw_log_buf); + ispif->fw_log_buf = NULL; + } + + for (i = 0; i < ISP4IF_MAX_STREAM_META_BUF_COUNT; i++) { + if (ispif->metainfo_buf_pool[i]) { + isp4if_gpu_mem_free(ispif, ispif->metainfo_buf_pool[i]); + ispif->metainfo_buf_pool[i] = NULL; + } + } + + return 0; +} + +static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + int i; + + ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif, FW_MEMORY_POOL_SIZE); + if (!ispif->fw_mem_pool) + goto error_no_memory; + + ispif->fw_cmd_resp_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE); + if (!ispif->fw_cmd_resp_buf) + goto error_no_memory; + + ispif->fw_log_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE); + if (!ispif->fw_log_buf) + goto error_no_memory; + + for (i = 0; i < ISP4IF_MAX_STREAM_META_BUF_COUNT; i++) { + ispif->metainfo_buf_pool[i] = + isp4if_gpu_mem_alloc(ispif, + ISP4IF_META_INFO_BUF_SIZE); + if (!ispif->metainfo_buf_pool[i]) + goto error_no_memory; + } + + return 0; + +error_no_memory: + dev_err(dev, "failed to allocate gpu memory"); + return -ENOMEM; +} + +static u32 isp4if_compute_check_sum(u8 *buf, u32 buf_size) +{ + u32 checksum = 0; + u8 *surplus_ptr; + u32 *buffer; + u32 i; + + buffer = (u32 *)buf; + for (i = 0; i < buf_size / sizeof(u32); i++) + checksum += buffer[i]; + + surplus_ptr = (u8 *)&buffer[i]; + /* add surplus data crc checksum */ + for (i = 0; i < buf_size % sizeof(u32); i++) + checksum += surplus_ptr[i]; + + return checksum; +} + +void isp4if_clear_cmdq(struct isp4_interface *ispif) +{ + struct isp4if_cmd_element *buf_node = NULL; + struct isp4if_cmd_element *tmp_node = NULL; + + guard(mutex)(&ispif->cmdq_mutex); + + list_for_each_entry_safe(buf_node, tmp_node, &ispif->cmdq, list) { + list_del(&buf_node->list); + kfree(buf_node); + } +} + +static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif, + enum isp4if_stream_id cmd_buf_idx) +{ + struct isp4if_rb_config *rb_config; + u32 rd_ptr, wr_ptr; + u32 new_wr_ptr; + u32 rreg; + u32 wreg; + u32 len; + + rb_config = &isp4if_cmd_rb_config[cmd_buf_idx]; + rreg = rb_config->reg_rptr; + wreg = rb_config->reg_wptr; + len = rb_config->val_size; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + + new_wr_ptr = wr_ptr + sizeof(struct isp4fw_cmd); + + if (wr_ptr >= rd_ptr) { + if (new_wr_ptr < len) { + return false; + } else if (new_wr_ptr == len) { + if (rd_ptr == 0) + return true; + + return false; + } + + new_wr_ptr -= len; + if (new_wr_ptr < rd_ptr) + return false; + + return true; + } + + if (new_wr_ptr < rd_ptr) + return false; + + return true; +} + +static struct isp4if_cmd_element * +isp4if_append_cmd_2_cmdq(struct isp4_interface *ispif, + struct isp4if_cmd_element *cmd_ele) +{ + struct isp4if_cmd_element *copy_command = NULL; + + copy_command = kmalloc(sizeof(*copy_command), GFP_KERNEL); + if (!copy_command) + return NULL; + + memcpy(copy_command, cmd_ele, sizeof(*copy_command)); + + guard(mutex)(&ispif->cmdq_mutex); + + list_add_tail(©_command->list, &ispif->cmdq); + + return copy_command; +} + +struct isp4if_cmd_element * +isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, + u32 cmd_id) +{ + struct isp4if_cmd_element *buf_node = NULL; + struct isp4if_cmd_element *tmp_node = NULL; + + guard(mutex)(&ispif->cmdq_mutex); + + list_for_each_entry_safe(buf_node, tmp_node, &ispif->cmdq, list) { + if (buf_node->seq_num == seq_num && + buf_node->cmd_id == cmd_id) { + list_del(&buf_node->list); + return buf_node; + } + } + + return NULL; +} + +static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + struct isp4fw_cmd *cmd) +{ + struct isp4if_rb_config *rb_config; + struct device *dev = ispif->dev; + u64 mem_addr; + u64 mem_sys; + u32 wr_ptr; + u32 rd_ptr; + u32 rreg; + u32 wreg; + u32 len; + + rb_config = &isp4if_cmd_rb_config[stream]; + rreg = rb_config->reg_rptr; + wreg = rb_config->reg_wptr; + mem_sys = (u64)rb_config->base_sys_addr; + mem_addr = rb_config->base_mc_addr; + len = rb_config->val_size; + + if (isp4if_is_cmdq_rb_full(ispif, stream)) { + dev_err(dev, "fail no cmdslot (%d)\n", stream); + return -EINVAL; + } + + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + + if (rd_ptr > len) { + dev_err(dev, "fail (%u),rd_ptr %u(should<=%u),wr_ptr %u\n", + stream, rd_ptr, len, wr_ptr); + return -EINVAL; + } + + if (wr_ptr > len) { + dev_err(dev, "fail (%u),wr_ptr %u(should<=%u), rd_ptr %u\n", + stream, wr_ptr, len, rd_ptr); + return -EINVAL; + } + + if (wr_ptr < rd_ptr) { + mem_addr += wr_ptr; + + memcpy((u8 *)(mem_sys + wr_ptr), + (u8 *)cmd, sizeof(struct isp4fw_cmd)); + } else { + if ((len - wr_ptr) >= (sizeof(struct isp4fw_cmd))) { + mem_addr += wr_ptr; + + memcpy((u8 *)(mem_sys + wr_ptr), + (u8 *)cmd, sizeof(struct isp4fw_cmd)); + } else { + u32 size; + u8 *src; + + src = (u8 *)cmd; + size = len - wr_ptr; + + memcpy((u8 *)(mem_sys + wr_ptr), src, size); + + src += size; + size = sizeof(struct isp4fw_cmd) - size; + memcpy((u8 *)(mem_sys), src, size); + } + } + + wr_ptr += sizeof(struct isp4fw_cmd); + if (wr_ptr >= len) + wr_ptr -= len; + + isp4hw_wreg(ispif->mmio, wreg, wr_ptr); + + return 0; +} + +static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id) +{ + return ISP4IF_STREAM_ID_1; +} + +static int isp4if_send_fw_cmd(struct isp4_interface *ispif, + u32 cmd_id, + void *package, + u32 package_size, + wait_queue_head_t *wq, + u32 *wq_cond, + u32 *seq) +{ + enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id); + struct isp4if_cmd_element command_element = { 0 }; + struct isp4if_gpu_mem_info *gpu_mem = NULL; + struct isp4if_cmd_element *cmd_ele = NULL; + struct isp4if_rb_config *rb_config; + struct device *dev = ispif->dev; + struct isp4fw_cmd cmd = {0}; + u64 package_base = 0; + u32 sleep_count; + u32 seq_num; + u32 rreg; + u32 wreg; + u32 len; + int ret; + + if (package_size > sizeof(cmd.cmd_param)) { + dev_err(dev, "fail pkgsize(%u)>%lu cmd:0x%x,stream %d\n", + package_size, sizeof(cmd.cmd_param), cmd_id, stream); + return -EINVAL; + } + + sleep_count = 0; + + rb_config = &isp4if_resp_rb_config[stream]; + rreg = rb_config->reg_rptr; + wreg = rb_config->reg_wptr; + len = rb_config->val_size; + + guard(mutex)(&ispif->isp4if_mutex); + + while (1) { + if (isp4if_is_cmdq_rb_full(ispif, stream)) { + u32 rd_ptr, wr_ptr; + + if (sleep_count < ISP4IF_MAX_SLEEP_COUNT) { + msleep(ISP4IF_MAX_SLEEP_TIME); + sleep_count++; + continue; + } + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + dev_err(dev, + "failed to get free cmdq slot, stream (%d)\n", + stream); + return -ETIMEDOUT; + } + break; + } + + cmd.cmd_id = cmd_id; + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + cmd.cmd_stream_id = (u16)STREAM_ID_INVALID; + break; + case ISP4IF_STREAM_ID_1: + cmd.cmd_stream_id = STREAM_ID_1; + break; + default: + dev_err(dev, "fail bad stream id %d\n", stream); + return -EINVAL; + } + + if (package && package_size) + memcpy(cmd.cmd_param, package, package_size); + + seq_num = ispif->host2fw_seq_num++; + cmd.cmd_seq_num = seq_num; + cmd.cmd_check_sum = + isp4if_compute_check_sum((u8 *)&cmd, sizeof(cmd) - 4); + + if (seq) + *seq = seq_num; + command_element.seq_num = seq_num; + command_element.cmd_id = cmd_id; + command_element.mc_addr = package_base; + command_element.wq = wq; + command_element.wq_cond = wq_cond; + command_element.gpu_pkg = gpu_mem; + command_element.stream = stream; + /* only append the fw cmd to queue when its response needs to be + * waited for, currently there are only two such commands, + * disable channel and stop stream which are only sent after close + * camera + */ + if (wq && wq_cond) { + cmd_ele = isp4if_append_cmd_2_cmdq(ispif, &command_element); + if (!cmd_ele) { + dev_err(dev, "fail for isp_append_cmd_2_cmdq\n"); + return -ENOMEM; + } + } + + ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); + if (ret) { + dev_err(dev, "fail for insert_isp_fw_cmd camId (0x%08x)\n", + cmd_id); + if (cmd_ele) { + isp4if_rm_cmd_from_cmdq(ispif, cmd_ele->seq_num, + cmd_ele->cmd_id); + kfree(cmd_ele); + } + } + + return ret; +} + +static int isp4if_send_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4fw_cmd_send_buffer cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.buffer_type = BUFFER_TYPE_PREVIEW; + cmd.buffer.vmid_space.bit.vmid = 0; + cmd.buffer.vmid_space.bit.space = ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(buf_info->planes[0].mc_addr, + &cmd.buffer.buf_base_a_lo, + &cmd.buffer.buf_base_a_hi); + cmd.buffer.buf_size_a = buf_info->planes[0].len; + + isp4if_split_addr64(buf_info->planes[1].mc_addr, + &cmd.buffer.buf_base_b_lo, + &cmd.buffer.buf_base_b_hi); + cmd.buffer.buf_size_b = buf_info->planes[1].len; + + isp4if_split_addr64(buf_info->planes[2].mc_addr, + &cmd.buffer.buf_base_c_lo, + &cmd.buffer.buf_base_c_hi); + cmd.buffer.buf_size_c = buf_info->planes[2].len; + + return isp4if_send_fw_cmd(ispif, CMD_ID_SEND_BUFFER, &cmd, + sizeof(cmd), NULL, NULL, NULL); +} + +static void isp4if_init_rb_config(struct isp4_interface *ispif, + struct isp4if_rb_config *rb_config) +{ + u32 lo; + u32 hi; + + isp4if_split_addr64(rb_config->base_mc_addr, &lo, &hi); + + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rb_config->reg_rptr, 0x0); + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rb_config->reg_wptr, 0x0); + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rb_config->reg_base_lo, lo); + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rb_config->reg_base_hi, hi); + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rb_config->reg_size, rb_config->val_size); +} + +static int isp4if_fw_init(struct isp4_interface *ispif) +{ + struct isp4if_rb_config *rb_config; + u32 offset; + int i; + + /* initialize CMD_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_cmd_rb_config + i); + offset = ispif->aligned_rb_chunk_size * + (rb_config->index + ispif->cmd_rb_base_index); + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + (u8 *)ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize RESP_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_resp_rb_config + i); + offset = ispif->aligned_rb_chunk_size * + (rb_config->index + ispif->resp_rb_base_index); + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + (u8 *)ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize LOG_RB stream */ + rb_config = &isp4if_log_rb_config; + rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE; + rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr; + rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr; + + isp4if_init_rb_config(ispif, rb_config); + + return 0; +} + +static int isp4if_wait_fw_ready(struct isp4_interface *ispif, + u32 isp_status_addr) +{ + struct device *dev = ispif->dev; + u32 fw_ready_timeout; + u32 timeout_ms = 100; + u32 interval_ms = 1; + u32 timeout = 0; + u32 reg_val; + + fw_ready_timeout = timeout_ms / interval_ms; + + /* wait for FW initialize done! */ + while (timeout < fw_ready_timeout) { + reg_val = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), + isp_status_addr); + if (reg_val & ISP_STATUS__CCPU_REPORT_MASK) + return 0; + + msleep(interval_ms); + timeout++; + } + + dev_err(dev, "ISP CCPU FW boot failed\n"); + + return -ETIME; +} + +static void isp4if_enable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), ISP_SOFT_RESET); + reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK); + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), ISP_SOFT_RESET, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), ISP_CCPU_CNTL); + reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK); + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), ISP_CCPU_CNTL, reg_val); +} + +static void isp4if_disable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), ISP_CCPU_CNTL); + reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK; + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), ISP_CCPU_CNTL, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), ISP_SOFT_RESET); + reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK; + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), ISP_SOFT_RESET, reg_val); +} + +static int isp4if_fw_boot(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + if (ispif->status != ISP4IF_STATUS_PWR_ON) { + dev_err(dev, "invalid isp power status %d\n", ispif->status); + return -EINVAL; + } + + isp4if_disable_ccpu(ispif); + + isp4if_fw_init(ispif); + + /* clear ccpu status */ + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), ISP_STATUS, 0x0); + + isp4if_enable_ccpu(ispif); + + if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) { + isp4if_disable_ccpu(ispif); + return -EINVAL; + } + + /* enable interrupts */ + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), ISP_SYS_INT0_EN, + ISP4IF_FW_RESP_RB_IRQ_EN_MASK); + + ispif->status = ISP4IF_STATUS_FW_RUNNING; + + dev_dbg(dev, "ISP CCPU FW boot success\n"); + + return 0; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + void *resp) +{ + struct isp4fw_resp *response = resp; + struct isp4if_rb_config *rb_config; + struct device *dev = ispif->dev; + u32 rd_ptr_dbg; + u32 wr_ptr_dbg; + void *mem_sys; + u64 mem_addr; + u32 checksum; + u32 rd_ptr; + u32 wr_ptr; + u32 rreg; + u32 wreg; + u32 len; + + rb_config = &isp4if_resp_rb_config[stream]; + rreg = rb_config->reg_rptr; + wreg = rb_config->reg_wptr; + mem_sys = rb_config->base_sys_addr; + mem_addr = rb_config->base_mc_addr; + len = rb_config->val_size; + + rd_ptr = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), rreg); + wr_ptr = isp4hw_rreg(GET_ISP4IF_REG_BASE(ispif), wreg); + rd_ptr_dbg = rd_ptr; + wr_ptr_dbg = wr_ptr; + + if (rd_ptr > len) { + dev_err(dev, "fail (%u),rd_ptr %u(should<=%u),wr_ptr %u\n", + stream, rd_ptr, len, wr_ptr); + return -EINVAL; + } + + if (wr_ptr > len) { + dev_err(dev, "fail (%u),wr_ptr %u(should<=%u), rd_ptr %u\n", + stream, wr_ptr, len, rd_ptr); + return -EINVAL; + } + + if (rd_ptr < wr_ptr) { + if ((wr_ptr - rd_ptr) >= (sizeof(struct isp4fw_resp))) { + memcpy((u8 *)response, (u8 *)mem_sys + rd_ptr, + sizeof(struct isp4fw_resp)); + + rd_ptr += sizeof(struct isp4fw_resp); + if (rd_ptr < len) { + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rreg, rd_ptr); + } else { + dev_err(dev, "(%u),rd %u(should<=%u),wr %u\n", + stream, rd_ptr, len, wr_ptr); + return -EINVAL; + } + + } else { + dev_err(dev, "sth wrong with wptr and rptr\n"); + return -EINVAL; + } + } else if (rd_ptr > wr_ptr) { + u64 src_addr; + u32 size; + u8 *dst; + + dst = (u8 *)response; + + src_addr = mem_addr + rd_ptr; + size = len - rd_ptr; + if (size > sizeof(struct isp4fw_resp)) { + mem_addr += rd_ptr; + memcpy((u8 *)response, + (u8 *)(mem_sys) + rd_ptr, + sizeof(struct isp4fw_resp)); + rd_ptr += sizeof(struct isp4fw_resp); + if (rd_ptr < len) { + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rreg, rd_ptr); + } else { + dev_err(dev, "(%u),rd %u(should<=%u),wr %u\n", + stream, rd_ptr, len, wr_ptr); + return -EINVAL; + } + + } else { + if ((size + wr_ptr) < (sizeof(struct isp4fw_resp))) { + dev_err(dev, "sth wrong with wptr and rptr1\n"); + return -EINVAL; + } + + memcpy(dst, (u8 *)(mem_sys) + rd_ptr, size); + + dst += size; + src_addr = mem_addr; + size = sizeof(struct isp4fw_resp) - size; + if (size) + memcpy(dst, (u8 *)(mem_sys), size); + rd_ptr = size; + if (rd_ptr < len) { + isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), + rreg, rd_ptr); + } else { + dev_err(dev, "(%u),rd %u(should<=%u),wr %u\n", + stream, rd_ptr, len, wr_ptr); + return -EINVAL; + } + } + } else { + return -ETIME; + } + + checksum = isp4if_compute_check_sum((u8 *)response, + (sizeof(struct isp4fw_resp) - 4)); + + if (checksum != response->resp_check_sum) { + dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", + checksum, response->resp_check_sum, + rd_ptr_dbg, wr_ptr_dbg); + + dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", + stream, + response->resp_seq_num, + response->resp_id); + + return -EINVAL; + } + + return 0; +} + +int isp4if_send_command(struct isp4_interface *ispif, + u32 cmd_id, + void *package, + u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, + cmd_id, package, + package_size, NULL, NULL, NULL); +} + +int isp4if_send_command_sync(struct isp4_interface *ispif, + u32 cmd_id, + void *package, + u32 package_size, + u32 timeout) +{ + struct device *dev = ispif->dev; + DECLARE_WAIT_QUEUE_HEAD(cmd_wq); + u32 wq_cond = 0; + int ret; + u32 seq; + + ret = isp4if_send_fw_cmd(ispif, + cmd_id, package, + package_size, &cmd_wq, &wq_cond, &seq); + + if (ret) { + dev_err(dev, "send fw cmd fail %d\n", ret); + return ret; + } + + ret = wait_event_timeout(cmd_wq, wq_cond != 0, + msecs_to_jiffies(timeout)); + + /* timeout occurred */ + if (ret == 0) { + struct isp4if_cmd_element *ele; + + ele = isp4if_rm_cmd_from_cmdq(ispif, seq, cmd_id); + kfree(ele); + return -ETIMEDOUT; + } + + return 0; +} + +void isp4if_clear_bufq(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node = NULL; + struct isp4if_img_buf_node *tmp_node = NULL; + + guard(mutex)(&ispif->bufq_mutex); + + list_for_each_entry_safe(buf_node, tmp_node, &ispif->bufq, + node) { + list_del(&buf_node->node); + kfree(buf_node); + } +} + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node) +{ + kfree(buf_node); +} + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) +{ + struct isp4if_img_buf_node *node = NULL; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (node) + node->buf_info = *buf_info; + + return node; +}; + +struct isp4if_img_buf_node * +isp4if_dequeue_buffer(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node = NULL; + + guard(mutex)(&ispif->bufq_mutex); + + buf_node = list_first_entry_or_null(&ispif->bufq, + typeof(*buf_node), + node); + if (buf_node) + list_del(&buf_node->node); + + return buf_node; +} + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node) +{ + int ret; + + ret = isp4if_send_buffer(ispif, &buf_node->buf_info); + if (ret) + return ret; + + guard(mutex)(&ispif->bufq_mutex); + + list_add_tail(&buf_node->node, &ispif->bufq); + + return 0; +} + +int isp4if_stop(struct isp4_interface *ispif) +{ + isp4if_disable_ccpu(ispif); + + isp4if_dealloc_fw_gpumem(ispif); + + return 0; +} + +int isp4if_start(struct isp4_interface *ispif) +{ + int ret; + + ret = isp4if_alloc_fw_gpumem(ispif); + if (ret) + goto failed_gpumem_alloc; + + ret = isp4if_fw_boot(ispif); + if (ret) + goto failed_fw_boot; + + return 0; + +failed_gpumem_alloc: + return -ENOMEM; + +failed_fw_boot: + isp4if_dealloc_fw_gpumem(ispif); + return ret; +} + +int isp4if_deinit(struct isp4_interface *ispif) +{ + isp4if_clear_cmdq(ispif); + + isp4if_clear_bufq(ispif); + + mutex_destroy(&ispif->cmdq_mutex); + mutex_destroy(&ispif->bufq_mutex); + mutex_destroy(&ispif->isp4if_mutex); + + return 0; +} + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void *amdgpu_dev, void __iomem *isp_mmip) +{ + ispif->dev = dev; + ispif->adev = amdgpu_dev; + ispif->mmio = isp_mmip; + + ispif->cmd_rb_base_index = 0; + ispif->resp_rb_base_index = ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1; + ispif->aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0; + + mutex_init(&ispif->cmdq_mutex); /* used for cmdq access */ + mutex_init(&ispif->bufq_mutex); /* used for bufq access */ + mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */ + + INIT_LIST_HEAD(&ispif->cmdq); + INIT_LIST_HEAD(&ispif->bufq); + + return 0; +} diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h new file mode 100644 index 000000000000..b2ca147b78b6 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_INTERFACE_ +#define _ISP4_INTERFACE_ + +#define ISP4IF_RB_MAX (25) +#define ISP4IF_RESP_CHAN_TO_RB_OFFSET (9) +#define ISP4IF_RB_PMBMAP_MEM_SIZE (16 * 1024 * 1024 - 1) +#define ISP4IF_RB_PMBMAP_MEM_CHUNK (ISP4IF_RB_PMBMAP_MEM_SIZE \ + / (ISP4IF_RB_MAX - 1)) +#define ISP4IF_ISP_MC_ADDR_ALIGN (1024 * 32) +#define ISP4IF_HOST2FW_COMMAND_SIZE (sizeof(struct isp4fw_cmd)) +#define ISP4IF_FW_CMD_BUF_COUNT 4 +#define ISP4IF_FW_RESP_BUF_COUNT 4 +#define ISP4IF_MAX_NUM_HOST2FW_COMMAND (40) +#define ISP4IF_FW_CMD_BUF_SIZE (ISP4IF_MAX_NUM_HOST2FW_COMMAND \ + * ISP4IF_HOST2FW_COMMAND_SIZE) +#define ISP4IF_MAX_SLEEP_COUNT (10) +#define ISP4IF_MAX_SLEEP_TIME (33) + +#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) +#define ISP4IF_MAX_STREAM_META_BUF_COUNT 6 + +#define ISP4IF_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024) + +#define ISP4IF_MAX_CMD_RESPONSE_BUF_SIZE (4 * 1024) + +#define GET_ISP4IF_REG_BASE(ispif) (((ispif))->mmio) + +enum isp4if_stream_id { + ISP4IF_STREAM_ID_GLOBAL = 0, + ISP4IF_STREAM_ID_1 = 1, + ISP4IF_STREAM_ID_MAX = 4 +}; + +enum isp4if_status { + ISP4IF_STATUS_PWR_OFF, + ISP4IF_STATUS_PWR_ON, + ISP4IF_STATUS_FW_RUNNING, + ISP4IF_FSM_STATUS_MAX +}; + +struct isp4if_gpu_mem_info { + u32 mem_domain; + u64 mem_size; + u32 mem_align; + u64 gpu_mc_addr; + void *sys_addr; + void *mem_handle; +}; + +struct isp4if_img_buf_info { + struct { + void *sys_addr; + u64 mc_addr; + u32 len; + } planes[3]; +}; + +struct isp4if_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +struct isp4if_cmd_element { + struct list_head list; + u32 seq_num; + u32 cmd_id; + enum isp4if_stream_id stream; + u64 mc_addr; + wait_queue_head_t *wq; + u32 *wq_cond; + struct isp4if_gpu_mem_info *gpu_pkg; +}; + +struct isp4_interface { + struct amdgpu_device *adev; + + struct device *dev; + void __iomem *mmio; + + struct mutex cmdq_mutex; /* used for cmdq access */ + struct mutex bufq_mutex; /* used for bufq access */ + struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */ + + struct list_head cmdq; /* commands sent to fw */ + struct list_head bufq; /* buffers sent to fw */ + + enum isp4if_status status; + u32 host2fw_seq_num; + + /* FW ring buffer configs */ + u32 cmd_rb_base_index; + u32 resp_rb_base_index; + u32 aligned_rb_chunk_size; + + /* ISP fw buffers */ + struct isp4if_gpu_mem_info *fw_log_buf; + struct isp4if_gpu_mem_info *fw_cmd_resp_buf; + struct isp4if_gpu_mem_info *fw_mem_pool; + struct isp4if_gpu_mem_info * + metainfo_buf_pool[ISP4IF_MAX_STREAM_META_BUF_COUNT]; +}; + +static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi) +{ + if (lo) + *lo = (u32)(addr & 0xffffffff); + if (hi) + *hi = (u32)(addr >> 32); +} + +static inline u64 isp4if_join_addr64(u32 lo, u32 hi) +{ + return (((u64)hi) << 32) | (u64)lo; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + void *response); + +int isp4if_send_command(struct isp4_interface *ispif, + u32 cmd_id, + void *package, + u32 package_size); + +int isp4if_send_command_sync(struct isp4_interface *ispif, + u32 cmd_id, + void *package, + u32 package_size, + u32 timeout); + +struct isp4if_cmd_element * +isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, + u32 cmd_id); + +void isp4if_clear_cmdq(struct isp4_interface *ispif); + +void isp4if_clear_bufq(struct isp4_interface *ispif); + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node); + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info); + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif); + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node); + +int isp4if_stop(struct isp4_interface *ispif); + +int isp4if_start(struct isp4_interface *ispif); + +int isp4if_deinit(struct isp4_interface *ispif); + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void *amdgpu_dev, void __iomem *isp_mmip); + +#endif From patchwork Sun Jun 8 14:49:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bin Du X-Patchwork-Id: 894859 Received: from NAM12-DM6-obe.outbound.protection.outlook.com (mail-dm6nam12on2069.outbound.protection.outlook.com [40.107.243.69]) (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 34300253346; Sun, 8 Jun 2025 14:50:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.243.69 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394214; cv=fail; b=EyWGlpZ3kkf15QdDUNXnk8MuGNWf30Q00BHAzZV+KgrR14UXbOd3BD0Pf1i2A+/s2Z5Ue20cKAzi/GyoVAcyDuEDunh2oiskKLrFEu2fDnEE/WWXz35ZuvQSsuVv/ZL+hU+T/0HIa6JjOHzNAIyQwNjc8Hs2EvPN0DkdJfcOQg8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394214; c=relaxed/simple; bh=W4wsFsOqy3u280H+qSYb0rug4hVC7JqCdjmR8Z4xRDM=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=tzAJEVdNuXsWWlvv/cC0TmNAsysuae18urcHwoU1NQqqC7kACqbfaoh5HYhbISSxhX1qH5gqTX8TehIWfTf1G6fM4lDpLN3HM4cJZWLyq0HCd2Iq5zBMmaMYBMjP0NaLwLLCY8yIdnVJ49sMvnLKNEgqEkbzKI+dUn9o/zj8rwo= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=Xl8tC4EV; arc=fail smtp.client-ip=40.107.243.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="Xl8tC4EV" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=v61zaKxQzAZtV3Yt6KmCX0EsOUSqcezdzYJgpKBEl8dWUoT32XAGefZcVS7aq7wJ0K0X/GwD3tpDbrUKfewnDRFzeSTc173MR6RNEf1dh10xtNB4B3bzT+R7NC4hpkyIZIazi/eVsrbVHeyt+8Sef8nTVYi96Nfz0mkSLCHqk4dothkvEmx6NNihBHeJg1TIXcStvZbr/pbRFtWTny/6bDc3WSHLUWvmL9aBynMlNUkel31RDhXeDCcSLIu91OdZ4LWnLXGnROvHM7qUsgTW+yTGy/ActCUkyS19DRjsEojH6Q1vC6wgKeLT9FaP5pkXULYFTOb8F8+8nSd5eo3IwA== 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=Jj8neAIhr/I6GR5Mv8vYpC+xdi8nYjXErV3Gbokybhk=; b=RbpQ8d9/wNQZc3dZtBz9welkwtCqevOWDCJzeEHWBsIG/Nae1OdGm8bBIHnMGTH/TbwdrsEl6qou4bZFYxQnvJZkKpCY4StRYQenVwhVCHu4Gpf8GoZPC/SHj8RYTbikhqLOlVdwpC+inwG/UJU0NeYz0ZjdLpCURO3d1jOewXrOaZ0lpCj5vPP1KMpmOnY5LCjk+ylgpNT8Hi+3C7oyOZEfj+wVD1Q/qlUuWsqRlTeUtzJsr+Rf6hD6hJTNsLH/j3/mCElc6bL1BBN7AzCpR4HwvdhBLqn74gBZmFEcSviRFKpMf3zrB/ktOOLMUr116DKQxAn4EZrIINkF7kuIlA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Jj8neAIhr/I6GR5Mv8vYpC+xdi8nYjXErV3Gbokybhk=; b=Xl8tC4EVvtzUxwBhixFZruQ0cQZX45sq5lQWg1UvJveK/30IueBF5FXCTgV3gDe3G8dEWi1XSjI2fZdWkTQP6WPIDjzitseOZJf457JazvMFfcZmxbWMINGPSp/zLAMwpFAVBxXmuaa8yP57gL6OKAhOxsHqNTZw8T5E1yAyFjM= Received: from MW4PR03CA0063.namprd03.prod.outlook.com (2603:10b6:303:b6::8) by BL3PR12MB6620.namprd12.prod.outlook.com (2603:10b6:208:38f::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8792.35; Sun, 8 Jun 2025 14:50:04 +0000 Received: from SJ5PEPF000001D7.namprd05.prod.outlook.com (2603:10b6:303:b6:cafe::c3) by MW4PR03CA0063.outlook.office365.com (2603:10b6:303:b6::8) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8769.19 via Frontend Transport; Sun, 8 Jun 2025 14:50:02 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by SJ5PEPF000001D7.mail.protection.outlook.com (10.167.242.59) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.8835.15 via Frontend Transport; Sun, 8 Jun 2025 14:50:02 +0000 Received: from 555e2b870847.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Sun, 8 Jun 2025 09:49:58 -0500 From: Bin Du To: , , , , , , , CC: , , , , , , , , "Bin Du" Subject: [PATCH v1 6/9] media: platform: amd: isp4 video node and buffers handling added Date: Sun, 8 Jun 2025 22:49:13 +0800 Message-ID: <20250608144916.222835-7-Bin.Du@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250608144916.222835-1-Bin.Du@amd.com> References: <20250608144916.222835-1-Bin.Du@amd.com> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: SATLEXMB04.amd.com (10.181.40.145) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ5PEPF000001D7:EE_|BL3PR12MB6620:EE_ X-MS-Office365-Filtering-Correlation-Id: e9e57a16-9337-4dde-b615-08dda69bbffc X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|1800799024|36860700013|82310400026|376014; X-Microsoft-Antispam-Message-Info: bqo08ir18XBhjf7XuD96VReCuoIr9kZYHx13pNqVTyPCCOEaZ8vU6tLtmhzXUYP6pzFZ0ti3jzBt6sF/Og0A6DMh3R1JNaDpS07RNkTCC9eQd0vSo5QgbWzoGQeEKSUUYz/WpY+gpexzKaUFjoV/kmChMKkw9fkgSqiip8QkIs4FzRgDKH+rWy+DVtYUePcSTRQT6H+J5ZGXbnUmB9q+taGLY8ABAvLrEPfZVha0UNE7sUHTQrXIppvnSYLCI7ttIFSaqD2EObqSDCNAdB66nthe43GduEqrz8hjxdS3g0IctWX8pQ753ErVVz6c28psA8It75fA0xVoJP4YxuevOhbm5SCAWhn0QQZbChW4TyE3R/5K/9O/XEJULshNLDa5cgMeWLy/lgM4DELoyWm8vGqXkJ9Je8hYc9MNg81fr1oErOVF53Ye+gEVLaIh++mRgemXa+OWRfW4LwyH4cmWb00EJqbX3tb87EhbQgnA5dksXaXrUi7uzIF6GZDTcvTmAMCBWoo7sHghuhSkgkupRBM28FZoCObU8lDKjdWeYDiTKDOMMI15XtJ7W3Wh1+NzZlvWFhKMtuXO38ytTIoQwERq+d3dtmEgrdcGrKSiRHt7Zd4SAWRkEE5ZJZVuFyMW0LAudrUzevobWqYpr1IjOxM0TFEcr/NxqP5D5TpEvcnGcn+kROFrXpHSS8AVWs5AjI7tWKfjKHBesJhJEcn2MZye3kHo08tf43k4r5XhEBNIFox06kYfo8OkDhf51u1neK9xmNCtu/bXxgSNex8YtRsIb350ldE777OcoeTnSMMcf/AXNj8ffklfdzpJOsQ0AWW9OlXPawsb/skKklq/p/Ne8C0JQ+QtM/DSHKlcWVXUB5VxYNcHhXyV4Vc6/hik9MvMh74ulfk9KLk1/LOofWy5URtgWBRZ6YUjUH+aIdMltXoGrlZxWdhs4MPXcgbJIQaYr2dF8b3vZ8yTl40G2jIdDUlw/6hXeF3K+l7e1iHMhIXEbe5NZWnsvM2EYAntaQ4KKpi7+SNjaAXmdP3xOduLF8MLl+IzjSvx9D5ynU/jhPQjqwvsjO9jDzIw2Vp+w6CZEU6cQkBq+VyayHfgoa9mGRx43YaKc2XvOwiPb6kOn0XCeNush4G5v9rofa9m35Y76BjVYmQBL4RlzrTZ8lTd4uqaTwYmhBVcv+eohvdS9gkY5XXf4w4gRiXuyK9xRaZGbRyaaWjJQobRdm2UEyqsPyZpkJOioXOPkBPTujV0EIqzash7/jmRb/LELJtREGsvSGIJapiMM4hFnsFiHii2JchuRDUm4/kdLio/PXzg3Ly18+Mmr/+j3yKFOVNGpoNGTyFAzGNJQcmrMTyRpQ9wN/FWnEPfTunWXDUtA4prD7rZDU3QyJ4FTXA27rNVUvegnL1YNFxutuvfg+VzZB6nU01kAYTLdS0ZMtNFC4UE4SRqfg0z0BhKoQxraJG9IzytvkAASi/oKeu+LQcxAJazyE3G44Jv+UTKNDgn/ZFwlgcDyjivFXUND2sfkHgU X-Forefront-Antispam-Report: CIP:165.204.84.17; CTRY:US; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:SATLEXMB04.amd.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(1800799024)(36860700013)(82310400026)(376014); DIR:OUT; SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Jun 2025 14:50:02.6377 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: e9e57a16-9337-4dde-b615-08dda69bbffc X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d; Ip=[165.204.84.17]; Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: SJ5PEPF000001D7.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BL3PR12MB6620 Isp video implements v4l2 video interface and supports NV12 and YUYV. It manages buffers, pipeline power and state. Signed-off-by: Bin Du Signed-off-by: Svetoslav Stoilov Change-Id: I42178ebe877dd1901bd801a23d8f825827282e56 --- drivers/media/platform/amd/isp4/Makefile | 1 + drivers/media/platform/amd/isp4/isp4.c | 13 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 128 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 2 + drivers/media/platform/amd/isp4/isp4_video.c | 1457 +++++++++++++++++ drivers/media/platform/amd/isp4/isp4_video.h | 93 ++ 6 files changed, 1684 insertions(+), 10 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_video.c create mode 100644 drivers/media/platform/amd/isp4/isp4_video.h diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index d2f1523ad07a..a68f18fe79b4 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -7,6 +7,7 @@ amd_capture-objs := isp4_subdev.o \ isp4_phy.o \ isp4_interface.o \ isp4.o \ + isp4_video.o \ isp4_hw.o \ ccflags-y += -I$(srctree)/drivers/media/platform/amd/isp4 diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 2dc7ea3b02e8..3beb35293504 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -310,6 +310,16 @@ static int isp4_capture_probe(struct platform_device *pdev) goto err_isp4_deinit; } + ret = media_create_pad_link(&isp_dev->isp_sdev.sdev.entity, + 1, &isp_dev->isp_sdev.isp_vdev.vdev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "fail to create pad link %d\n", ret); + goto err_unreg_video_dev_notifier; + } + platform_set_drvdata(pdev, isp_dev); pm_runtime_set_suspended(dev); @@ -317,9 +327,10 @@ static int isp4_capture_probe(struct platform_device *pdev) return 0; -err_isp4_deinit: +err_unreg_video_dev_notifier: v4l2_async_nf_unregister(&isp_dev->notifier); v4l2_async_nf_cleanup(&isp_dev->notifier); +err_isp4_deinit: isp4sd_deinit(&isp_dev->isp_sdev); err_unreg_v4l2: v4l2_device_unregister(&isp_dev->v4l2_dev); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 4b28fbd6867b..816fa3a127f5 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -167,6 +167,24 @@ static int isp4sd_enum_mbus_code(struct v4l2_subdev *isp_subdev, return 0; } +static int isp4sd_enum_frame_size(struct v4l2_subdev *isp_subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_frmsize_discrete min, max; + + if (fse->index >= ARRAY_SIZE(isp4sd_image_formats)) + return -EINVAL; + + isp4vid_frmsize_range(&min, &max); + fse->min_width = min.width; + fse->max_width = max.width; + fse->min_height = min.height; + fse->max_height = max.height; + + return 0; +} + static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, struct isp4fw_image_prop *out_prop, struct v4l2_subdev_state *state, u32 pad) @@ -509,17 +527,14 @@ isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, static struct isp4if_img_buf_node * isp4sd_preview_done(struct isp4_subdev *isp_subdev, - struct isp4fw_meta_info *meta) + struct isp4fw_meta_info *meta, + struct isp4vid_framedone_param *pcb) { struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4if_img_buf_node *prev = NULL; struct device *dev = isp_subdev->dev; - if (!meta) { - dev_err(dev, "fail bad param for preview done\n"); - return prev; - } - + pcb->preview.status = ISP4VID_BUF_DONE_STATUS_ABSENT; if (meta->preview.enabled && (meta->preview.status == BUFFER_STATUS_SKIPPED || meta->preview.status == BUFFER_STATUS_DONE || @@ -529,9 +544,12 @@ isp4sd_preview_done(struct isp4_subdev *isp_subdev, str_info = &isp_subdev->sensor_info.output_info; prev = isp4if_dequeue_buffer(ispif); - if (!prev) + if (!prev) { dev_err(dev, "fail null prev buf\n"); - + } else { + pcb->preview.buf = prev->buf_info; + pcb->preview.status = ISP4VID_BUF_DONE_STATUS_SUCCESS; + } } else if (meta->preview.enabled) { dev_err(dev, "fail bad preview status %u\n", meta->preview.status); @@ -578,6 +596,7 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id, struct isp4fw_resp_param_package *para) { + struct isp4vid_framedone_param pcb = {0}; struct isp4if_img_buf_node *prev = NULL; struct device *dev = isp_subdev->dev; struct isp4fw_meta_info *meta; @@ -590,12 +609,17 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, return; } + pcb.poc = meta->poc; + pcb.cam_id = 0; + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, meta->preview.status); - prev = isp4sd_preview_done(isp_subdev, meta); + prev = isp4sd_preview_done(isp_subdev, meta, &pcb); + if (pcb.preview.status != ISP4VID_BUF_DONE_STATUS_ABSENT) + isp4vid_notify(&isp_subdev->isp_vdev, &pcb); isp4if_dealloc_buffer_node(prev); @@ -974,6 +998,84 @@ static int isp4sd_start_stream(struct isp4_subdev *isp_subdev, return ret; } +static int ispsd4_subdev_link_validate(struct media_link *link) +{ + return 0; +} + +static const struct media_entity_operations isp4sd_subdev_ent_ops = { + .link_validate = ispsd4_subdev_link_validate, +}; + +static int isp4sd_ioc_send_img_buf(struct isp4vid_dev *sd, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4_subdev *isp_subdev = to_isp4_vdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_img_buf_node *buf_node = NULL; + struct device *dev = isp_subdev->dev; + int ret = -EINVAL; + + mutex_lock(&isp_subdev->ops_mutex); + /* TODO: remove isp_status */ + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail send img buf for bad fsm %d\n", + ispif->status); + mutex_unlock(&isp_subdev->ops_mutex); + return -EINVAL; + } + + buf_node = isp4if_alloc_buffer_node(buf_info); + if (!buf_node) { + dev_err(dev, "fail alloc sys img buf info node\n"); + ret = -ENOMEM; + goto unlock_and_return; + } + + ret = isp4if_queue_buffer(ispif, buf_node); + if (ret) { + dev_err(dev, "fail to queue image buf, %d\n", ret); + goto error_release_buf_node; + } + + if (!isp_subdev->sensor_info.start_stream_cmd_sent) { + isp_subdev->sensor_info.buf_sent_cnt++; + + if (isp_subdev->sensor_info.buf_sent_cnt >= + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + ret = isp4if_send_command(ispif, CMD_ID_START_STREAM, + NULL, 0); + + if (ret) { + dev_err(dev, "fail to START_STREAM"); + goto error_release_buf_node; + } + isp_subdev->sensor_info.start_stream_cmd_sent = true; + isp_subdev->sensor_info.output_info.start_status = + ISP4SD_START_STATUS_STARTED; + isp_subdev->sensor_info.status = + ISP4SD_START_STATUS_STARTED; + } else { + dev_dbg(dev, + "no send start,required %u,buf sent %u\n", + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM, + isp_subdev->sensor_info.buf_sent_cnt); + } + } + + mutex_unlock(&isp_subdev->ops_mutex); + + return 0; + +error_release_buf_node: + isp4if_dealloc_buffer_node(buf_node); + +unlock_and_return: + mutex_unlock(&isp_subdev->ops_mutex); + + return ret; +} + static int isp4sd_subdev_pre_streamon(struct v4l2_subdev *sd, u32 flags) { struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); @@ -1098,6 +1200,7 @@ static int isp4sd_disable_streams(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = { .enum_mbus_code = isp4sd_enum_mbus_code, + .enum_frame_size = isp4sd_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = isp4sd_set_pad_format, .enable_streams = isp4sd_enable_streams, @@ -1123,6 +1226,10 @@ static const struct media_entity_operations isp4sd_sdev_ent_ops = { .link_validate = isp4sd_sdev_link_validate, }; +static const struct isp4vid_ops isp4sd_isp4vid_ops = { + .send_buffer = isp4sd_ioc_send_img_buf, +}; + int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, void *amdgpu_dev) @@ -1188,6 +1295,8 @@ int isp4sd_init(struct isp4_subdev *isp_subdev, isp_subdev->host2fw_seq_num = 1; ispif->status = ISP4IF_STATUS_PWR_OFF; + ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev, + &isp4sd_isp4vid_ops, amdgpu_dev); if (ret) goto err_media_clean_up; return ret; @@ -1203,6 +1312,7 @@ void isp4sd_deinit(struct isp4_subdev *isp_subdev) struct isp4sd_output_info *output_info; output_info = &isp_subdev->sensor_info.output_info; + isp4vid_dev_deinit(&isp_subdev->isp_vdev); media_entity_cleanup(&isp_subdev->sdev.entity); isp4if_deinit(ispif); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index 99ec914a95ce..e04811dd2591 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -18,6 +18,7 @@ #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" +#include "isp4_video.h" /* * one is for none sesnor specefic response which is not used now @@ -97,6 +98,7 @@ struct isp4_subdev_thread_param { struct isp4_subdev { struct v4l2_subdev sdev; struct isp4_interface ispif; + struct isp4vid_dev isp_vdev; /* * sdev_pad[0] is sink pad connected to sensor diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c new file mode 100644 index 000000000000..39f0b6e49713 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.c @@ -0,0 +1,1457 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include + +#include "amdgpu_object.h" +#include "isp4_interface.h" +#include "isp4_subdev.h" +#include "isp4_video.h" + +#define ISP4VID_ISP_DRV_NAME "amd_isp_capture" +#define ISP4VID_VIDEO_BUF_NUM 5 +#define ISP4VID_MAX_PREVIEW_FPS 30 +#define ISP4VID_DEFAULT_FMT isp4vid_formats[0] + +#define ISP4VID_PAD_VIDEO_OUTPUT 1 + +/* timeperframe default */ +#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0] + +/* amdisp buffer for vb2 operations */ +struct isp4vid_vb2_buf { + struct device *dev; + void *vaddr; + struct frame_vector *vec; + enum dma_data_direction dma_dir; + unsigned long size; + refcount_t refcount; + struct dma_buf *dbuf; + void *bo; + u64 gpu_addr; + struct vb2_vmarea_handler handler; + bool is_expbuf; +}; + +static int isp4vid_vb2_mmap(void *buf_priv, struct vm_area_struct *vma); + +static void isp4vid_vb2_put(void *buf_priv); + +static const char *isp4vid_video_dev_name = "Preview"; + +/* Sizes must be in increasing order */ +static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = { + {640, 360}, + {640, 480}, + {1280, 720}, + {1280, 960}, + {1920, 1080}, + {1920, 1440}, + {2560, 1440}, + {2880, 1620}, + {2880, 1624}, + {2888, 1808}, +}; + +static const u32 isp4vid_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUYV +}; + +/* timeperframe list */ +static const struct v4l2_fract isp4vid_tpfs[] = { + {.numerator = 1, .denominator = ISP4VID_MAX_PREVIEW_FPS} +}; + +static void +isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf, + bool done_suc) +{ + struct isp4vid_capture_buffer *isp4vid_buf; + enum vb2_buffer_state state; + void *vbuf; + + mutex_lock(&isp_vdev->buf_list_lock); + + /* Get the first entry of the list */ + isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list, + typeof(*isp4vid_buf), list); + if (!isp4vid_buf) { + mutex_unlock(&isp_vdev->buf_list_lock); + return; + } + + vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0); + + if (vbuf != img_buf->planes[0].sys_addr) { + dev_err(isp_vdev->dev, "Invalid vbuf"); + mutex_unlock(&isp_vdev->buf_list_lock); + return; + } + + /* Remove this entry from the list */ + list_del(&isp4vid_buf->list); + + mutex_unlock(&isp_vdev->buf_list_lock); + + /* Fill the buffer */ + isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); + isp4vid_buf->vb2.sequence = isp_vdev->sequence++; + isp4vid_buf->vb2.field = V4L2_FIELD_ANY; + + /* at most 2 planes */ + vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf, + 0, isp_vdev->format.sizeimage); + + state = done_suc ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, state); + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n", + isp_vdev->format.sizeimage); +} + +s32 isp4vid_notify(void *cb_ctx, struct isp4vid_framedone_param *evt_param) +{ + struct isp4vid_dev *isp4vid_vdev = cb_ctx; + + if (evt_param->preview.status != ISP4VID_BUF_DONE_STATUS_ABSENT) { + bool suc; + + suc = (evt_param->preview.status == + ISP4VID_BUF_DONE_STATUS_SUCCESS); + isp4vid_handle_frame_done(isp4vid_vdev, + &evt_param->preview.buf, + suc); + } + + return 0; +} + +void isp4vid_frmsize_range(struct v4l2_frmsize_discrete *min, + struct v4l2_frmsize_discrete *max) +{ + if (!min || !max) { + pr_err("fail, null param\n"); + return; + } + + min->width = isp4vid_frmsize[0].width; + min->height = isp4vid_frmsize[0].height; + max->width = isp4vid_frmsize[ARRAY_SIZE(isp4vid_frmsize) - 1].width; + max->height = isp4vid_frmsize[ARRAY_SIZE(isp4vid_frmsize) - 1].height; +} + +static unsigned int isp4vid_vb2_num_users(void *buf_priv) +{ + struct isp4vid_vb2_buf *buf = buf_priv; + + if (!buf) { + pr_err("fail null buf handle\n"); + return 0; + } + return refcount_read(&buf->refcount); +} + +static int isp4vid_vb2_mmap(void *buf_priv, struct vm_area_struct *vma) +{ + struct isp4vid_vb2_buf *buf = buf_priv; + int ret; + + if (!buf) { + pr_err("fail no memory to map\n"); + return -EINVAL; + } + + ret = remap_vmalloc_range(vma, buf->vaddr, 0); + if (ret) { + dev_err(buf->dev, "fail remap vmalloc mem, %d\n", ret); + return ret; + } + + /* + * Make sure that vm_areas for 2 buffers won't be merged together + */ + vm_flags_set(vma, VM_DONTEXPAND); + + dev_dbg(buf->dev, "mmap isp user bo 0x%llx size %ld refcount %d\n", + buf->gpu_addr, buf->size, buf->refcount.refs.counter); + + return 0; +} + +static void *isp4vid_vb2_vaddr(struct vb2_buffer *vb, void *buf_priv) +{ + struct isp4vid_vb2_buf *buf = buf_priv; + + if (!buf) { + pr_err("fail for empty buf\n"); + return NULL; + } + + if (!buf->vaddr) { + dev_err(buf->dev, + "fail for buf vaddr is null\n"); + return NULL; + } + return buf->vaddr; +} + +static void isp4vid_vb2_detach_dmabuf(void *mem_priv) +{ + struct isp4vid_vb2_buf *buf = mem_priv; + + if (!buf) { + pr_err("fail invalid buf handle\n"); + return; + } + + struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr); + + dev_dbg(buf->dev, "detach dmabuf of isp user bo 0x%llx size %ld", + buf->gpu_addr, buf->size); + + if (buf->vaddr) + dma_buf_vunmap_unlocked(buf->dbuf, &map); + + // put dmabuf for exported ones + dma_buf_put(buf->dbuf); + + kfree(buf); +} + +static void *isp4vid_vb2_attach_dmabuf(struct vb2_buffer *vb, + struct device *dev, + struct dma_buf *dbuf, + unsigned long size) +{ + struct isp4vid_vb2_buf *buf; + + if (dbuf->size < size) { + dev_err(dev, "Invalid dmabuf size %ld %ld", dbuf->size, size); + return ERR_PTR(-EFAULT); + } + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + struct isp4vid_vb2_buf *dbg_buf = (struct isp4vid_vb2_buf *)dbuf->priv; + + buf->dev = dev; + buf->dbuf = dbuf; + buf->dma_dir = vb->vb2_queue->dma_dir; + buf->size = size; + + dev_dbg(dev, "attach dmabuf of isp user bo 0x%llx size %ld", + dbg_buf->gpu_addr, dbg_buf->size); + + return buf; +} + +static void isp4vid_vb2_unmap_dmabuf(void *mem_priv) +{ + struct isp4vid_vb2_buf *buf = mem_priv; + + if (!buf) { + pr_err("fail invalid buf handle\n"); + return; + } + + struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr); + + dev_dbg(buf->dev, "unmap dmabuf of isp user bo 0x%llx size %ld", + buf->gpu_addr, buf->size); + + dma_buf_vunmap_unlocked(buf->dbuf, &map); + buf->vaddr = NULL; +} + +static int isp4vid_vb2_map_dmabuf(void *mem_priv) +{ + struct isp4vid_vb2_buf *mmap_buf = NULL; + struct isp4vid_vb2_buf *buf = mem_priv; + struct iosys_map map; + int ret; + + memset(&map, 0x0, sizeof(map)); + + if (!buf) { + pr_err("fail invalid buf handle\n"); + return -EINVAL; + } + + ret = dma_buf_vmap_unlocked(buf->dbuf, &map); + if (ret) { + dev_err(buf->dev, "vmap_unlocked fail"); + return -EFAULT; + } + buf->vaddr = map.vaddr; + + mmap_buf = (struct isp4vid_vb2_buf *)buf->dbuf->priv; + buf->gpu_addr = mmap_buf->gpu_addr; + + dev_dbg(buf->dev, "map dmabuf of isp user bo 0x%llx size %ld", + buf->gpu_addr, buf->size); + + return 0; +} + +#ifdef CONFIG_HAS_DMA +struct isp4vid_vb2_amdgpu_attachment { + struct sg_table sgt; + enum dma_data_direction dma_dir; +}; + +static int isp4vid_vb2_dmabuf_ops_attach(struct dma_buf *dbuf, + struct dma_buf_attachment *dbuf_attach) +{ + struct isp4vid_vb2_buf *buf = dbuf->priv; + int num_pages = PAGE_ALIGN(buf->size) / PAGE_SIZE; + struct isp4vid_vb2_amdgpu_attachment *attach; + void *vaddr = buf->vaddr; + struct scatterlist *sg; + struct sg_table *sgt; + int ret; + int i; + + attach = kzalloc(sizeof(*attach), GFP_KERNEL); + if (!attach) + return -ENOMEM; + + sgt = &attach->sgt; + ret = sg_alloc_table(sgt, num_pages, GFP_KERNEL); + if (ret) { + kfree(attach); + return ret; + } + for_each_sgtable_sg(sgt, sg, i) { + struct page *page = vmalloc_to_page(vaddr); + + if (!page) { + sg_free_table(sgt); + kfree(attach); + return -ENOMEM; + } + sg_set_page(sg, page, PAGE_SIZE, 0); + vaddr = ((char *)vaddr) + PAGE_SIZE; + } + + attach->dma_dir = DMA_NONE; + dbuf_attach->priv = attach; + + return 0; +} + +static void +isp4vid_vb2_dmabuf_ops_detach(struct dma_buf *dbuf, + struct dma_buf_attachment *dbuf_attach) +{ + struct isp4vid_vb2_amdgpu_attachment *attach = dbuf_attach->priv; + struct sg_table *sgt; + + if (!attach) { + pr_err("fail invalid attach handler\n"); + return; + } + + sgt = &attach->sgt; + + /* release the scatterlist cache */ + if (attach->dma_dir != DMA_NONE) + dma_unmap_sgtable(dbuf_attach->dev, sgt, attach->dma_dir, 0); + sg_free_table(sgt); + kfree(attach); + dbuf_attach->priv = NULL; +} + +static struct sg_table +*isp4vid_vb2_dmabuf_ops_map(struct dma_buf_attachment *dbuf_attach, + enum dma_data_direction dma_dir) +{ + struct isp4vid_vb2_amdgpu_attachment *attach = dbuf_attach->priv; + struct sg_table *sgt; + + sgt = &attach->sgt; + /* return previously mapped sg table */ + if (attach->dma_dir == dma_dir) + return sgt; + + /* release any previous cache */ + if (attach->dma_dir != DMA_NONE) { + dma_unmap_sgtable(dbuf_attach->dev, sgt, attach->dma_dir, 0); + attach->dma_dir = DMA_NONE; + } + + /* mapping to the client with new direction */ + if (dma_map_sgtable(dbuf_attach->dev, sgt, dma_dir, 0)) { + dev_err(dbuf_attach->dev, "fail to map scatterlist"); + return ERR_PTR(-EIO); + } + + attach->dma_dir = dma_dir; + + return sgt; +} + +static void isp4vid_vb2_dmabuf_ops_unmap(struct dma_buf_attachment *dbuf_attach, + struct sg_table *sgt, + enum dma_data_direction dma_dir) +{ + /* nothing to be done here */ +} + +static int isp4vid_vb2_dmabuf_ops_vmap(struct dma_buf *dbuf, + struct iosys_map *map) +{ + struct isp4vid_vb2_buf *buf = dbuf->priv; + + iosys_map_set_vaddr(map, buf->vaddr); + + return 0; +} + +static void isp4vid_vb2_dmabuf_ops_release(struct dma_buf *dbuf) +{ + struct isp4vid_vb2_buf *buf = dbuf->priv; + + /* drop reference obtained in vb2_isp4vid_get_dmabuf */ + if (buf->is_expbuf) + isp4vid_vb2_put(dbuf->priv); + else + dev_dbg(buf->dev, "ignore buf release for implicit case"); +} + +static int isp4vid_vb2_dmabuf_ops_mmap(struct dma_buf *dbuf, + struct vm_area_struct *vma) +{ + return isp4vid_vb2_mmap(dbuf->priv, vma); +} + +static const struct dma_buf_ops vb2_isp4vid_dmabuf_ops = { + .attach = isp4vid_vb2_dmabuf_ops_attach, + .detach = isp4vid_vb2_dmabuf_ops_detach, + .map_dma_buf = isp4vid_vb2_dmabuf_ops_map, + .unmap_dma_buf = isp4vid_vb2_dmabuf_ops_unmap, + .vmap = isp4vid_vb2_dmabuf_ops_vmap, + .mmap = isp4vid_vb2_dmabuf_ops_mmap, + .release = isp4vid_vb2_dmabuf_ops_release, +}; + +static struct dma_buf *isp4vid_get_dmabuf(struct vb2_buffer *vb, + void *buf_priv, + unsigned long flags) +{ + struct isp4vid_vb2_buf *buf = buf_priv; + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dbuf; + + if (!buf) { + pr_err("fail invalid buf handle\n"); + return ERR_PTR(-EINVAL); + } + + exp_info.ops = &vb2_isp4vid_dmabuf_ops; + exp_info.size = buf->size; + exp_info.flags = flags; + exp_info.priv = buf; + + if (WARN_ON(!buf->vaddr)) + return NULL; + + dbuf = dma_buf_export(&exp_info); + if (IS_ERR(dbuf)) + return NULL; + + return dbuf; +} + +static struct dma_buf *isp4vid_vb2_get_dmabuf(struct vb2_buffer *vb, + void *buf_priv, + unsigned long flags) +{ + struct isp4vid_vb2_buf *buf = buf_priv; + struct dma_buf *dbuf; + + if (buf->dbuf) { + dev_dbg(buf->dev, + "dbuf already created, reuse implicit dbuf\n"); + dbuf = buf->dbuf; + } else { + dbuf = isp4vid_get_dmabuf(vb, buf_priv, flags); + dev_dbg(buf->dev, "created new dbuf\n"); + } + buf->is_expbuf = true; + refcount_inc(&buf->refcount); + + dev_dbg(buf->dev, "buf exported, refcount %d\n", + buf->refcount.refs.counter); + + return dbuf; +} + +#endif + +static void isp4vid_vb2_put_userptr(void *buf_priv) +{ + struct isp4vid_vb2_buf *buf = buf_priv; + + if (!buf->vec->is_pfns) { + unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; + unsigned int n_pages; + + n_pages = frame_vector_count(buf->vec); + if (vaddr) + vm_unmap_ram((void *)vaddr, n_pages); + if (buf->dma_dir == DMA_FROM_DEVICE || + buf->dma_dir == DMA_BIDIRECTIONAL) { + struct page **pages; + + pages = frame_vector_pages(buf->vec); + if (!WARN_ON_ONCE(IS_ERR(pages))) { + unsigned int i; + + for (i = 0; i < n_pages; i++) + set_page_dirty_lock(pages[i]); + } + } + } else { + iounmap((__force void __iomem *)buf->vaddr); + } + vb2_destroy_framevec(buf->vec); + kfree(buf); +} + +static void *isp4vid_vb2_get_userptr(struct vb2_buffer *vb, struct device *dev, + unsigned long vaddr, unsigned long size) +{ + struct isp4vid_vb2_buf *buf; + struct frame_vector *vec; + int n_pages, offset, i; + int ret = -ENOMEM; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + buf->dev = dev; + buf->dma_dir = vb->vb2_queue->dma_dir; + offset = vaddr & ~PAGE_MASK; + buf->size = size; + vec = vb2_create_framevec(vaddr, size, + buf->dma_dir == DMA_FROM_DEVICE || + buf->dma_dir == DMA_BIDIRECTIONAL); + if (IS_ERR(vec)) { + kfree(buf); + return vec; + } + buf->vec = vec; + n_pages = frame_vector_count(vec); + if (frame_vector_to_pages(vec) < 0) { + unsigned long *nums = frame_vector_pfns(vec); + + /* + * We cannot get page pointers for these pfns. Check memory is + * physically contiguous and use direct mapping. + */ + for (i = 1; i < n_pages; i++) + if (nums[i - 1] + 1 != nums[i]) + goto err_destroy_free; + buf->vaddr = (__force void *) + ioremap(__pfn_to_phys(nums[0]), size + offset); + } else { + buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1); + } + + if (!buf->vaddr) + goto err_destroy_free; + + buf->vaddr = ((char *)buf->vaddr) + offset; + return buf; + +err_destroy_free: + vb2_destroy_framevec(vec); + kfree(buf); + return ERR_PTR(ret); +} + +static void isp4vid_vb2_put(void *buf_priv) +{ + struct isp4vid_vb2_buf *buf = (struct isp4vid_vb2_buf *)buf_priv; + struct amdgpu_bo *bo = (struct amdgpu_bo *)buf->bo; + + dev_dbg(buf->dev, + "release isp user bo 0x%llx size %ld refcount %d is_expbuf %d", + buf->gpu_addr, buf->size, + buf->refcount.refs.counter, buf->is_expbuf); + + if (refcount_dec_and_test(&buf->refcount)) { + amdgpu_bo_free_isp_user(bo); + + // put implicit dmabuf here, detach_dmabuf will not be called + if (!buf->is_expbuf) + dma_buf_put(buf->dbuf); + + vfree(buf->vaddr); + kfree(buf); + buf = NULL; + } else { + dev_warn(buf->dev, "ignore buffer free, refcount %u > 0", + refcount_read(&buf->refcount)); + } +} + +static void *isp4vid_vb2_alloc(struct vb2_buffer *vb, struct device *dev, + unsigned long size) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + struct isp4vid_vb2_buf *buf = NULL; + struct amdgpu_bo *bo; + u64 gpu_addr; + u32 ret; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL | vb->vb2_queue->gfp_flags); + if (!buf) + return ERR_PTR(-ENOMEM); + + buf->dev = dev; + buf->size = size; + buf->vaddr = vmalloc_user(buf->size); + if (!buf->vaddr) { + kfree(buf); + return ERR_PTR(-ENOMEM); + } + + buf->dma_dir = vb->vb2_queue->dma_dir; + buf->handler.refcount = &buf->refcount; + buf->handler.put = isp4vid_vb2_put; + buf->handler.arg = buf; + + // get implicit dmabuf + buf->dbuf = isp4vid_get_dmabuf(vb, buf, 0); + if (!buf->dbuf) { + dev_err(dev, "fail to get dmabuf\n"); + return ERR_PTR(-EINVAL); + } + + // create isp user BO and obtain gpu_addr + ret = amdgpu_bo_create_isp_user(isp_vdev->amdgpu_dev, buf->dbuf, + AMDGPU_GEM_DOMAIN_GTT, &bo, &gpu_addr); + if (ret) { + dev_err(dev, "fail to create BO\n"); + return ERR_PTR(-EINVAL); + } + + buf->bo = (void *)bo; + buf->gpu_addr = gpu_addr; + + refcount_set(&buf->refcount, 1); + + dev_dbg(dev, "allocated isp user bo 0x%llx size %ld refcount %d", + buf->gpu_addr, buf->size, buf->refcount.refs.counter); + + return buf; +} + +const struct vb2_mem_ops isp4vid_vb2_memops = { + .alloc = isp4vid_vb2_alloc, + .put = isp4vid_vb2_put, + .get_userptr = isp4vid_vb2_get_userptr, + .put_userptr = isp4vid_vb2_put_userptr, +#ifdef CONFIG_HAS_DMA + .get_dmabuf = isp4vid_vb2_get_dmabuf, +#endif + .map_dmabuf = isp4vid_vb2_map_dmabuf, + .unmap_dmabuf = isp4vid_vb2_unmap_dmabuf, + .attach_dmabuf = isp4vid_vb2_attach_dmabuf, + .detach_dmabuf = isp4vid_vb2_detach_dmabuf, + .vaddr = isp4vid_vb2_vaddr, + .mmap = isp4vid_vb2_mmap, + .num_users = isp4vid_vb2_num_users, +}; + +static const struct v4l2_pix_format isp4vid_fmt_default = { + .width = 1920, + .height = 1080, + .pixelformat = V4L2_PIX_FMT_NV12, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, +}; + +static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev, + enum vb2_buffer_state state) +{ + struct isp4vid_capture_buffer *vbuf, *node; + + mutex_lock(&isp_vdev->buf_list_lock); + + list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list) { + list_del(&vbuf->list); + vb2_buffer_done(&vbuf->vb2.vb2_buf, state); + } + mutex_unlock(&isp_vdev->buf_list_lock); + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state); +} + +static int isp4vid_vdev_link_validate(struct media_link *link) +{ + return 0; +} + +static const struct media_entity_operations isp4vid_vdev_ent_ops = { + .link_validate = isp4vid_vdev_link_validate, +}; + +static const struct v4l2_file_operations isp4vid_vdev_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int isp4vid_ioctl_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", ISP4VID_ISP_DRV_NAME); + + cap->capabilities |= (V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_IO_MC); + + dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X", + isp_vdev->vdev.name, cap->capabilities); + + return 0; +} + +static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + f->fmt.pix = isp_vdev->format; + + return 0; +} + +static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + unsigned int i; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* + * Check if the hardware supports the requested format, use the default + * format otherwise. + */ + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == format->pixelformat) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + format->pixelformat = ISP4VID_DEFAULT_FMT; + + switch (format->pixelformat) { + case V4L2_PIX_FMT_NV12: { + const struct v4l2_frmsize_discrete *fsz = + v4l2_find_nearest_size(isp4vid_frmsize, + ARRAY_SIZE(isp4vid_frmsize), + width, height, + format->width, format->height); + + format->width = fsz->width; + format->height = fsz->height; + + format->bytesperline = format->width; + format->sizeimage = format->bytesperline * + format->height * 3 / 2; + } + break; + case V4L2_PIX_FMT_YUYV: { + const struct v4l2_frmsize_discrete *fsz = + v4l2_find_nearest_size(isp4vid_frmsize, + ARRAY_SIZE(isp4vid_frmsize), + width, height, + format->width, format->height); + + format->width = fsz->width; + format->height = fsz->height; + + format->bytesperline = format->width * 2; + format->sizeimage = format->bytesperline * format->height; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u", + isp_vdev->vdev.name, format->pixelformat); + return -EINVAL; + } + + if (format->field == V4L2_FIELD_ANY) + format->field = isp4vid_fmt_default.field; + + if (format->colorspace == V4L2_COLORSPACE_DEFAULT) + format->colorspace = isp4vid_fmt_default.colorspace; + + return 0; +} + +static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev, + struct v4l2_pix_format *pix_fmt) +{ + struct v4l2_subdev_format fmt = {0}; + + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8; + break; + case V4L2_PIX_FMT_YUYV: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16; + break; + default: + return -EINVAL; + }; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT; + fmt.format.width = pix_fmt->width; + fmt.format.height = pix_fmt->height; + return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt); +} + +static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + u32 pitch; + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&isp_vdev->vbq)) + return -EBUSY; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + ret = isp4vid_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u", + isp_vdev->vdev.name, + isp_vdev->format.width, isp_vdev->format.height, + f->fmt.pix.width, f->fmt.pix.height); + dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x", + isp_vdev->vdev.name, isp_vdev->format.pixelformat, + f->fmt.pix.pixelformat); + dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u", + isp_vdev->vdev.name, isp_vdev->format.bytesperline, + f->fmt.pix.bytesperline); + dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u", + isp_vdev->vdev.name, isp_vdev->format.sizeimage, + f->fmt.pix.sizeimage); + + isp_vdev->format = f->fmt.pix; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + pitch = isp_vdev->format.width; + break; + case V4L2_PIX_FMT_YUYV: + pitch = isp_vdev->format.width * 2; + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=0x%x\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return -EINVAL; + } + + ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format); + + return ret; +} + +static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + default: + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X", + isp_vdev->vdev.name, f->index, f->pixelformat); + + return 0; +} + +static int isp4vid_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) { + if (isp4vid_formats[i] == fsize->pixel_format) + break; + } + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = isp4vid_frmsize[fsize->index]; + dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d", + isp_vdev->vdev.name, fsize->index, + fsize->discrete.width, fsize->discrete.height); + } else { + return -EINVAL; + } + + return 0; +} + +static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + int i; + + if (fival->index >= ARRAY_SIZE(isp4vid_tpfs)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == fival->pixel_format) + break; + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++) + if (isp4vid_frmsize[i].width == fival->width && + isp4vid_frmsize[i].height == fival->height) + break; + if (i == ARRAY_SIZE(isp4vid_frmsize)) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = isp4vid_tpfs[fival->index]; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d", + isp_vdev->vdev.name, fival->index, + fival->discrete.numerator, + fival->discrete.denominator); + + return 0; +} + +static int isp4vid_ioctl_g_param(struct file *file, void *priv, + struct v4l2_streamparm *param) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + param->parm.capture.timeperframe = isp_vdev->timeperframe; + param->parm.capture.readbuffers = 0; + + dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d", isp_vdev->vdev.name, + param->parm.capture.timeperframe.numerator, + param->parm.capture.timeperframe.denominator); + return 0; +} + +static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = { + /* VIDIOC_QUERYCAP handler */ + .vidioc_querycap = isp4vid_ioctl_querycap, + + /* VIDIOC_ENUM_FMT handlers */ + .vidioc_enum_fmt_vid_cap = isp4vid_enum_fmt_vid_cap, + + /* VIDIOC_G_FMT handlers */ + .vidioc_g_fmt_vid_cap = isp4vid_g_fmt_vid_cap, + + /* VIDIOC_S_FMT handlers */ + .vidioc_s_fmt_vid_cap = isp4vid_s_fmt_vid_cap, + + /* VIDIOC_TRY_FMT handlers */ + .vidioc_try_fmt_vid_cap = isp4vid_try_fmt_vid_cap, + + /* Buffer handlers */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + + /* Stream on/off */ + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + /* Stream type-dependent parameter ioctls */ + .vidioc_g_parm = isp4vid_ioctl_g_param, + + /* Debugging ioctls */ + .vidioc_enum_framesizes = isp4vid_enum_framesizes, + + /* VIDIOC_ENUM_FRAMEINTERVALS */ + .vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals, + +}; + +static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt) +{ + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + return fmt->width * fmt->height * 3 / 2; + case V4L2_PIX_FMT_YUYV: + return fmt->width * fmt->height * 2; + default: + return 0; + } +}; + +static int isp4vid_qops_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); + + if (*nplanes > 1) { + dev_err(isp_vdev->dev, + "fail to setup queue, no mplane supported %u\n", + *nplanes); + return -EINVAL; + }; + + if (*nplanes == 1) { + unsigned int size; + + size = isp4vid_get_image_size(&isp_vdev->format); + if (sizes[0] < size) { + dev_err(isp_vdev->dev, + "fail for small plane size %u, %u expected\n", + sizes[0], size); + return -EINVAL; + } + } + + if (q_num_bufs + *nbuffers < ISP4VID_VIDEO_BUF_NUM) + *nbuffers = ISP4VID_VIDEO_BUF_NUM - q_num_bufs; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: { + *nplanes = 1; + sizes[0] = max(sizes[0], isp_vdev->format.sizeimage); + isp_vdev->format.sizeimage = sizes[0]; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n", + isp_vdev->vdev.name, + *nbuffers, *nplanes, sizes[0]); + + return 0; +} + +static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + + struct isp4vid_vb2_buf *priv_buf = vb->planes[0].mem_priv; + struct isp4if_img_buf_info *img_buf = &buf->img_buf; + + dev_dbg(isp_vdev->dev, "%s|index=%u", isp_vdev->vdev.name, vb->index); + + dev_dbg(isp_vdev->dev, "queue isp user bo 0x%llx size=%lu", + priv_buf->gpu_addr, + priv_buf->size); + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: { + u32 y_size = isp_vdev->format.sizeimage / 3 * 2; + u32 uv_size = isp_vdev->format.sizeimage / 3; + + img_buf->planes[0].len = y_size; + img_buf->planes[0].sys_addr = priv_buf->vaddr; + img_buf->planes[0].mc_addr = priv_buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = uv_size; + img_buf->planes[1].sys_addr = + (void *)((u64)priv_buf->vaddr + y_size); + img_buf->planes[1].mc_addr = priv_buf->gpu_addr + y_size; + + dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u", + img_buf->planes[1].mc_addr, + img_buf->planes[1].len); + + img_buf->planes[2].len = 0; + } + break; + case V4L2_PIX_FMT_YUYV: { + img_buf->planes[0].len = isp_vdev->format.sizeimage; + img_buf->planes[0].sys_addr = priv_buf->vaddr; + img_buf->planes[0].mc_addr = priv_buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = 0; + img_buf->planes[2].len = 0; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return; + } + + if (isp_vdev->stream_started) + isp_vdev->ops->send_buffer(isp_vdev, img_buf); + + mutex_lock(&isp_vdev->buf_list_lock); + list_add_tail(&buf->list, &isp_vdev->buf_list); + mutex_unlock(&isp_vdev->buf_list_lock); +} + +static void isp4vid_qops_buffer_cleanup(struct vb2_buffer *vb) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + struct isp4vid_vb2_buf *buf = vb->planes[0].mem_priv; + + dev_dbg(isp_vdev->dev, "%s|index=%u vb->memory %u", + isp_vdev->vdev.name, vb->index, vb->memory); + + if (!buf) { + dev_err(isp_vdev->dev, "Invalid buf handle"); + return; + } + + // release implicit dmabuf reference here for vb2 buffer + // of type MMAP and is exported + if (vb->memory == VB2_MEMORY_MMAP && buf->is_expbuf) { + dma_buf_put(buf->dbuf); + dev_dbg(isp_vdev->dev, + "put dmabuf for vb->memory %d expbuf %d", + vb->memory, + buf->is_expbuf); + } +} + +static int isp4vid_qops_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct isp4vid_capture_buffer *isp_buf; + struct v4l2_subdev *isp_subdev = NULL; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret = 0; + + if (isp_vdev->stream_started) { + dev_dbg(isp_vdev->dev, + "%s do nothing for already streaming\n", + isp_vdev->vdev.name); + return 0; + } + isp_vdev->sequence = 0; + ret = v4l2_pipeline_pm_get(&isp_vdev->vdev.entity); + if (ret) { + dev_err(isp_vdev->dev, "power up isp fail %d\n", ret); + return ret; + } + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (entity->function == MEDIA_ENT_F_PROC_VIDEO_ISP) { + ret = v4l2_subdev_call(subdev, video, pre_streamon, 0); + /* The isp s_stream should be called last! */ + isp_subdev = subdev; + } else { + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + } + + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n", + subdev->name, ret); + return ret; + } + } + + if (isp_subdev) { + ret = v4l2_subdev_call(isp_subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(isp_vdev->dev, "fail start stream: %s %d\n", + isp_subdev->name, ret); + return ret; + } + } + + list_for_each_entry(isp_buf, &isp_vdev->buf_list, list) { + isp_vdev->ops->send_buffer(isp_vdev, &isp_buf->img_buf); + } + + /* Start the media pipeline */ + ret = video_device_pipeline_start(&isp_vdev->vdev, &isp_vdev->pipe); + if (ret) { + dev_err(isp_vdev->dev, "video_device_pipeline_start fail:%d", + ret); + isp4vid_capture_return_all_buffers(isp_vdev, + VB2_BUF_STATE_QUEUED); + return ret; + } + isp_vdev->stream_started = true; + + return 0; +} + +static void isp4vid_qops_stop_streaming(struct vb2_queue *vq) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct v4l2_subdev *subdev, *isp_subdev = NULL; + struct media_entity *entity; + struct media_pad *pad; + int ret; + + if (!isp_vdev->stream_started) { + dev_dbg(isp_vdev->dev, + "%s stop_streaming, do none for not started\n", + isp_vdev->vdev.name); + return; + } + dev_dbg(isp_vdev->dev, "%s stop_streaming\n", + isp_vdev->vdev.name); + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (entity->function == MEDIA_ENT_F_PROC_VIDEO_ISP) { + /* + * isp subdev to call isp post_streamoff + * after s_stream sequence + */ + isp_subdev = subdev; + } + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n", + subdev->name, ret); + } + + if (isp_subdev) { + ret = v4l2_subdev_call(isp_subdev, video, post_streamoff); + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_dbg(isp_vdev->dev, "fail start stream: %s %d\n", + isp_subdev->name, ret); + } + + isp_vdev->stream_started = false; + v4l2_pipeline_pm_put(&isp_vdev->vdev.entity); + + /* Stop the media pipeline */ + video_device_pipeline_stop(&isp_vdev->vdev); + + /* Release all active buffers */ + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR); +} + +static int isp4vid_fill_buffer_size(struct isp4vid_dev *isp_vdev) +{ + int ret = 0; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + isp_vdev->format.bytesperline = isp_vdev->format.width; + isp_vdev->format.sizeimage = isp_vdev->format.bytesperline * + isp_vdev->format.height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + isp_vdev->format.bytesperline = isp_vdev->format.width; + isp_vdev->format.sizeimage = isp_vdev->format.bytesperline * + isp_vdev->format.height * 2; + break; + default: + dev_err(isp_vdev->dev, "fail for invalid default format 0x%x", + isp_vdev->format.pixelformat); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct vb2_ops isp4vid_qops = { + .queue_setup = isp4vid_qops_queue_setup, + .buf_cleanup = isp4vid_qops_buffer_cleanup, + .buf_queue = isp4vid_qops_buffer_queue, + .start_streaming = isp4vid_qops_start_streaming, + .stop_streaming = isp4vid_qops_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, + struct v4l2_subdev *isp_sdev, + const struct isp4vid_ops *ops, + void *amdgpu_dev) +{ + const char *vdev_name = isp4vid_video_dev_name; + struct v4l2_device *v4l2_dev; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + if (!isp_vdev || !isp_sdev || !isp_sdev->v4l2_dev || !amdgpu_dev) + return -EINVAL; + + v4l2_dev = isp_sdev->v4l2_dev; + vdev = &isp_vdev->vdev; + + isp_vdev->isp_sdev = isp_sdev; + isp_vdev->amdgpu_dev = amdgpu_dev; + isp_vdev->dev = v4l2_dev->dev; + isp_vdev->ops = ops; + + /* Initialize the vb2_queue struct */ + mutex_init(&isp_vdev->vbq_lock); + q = &isp_vdev->vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct isp4vid_capture_buffer); + q->min_queued_buffers = 2; + q->ops = &isp4vid_qops; + q->drv_priv = isp_vdev; + q->mem_ops = &isp4vid_vb2_memops; + q->lock = &isp_vdev->vbq_lock; + q->dev = v4l2_dev->dev; + ret = vb2_queue_init(q); + if (ret) { + dev_err(v4l2_dev->dev, "vb2_queue_init error:%d", ret); + return ret; + } + /* Initialize buffer list and its lock */ + mutex_init(&isp_vdev->buf_list_lock); + INIT_LIST_HEAD(&isp_vdev->buf_list); + + /* Set default frame format */ + isp_vdev->format = isp4vid_fmt_default; + isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT; + v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator, + &isp_vdev->timeperframe.denominator, 8, 333); + + ret = isp4vid_fill_buffer_size(isp_vdev); + if (ret) { + dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret); + return ret; + } + + ret = isp4vid_set_fmt_2_isp(isp_sdev, &isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail init format :%d\n", ret); + return ret; + } + + /* Initialize the video_device struct */ + isp_vdev->vdev.entity.name = vdev_name; + isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1, + &isp_vdev->vdev_pad); + + if (ret) { + dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret); + return ret; + } + + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &isp4vid_vdev_ent_ops; + vdev->release = video_device_release_empty; + vdev->fops = &isp4vid_vdev_fops; + vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops; + vdev->lock = NULL; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + video_set_drvdata(vdev, isp_vdev); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret); + + return ret; +} + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev) +{ + video_unregister_device(&isp_vdev->vdev); +} diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h new file mode 100644 index 000000000000..4d6705174d34 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_VIDEO_H_ +#define _ISP4_VIDEO_H_ + +#include +#include +#include +#include +#include "isp4_interface.h" + +enum isp4vid_buf_done_status { + /* It means no corresponding image buf in fw response */ + ISP4VID_BUF_DONE_STATUS_ABSENT, + ISP4VID_BUF_DONE_STATUS_SUCCESS, + ISP4VID_BUF_DONE_STATUS_FAILED +}; + +struct isp4vid_buf_done_info { + enum isp4vid_buf_done_status status; + struct isp4if_img_buf_info buf; +}; + +/* call back parameter for CB_EVT_ID_FRAME_DONE */ +struct isp4vid_framedone_param { + s32 poc; + s32 cam_id; + s64 time_stamp; + struct isp4vid_buf_done_info preview; +}; + +struct isp4vid_capture_buffer { + /* + * struct vb2_v4l2_buffer must be the first element + * the videobuf2 framework will allocate this struct based on + * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of + * memory as a vb2_buffer + */ + struct vb2_v4l2_buffer vb2; + struct isp4if_img_buf_info img_buf; + struct list_head list; +}; + +struct isp4vid_dev; + +struct isp4vid_ops { + int (*send_buffer)(struct isp4vid_dev *vid, + struct isp4if_img_buf_info *img_buf); +}; + +struct isp4vid_dev { + struct video_device vdev; + struct media_pad vdev_pad; + struct v4l2_pix_format format; + + /* mutex that protects vbq */ + struct mutex vbq_lock; + struct vb2_queue vbq; + + /* mutex that protects buf_list */ + struct mutex buf_list_lock; + struct list_head buf_list; + + u32 sequence; + bool stream_started; + struct task_struct *kthread; + + struct media_pipeline pipe; + struct device *dev; + void *amdgpu_dev; + struct v4l2_subdev *isp_sdev; + struct v4l2_fract timeperframe; + + /* Callback operations */ + const struct isp4vid_ops *ops; +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, + struct v4l2_subdev *isp_sdev, + const struct isp4vid_ops *ops, + void *amdgpu_dev); + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev); + +s32 isp4vid_notify(void *cb_ctx, struct isp4vid_framedone_param *evt_param); + +void isp4vid_frmsize_range(struct v4l2_frmsize_discrete *min, + struct v4l2_frmsize_discrete *max); + +#endif From patchwork Sun Jun 8 14:49:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bin Du X-Patchwork-Id: 894858 Received: from NAM10-DM6-obe.outbound.protection.outlook.com (mail-dm6nam10on2082.outbound.protection.outlook.com [40.107.93.82]) (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 629E324E4AD; Sun, 8 Jun 2025 14:50:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.93.82 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394220; cv=fail; b=sxTjtAEij3NhuRsrShkxppgEQetBcpZr/bArqQLpjRmu2wUiFRC6T1ia7Unxvnt/poy2I8zNY9xlQ+49itLjU6SOyQYOLy/+rDPkkXa9JBTtb7h3BlkBAivpgli7yMYlspbL49aIUdZCcrQJ6H8zYiZ2Jnm2o/eKLWhIA5lQHL0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749394220; c=relaxed/simple; bh=qL/EjjlzzhjIrh1w/93MXxF8OhKfxN/EHvCZbIjZKKA=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HAnWUc/tZgzqB6Ak/tK1y98C/WRvjWu65gX6F733qSgHKSWLp4jQ6GQipZnXHOaCeY1p4SA2yKUho0qRcHkXX0kMxGeDht3bdtQK+7UfSps9SleK3n4tn7YSDsspBPz+kUo50Oor6NiG1q4O2cshjk3/myQeIfU6Rh6QgkYBWm0= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=sYIuB8a2; arc=fail smtp.client-ip=40.107.93.82 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="sYIuB8a2" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=ZX7Yl+AYeQ5Ls9iKnj2JPO7BtiJpoGo1/n/XEBIouN6qGpOPhys0/GtxTiwVJSw38KoZtAWmssNg64lO+pimFwmE3JqbvzDrHmWUV7SOUTcFueBAMDQEVPhg04ANoteMQ2wNC4Q09Z7wMB2MjvBpYuW96RXvYsaIr464IAr3MDBDjOHPcJgg2JeV755aNUATMsYNQiJDsxxO1/qhyYi1FTHKUgD04OQFeuny9bBF4+Q7IVz10DHWty8UT1O2Lzoj/gSkJsCCzJr9iH+MC177c75qnk/UDra+3LqTuvBOZKgEU7qgOxrUgj+mc4CzZzBsGTQmormO8Nd5O1USDA8XTA== 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=WrzMMVXAnmsDF+Xra1QS1B6ip+/6NjYNc4GJU6EyPcM=; b=tW75FWpYfSICVx1AutFOUULCuHBWq72WHRMZlIwbs0UVyvqopT/Prk4d/xGnRtMU1yracSvmVPNJGzCU7TPcGYwgbHFWyDX9Akt/Xf5ye/UXDNxkFP8pCfR2Nrx5f/H/c+HlZHtGcuFwwvpkN3H43LkMg2d9cfQ9B4XGEESRpo2OX3aNGUl7gdyTpX/PcyJFonTqPSbTDGCbe64bRymXlI3z1h1HIgpdcpzmj2UAO2oMet0bX+L9rmvwCRv6BegEHDoLoeAftaVtwtZ0x+c6BsH2MHqMMWnQHgcqSeZPTyhSKv2hTUZgz9BfUpCZki2YBEpioO9HE7kBDBl21FtEFg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WrzMMVXAnmsDF+Xra1QS1B6ip+/6NjYNc4GJU6EyPcM=; b=sYIuB8a2QtPPUcpaNByaV6KLiJ3QpB7mtDKzWJzzEVBA1NmJo4hB3cm93JbVL+4lU8GpXPDlQ8E0olEkwsW4s/59Am2uCUj+Fg7QO6chvuQBr0X/tS2ujnx0s/HV/axLB3jPiqmHf8/UdnZOEpBez6U5KdIzlnQoGa3a6fhrWTE= Received: from MW4PR04CA0326.namprd04.prod.outlook.com (2603:10b6:303:82::31) by IA1PR12MB6435.namprd12.prod.outlook.com (2603:10b6:208:3ad::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8792.38; Sun, 8 Jun 2025 14:50:10 +0000 Received: from CO1PEPF000044F2.namprd05.prod.outlook.com (2603:10b6:303:82:cafe::d5) by MW4PR04CA0326.outlook.office365.com (2603:10b6:303:82::31) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8792.34 via Frontend Transport; Sun, 8 Jun 2025 14:50:10 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by CO1PEPF000044F2.mail.protection.outlook.com (10.167.241.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.8835.15 via Frontend Transport; Sun, 8 Jun 2025 14:50:08 +0000 Received: from 555e2b870847.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Sun, 8 Jun 2025 09:50:02 -0500 From: Bin Du To: , , , , , , , CC: , , , , , , , , "Bin Du" Subject: [PATCH v1 7/9] media: platform: amd: isp4 debug fs logging and more descriptive errors Date: Sun, 8 Jun 2025 22:49:14 +0800 Message-ID: <20250608144916.222835-8-Bin.Du@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250608144916.222835-1-Bin.Du@amd.com> References: <20250608144916.222835-1-Bin.Du@amd.com> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: SATLEXMB04.amd.com (10.181.40.145) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CO1PEPF000044F2:EE_|IA1PR12MB6435:EE_ X-MS-Office365-Filtering-Correlation-Id: 0a5a4e25-5a0c-421b-8b9e-08dda69bc376 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|82310400026|36860700013|1800799024|376014; X-Microsoft-Antispam-Message-Info: ubr5IdyvEAGJvUTbWGSOb2490GfK7k22gM1AgXV8uafzWe4wsEVsLtp2jf788KwKkIlh7ACkJAVEhFsrRJ/wAYGpgVf6tgZcXZwOvgnHTbjoSKbfBljGE4I8egsj1K1DjjcmRiMJqe1XN4spy6uGSQqj7dDm7g4eGaC21fOejNoi1KDeK4iFQKWXGIR+kyQyMpkujVLdskUYA1AkIeJq0DQX6I/ArpDk7dGl3NOwWj9Mj36N1IP1HkCT+Xo2zA9HsGGHCQ64mG9ogjB08Y5RuA5gihb9YLc+fsJ7+csMxs8M+fZwofM0uU3Zk466juuLf4x6SW/isPe0nB4QiqYid0/Wgqi+lgYmLh5nag49dQRCbMzeYi8EIQX+rmGWYCCP/AHOO5jiTuGFCzPGoZaH0enzuYy9iSw2Xk5rvx8PZ6MGtxH0WpzI5o8w5hXM60cj859P3Be5E5eUtODo7mvJPg4NmOgOtCMkOTAGqzyei9rstJ7/Gaufv8IwASp7+e8jZQl3dWoYy+YsbIhL6DzgP9jtaovOu4P5og600PYR3WtfABUcz3AD9dhwX86USkkYc6QiZqd3SeyV9GXY+5KH433Fob9Iee1Z+TwtHc6STMWJVxTE3t7GGIJo5GNxJcXlZJ2+dMVY35/Mk27VSrJO0e16o12/aLVr6hrouYrsiwaD8qzW0Q1SL0MQavURLPT+pFzEWydXfuCKbIT2WWRPWvGn3B7zR2Dm6GHFIR4uYB5IsRhqhHxO4Pi0dGzdnmaOYFQ4kQjlouts4X/eevWTYVMbJHBBPRvrcaR1tMJQiES/BHPk7wIIltySPv7JGpSq/ZEusaj+kIDPEZB+ugClNQPCSPCrZzJaRDPD5PJX464CZ9re6up7avs6YkLfnfZcIhrODQzElDiQ9Fo4yuHvBp0yee5IjNpoUOiw4dusZI9TjnIMgYsK4ck4xvsb/dKAoZdZRFYr5tMjoAcPmYWpgHLnOGhWT2cEL4cruQqWo63v0mi3JtDNpo81QBFXSadzbyurX8XrBcQQHl0nlxl3LD3ZYuZxxceQobF5HIKpFI1IypfxCbn7RMvjv4DPENFfyo5ZiXyiGBNCSjc8KsWLM5ag+vll7BLggsD5lmZ+JJGwbjWtuorihMnoDfWP+RhI1zJZGivzIJZva5d8mQiWCVCbIgrGzFN4knXae2wltZhyhPfRZMKLEcbETUBBFZuJjFMw2L+NQt6NbV0m0jbOq2IUqtCs+rhsKJ9jFnriLvy9O7lUFXB7qn0NfdUCXpRqLAbgeL3rFPM9PG6Oln8+9USHLhCSWqeJXaqKTcKxk/P9ePF/YaxrRENc6cNw0LV0jtTAvAO3pJUP4Cg/A3YqwSQbo1facVPQgiq6faMu7fbtIHgF2OYgXY6ykjRu1EaKQB21hv50I/wKUbo2Vf0sfyH5C5CKZtGPl629NDpjWr1SIXhXolILhwRoc8pXDb2TEJwF5k73fHxBHjFgAn5cyfm7c+YINb+NQFbUkX+sQOkqXYGfQykVG7e7m8b4Tn9L X-Forefront-Antispam-Report: CIP:165.204.84.17; CTRY:US; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:SATLEXMB04.amd.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(82310400026)(36860700013)(1800799024)(376014); DIR:OUT; SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Jun 2025 14:50:08.4472 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 0a5a4e25-5a0c-421b-8b9e-08dda69bc376 X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d; Ip=[165.204.84.17]; Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: CO1PEPF000044F2.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA1PR12MB6435 Signed-off-by: Bin Du Signed-off-by: Svetoslav Stoilov Change-Id: I3ce167dbeb612b94c89df0b48f7ffa0988b5336f --- drivers/media/platform/amd/isp4/Makefile | 1 + drivers/media/platform/amd/isp4/isp4.c | 5 + drivers/media/platform/amd/isp4/isp4_debug.c | 272 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_debug.h | 41 +++ .../media/platform/amd/isp4/isp4_interface.c | 37 ++- drivers/media/platform/amd/isp4/isp4_subdev.c | 29 +- 6 files changed, 365 insertions(+), 20 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.c create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.h diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index a68f18fe79b4..da1e047cdb7d 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AMD_ISP4) += amd_capture.o amd_capture-objs := isp4_subdev.o \ + isp4_debug.o \ isp4_phy.o \ isp4_interface.o \ isp4.o \ diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 3beb35293504..f7e716ec47da 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -10,6 +10,7 @@ #include "amdgpu_object.h" #include "isp4.h" +#include "isp4_debug.h" #include "isp4_hw.h" #define ISP4_DRV_NAME "amd_isp_capture" @@ -325,6 +326,8 @@ static int isp4_capture_probe(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + isp_debugfs_create(isp_dev); + return 0; err_unreg_video_dev_notifier: @@ -342,6 +345,8 @@ static void isp4_capture_remove(struct platform_device *pdev) { struct isp4_device *isp_dev = platform_get_drvdata(pdev); + isp_debugfs_remove(isp_dev); + v4l2_async_nf_unregister(&isp_dev->notifier); v4l2_async_nf_cleanup(&isp_dev->notifier); v4l2_device_unregister_subdev(&isp_dev->isp_sdev.sdev); diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c new file mode 100644 index 000000000000..0b8b1ad87525 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_hw.h" +#include "isp4_interface.h" + +#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024) +#define ISP4DBG_MACRO_2_STR(X) #X +#define ISP4DBG_MAX_ONE_TIME_LOG_LEN 510 + +#ifdef CONFIG_DEBUG_FS + +void isp_debugfs_create(struct isp4_device *isp_dev) +{ + isp_dev->isp_sdev.debugfs_dir = debugfs_create_dir("amd_isp", NULL); + debugfs_create_bool("fw_log_enable", 0644, + isp_dev->isp_sdev.debugfs_dir, + &isp_dev->isp_sdev.enable_fw_log); + isp_dev->isp_sdev.fw_log_output = + devm_kzalloc(&isp_dev->pdev->dev, + ISP4DBG_FW_LOG_RINGBUF_SIZE + 32, + GFP_KERNEL); +} + +void isp_debugfs_remove(struct isp4_device *isp_dev) +{ + debugfs_remove_recursive(isp_dev->isp_sdev.debugfs_dir); + isp_dev->isp_sdev.debugfs_dir = NULL; +} + +static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, u8 *sys, u32 rb_size) +{ + struct isp4_interface *ispif = &isp->ispif; + struct device *dev = isp->dev; + u8 *buf = isp->fw_log_output; + u32 rd_ptr, wr_ptr; + u32 total_cnt = 0; + u32 offset = 0; + u32 cnt; + + if (!sys || rb_size == 0) + return 0; + + mutex_lock(&ispif->isp4if_mutex); + + rd_ptr = isp4hw_rreg(ISP4_GET_ISP_REG_BASE(isp), ISP_LOG_RB_RPTR0); + wr_ptr = isp4hw_rreg(ISP4_GET_ISP_REG_BASE(isp), ISP_LOG_RB_WPTR0); + + do { + if (wr_ptr > rd_ptr) + cnt = wr_ptr - rd_ptr; + else if (wr_ptr < rd_ptr) + cnt = rb_size - rd_ptr; + else + goto unlock_and_quit; + + if (cnt > rb_size) { + dev_err(dev, "fail bad fw log size %u\n", cnt); + goto unlock_and_quit; + } + + memcpy(buf + offset, (u8 *)(sys + rd_ptr), cnt); + + offset += cnt; + total_cnt += cnt; + rd_ptr = (rd_ptr + cnt) % rb_size; + } while (rd_ptr < wr_ptr); + + isp4hw_wreg(ISP4_GET_ISP_REG_BASE(isp), ISP_LOG_RB_RPTR0, rd_ptr); + +unlock_and_quit: + mutex_unlock(&ispif->isp4if_mutex); + return total_cnt; +} + +void isp_fw_log_print(struct isp4_subdev *isp) +{ + struct isp4_interface *ispif = &isp->ispif; + char *fw_log_buf = isp->fw_log_output; + u32 cnt; + + if (!isp->enable_fw_log || !fw_log_buf) + return; + + cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr, + ispif->fw_log_buf->mem_size); + + if (cnt) { + char temp_ch; + char *str; + char *end; + char *line_end; + + str = (char *)fw_log_buf; + end = ((char *)fw_log_buf + cnt); + fw_log_buf[cnt] = 0; + + while (str < end) { + line_end = strchr(str, 0x0A); + if ((line_end && (str + ISP4DBG_MAX_ONE_TIME_LOG_LEN) >= line_end) || + (!line_end && (str + ISP4DBG_MAX_ONE_TIME_LOG_LEN) >= end)) { + if (line_end) + *line_end = 0; + + if (*str != '\0') + dev_dbg(isp->dev, + "%s", str); + + if (line_end) { + *line_end = 0x0A; + str = line_end + 1; + } else { + break; + } + } else { + u32 tmp_len = ISP4DBG_MAX_ONE_TIME_LOG_LEN; + + temp_ch = str[tmp_len]; + str[tmp_len] = 0; + dev_dbg(isp->dev, "%s", str); + str[tmp_len] = temp_ch; + str = &str[tmp_len]; + } + } + } +} +#endif + +char *isp4dbg_get_buf_src_str(u32 src) +{ + switch (src) { + case BUFFER_SOURCE_STREAM: + return ISP4DBG_MACRO_2_STR(BUFFER_SOURCE_STREAM); + default: + return "Unknown buf source"; + } +} + +char *isp4dbg_get_buf_done_str(u32 status) +{ + switch (status) { + case BUFFER_STATUS_INVALID: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_INVALID); + case BUFFER_STATUS_SKIPPED: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_SKIPPED); + case BUFFER_STATUS_EXIST: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_EXIST); + case BUFFER_STATUS_DONE: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_DONE); + case BUFFER_STATUS_LACK: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_LACK); + case BUFFER_STATUS_DIRTY: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_DIRTY); + case BUFFER_STATUS_MAX: + return ISP4DBG_MACRO_2_STR(BUFFER_STATUS_MAX); + default: + return "Unknown Buf Done Status"; + } +}; + +char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */) +{ + switch (fmt) { + case IMAGE_FORMAT_NV12: + return "NV12"; + case IMAGE_FORMAT_YUV422INTERLEAVED: + return "YUV422INTERLEAVED"; + default: + return "unknown fmt"; + } +} + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, + void *in, void *orig_buf) +{ + struct isp4if_img_buf_info *orig; + struct isp4fw_buffer_meta_info *p; + + if (!in) + return; + + if (!pre) + pre = ""; + + p = (struct isp4fw_buffer_meta_info *)in; + orig = (struct isp4if_img_buf_info *)orig_buf; + + dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre, + isp4dbg_get_img_fmt_str(p->image_prop.image_format), + p->enabled, isp4dbg_get_buf_done_str(p->status), p->status, + isp4dbg_get_buf_src_str(p->source)); + + dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n", + orig->planes[0].sys_addr, orig->planes[0].mc_addr, + orig->planes[0].len, orig->planes[1].sys_addr, + orig->planes[1].mc_addr, orig->planes[1].len, + orig->planes[2].sys_addr, orig->planes[2].mc_addr, + orig->planes[2].len); +} + +char *isp4dbg_get_buf_type(u32 type) +{ + /* enum isp4fw_buffer_type */ + switch (type) { + case BUFFER_TYPE_PREVIEW: + return ISP4DBG_MACRO_2_STR(BUFFER_TYPE_PREVIEW); + case BUFFER_TYPE_META_INFO: + return ISP4DBG_MACRO_2_STR(BUFFER_TYPE_META_INFO); + case BUFFER_TYPE_MEM_POOL: + return ISP4DBG_MACRO_2_STR(BUFFER_TYPE_MEM_POOL); + default: + return "unknown type"; + } +} + +char *isp4dbg_get_cmd_str(u32 cmd) +{ + switch (cmd) { + case CMD_ID_START_STREAM: + return ISP4DBG_MACRO_2_STR(CMD_ID_START_STREAM); + case CMD_ID_STOP_STREAM: + return ISP4DBG_MACRO_2_STR(CMD_ID_STOP_STREAM); + case CMD_ID_SEND_BUFFER: + return ISP4DBG_MACRO_2_STR(CMD_ID_SEND_BUFFER); + case CMD_ID_SET_STREAM_CONFIG: + return ISP4DBG_MACRO_2_STR(CMD_ID_SET_STREAM_CONFIG); + case CMD_ID_SET_OUT_CHAN_PROP: + return ISP4DBG_MACRO_2_STR(CMD_ID_SET_OUT_CHAN_PROP); + case CMD_ID_ENABLE_OUT_CHAN: + return ISP4DBG_MACRO_2_STR(CMD_ID_ENABLE_OUT_CHAN); + default: + return "unknown cmd"; + }; +} + +char *isp4dbg_get_resp_str(u32 cmd) +{ + switch (cmd) { + case RESP_ID_CMD_DONE: + return ISP4DBG_MACRO_2_STR(RESP_ID_CMD_DONE); + case RESP_ID_NOTI_FRAME_DONE: + return ISP4DBG_MACRO_2_STR(RESP_ID_NOTI_FRAME_DONE); + default: + return "unknown respid"; + }; +} + +char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */) +{ + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + return "STREAM_GLOBAL"; + case ISP4IF_STREAM_ID_1: + return "STREAM1"; + default: + return "unknown streamID"; + } +} + +char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */) +{ + switch ((enum isp4fw_pipe_out_ch)ch) { + case ISP_PIPE_OUT_CH_PREVIEW: + return "prev"; + default: + return "unknown channel"; + } +} diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h new file mode 100644 index 000000000000..acf99bf129ae --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_DEBUG_H_ +#define _ISP4_DEBUG_H_ + +#include +#include + +#include "isp4_subdev.h" + +#ifdef CONFIG_DEBUG_FS +struct isp4_device; + +void isp_debugfs_create(struct isp4_device *isp_dev); +void isp_debugfs_remove(struct isp4_device *isp_dev); +void isp_fw_log_print(struct isp4_subdev *isp); + +#else + +/*to avoid checkpatch warning*/ +#define isp_debugfs_create(cam) cam +#define isp_debugfs_remove(cam) cam +#define isp_fw_log_print(isp) isp + +#endif /* CONFIG_DEBUG_FS */ + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p, + void *orig_buf /* struct sys_img_buf_handle* */); +char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t * */); +char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */); +char *isp4dbg_get_cmd_str(u32 cmd); +char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */ +char *isp4dbg_get_resp_str(u32 resp); +char *isp4dbg_get_buf_src_str(u32 src); +char *isp4dbg_get_buf_done_str(u32 status); +char *isp4dbg_get_if_stream_str(u32 stream); + +#endif diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index d46d7487a994..2a57707925cf 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -7,6 +7,7 @@ #include "amdgpu_object.h" +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_hw.h" #include "isp4_hw_reg.h" @@ -392,7 +393,8 @@ static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, len = rb_config->val_size; if (isp4if_is_cmdq_rb_full(ispif, stream)) { - dev_err(dev, "fail no cmdslot (%d)\n", stream); + dev_err(dev, "fail no cmdslot %s(%d)\n", + isp4dbg_get_if_stream_str(stream), stream); return -EINVAL; } @@ -400,13 +402,15 @@ static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, rd_ptr = isp4hw_rreg(ispif->mmio, rreg); if (rd_ptr > len) { - dev_err(dev, "fail (%u),rd_ptr %u(should<=%u),wr_ptr %u\n", + dev_err(dev, "fail %s(%u),rd_ptr %u(should<=%u),wr_ptr %u\n", + isp4dbg_get_if_stream_str(stream), stream, rd_ptr, len, wr_ptr); return -EINVAL; } if (wr_ptr > len) { - dev_err(dev, "fail (%u),wr_ptr %u(should<=%u), rd_ptr %u\n", + dev_err(dev, "fail %s(%u),wr_ptr %u(should<=%u), rd_ptr %u\n", + isp4dbg_get_if_stream_str(stream), stream, wr_ptr, len, rd_ptr); return -EINVAL; } @@ -501,7 +505,8 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, rd_ptr = isp4hw_rreg(ispif->mmio, rreg); wr_ptr = isp4hw_rreg(ispif->mmio, wreg); dev_err(dev, - "failed to get free cmdq slot, stream (%d)\n", + "failed to get free cmdq slot, stream %s(%d)\n", + isp4dbg_get_if_stream_str(stream), stream); return -ETIMEDOUT; } @@ -553,8 +558,8 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); if (ret) { - dev_err(dev, "fail for insert_isp_fw_cmd camId (0x%08x)\n", - cmd_id); + dev_err(dev, "fail for insert_isp_fw_cmd camId %s(0x%08x)\n", + isp4dbg_get_cmd_str(cmd_id), cmd_id); if (cmd_ele) { isp4if_rm_cmd_from_cmdq(ispif, cmd_ele->seq_num, cmd_ele->cmd_id); @@ -783,13 +788,15 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, wr_ptr_dbg = wr_ptr; if (rd_ptr > len) { - dev_err(dev, "fail (%u),rd_ptr %u(should<=%u),wr_ptr %u\n", + dev_err(dev, "fail %s(%u),rd_ptr %u(should<=%u),wr_ptr %u\n", + isp4dbg_get_if_stream_str(stream), stream, rd_ptr, len, wr_ptr); return -EINVAL; } if (wr_ptr > len) { - dev_err(dev, "fail (%u),wr_ptr %u(should<=%u), rd_ptr %u\n", + dev_err(dev, "fail %s(%u),wr_ptr %u(should<=%u), rd_ptr %u\n", + isp4dbg_get_if_stream_str(stream), stream, wr_ptr, len, rd_ptr); return -EINVAL; } @@ -804,7 +811,8 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), rreg, rd_ptr); } else { - dev_err(dev, "(%u),rd %u(should<=%u),wr %u\n", + dev_err(dev, "%s(%u),rd %u(should<=%u),wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, rd_ptr, len, wr_ptr); return -EINVAL; } @@ -832,7 +840,8 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), rreg, rd_ptr); } else { - dev_err(dev, "(%u),rd %u(should<=%u),wr %u\n", + dev_err(dev, "%s(%u),rd %u(should<=%u),wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, rd_ptr, len, wr_ptr); return -EINVAL; } @@ -855,7 +864,8 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, isp4hw_wreg(GET_ISP4IF_REG_BASE(ispif), rreg, rd_ptr); } else { - dev_err(dev, "(%u),rd %u(should<=%u),wr %u\n", + dev_err(dev, "%s(%u),rd %u(should<=%u),wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, rd_ptr, len, wr_ptr); return -EINVAL; } @@ -872,9 +882,10 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, checksum, response->resp_check_sum, rd_ptr_dbg, wr_ptr_dbg); - dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", - stream, + dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n", + isp4dbg_get_if_stream_str(stream), stream, response->resp_seq_num, + isp4dbg_get_resp_str(response->resp_id), response->resp_id); return -EINVAL; diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 816fa3a127f5..11210b13cd02 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -7,6 +7,7 @@ #include #include +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_hw.h" #include "isp4_interface.h" @@ -304,7 +305,9 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return -EINVAL; } - dev_dbg(dev, "channel: w:h=%u:%u,lp:%u,cp%u\n", + dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", + isp4dbg_get_out_ch_str(cmd_ch_prop.ch), + isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, cmd_ch_prop.image_prop.luma_pitch, cmd_ch_prop.image_prop.chroma_pitch); @@ -327,6 +330,9 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return ret; } + dev_dbg(dev, "enable channel %s\n", + isp4dbg_get_out_ch_str(cmd_ch_en.ch)); + if (!sensor_info->start_stream_cmd_sent) { ret = isp4sd_kickoff_stream(isp_subdev, out_prop->width, out_prop->height); @@ -489,8 +495,9 @@ static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); struct device *dev = isp_subdev->dev; - dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", stream_id, + isp4dbg_get_cmd_str(para->cmd_id), para->cmd_id, para->cmd_status, para->cmd_seq_num, ele); @@ -551,8 +558,9 @@ isp4sd_preview_done(struct isp4_subdev *isp_subdev, pcb->preview.status = ISP4VID_BUF_DONE_STATUS_SUCCESS; } } else if (meta->preview.enabled) { - dev_err(dev, "fail bad preview status %u\n", - meta->preview.status); + dev_err(dev, "fail bad preview status %u(%s)\n", + meta->preview.status, + isp4dbg_get_buf_done_str(meta->preview.status)); } return prev; @@ -612,14 +620,18 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, pcb.poc = meta->poc; pcb.cam_id = 0; - dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,(%i)\n", + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,%s(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + isp4dbg_get_buf_done_str(meta->preview.status), meta->preview.status); prev = isp4sd_preview_done(isp_subdev, meta, &pcb); - if (pcb.preview.status != ISP4VID_BUF_DONE_STATUS_ABSENT) + if (pcb.preview.status != ISP4VID_BUF_DONE_STATUS_ABSENT) { + isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, + &pcb.preview.buf); isp4vid_notify(&isp_subdev->isp_vdev, &pcb); + } isp4if_dealloc_buffer_node(prev); @@ -640,6 +652,8 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, if (ispif->status < ISP4IF_STATUS_FW_RUNNING) return; + isp_fw_log_print(isp_subdev); + while (true) { s32 ret; @@ -657,7 +671,8 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, &resp.param.frame_done); break; default: - dev_err(dev, "-><- fail respid (0x%x)\n", + dev_err(dev, "-><- fail respid %s(0x%x)\n", + isp4dbg_get_resp_str(resp.resp_id), resp.resp_id); break; }