From patchwork Tue Apr 22 09:31:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nas Chung X-Patchwork-Id: 883355 Received: from PUWP216CU001.outbound.protection.outlook.com (mail-koreasouthazon11020072.outbound.protection.outlook.com [52.101.156.72]) (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 9C7321FDA94; Tue, 22 Apr 2025 09:31:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.156.72 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314301; cv=fail; b=opjVrEGAfjzU1eeyWaN3gxCPJpv6Do4zs+FasWN0gFMmzS61N89NCnlqwORSJX3+Ol7gmD3iCL/25ruFzbWjnOhcAb2x1e8RRNlOpVO0uqtsAdVLL1Imm+XjmHxjuyu+X6OmldovxgONy8leRVwfUcwclx1qa30U6cwMZeI6610= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314301; c=relaxed/simple; bh=AX+cRdpE1OPMFzVXBGxkiD+opH5g7/jEzkoYiFmqWiQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=RWFIFw4Eh3bhyIGqUZeDEHCCfz+PUuhjFRMyTAErq6m1aiPu3NMi5FWeipA6Hq30PUp0ehnva8hzR1wCiHbShJTyQg2FOyI6zmDMEmN+yt/XGngXm61FVDPpSJxvvxBBQSyBw+DcVGycasd5e5EVWrX0MpSpt46zIFrMjU+iO0I= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com; spf=fail smtp.mailfrom=chipsnmedia.com; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b=f/fIAqnh; arc=fail smtp.client-ip=52.101.156.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b="f/fIAqnh" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=LK8CACAknWiGG0GclnvbhnBcLCEa+PQk1/1HcTTpbjedFSukL+fIcfskbAEyhks/cBMpKiUk/tTbqSD3LL2HIVJxr1KRMaqo17NimEL4ND+eepTA6stBRvSXQFRf7FFy1Kh0wQ9O2BqgeOpI3z12CRWHfAx8H7xJfnhj1qnDJeKwBxMDzRNBpMWd/RSMZatb1fhloWamBn5Ino7jPjsa2sWWfE3gTjdV7rssGEEiB/QuyO9HAsv54VdajJfgqqBFH3FfhVX91MwNNYhutuDD9wmEQfFxFSycdFZE0wpYjQC7F+7BFmM5xnVGksCDxV8idDCPIYR8H3yH3BqSMJRGgg== 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=HJu7p5qw+zcW9C4CqwCppj80pM+rhk8jH+C9euM1+QI=; b=s7NTwx6iK+j3Cc6DqSRvGu30hA1pJ2kix8dDIh074rOkHjrbUJ4JBuEm906CZ4NhU87rGNrOYa9i3ZkmGiWdRiokBjabB7eKYzpQXbcXvBVqPTiyKkp/216V6foBNUItu6yjvJRChyfVCjzX2vZK09IGvKywccy7MgKOSZJnws4DXt3d+Hee/yTLCg70cBGgs+tpYfF9nGukH6xAGZjA0a8NOZk/IBgz0OkoYj0ECwPp3h85V8itsUeIaoxb72ixLlJ99woQv8hhUwfAw3QyCfGbaKL+r0OmMf4h+mvCPf2qijaUt4aQ8wDv+zGosDdPSpIYnEYdYikl1U4ASKa+3Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=chipsnmedia.com; dmarc=pass action=none header.from=chipsnmedia.com; dkim=pass header.d=chipsnmedia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chipsnmedia.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=HJu7p5qw+zcW9C4CqwCppj80pM+rhk8jH+C9euM1+QI=; b=f/fIAqnh9BkVH4uHT35KFrea/78PmM4jsLiaYnO3BpFm/0LYZAin+Yv/iNk10sf1EcFWiT/bkQlaC21vcGMyddOCbBxuo++ALLwzAXXBX1fhhZrd6P2f5eIsDHYcp/1sGftlycUjk61xj9zdzPwEqAtyq+tfZQldR2rsCFr1nWc= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=chipsnmedia.com; Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) by SE2P216MB1521.KORP216.PROD.OUTLOOK.COM (2603:1096:101:1e::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8655.34; Tue, 22 Apr 2025 09:31:36 +0000 Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07]) by SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07%3]) with mapi id 15.20.8655.033; Tue, 22 Apr 2025 09:31:35 +0000 From: Nas Chung To: mchehab@kernel.org, hverkuil@xs4all.nl, sebastian.fricke@collabora.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-imx@nxp.com, marex@denx.de, jackson.lee@chipsnmedia.com, lafley.kim@chipsnmedia.com, Nas Chung Subject: [PATCH v2 1/8] media: v4l2-common: Add YUV24 format info Date: Tue, 22 Apr 2025 18:31:12 +0900 Message-Id: <20250422093119.595-2-nas.chung@chipsnmedia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250422093119.595-1-nas.chung@chipsnmedia.com> References: <20250422093119.595-1-nas.chung@chipsnmedia.com> X-ClientProxiedBy: SE2P216CA0120.KORP216.PROD.OUTLOOK.COM (2603:1096:101:2c9::7) To SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SL2P216MB1246:EE_|SE2P216MB1521:EE_ X-MS-Office365-Filtering-Correlation-Id: 5f20e2ae-c2e9-4376-4628-08dd818079df X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|7416014|52116014|376014|1800799024|366016|38350700014; X-Microsoft-Antispam-Message-Info: 7aHDX7XRusfuD3kcFaqlrvEu8hfuFoq1bXLw4ubmsj2CF1y1DUpZpJttHXfIkzmoKWuOv2qbTV4qI6BECx6NxBamIVwIW2EY3Xe+GNdOUf7EGInFYIVUJwZeR1orNukTRGjM7vB4ykbHcZKkLHEt9Mt+RVli0dHp75nazcwtBN6KW74wAgKli/W9mPp5dlIRjARckJ39NfJd+FJ58qYRl1Yywt8IV87qnnK+c1yWMsYEXibSxIXU11gPiLXcv6QOLSj6ZwrB5YicRtUfB3Zd3YzNj0Eujx1oxaIQjZTuR7hw0mFeG3Z+DZ6NbLfCOI5c4IZ0SdoTPmt9PSSD1w24M+LoRIT0cbjJECVH4mFAAP2cf2FCu+U0Ke4lMLS+qvziywztEL14MRsPV4bUKbwil7gASPuVaJyWkRbg8zKAUs6eSgz1TA1nT8LWXd0sdLa55Lrx2ASX2sowP+4qeKR1xbl7u6an7mK4AVZ8nG/2zwEkF43+l0ZDJfxucOFaC3zsp5rr6fyUMgIAs1u3bgUypF0+25rog3xjXohYqlwTYPFNk5YPblwX6Ua6IcGks4UwP+d4O9LVr6DdAvwkFtDUuBhleZAj/ErQsAILe02/5krnBc/WRZjdkwrk1e+X6MiFEWAghslqalE7DOIZVNZ801QkyOV5CUBwHv2XSYpK+D7bZ4G2M6h1nB6Cs0ekanUEBZSSXMSLDgsdIZg6d/9Lm/zyZ9Bw7pwXJUJDeWpgj6XoiST1flPmM1AMsvQ11jqab2XhTRoR/FkFPgN1I1/qkUewTSm30c7IXMozWEWB3c59+YWi8K54275zZeNE6oZJiqQkV1fOEryG8qdRJgEiU7s25y2OHLOOcuMymsg4Xo80fLUecIq6Je44Ef+4tRS+PbUcbLnnrTfw9XZwPnO1EtEv+DfmtbFOYolfn6l1eiA1KWXGVs260NnZZdSxhyDoATaU7jhTk7wwdAOeKbvYkZiZj8w9H/rb77xKMAVglIhBUCErhhls3jyEx+jGa7XTEsyIbUBgPnBlqdCvkHVq6TVZcq8b5IJwUY1AB/R2nsjiSXresv1gD378/aUyipjpEnpzRi/vpqZHrY7a+h/CiN7tBOhik0cgfVTdFyQw0AnJ+d99/oyFRqMmx3e4/SqS7I/hir/Pn7My+9B2w5LJi5AZ2LSczr1sbSdd0NIvdPQyQP21WzwD4pFBDexRXsAG/HWPR0RdRZdPBOICeg98oHGXoifxty/Wsw024KbtiLLKAHgfpZL6qzjnGmwtG2TlLGtX7oRqDApWHpfPzU8tV1ruSbPBiSdA4p6UCTeeEDtyHoNWlPXZ77xJ9y9SKH96+EY4MjSplWuZ0MLBXbWq9kn+SlV9N821eEU+M6KnamiIFRMsP7VmS43NlGxWDA1L0UqP4C6Z6y4I5H6C1NEvQAJVDGSg1bkWd06oR1d2ZZzJ7gFcMhvhcuoP5tqBj3CRN5S5Dh66kg1hbKLST25TRg== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SL2P216MB1246.KORP216.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230040)(7416014)(52116014)(376014)(1800799024)(366016)(38350700014); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: RVU5xhhUeERERcTdNsPRjShYcJZfrjqra1h1Z/v0ZUHN00teM55QGnc/i75JWTyT1AhgiDfc3QQK35lDnMQ4vsRNHTo18h06ghl+nFn1ih1Rdh5+HyjhrHunC6Xj88wLumPnLK/jJINVGf9uzyOefoiI32NIFVFArs42JGGD7ReJhdvnKSrrUQD2luHBhWpTdwNeTgF+P/hQbi7JzQpme/8SFIZoM3HjkRXq7C5kAyMNSA9gshEk4uuD+csF2y1lwaVPENaAK4OJYIBSUgLDEPUHK9ZsWJVavt+0qXLavQNg4h9VY5h10pjzm/m8J/AodCaSiDuBWTdli1IfMTR/8+nakm+OnWD9PY5CK3cRJX2smCd+yMKLQuVSbbWtQzLBdSrHMWoWMn3dENm35SamtosfuzX+3iTw9T87/o+Rc8ZCuhQ8S2lfOTWwM2sdKdPG+wdWm9c5HHfX5Wtx2n5Ibq0mkZ88kRqZxE9AjVj12CrPMAl1X0BJnQsRC2NjbUTdF62jVSVuQt6wfFvKdhB1AYSB8hO2Nr+xZKzVIJfHiGSMA88UDyjaw9XVngw2Hgdst8bVTpVsETnzIddFvdl+QpxZ/8cmITPrBtyn1SouPyL5sR/5qcGGM1uGs/14m9emRZYARLzrUp2mqmuqNJreOCIg+6FadY9ZDk9qPiuCYCsHLV4Kas6+w6bcUisyeWn3F5/r4LEvrQorDLRCcjU68WzqlQeO7Mktt6K77A3v8kDVgEy47udhVnTDFKRmjCdhTvvjYxc2AqWZ3jUEAxhypzpOyHXYrpktH8f0LBO9FCeZsUHy8GwjBSPTdMaMWGRYIzvRb4HxQoI7ybnTXbL2hgrWIo3l29ZbLAtTPG+COe7t5SCoWLDjFGIU03YppRCJkpR2y+kcYUpCPTaOpwltndfgZ1KvxDEU2ojy+M77rJoakhUhek16A0AMx/VaXusTqWMbSnUqwRpT/USirG1I38jmogwydJf5bM5Si4QRDfiaPcKKyhC1r01/nlLF+OH5l4UvikyjdNGLdLnd1csW1gRTC1lfySD1Bf5Hnit21o+SIdenM5iSXVj9FmAKphQpVgwkfAoo9PeUQyi4Y+ALiCRIYgl8TpW7DLkbuytSbVuyIOl/xT5bNwiV9QTaCkNr+C2fMVqC7sY7oyYcFuQUL08dgVx46JuEun0Veu4yzcBlT2N6sbNYXVlDmhMoupwfTfF1dCzGZFxpJOXr14EStX5RFtrem5N+35CX7GNL3vpQclKCxclzA7KfyOhR44+CjFlAUnSYb6dLksjPXQfoRSPYSEZewVu71UNxtuzWp7GCIPBN8RESpDvEd2eoN2iZTpOaCPenIfNj0bExasFcpFCMu1fnNT9wsMjUGe8qp9CX3XX6CUs8JtUrNxwM081S+G6B8SV6WsChL8nPw0mOVQ6RVQ9HvIKKyiGKWdWCxu4JuajEo9QqlDaNhXfYwQhZPhTiCYm18GaP+zpbNFmRGvQOd2tRGrmszYJPw55SRq5896VgNueyVeH6q6KJlcppSjf/aQ3JjV+GAN83pbqvZ8hk1TfGAo0B36v2qNTgNU2fsgOFjJRLwTzn62KwQSZLVvQfGZeQ5I300zUntbFbFA== X-OriginatorOrg: chipsnmedia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 5f20e2ae-c2e9-4376-4628-08dd818079df X-MS-Exchange-CrossTenant-AuthSource: SL2P216MB1246.KORP216.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Apr 2025 09:31:35.8410 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 4d70c8e9-142b-4389-b7f2-fa8a3c68c467 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: ISoD6dbwl9GsD1xC3yT4AIzLyXAB7fMjuNcIe1hnKFmijV8sNpER7Bt0v9E15p7DwoDwhdMLi/EqXJjZ14v32Nb2wKkmxgVqzMk/FboVces= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SE2P216MB1521 The YUV24 format is missing an entry in the v4l2_format_info(). The YUV24 format is the packed YUV 4:4:4 formats with 8 bits per component. Fixes: 0376a51fbe5e ("media: v4l: Add packed YUV444 24bpp pixel format") Signed-off-by: Nas Chung --- drivers/media/v4l2-core/v4l2-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 0a2f4f0d0a07..de3636f1cdf1 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -269,6 +269,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, From patchwork Tue Apr 22 09:31:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nas Chung X-Patchwork-Id: 883353 Received: from PUWP216CU001.outbound.protection.outlook.com (mail-koreasouthazon11020072.outbound.protection.outlook.com [52.101.156.72]) (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 26A9F202F79; Tue, 22 Apr 2025 09:31:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.156.72 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314309; cv=fail; b=d1QzmyaKDXpICNhoz2+3bA6SIwpiL1Sp/VpQzEXHPuBGc148cqMGSlNUqJdVMg8pT8OhXRCjETPVEa4WegJKsekE4xRwkNPvz2U+vJVgt4oSiryH4PoKMBYke2fQ1qQRG8g66d3rCZJQjwuznBRtXsLSgbmSK6LwY/0t3A48VAE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314309; c=relaxed/simple; bh=AIQ/5DKkQMm6DtvjmKnTR+ZtBy+W6Jec5tuyI5lRiQs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=r9XAXaGPqCUNIoH64zAfDv9Mt7sCsqblDejpcTkw0YiiT6Fkws/H9uog25wLysDh25RRUg004TbLRuM8rnxAwnTvA5gUEVOv9SxqmkBT4+KW3m2q6HS569uwjXB7nm7rRA5lHV+DYaLzq9bJMqw7e78dziG61buCwoI3mo0peCk= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com; spf=fail smtp.mailfrom=chipsnmedia.com; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b=SQEE61sa; arc=fail smtp.client-ip=52.101.156.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b="SQEE61sa" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=kGhDUoACJJrD7mT5ElTst1ds7+6sge4HucAagcBXFQg1A3Izt+HXZC80+J3jI8ysq8S35h2nk/DkdGvzs/2sIKHq1nD5qjEUVzBD3gJGNktfPHYXGgNBO5iuPSkTuK7yQ3Zr49jJP5c9I9bGsfmSSnNVeHjjFy0JVCrfrE569eZGef+QyZNf+r3MVI5c6iRsPCsaCx/lmoPkk8a0L47H8dQEy+IWqE3Pil5UnBSkJubpMMHcgztcJvK0Mw4nH2Lq0CEJtPvzOlYFrMoECK+2kt9Qjjh1N/MG0H71XYVkqtfJr5Bo58uiSOk1BF88z8FyPCw87GeQJt3FS1out8+aUw== 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=+N4TpxLRrGgeXa2rhEIq/bSgqpBgBSr8WrtLE9SwHow=; b=chOW4fXpfmfR4zfiJxY1PbfSFjlZJ2a+u5+sU6lYxdhlM3esdZbiNpY7mqeWyGgnJhkLwNYs44u2pSeKru7zFLncdMVfviVXT/IpuL1iIGfZVTvLJaUiRL2L/wEvgwUuB79Itkh3xLAk/X//VC9BpGZMwcfTYhdvcq/nOkioRog3Q5I9oLjK2fADD1B5PGeEGylKlBHtoBf6wyu7WdgJlFnEa/qqvTWVApdnHxGbiFXXlnnwu6f7jpZdKb1K6nlbQkcEb561GrmEj/iZBmjK4vpy5d2KNBxlGW64WKZw1LjaMw85zpQtfYLxX41gqCsqiOKdGzJzecTS90JWbhBZVw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=chipsnmedia.com; dmarc=pass action=none header.from=chipsnmedia.com; dkim=pass header.d=chipsnmedia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chipsnmedia.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=+N4TpxLRrGgeXa2rhEIq/bSgqpBgBSr8WrtLE9SwHow=; b=SQEE61sacdAzFj0cjyrxS/tj2MrYg6iQ7hvl5dQarE4kJ6ez5/XoGvh3T71LnKaWJB1Q2BpvoO6fdNIn9PTakOTZ5+5tjEI6+x7wGVmn8x8wKJwy8ocCRSNUhluhvI15nWWQlXjoaJyGS0vWEZFCwvMPTpY/yKJhuI8BRQHWAXg= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=chipsnmedia.com; Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) by SE2P216MB1521.KORP216.PROD.OUTLOOK.COM (2603:1096:101:1e::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8655.34; Tue, 22 Apr 2025 09:31:38 +0000 Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07]) by SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07%3]) with mapi id 15.20.8655.033; Tue, 22 Apr 2025 09:31:38 +0000 From: Nas Chung To: mchehab@kernel.org, hverkuil@xs4all.nl, sebastian.fricke@collabora.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-imx@nxp.com, marex@denx.de, jackson.lee@chipsnmedia.com, lafley.kim@chipsnmedia.com, Nas Chung , Ming Qian Subject: [PATCH v2 6/8] media: chips-media: wave6: Improve debugging capabilities Date: Tue, 22 Apr 2025 18:31:17 +0900 Message-Id: <20250422093119.595-7-nas.chung@chipsnmedia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250422093119.595-1-nas.chung@chipsnmedia.com> References: <20250422093119.595-1-nas.chung@chipsnmedia.com> X-ClientProxiedBy: SE2P216CA0120.KORP216.PROD.OUTLOOK.COM (2603:1096:101:2c9::7) To SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SL2P216MB1246:EE_|SE2P216MB1521:EE_ X-MS-Office365-Filtering-Correlation-Id: 1c4efc4b-af1b-442e-7159-08dd81807b4b X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|7416014|52116014|376014|1800799024|366016|38350700014; X-Microsoft-Antispam-Message-Info: EMylFqXgDrT7cmK11U6sHEVgnN+p5ofMH2ke1ZfG9byNZGk+diFDix9DIxnD/HT+JoSxR7hRjKTYPV2LKghM8ZlRmQ33QJCACNxcUTrz/dBg5YwIx6hWO6TKb3PTDy2j6GRu1QvyD7xJ/myaJFmTngJI3Bj7Jd6L0zSHF/feRmXcjbTSpPVxlI6x7noznSCXV/eBAw99E3HMAdWUCCgQr/JCsrWr/N4l3s5fkhZyG9oPwi4h9LNZqSAx5/40ylZyYkuQdaUzVuE8ahCiaAhVVmFkrBErPI1ITN6LZAG0VuiCxg2eV9WAnbs0CpZpNnpifi+tBWSBgr+C/G2TiYk1OS1R4QPZ9fdQaiPAayglhA4X1NjHGe2RnC9orWDSQPGlaE+vnj6rRicL7+qNz+yCdlaPbWduEf/Zi5sXo7UcDnZR+Fl+C/3Hs3C5iZ/deHtX9Y9HI5fAIM7Jc2khPsJCzKUkJ1k3sQCqs+46LnA4+LK7OIMqJlHJxPla7WhzVf+hsMgfqzPCUyIISDVvpYnLl1SzXlstpPON8Upg7kls+Kjq/P9jfb8WUahZWcA/sk7/s6rXk5R+MRrKKrO5l1uDhPxJdsolxIQ4pSWgHtpWCiOVp4JlfljeVcY0TCLM/OmTK3kabR4+dFksvnUgmz4r4EJeOgH77+y8ybCNJoT9J6tErfdumPsKUrayBRas8TN75Spnn7Db5eAn+QMY+VW0dFJZMJBZFxG5BAV/9Q6BebCl2hWcdE7zUf1eOZNmyvBlC2md9B2uZY9WVCeODInuZj9kh4DYQ81ElKbe5WPMhCVyznt+AGXBcjiueflttO1/ik1hwGHsgFi01K3dyInHU7bLJtGdswKjp3SBN04WLU5CfF85/pNgbh4kDts1Iq+sMhcppZljX3ILEnFrsXHSvqx1jBXWhvi98mXEuwainLp71HzBqcA2ADftPsPXWJtaXW1V3NYExxy/F0350TPIbaR8rFP0pEcdhS5V6Hh23732KAJMotRLNfEWoRXHF+3JpKgB27wjNwV1cjcMe881/d3S0PCLOayVeRLG0s6r3gKHf5aaJxgdxVfd8OJcxgmXrMJFjUXkjx1C2YgdPGCSOuwmAoavw9rsnIdt2VJimAqkgQEChTZK5vtjUI0nxReqYUjQb1J+oWhdVP4Cqwh/PkdYWVMYALnJVEWzOBZiIIyS1ZDJwFS8naKHOANGbue8XpPC473rmRdTh5893kawXu46SeNledKsbYbwh3jjrjC85G1blzJ2KBZpYGZDzO7MAE1HMPCjOpDkKZubxl348ud9BV28f6kjIlYJYO8/O6zbGXbZqCrmHfHJRp+xu+2ZRXSeso3KJoduzIAD4SYNcNQzSIkbVxxi2LPB742Y9SRpC408CG5y6cr1kK8w02n+PxDyAVb6/34TP1DhFcqnMIAWuDCY2YngZA3JVGWGzCjpnfGNJah7wy4+twy19wcRxpl9SSm9TVyMnlfMImCchQ== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SL2P216MB1246.KORP216.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230040)(7416014)(52116014)(376014)(1800799024)(366016)(38350700014); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: 72k6NUMDZpzH3XTmbIXFXA2YQ9brXD0lr4oyhTcduxPuZVYhvviIQutKiqWrHedz6/1JXTieILp5RQEp94qkkxwLSh1Ygnv9iDlt0/5N523flofWN+bEiN7GuGtmWU6ncmsdau4J3Jrux8qzM9/BfAnobxgpI68VZhOO4x1xKXbK+vi+IqxdQBhjin93/j/Mb/QU13FBjAwI7esID4bxl0wt8UAuaD6L4KHEnbo52smE/ySUI3J5F0Cxz0EIRcW3IeFWWk86aafHgYPixi9apr15pJFb+fqgFEYSjiIGT8qTFUhuJ2uXczORVXRUSbjGjAI0btP9U21ixJElYY1xIlYtGxdPvVwlNttT9hAVqPy6zdbo9A2lVyIbwxlGh/c44qsfm/yXzVd4w6CgCWXUm1+9eZOfPaHnp14FmqMuNs8iAitdS5C9i/3UNnLhk59GZRnQCq6F7IjxCtzuG2B7JPdzjxEpLQhoVs3oRKkNcfGRHCSyxZLklrRD66vevRvT0fo4h18t515fwV2lkEqGfdMNj/+N5LSveFxwsr3o5WgnZsmij6xfNUjsa9v9LjNbq/Z1qPs0UEkjnME9rD8PJsEBixlOmx81wnOX2byidLITIuJSZp9zlzGrbdz6ItmUG1s7CCwMBB9MYyBSGYIhZaVlhdJChcZgEmcqrifE5gRJixyBQ2cVFRCUxowWi/CkOodF4O/QcW2R2uuTdCj/78gQP6w/VJRoOAlTT0rZG1Hkd3m9zxQn5dBNCIcDZCl/0N7NUNEM0tt1q0R+5YIkcdZZXK6i3tt2esU9ffvXkp1zzc9Ncyk1XuQwN7RKwEzcAu1MOcLmWwYSGsmzC6gF+OnTG4mbjH1okJ1lk9XbrKYhZGiMVVNH89lq0eRndHfoLsxg2APVO/y0pv3MOKymlRsIBhgWFAkioJHk/1CPHLLecjILcSzngOjWd2ykoynfDCJ8VyTK9t7Ux8tqKQw4v+domL3SljgIvVibAWP8S5WcJCwUlBgb9PGvKiMWtUeQPyy4d6V7gU88eoATTz+yj11aBEZ/Kwg5e1TcdUaQARQytd1eiYwY+kDwNJ3v3XnLBhOcPewhlIz6Hk1hoPw7tAEyQ5VmECP229FPzau0dgHUDubi0fVhavVuWNMiIZQUXUu52wOh7NCTDad2LKmx+oS7JswKtbBaLh+zVoneejsQHfhD7kdtVuFhqDweqOcBEtt2T2I0RRR2D9FHUDigqk9MqCxm2pnH5nHurIAh8Gp+Gwg3FN/SQQyiSsi/NtFOQ1AGkDPq+fV2rmSdMvl23lO6vO65YPSWwDXbUB8RbV6dGNHVM44WujKDfWWA/sWRhadk8dZKZzzCzM/a3IhWQ/WpHsAO7KbpjuLRoF5dTBAGE/9mOysCwoN1+Ow3VzHTU0kv9iboTZT1vpEBKCVCMJvGO4Sd5rIJFjVNNEzP+kBQFHtjwy0OOoEeCEk77LzQLDD2eMawMtcUbnNx/FtW8jAqn4ZRRlPtMStfG3fLsOo5cDDWrVtqq8HL/BbfvuybUNCoWIsAv4QVa9GbivBm9D2I9dA5UEF7kD6qoVhxBqe3qH41eJ23UHRQ2kB1UUQbcRAaIT2HuxP6tMiBEVvGfw== X-OriginatorOrg: chipsnmedia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1c4efc4b-af1b-442e-7159-08dd81807b4b X-MS-Exchange-CrossTenant-AuthSource: SL2P216MB1246.KORP216.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Apr 2025 09:31:38.2394 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 4d70c8e9-142b-4389-b7f2-fa8a3c68c467 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: zkNsZrP1HCvClIRkvosbBaCAp19efZ+e1MqC7/OCCNPUheTr+vDP+PbZ81kM1pumA32TfSPG6wGqYxvDoNkOwiR3RXcl5TSQ1dTcJqWxjpg= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SE2P216MB1521 This adds debugfs entries and trace events to provide detailed debugging information. These enhancements help diagnose issues and improve debugging capabilities for the Wave6 core driver. Signed-off-by: Nas Chung Tested-by: Ming Qian --- .../platform/chips-media/wave6/wave6-trace.h | 336 ++++++++++++++++++ .../chips-media/wave6/wave6-vpu-dbg.c | 230 ++++++++++++ .../chips-media/wave6/wave6-vpu-dbg.h | 14 + 3 files changed, 580 insertions(+) create mode 100644 drivers/media/platform/chips-media/wave6/wave6-trace.h create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.c create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.h diff --git a/drivers/media/platform/chips-media/wave6/wave6-trace.h b/drivers/media/platform/chips-media/wave6/wave6-trace.h new file mode 100644 index 000000000000..5b7a0aff5132 --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-trace.h @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave6 series multi-standard codec IP - wave6 driver tracer + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM wave6 + +#if !defined(__WAVE6_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __WAVE6_TRACE_H__ + +#include +#include + +DECLARE_EVENT_CLASS(register_access, + TP_PROTO(struct device *dev, u32 addr, u32 value), + TP_ARGS(dev, addr, value), + TP_STRUCT__entry( + __string(name, dev_name(dev)) + __field(u32, addr) + __field(u32, value) + ), + TP_fast_assign( + __assign_str(name); + __entry->addr = addr; + __entry->value = value; + ), + TP_printk("%s:0x%03x 0x%08x", __get_str(name), __entry->addr, __entry->value) +); + +DEFINE_EVENT(register_access, writel, + TP_PROTO(struct device *dev, u32 addr, u32 value), + TP_ARGS(dev, addr, value)); +DEFINE_EVENT(register_access, readl, + TP_PROTO(struct device *dev, u32 addr, u32 value), + TP_ARGS(dev, addr, value)); + +TRACE_EVENT(send_command, + TP_PROTO(struct vpu_core_device *vpu_dev, u32 id, u32 std, u32 cmd), + TP_ARGS(vpu_dev, id, std, cmd), + TP_STRUCT__entry( + __string(name, dev_name(vpu_dev->dev)) + __field(u32, id) + __field(u32, std) + __field(u32, cmd) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = id; + __entry->std = std; + __entry->cmd = cmd; + ), + TP_printk("%s: inst id %d, std 0x%x, cmd 0x%x", + __get_str(name), __entry->id, __entry->std, __entry->cmd) +); + +TRACE_EVENT(irq, + TP_PROTO(struct vpu_core_device *vpu_dev, u32 irq), + TP_ARGS(vpu_dev, irq), + TP_STRUCT__entry( + __string(name, dev_name(vpu_dev->dev)) + __field(u32, irq) + ), + TP_fast_assign( + __assign_str(name); + __entry->irq = irq; + ), + TP_printk("%s: irq 0x%x", __get_str(name), __entry->irq) +); + +TRACE_EVENT(set_state, + TP_PROTO(struct vpu_instance *inst, u32 state), + TP_ARGS(inst, state), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __string(cur_state, wave6_vpu_instance_state_name(inst->state)) + __string(nxt_state, wave6_vpu_instance_state_name(state)) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __assign_str(cur_state); + __assign_str(nxt_state); + ), + TP_printk("%s: inst[%d] set state %s -> %s", + __get_str(name), __entry->id, __get_str(cur_state), __get_str(nxt_state)) +); + +DECLARE_EVENT_CLASS(inst_internal, + TP_PROTO(struct vpu_instance *inst, u32 type), + TP_ARGS(inst, type), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __string(type, V4L2_TYPE_IS_OUTPUT(type) ? "output" : "capture") + __field(u32, pixelformat) + __field(u32, width) + __field(u32, height) + __field(u32, buf_cnt_src) + __field(u32, buf_cnt_dst) + __field(u32, processed_cnt) + __field(u32, error_cnt) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __assign_str(type); + __entry->pixelformat = V4L2_TYPE_IS_OUTPUT(type) ? inst->src_fmt.pixelformat : + inst->dst_fmt.pixelformat; + __entry->width = V4L2_TYPE_IS_OUTPUT(type) ? inst->src_fmt.width : + inst->dst_fmt.width; + __entry->height = V4L2_TYPE_IS_OUTPUT(type) ? inst->src_fmt.height : + inst->dst_fmt.height; + __entry->buf_cnt_src = inst->queued_src_buf_num; + __entry->buf_cnt_dst = inst->queued_dst_buf_num; + __entry->processed_cnt = inst->processed_buf_num; + __entry->error_cnt = inst->error_buf_num; + ), + TP_printk("%s: inst[%d] %s %c%c%c%c %dx%d, input %d, %d, process %d, error %d", + __get_str(name), __entry->id, __get_str(type), + __entry->pixelformat, + __entry->pixelformat >> 8, + __entry->pixelformat >> 16, + __entry->pixelformat >> 24, + __entry->width, __entry->height, + __entry->buf_cnt_src, __entry->buf_cnt_dst, + __entry->processed_cnt, __entry->error_cnt) +); + +DEFINE_EVENT(inst_internal, start_streaming, + TP_PROTO(struct vpu_instance *inst, u32 type), + TP_ARGS(inst, type)); + +DEFINE_EVENT(inst_internal, stop_streaming, + TP_PROTO(struct vpu_instance *inst, u32 type), + TP_ARGS(inst, type)); + +TRACE_EVENT(dec_pic, + TP_PROTO(struct vpu_instance *inst, u32 srcidx, u32 size), + TP_ARGS(inst, srcidx, size), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __field(u32, srcidx) + __field(u32, start) + __field(u32, size) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __entry->srcidx = srcidx; + __entry->start = inst->codec_info->dec_info.stream_rd_ptr; + __entry->size = size; + ), + TP_printk("%s: inst[%d] src[%2d] %8x, %d", + __get_str(name), __entry->id, __entry->srcidx, __entry->start, __entry->size) +); + +TRACE_EVENT(source_change, + TP_PROTO(struct vpu_instance *inst, struct dec_initial_info *info), + TP_ARGS(inst, info), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __field(u32, width) + __field(u32, height) + __field(u32, profile) + __field(u32, level) + __field(u32, tier) + __field(u32, min_fb_cnt) + __field(u32, disp_delay) + __field(u32, quantization) + __field(u32, colorspace) + __field(u32, xfer_func) + __field(u32, ycbcr_enc) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __entry->width = info->pic_width, + __entry->height = info->pic_height, + __entry->profile = info->profile, + __entry->level = info->level; + __entry->tier = info->tier; + __entry->min_fb_cnt = info->min_frame_buffer_count; + __entry->disp_delay = info->frame_buf_delay; + __entry->quantization = inst->quantization; + __entry->colorspace = inst->colorspace; + __entry->xfer_func = inst->xfer_func; + __entry->ycbcr_enc = inst->ycbcr_enc; + ), + TP_printk("%s: inst[%d] %dx%d profile %d, %d, %d min_fb %d, delay %d, color %d,%d,%d,%d", + __get_str(name), __entry->id, + __entry->width, __entry->height, + __entry->profile, __entry->level, __entry->tier, + __entry->min_fb_cnt, __entry->disp_delay, + __entry->quantization, + __entry->colorspace, __entry->xfer_func, __entry->ycbcr_enc) +); + +TRACE_EVENT(dec_done, + TP_PROTO(struct vpu_instance *inst, struct dec_output_info *info), + TP_ARGS(inst, info), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __field(u32, dec_flag) + __field(u32, dec_poc) + __field(u32, disp_flag) + __field(u32, disp_cnt) + __field(u32, rel_cnt) + __field(u32, src_ch) + __field(u32, eos) + __field(u32, error) + __field(u32, warn) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __entry->dec_flag = info->frame_decoded; + __entry->dec_poc = info->decoded_poc; + __entry->disp_flag = info->frame_display; + __entry->disp_cnt = info->disp_frame_num; + __entry->rel_cnt = info->release_disp_frame_num; + __entry->src_ch = info->notification_flags & DEC_NOTI_FLAG_SEQ_CHANGE; + __entry->eos = info->stream_end; + __entry->error = info->error_reason; + __entry->warn = info->warn_info; + ), + TP_printk("%s: inst[%d] dec %d %d; disp %d(%d); rel %d, src_ch %d, eos %d, error 0x%x 0x%x", + __get_str(name), __entry->id, + __entry->dec_flag, __entry->dec_poc, + __entry->disp_flag, __entry->disp_cnt, + __entry->rel_cnt, + __entry->src_ch, __entry->eos, + __entry->error, __entry->warn) +); + +TRACE_EVENT(enc_pic, + TP_PROTO(struct vpu_instance *inst, struct enc_param *param), + TP_ARGS(inst, param), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __field(u32, srcidx) + __field(u32, buf_y) + __field(u32, buf_cb) + __field(u32, buf_cr) + __field(u32, stride) + __field(u32, buf_strm) + __field(u32, size_strm) + __field(u32, force_type_enable) + __field(u32, force_type) + __field(u32, end_flag) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __entry->srcidx = param->src_idx; + __entry->buf_y = param->source_frame->buf_y; + __entry->buf_cb = param->source_frame->buf_cb; + __entry->buf_cr = param->source_frame->buf_cr; + __entry->stride = param->source_frame->stride; + __entry->buf_strm = param->pic_stream_buffer_addr; + __entry->size_strm = param->pic_stream_buffer_size; + __entry->force_type_enable = param->force_pic_type_enable; + __entry->force_type = param->force_pic_type; + __entry->end_flag = param->src_end; + ), + TP_printk("%s: inst[%d] src[%2d] %8x %8x %8x (%d); dst %8x(%d); force type %d(%d), end %d", + __get_str(name), __entry->id, __entry->srcidx, + __entry->buf_y, __entry->buf_cb, __entry->buf_cr, __entry->stride, + __entry->buf_strm, __entry->size_strm, + __entry->force_type_enable, __entry->force_type, + __entry->end_flag) +); + +TRACE_EVENT(enc_done, + TP_PROTO(struct vpu_instance *inst, struct enc_output_info *info), + TP_ARGS(inst, info), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __field(u32, srcidx) + __field(u32, frmidx) + __field(u32, size) + __field(u32, type) + __field(u32, avg_qp) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __entry->srcidx = info->enc_src_idx; + __entry->frmidx = info->recon_frame_index; + __entry->size = info->bitstream_size; + __entry->type = info->pic_type; + __entry->avg_qp = info->avg_ctu_qp; + ), + TP_printk("%s: inst[%d] src %d, frame %d, size %d, type %d, qp %d, eos %d", + __get_str(name), __entry->id, + __entry->srcidx, __entry->frmidx, + __entry->size, __entry->type, __entry->avg_qp, + __entry->frmidx == RECON_IDX_FLAG_ENC_END) +); + +TRACE_EVENT(s_ctrl, + TP_PROTO(struct vpu_instance *inst, struct v4l2_ctrl *ctrl), + TP_ARGS(inst, ctrl), + TP_STRUCT__entry( + __string(name, dev_name(inst->dev->dev)) + __field(u32, id) + __string(ctrl_name, ctrl->name) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(name); + __entry->id = inst->id; + __assign_str(ctrl_name); + __entry->val = ctrl->val; + ), + TP_printk("%s: inst[%d] %s = %d", + __get_str(name), __entry->id, __get_str(ctrl_name), __entry->val) +); + +#endif /* __WAVE6_TRACE_H__ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE wave6-trace + +/* This part must be outside protection */ +#include diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.c new file mode 100644 index 000000000000..f3bcb9ec5ea1 --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - debug interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#include +#include +#include "wave6-vpu-core.h" +#include "wave6-vpu-dbg.h" + +static int wave6_vpu_dbg_instance(struct seq_file *s, void *data) +{ + struct vpu_instance *inst = s->private; + struct vpu_performance_info *perf = &inst->performance; + struct vb2_queue *vq; + char str[128]; + int num; + s64 tmp; + s64 fps; + + if (!inst->v4l2_fh.m2m_ctx) + return 0; + + num = scnprintf(str, sizeof(str), "[%s]\n", + inst->type == VPU_INST_TYPE_DEC ? "Decoder" : "Encoder"); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "%s : product 0x%x, fw_ver %d.%d.%d(r%d), hw_ver 0x%x\n", + dev_name(inst->dev->dev), inst->dev->product_code, + (inst->dev->fw_version >> 24) & 0xFF, + (inst->dev->fw_version >> 16) & 0xFF, + (inst->dev->fw_version >> 0) & 0xFFFF, + inst->dev->fw_revision, inst->dev->hw_version); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "state = %s, pause request %d\n", + wave6_vpu_instance_state_name(inst->state), + inst->dev->pause_request); + if (seq_write(s, str, num)) + return 0; + + vq = v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx); + num = scnprintf(str, sizeof(str), + "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;\n", + vb2_is_streaming(vq), + vb2_get_num_buffers(vq), + inst->src_fmt.pixelformat, + inst->src_fmt.pixelformat >> 8, + inst->src_fmt.pixelformat >> 16, + inst->src_fmt.pixelformat >> 24, + inst->src_fmt.width, + inst->src_fmt.height, + vq->last_buffer_dequeued); + if (seq_write(s, str, num)) + return 0; + + vq = v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx); + num = scnprintf(str, sizeof(str), + "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;\n", + vb2_is_streaming(vq), + vb2_get_num_buffers(vq), + inst->dst_fmt.pixelformat, + inst->dst_fmt.pixelformat >> 8, + inst->dst_fmt.pixelformat >> 16, + inst->dst_fmt.pixelformat >> 24, + inst->dst_fmt.width, + inst->dst_fmt.height, + vq->last_buffer_dequeued); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), + "capture queued %d, consumed %d, used %d\n", + v4l2_m2m_num_dst_bufs_ready(inst->v4l2_fh.m2m_ctx), + wave6_vpu_get_consumed_fb_num(inst), + wave6_vpu_get_used_fb_num(inst)); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "crop: (%d, %d) %d x %d\n", + inst->crop.left, + inst->crop.top, + inst->crop.width, + inst->crop.height); + if (seq_write(s, str, num)) + return 0; + + if (inst->scaler_info.enable) { + num = scnprintf(str, sizeof(str), "scale: %d x %d\n", + inst->scaler_info.width, inst->scaler_info.height); + if (seq_write(s, str, num)) + return 0; + } + + num = scnprintf(str, sizeof(str), + "queued src %d, dst %d, process %d, sequence %d, error %d, drain %d:%d\n", + inst->queued_src_buf_num, + inst->queued_dst_buf_num, + inst->processed_buf_num, + inst->sequence, + inst->error_buf_num, + inst->v4l2_fh.m2m_ctx->out_q_ctx.buffered, + inst->eos); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "fps"); + if (seq_write(s, str, num)) + return 0; + tmp = MSEC_PER_SEC * inst->processed_buf_num; + if (perf->ts_last > perf->ts_first + NSEC_PER_MSEC) { + fps = DIV_ROUND_CLOSEST(tmp, (perf->ts_last - perf->ts_first) / NSEC_PER_MSEC); + num = scnprintf(str, sizeof(str), " actual: %lld;", fps); + if (seq_write(s, str, num)) + return 0; + } + if (perf->total_sw_time) { + fps = DIV_ROUND_CLOSEST(tmp, perf->total_sw_time / NSEC_PER_MSEC); + num = scnprintf(str, sizeof(str), " sw: %lld;", fps); + if (seq_write(s, str, num)) + return 0; + } + if (perf->total_hw_time) { + fps = DIV_ROUND_CLOSEST(tmp, perf->total_hw_time / NSEC_PER_MSEC); + num = scnprintf(str, sizeof(str), " hw: %lld", fps); + if (seq_write(s, str, num)) + return 0; + } + num = scnprintf(str, sizeof(str), "\n"); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), + "latency(ms) first: %llu.%06llu, max %llu.%06llu\n", + perf->latency_first / NSEC_PER_MSEC, + perf->latency_first % NSEC_PER_MSEC, + perf->latency_max / NSEC_PER_MSEC, + perf->latency_max % NSEC_PER_MSEC); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), + "process frame time(ms) min: %llu.%06llu, max %llu.%06llu\n", + perf->min_process_time / NSEC_PER_MSEC, + perf->min_process_time % NSEC_PER_MSEC, + perf->max_process_time / NSEC_PER_MSEC, + perf->max_process_time % NSEC_PER_MSEC); + if (seq_write(s, str, num)) + return 0; + + if (inst->type == VPU_INST_TYPE_DEC) { + num = scnprintf(str, sizeof(str), "%s order\n", + inst->disp_mode == DISP_MODE_DISP_ORDER ? "display" : "decode"); + if (seq_write(s, str, num)) + return 0; + } else { + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct enc_codec_param *param = &p_enc_info->open_param.codec_param; + + num = scnprintf(str, sizeof(str), "profile %d, level %d, tier %d\n", + param->profile, param->level, param->tier); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "frame_rate %d, idr_period %d, intra_period %d\n", + param->frame_rate, param->idr_period, param->intra_period); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "rc %d, mode %d, bitrate %d\n", + param->en_rate_control, + param->rc_mode, + param->bitrate); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), + "qp %d, i_qp [%d, %d], p_qp [%d, %d], b_qp [%d, %d]\n", + param->qp, + param->min_qp_i, param->max_qp_i, + param->min_qp_p, param->max_qp_p, + param->min_qp_b, param->max_qp_b); + if (seq_write(s, str, num)) + return 0; + } + + return 0; +} + +static int wave6_vpu_dbg_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, wave6_vpu_dbg_instance, inode->i_private); +} + +static const struct file_operations wave6_vpu_dbg_fops = { + .owner = THIS_MODULE, + .open = wave6_vpu_dbg_open, + .release = single_release, + .read = seq_read, +}; + +int wave6_vpu_create_dbgfs_file(struct vpu_instance *inst) +{ + char name[64]; + + if (!inst || !inst->dev || IS_ERR_OR_NULL(inst->dev->debugfs)) + return -EINVAL; + + scnprintf(name, sizeof(name), "instance.%d", inst->id); + inst->debugfs = debugfs_create_file((const char *)name, + VERIFY_OCTAL_PERMISSIONS(0444), + inst->dev->debugfs, + inst, + &wave6_vpu_dbg_fops); + + return 0; +} + +void wave6_vpu_remove_dbgfs_file(struct vpu_instance *inst) +{ + if (!inst || !inst->debugfs) + return; + + debugfs_remove(inst->debugfs); + inst->debugfs = NULL; +} diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.h b/drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.h new file mode 100644 index 000000000000..6453eb2de76f --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave6 series multi-standard codec IP - debug interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#ifndef __WAVE6_VPU_DBG_H__ +#define __WAVE6_VPU_DBG_H__ + +int wave6_vpu_create_dbgfs_file(struct vpu_instance *inst); +void wave6_vpu_remove_dbgfs_file(struct vpu_instance *inst); + +#endif /* __WAVE6_VPU_DBG_H__ */ From patchwork Tue Apr 22 09:31:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nas Chung X-Patchwork-Id: 883351 Received: from SEVP216CU002.outbound.protection.outlook.com (mail-koreacentralazon11022082.outbound.protection.outlook.com [40.107.43.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 CE12720C48C; Tue, 22 Apr 2025 09:31:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.43.82 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314321; cv=fail; b=N6/AjLrFEzzMEX2BbLZJd1kw/AZyctWDTx0Q6tN8K/qZ3Te3mDmFJPJ4IS+xgSrfu4asMscu58ihVcK1ULw4AJG2rw+P23qsAiUcTsFXBd99+P4rujkREAdxjwrfB7a0bToIyzqeybAMh8PQQW2bDyDDApiuqljNKd8LgUGjc1Y= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314321; c=relaxed/simple; bh=KKovH7jBLOlIMRPHgwQzah+MrSvlOjdi/793yTIGJu0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=U3ouR5Qdz0k75wATjx43ugkA09NECHJwKz6xhWnz6/I2Ya40npUukXzJLsenZ3wMdpZj92p5+u0VfWBa8FB8dUiP8rS1Un5D1Irv6lsySWL6xNhqPNR1SwP1TpV3mLSS8TjB9y31Wi0pMHcoMpmADVOVYfQwNdkyIX4wJmeqyv8= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com; spf=fail smtp.mailfrom=chipsnmedia.com; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b=Ztg7XBJn; arc=fail smtp.client-ip=40.107.43.82 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b="Ztg7XBJn" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=HfhwgkQjLdKIO6Mgj+g4QvrsWOxSI0l5AIcqOYtJ590IwTk7EQJqjmvxLk/B9GopvIPeX+3SpI8pJIAQF+3hEVI80cuI0ai9gSvAEnfLVbNBUixAqM9IB9L31U/9N/+XQCa9GICyupKBv+++253v2MKpr7ZJ4lIW0hkPGJZ/vYQ0Ira1OOMdeC+MY8Qif7WRcNxjOsjLTsVQ0bca4c1MG6DXRlWSqx+NIimHu6gncclvxhrddyebCcxjfMV+1QbW8o2A/J6WLQY3RbRyVX36F6tx80kKMqTp2x/HH9c27wHbR7uSdDs2IVM8LqJVmcI9NMAK2J2jOvlr2r7qpJQMRA== 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=hRXKZMwG0VUlE28giMw4FCb2IDef4dkpAZSQIsSeI0A=; b=xAhkIA7CXhZp/EvoGKSEI7VWHxcem9ovcuLkeRMl1cxu0BGxB8ZU2fJ5j6EkncCxhfzQNPt55p/xwH96ujNY1wgRO7K9DdDSVIIWiWtCr78IcYyxGFNxUdlJGaoFsjaFnGr/EuU95P94hkmQdmLNQCqmMbkwmqhrDTIm0mBSQxwMPNXOZ73nftV0ywuOecD5FjD+saF0/HZ9h5WtpRK7uyxLQIpRcoNckyapOelyDqGIAq6yNv+TXquADyLiI3j2EoE8GYTgZ+A7rcaeHggF8QDAlsQpbqdZP/QxPRVSi/bSwpkoqyhIaa2yOJ+Pzr6ftgSDltpqdVWkEJSleMKUMQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=chipsnmedia.com; dmarc=pass action=none header.from=chipsnmedia.com; dkim=pass header.d=chipsnmedia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chipsnmedia.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=hRXKZMwG0VUlE28giMw4FCb2IDef4dkpAZSQIsSeI0A=; b=Ztg7XBJnyrCfhQteLT1FJLaaRgZVejk+UOYFeQcJh6bXTujF/QgPMDz9Rf3UfwNaMWKhlktEySmd8Zyw8Fjx1Il+L/q0HgBQXHe63lrT/Fo6rNAkGrC++rE7mXdPhbQp8vy+OKM2g6PpSkj4QOFWvLe5TFHcAloO43Ddes+fsmU= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=chipsnmedia.com; Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) by SE2P216MB1521.KORP216.PROD.OUTLOOK.COM (2603:1096:101:1e::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8655.34; Tue, 22 Apr 2025 09:31:39 +0000 Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07]) by SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07%3]) with mapi id 15.20.8655.033; Tue, 22 Apr 2025 09:31:39 +0000 From: Nas Chung To: mchehab@kernel.org, hverkuil@xs4all.nl, sebastian.fricke@collabora.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-imx@nxp.com, marex@denx.de, jackson.lee@chipsnmedia.com, lafley.kim@chipsnmedia.com, Nas Chung , Ming Qian Subject: [PATCH v2 7/8] media: chips-media: wave6: Add v4l2 m2m driver support Date: Tue, 22 Apr 2025 18:31:18 +0900 Message-Id: <20250422093119.595-8-nas.chung@chipsnmedia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250422093119.595-1-nas.chung@chipsnmedia.com> References: <20250422093119.595-1-nas.chung@chipsnmedia.com> X-ClientProxiedBy: SE2P216CA0120.KORP216.PROD.OUTLOOK.COM (2603:1096:101:2c9::7) To SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SL2P216MB1246:EE_|SE2P216MB1521:EE_ X-MS-Office365-Filtering-Correlation-Id: 9fd777ec-0fb2-4e8d-3758-08dd81807b83 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|7416014|52116014|376014|1800799024|366016|4022899009|38350700014; X-Microsoft-Antispam-Message-Info: 3AdNlyUNSMJ/WIsROK/9Yi8nXqifSxPnHoCeoVUllB9EO7sLlsZi9wmZElafvh1DpavYJ7lbi21eFlkcki+5jkvEvrjOiV5r/f7MBN7rWk2SSR25asS7sKkdZ3LRXVCE1SEro2K8uZMxFrmCOIzMHKH9fUA4aL33+DVdf3QyWEGlhjy7pbueiPj+0yhXHWJmrdiEfqa2UI0trgBVnGDsE/06/06a5604xxTGWqF8qdbMAbpQ7ZkV/JeVkgcj7x7d1SFNz2u2h10KdNaGO5iMa4K0uPKqZOByBlkJmDZGZKzESbAToPgTVuW5+vBrfUlpO1xvj2WmTxs5R6urpb8YcGTuw0ByYe804rzRR5w4V9z8vYJMTzEh8QeEQ26LOcpggXYlJIzktrlk0OYu5w3LtEn3TI+eN5F3kCrCnHQ7mJawFdehbaPnUM+fKQFPnKuWoIJtrL8ULlVhZU27KmDIMJBUsB527FUE8UKh2IyrOEQaA5S4G9GQfTx/e8PJeMz4gZOBBict93rONDP6TW0qWgEVa04cN0M/PJgI4P/efU9zjxygfqONrEAKmDCTIySbhijc5qdM1IYrhXQa6MJol2660SpPJHfdBb6mcsW69ZiTE0OWRUoyfuAw6mKKYnwRXUsFGpONvTzcbphJQlPUjG5umXPLi1BqRSfd5Dynl6zIMYsMw3G+Jp0QzcGH/jc3c1/OAb4yf/JjjEk7dsHhVPPMd662adsIcUNtRZHjKyqdM/2OaMug+MSIHVZlAEO1pUpu0fyznNVtDjidGjgizKxAQePNPxkfyUzHYlD2HJbLmCE9M4N5CLrtL8XXPa0Xsthje0DhPrJfzdebrVb/r66M3LpnIEDh+8FuUWzb+jjsc1bxdNbfLYxP7teujiOZYPNUmQ6JvgffszwuMb4h/Aqm26AXjQoko+IWhN5kVej4buAbw/R06K+PNjcrwVQatgtn6nFnKt5wDNpnysFP7dWvP/KYA6kIDAkqkQOhGZdR+fNJlFLUm+Xtuqim/z4k9oZChlchzfk1lES1+12IOabB3bkSFTcgD4CIms3eAeWBXrTv7J7Z+fsSyf3A/C+JCEuGGZ6Lntv/T9Qk+usOCDqZI901gGpPkh2H/n5iPj1dB9+y5J7Vbj6xN04Zf+YVpLXgk24TPYN4pGLjf7N/qs0pJuIC4TvGzKZKGEe/sjzkwLenFf4geIQt40UxGzCFQQMZC9AAn9DkMWzUvo2veeSiuM9ql00h3lrQD6Ka6jbTmPr8fEQP72D13T8eR6FKoQgVvy1bAMUH55nuazHNYu26orvZDTBZmXunWMGR1clvlrGs7DclpD84WS/EuwY6kbIAF3sisS4pBvhxZiDR3XJEIhxvidsVr9vStvGmYQKoPNLZ3V3cam6OQOII/RUGfY1JSMBdjuiOV90y73zUIv+L8stZdMPKQG9/+0dhBXzJAyVBjIsdvzc99tn5yEcOhlzFQNguSQ+hkSITUcM/XA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SL2P216MB1246.KORP216.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230040)(7416014)(52116014)(376014)(1800799024)(366016)(4022899009)(38350700014); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: EpwcVZdqGzhvju38TvY6UwF1tjrVww75aHhgXzHiKWDkXSaunkKh5cboFeRcwt73ijVGucD792RLBZlv0PaM8Fog7WqjkXD5MmzozFGUS1r4p1Mf2ozobxjckDTi9D+DRCampGvvaJWthadra+JxsmjoH+1UAvnAAINTXeFw7IuFzPW2P4+Hw+m3ltNDI41FMObV4hcoybZ8k20nzmHDc44bFGyxp+NwDKlcXUCGuZ6kwoYqZCdevyraMAlIHTNPTUcs6GFYAONuQAefxUnXcS92cnF09Gv9UrPPCtfnjj18ywHDlYrNykLXBpcY+9sCUNYIBNzr+PMFABK1SprNSJotg9EJJX0S6np3XHb0rTn67y8+Kb+AA5J4JPiYOidHd/LY6k48DWP+kXx4IAAQNobqP+XDp1F7M3twrCUqv7OF7fVvh/S8B8imcmcDxU1JfDKLh+zKRdCsrZF66zfEqn+zyHeere6fRXOQ7kGbaujuAlUdQq61rOz1cFTqJyxRw46jHtdA/8RM7fOKLwFtsirnjO0dFwshgdCT5SIJJyhMtG9DxawMel75nMxZFmSucsTsjHfmXhsnt4dMPi8RZZPG/zq7o0/K8pIaPW7aO3X4QY56AUei9ICOofV6TAXkKRL9IrQ6fO3D4lP25RYOPdgTqtCFVuwhWyTu5wAe6+FF8I3/0e/tQrJ6lMRINpl2wRMT+tbQ4jqOLOW7eTw1omoSRGKr6GJuHwvcmPAU3/Ko9QRAFGbhgOeGxU25TPdJdApT+EDBrreDbw2Dd0JcVTQvWwfgyRsrJESIrc3nWpjnfX9zEdsauoLLOaws5dcP8Gtm4WJecKY9ZaMq5YBrXiaZRxJmQQN1S/s3daedBlxK2CPbscDS+heaHKSrA1r5bYCDpouB8EZibWq49PxG/aMdq5Uy2WgdoHrOjxv6H1lxzrzWP26qqKyQkmK9LDu1AAXm8gkZtunRqwd0WX9Xs+Pu+8G4OjFhFKE+SYUneQHZNi22Tqk8S93Q/SvKeYO3DCzR9iogKxBQzWTN5LfX7EAhT0I6MF4+8n5HQpSnBztCRT4D5mMV1JUhefG7nPEhkfMablyOd2SsgNir3uwcnfpxnp7JYA8G41KC1+7vwpTvNIXd6i6qUVakXb5U0+mDabkSzZs2EpOmxneTMuX4awEJQ8hsFhQ56oA5jXaFa+MTcsBYi7SgZoJ9waStqLb1hL4wpkvKbc5TWBZUp87PtlL4iwk0ESvMkMc1mUWsDLeQ+ccW+Ibz+p01yEO8fdxnoQsixkKdH0xbREC1IDFHoJVQDJE58Enm0TdePU+YZOvmxxtIUgstoSzRRov54SrGXicoTxcCQIUVyIJAiwiaDDrOeQC9HSkQbAJsfoV0fB2z1KElFhuOCbO22Mm95c9+VJUZLqHc86Bj0bc/i3s/nf1IuCUeukukePAMFLkK273H+0up9AAzIkHtuWaBEyyp2u4KeUssWk54LF/CRfhCeGO4EFjv6tN9c+oBbB9Y/RHq8XGG8GSV4c4kQ840FZ+3O+YIFdIX3WQ8lu99XumlX7gZp56bIYGJwfZ5o8Jn+swKFSAeOPmrs0FIDEFRBZXfOIc48e86A3rhlLIj2kPo2A== X-OriginatorOrg: chipsnmedia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9fd777ec-0fb2-4e8d-3758-08dd81807b83 X-MS-Exchange-CrossTenant-AuthSource: SL2P216MB1246.KORP216.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Apr 2025 09:31:38.9912 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 4d70c8e9-142b-4389-b7f2-fa8a3c68c467 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: e2rtLDlWe+SAKvI7nSLU8bjs6lA7neD0kh5WbGbafE5aquYSTNl7URU4A+68WzphK+ANkLAO+1am7S3S6aHT21E7DZLkFgLh1zd6tMTJF9I= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SE2P216MB1521 This adds v4l2 m2m stateful driver support for the Chips&Media Wave6 video codec IP, including both encoder and decoder. The following features and formats are supported: Encoder (8bit only): - Supported codecs: AVC, HEVC - Supported input formats: * V4L2_PIX_FMT_YUV420/NV12/NV21 * V4L2_PIX_FMT_YUV422P/NV16/NV61 * V4L2_PIX_FMT_YUYV * V4L2_PIX_FMT_YUV24/NV24/NV42 * V4L2_PIX_FMT_RGB24 * V4L2_PIX_FMT_P010 * V4L2_PIX_FMT_ARGB32 * V4L2_PIX_FMT_RGBA32 * V4L2_PIX_FMT_XRGB32 * V4L2_PIX_FMT_ARGB2101010 - Supports multiplanar format - Although 10-bit input formats are supported (e.g., P010, ARGB2101010), encoded output is limited to 8-bit streams - Supports crop - Supports extensive v4l2 encoder control Decoder (8bit only): - Supported codecs: AVC, HEVC (8bit only) - Supported output formats: V4L2_PIX_FMT_YUV420/NV12/NV21 - Supports multiplanar format - Supports scale down - Supports dynamic resolution change Note: While other versions of the Wave6 IP may support VP9 decoding and AV1 decoding and encoding those formats are not implemented or validated in this driver at this time. Signed-off-by: Nas Chung Tested-by: Ming Qian --- .../chips-media/wave6/wave6-vpu-dec.c | 1854 +++++++++++ .../chips-media/wave6/wave6-vpu-enc.c | 2699 +++++++++++++++++ .../chips-media/wave6/wave6-vpu-v4l2.c | 387 +++ 3 files changed, 4940 insertions(+) create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c new file mode 100644 index 000000000000..5fdb3378d0b6 --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c @@ -0,0 +1,1854 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - v4l2 stateful decoder interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#include +#include +#include "wave6-vpu-core.h" +#include "wave6-vpu-dbg.h" +#include "wave6-trace.h" + +#define VPU_DEC_DEV_NAME "C&M Wave6 VPU decoder" +#define VPU_DEC_DRV_NAME "wave6-dec" + +static const struct vpu_format wave6_vpu_dec_fmt_list[2][6] = { + [VPU_FMT_TYPE_CODEC] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 1, + }, + }, + [VPU_FMT_TYPE_RAW] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 3, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 2, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, + .max_width = W6_MAX_DEC_PIC_WIDTH, + .min_width = W6_MIN_DEC_PIC_WIDTH, + .max_height = W6_MAX_DEC_PIC_HEIGHT, + .min_height = W6_MIN_DEC_PIC_HEIGHT, + .num_planes = 2, + }, + } +}; + +static int wave6_vpu_dec_seek_header(struct vpu_instance *inst); + +static const struct vpu_format *wave6_find_vpu_fmt(unsigned int v4l2_pix_fmt, + enum vpu_fmt_type type) +{ + unsigned int index; + + for (index = 0; index < ARRAY_SIZE(wave6_vpu_dec_fmt_list[type]); index++) { + if (wave6_vpu_dec_fmt_list[type][index].v4l2_pix_fmt == v4l2_pix_fmt) + return &wave6_vpu_dec_fmt_list[type][index]; + } + + return NULL; +} + +static const struct vpu_format *wave6_find_vpu_fmt_by_idx(unsigned int idx, + enum vpu_fmt_type type) +{ + if (idx >= ARRAY_SIZE(wave6_vpu_dec_fmt_list[type])) + return NULL; + + if (!wave6_vpu_dec_fmt_list[type][idx].v4l2_pix_fmt) + return NULL; + + return &wave6_vpu_dec_fmt_list[type][idx]; +} + +static void wave6_vpu_dec_release_fb(struct vpu_instance *inst) +{ + int i; + + for (i = 0; i < WAVE6_MAX_FBS; i++) { + wave6_free_dma(&inst->frame_vbuf[i]); + memset(&inst->frame_buf[i], 0, sizeof(struct frame_buffer)); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_FBC_Y_TBL][i]); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_FBC_C_TBL][i]); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_MV_COL][i]); + } +} + +static void wave6_vpu_dec_destroy_instance(struct vpu_instance *inst) +{ + u32 fail_res; + int ret; + + wave6_vpu_remove_dbgfs_file(inst); + + ret = wave6_vpu_dec_close(inst, &fail_res); + if (ret) { + dev_err(inst->dev->dev, "failed destroy instance: %d (%d)\n", + ret, fail_res); + } + + wave6_vpu_dec_release_fb(inst); + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_NONE); + + if (!pm_runtime_suspended(inst->dev->dev)) + pm_runtime_put_sync(inst->dev->dev); +} + +static void wave6_handle_bitstream_buffer(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *src_buf; + u32 src_size = 0; + int ret; + + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); + if (src_buf) { + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(src_buf); + dma_addr_t rd_ptr = wave6_get_dma_addr(src_buf, 0); + + if (vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "%s: Already consumed buffer\n", + __func__); + return; + } + + vpu_buf->ts_start = ktime_get_raw(); + vpu_buf->consumed = true; + wave6_vpu_dec_set_rd_ptr(inst, rd_ptr, true); + + src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + } + + if (!src_size) { + dma_addr_t rd = 0, wr = 0; + + wave6_vpu_dec_get_bitstream_buffer(inst, &rd, &wr); + wave6_vpu_dec_set_rd_ptr(inst, wr, true); + } + + trace_dec_pic(inst, src_buf ? src_buf->vb2_buf.index : -1, src_size); + + ret = wave6_vpu_dec_update_bitstream_buffer(inst, src_size); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Update bitstream buffer fail %d\n", + __func__, ret); + return; + } +} + +static void wave6_update_pix_fmt_cap(struct v4l2_pix_format_mplane *pix_mp, + unsigned int width, + unsigned int height, + bool new_resolution) +{ + unsigned int aligned_width; + + if (new_resolution) + pix_mp->plane_fmt[0].bytesperline = 0; + + aligned_width = round_up(width, W6_DEC_BUF_ALIGNMENT); + wave6_update_pix_fmt(pix_mp, aligned_width, height); +} + +static int wave6_allocate_aux_buffer(struct vpu_instance *inst, + enum aux_buffer_type type, + int num) +{ + struct aux_buffer buf[WAVE6_MAX_FBS]; + struct aux_buffer_info buf_info; + struct dec_aux_buffer_size_info size_info; + unsigned int size; + int i, ret; + + memset(buf, 0, sizeof(buf)); + + size_info.width = inst->src_fmt.width; + size_info.height = inst->src_fmt.height; + size_info.type = type; + + ret = wave6_vpu_dec_get_aux_buffer_size(inst, size_info, &size); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Get size fail (type %d)\n", __func__, type); + return ret; + } + + num = min_t(u32, num, WAVE6_MAX_FBS); + for (i = 0; i < num; i++) { + inst->aux_vbuf[type][i].size = size; + ret = wave6_alloc_dma(inst->dev->dev, &inst->aux_vbuf[type][i]); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Alloc fail (type %d)\n", __func__, type); + return ret; + } + + buf[i].index = i; + buf[i].addr = inst->aux_vbuf[type][i].daddr; + buf[i].size = inst->aux_vbuf[type][i].size; + } + + buf_info.type = type; + buf_info.num = num; + buf_info.buf_array = buf; + + ret = wave6_vpu_dec_register_aux_buffer(inst, buf_info); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Register fail (type %d)\n", __func__, type); + return ret; + } + + return 0; +} + +static void wave6_vpu_dec_handle_dst_buffer(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *dst_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vpu_buffer *vpu_buf; + u32 fb_stride = inst->dst_fmt.plane_fmt[0].bytesperline; + u32 luma_size = fb_stride * inst->dst_fmt.height; + u32 chroma_size = (fb_stride / 2) * (inst->dst_fmt.height / 2); + struct frame_buffer disp_buffer = {0}; + struct dec_initial_info initial_info = {0}; + int consumed_num = wave6_vpu_get_consumed_fb_num(inst); + int ret; + + wave6_vpu_dec_give_command(inst, DEC_GET_SEQ_INFO, &initial_info); + + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + dst_buf = &v4l2_m2m_buf->vb; + vpu_buf = wave6_to_vpu_buf(dst_buf); + + if (vpu_buf->consumed) + continue; + + if (consumed_num >= WAVE6_MAX_FBS) + break; + + if (inst->dst_fmt.num_planes == 1) { + disp_buffer.buf_y = wave6_get_dma_addr(dst_buf, 0); + disp_buffer.buf_cb = disp_buffer.buf_y + luma_size; + disp_buffer.buf_cr = disp_buffer.buf_cb + chroma_size; + } else if (inst->dst_fmt.num_planes == 2) { + disp_buffer.buf_y = wave6_get_dma_addr(dst_buf, 0); + disp_buffer.buf_cb = wave6_get_dma_addr(dst_buf, 1); + disp_buffer.buf_cr = disp_buffer.buf_cb + chroma_size; + } else if (inst->dst_fmt.num_planes == 3) { + disp_buffer.buf_y = wave6_get_dma_addr(dst_buf, 0); + disp_buffer.buf_cb = wave6_get_dma_addr(dst_buf, 1); + disp_buffer.buf_cr = wave6_get_dma_addr(dst_buf, 2); + } + disp_buffer.width = inst->src_fmt.width; + disp_buffer.height = inst->src_fmt.height; + disp_buffer.stride = fb_stride; + disp_buffer.map_type = LINEAR_FRAME_MAP; + disp_buffer.luma_bitdepth = initial_info.luma_bitdepth; + disp_buffer.chroma_bitdepth = initial_info.chroma_bitdepth; + disp_buffer.chroma_format_idc = initial_info.chroma_format_idc; + + ret = wave6_vpu_dec_register_display_buffer_ex(inst, disp_buffer); + if (ret) { + dev_err(inst->dev->dev, "fail register display buffer %d", ret); + break; + } + + vpu_buf->consumed = true; + consumed_num++; + } +} + +static enum v4l2_quantization to_v4l2_quantization(u32 video_full_range_flag) +{ + switch (video_full_range_flag) { + case 0: + return V4L2_QUANTIZATION_LIM_RANGE; + case 1: + return V4L2_QUANTIZATION_FULL_RANGE; + default: + return V4L2_QUANTIZATION_DEFAULT; + } +} + +static enum v4l2_colorspace to_v4l2_colorspace(u32 colour_primaries) +{ + switch (colour_primaries) { + case 1: + return V4L2_COLORSPACE_REC709; + case 4: + return V4L2_COLORSPACE_470_SYSTEM_M; + case 5: + return V4L2_COLORSPACE_470_SYSTEM_BG; + case 6: + return V4L2_COLORSPACE_SMPTE170M; + case 7: + return V4L2_COLORSPACE_SMPTE240M; + case 9: + return V4L2_COLORSPACE_BT2020; + case 11: + return V4L2_COLORSPACE_DCI_P3; + default: + return V4L2_COLORSPACE_DEFAULT; + } +} + +static enum v4l2_xfer_func to_v4l2_xfer_func(u32 transfer_characteristics) +{ + switch (transfer_characteristics) { + case 1: + return V4L2_XFER_FUNC_709; + case 6: + return V4L2_XFER_FUNC_709; + case 7: + return V4L2_XFER_FUNC_SMPTE240M; + case 8: + return V4L2_XFER_FUNC_NONE; + case 13: + return V4L2_XFER_FUNC_SRGB; + case 14: + return V4L2_XFER_FUNC_709; + case 16: + return V4L2_XFER_FUNC_SMPTE2084; + default: + return V4L2_XFER_FUNC_DEFAULT; + } +} + +static enum v4l2_ycbcr_encoding to_v4l2_ycbcr_encoding(u32 matrix_coeffs) +{ + switch (matrix_coeffs) { + case 1: + return V4L2_YCBCR_ENC_709; + case 5: + return V4L2_YCBCR_ENC_601; + case 6: + return V4L2_YCBCR_ENC_601; + case 7: + return V4L2_YCBCR_ENC_SMPTE240M; + case 9: + return V4L2_YCBCR_ENC_BT2020; + case 10: + return V4L2_YCBCR_ENC_BT2020_CONST_LUM; + default: + return V4L2_YCBCR_ENC_DEFAULT; + } +} + +static void wave6_update_color_info(struct vpu_instance *inst, + struct dec_initial_info *initial_info) +{ + struct color_param *color = &initial_info->color; + + if (!color->video_signal_type_present) + goto set_default_all; + + inst->quantization = to_v4l2_quantization(color->color_range); + + if (!color->color_description_present) + goto set_default_color; + + inst->colorspace = to_v4l2_colorspace(color->color_primaries); + inst->xfer_func = to_v4l2_xfer_func(color->transfer_characteristics); + inst->ycbcr_enc = to_v4l2_ycbcr_encoding(color->matrix_coefficients); + + return; + +set_default_all: + inst->quantization = V4L2_QUANTIZATION_DEFAULT; +set_default_color: + inst->colorspace = V4L2_COLORSPACE_DEFAULT; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static enum v4l2_mpeg_video_hevc_profile to_v4l2_hevc_profile(u32 profile) +{ + switch (profile) { + case HEVC_PROFILE_MAIN: + return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN; + default: + return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN; + } +} + +static enum v4l2_mpeg_video_h264_profile to_v4l2_h264_profile(u32 profile) +{ + switch (profile) { + case H264_PROFILE_BP: + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case H264_PROFILE_MP: + return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case H264_PROFILE_EXTENDED: + return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; + case H264_PROFILE_HP: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + default: + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + } +} + +static void wave6_update_v4l2_ctrls(struct vpu_instance *inst, + struct dec_initial_info *info) +{ + struct v4l2_ctrl *ctrl; + u32 min_disp_cnt; + + min_disp_cnt = info->frame_buf_delay + 1; + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, min_disp_cnt); + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_HEVC) { + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, to_v4l2_hevc_profile(info->profile)); + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_H264) { + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MPEG_VIDEO_H264_PROFILE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, to_v4l2_h264_profile(info->profile)); + } +} + +static int wave6_vpu_dec_start_decode(struct vpu_instance *inst) +{ + struct dec_param pic_param; + int ret; + u32 fail_res = 0; + + memset(&pic_param, 0, sizeof(struct dec_param)); + + wave6_handle_bitstream_buffer(inst); + if (inst->state == VPU_INST_STATE_OPEN) { + ret = wave6_vpu_dec_seek_header(inst); + if (ret) { + vb2_queue_error(v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx)); + vb2_queue_error(v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx)); + } + return -EAGAIN; + } + + wave6_vpu_dec_handle_dst_buffer(inst); + + ret = wave6_vpu_dec_start_one_frame(inst, &pic_param, &fail_res); + if (ret) { + struct vb2_v4l2_buffer *src_buf = NULL; + + dev_err(inst->dev->dev, "[%d] %s: fail %d\n", inst->id, __func__, ret); + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_STOP); + + src_buf = v4l2_m2m_src_buf_remove(inst->v4l2_fh.m2m_ctx); + if (src_buf) { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + inst->sequence++; + inst->processed_buf_num++; + inst->error_buf_num++; + } + } + + return ret; +} + +static void wave6_handle_decoded_frame(struct vpu_instance *inst, + struct dec_output_info *info) +{ + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct vpu_buffer *vpu_buf; + enum vb2_buffer_state state; + + state = info->decoding_success ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); + if (!src_buf) { + dev_err(inst->dev->dev, "[%d] decoder can't find src buffer\n", inst->id); + return; + } + + vpu_buf = wave6_to_vpu_buf(src_buf); + if (!vpu_buf || !vpu_buf->consumed) { + dev_err(inst->dev->dev, "[%d] src buffer is not consumed\n", inst->id); + return; + } + + dst_buf = wave6_get_dst_buf_by_addr(inst, info->frame_decoded_addr); + if (dst_buf) { + struct vpu_buffer *dst_vpu_buf = wave6_to_vpu_buf(dst_buf); + + if (wave6_to_vpu_buf(dst_buf)->used) { + dev_warn(inst->dev->dev, "[%d] duplication frame buffer\n", inst->id); + inst->sequence++; + } + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + dst_vpu_buf->used = true; + if (state == VB2_BUF_STATE_ERROR) + dst_vpu_buf->error = true; + dst_vpu_buf->ts_input = vpu_buf->ts_input; + dst_vpu_buf->ts_start = vpu_buf->ts_start; + dst_vpu_buf->ts_finish = ktime_get_raw(); + dst_vpu_buf->hw_time = wave6_vpu_cycle_to_ns(inst->dev, info->cycle.frame_cycle); + } + + v4l2_m2m_src_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, src_buf); + if (state == VB2_BUF_STATE_ERROR) { + dev_dbg(inst->dev->dev, "[%d] error frame %d\n", inst->id, inst->sequence); + inst->error_buf_num++; + } + v4l2_m2m_buf_done(src_buf, state); + inst->processed_buf_num++; +} + +static void wave6_handle_skipped_frame(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *src_buf; + struct vpu_buffer *vpu_buf; + + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); + if (!src_buf) + return; + + vpu_buf = wave6_to_vpu_buf(src_buf); + if (!vpu_buf || !vpu_buf->consumed) + return; + + dev_dbg(inst->dev->dev, "[%d] skip frame %d\n", inst->id, inst->sequence); + + inst->sequence++; + inst->processed_buf_num++; + inst->error_buf_num++; + v4l2_m2m_src_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, src_buf); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); +} + +static void wave6_handle_display_frame(struct vpu_instance *inst, + dma_addr_t addr, enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *dst_buf; + struct vpu_buffer *vpu_buf; + + dst_buf = wave6_get_dst_buf_by_addr(inst, addr); + if (!dst_buf) + return; + + vpu_buf = wave6_to_vpu_buf(dst_buf); + if (!vpu_buf->used) { + dev_dbg(inst->dev->dev, "[%d] recycle display buffer\n", inst->id); + vpu_buf->consumed = false; + return; + } + + if (inst->dst_fmt.num_planes == 1) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + } else if (inst->dst_fmt.num_planes == 2) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, + inst->dst_fmt.plane_fmt[1].sizeimage); + } else if (inst->dst_fmt.num_planes == 3) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, + inst->dst_fmt.plane_fmt[1].sizeimage); + vb2_set_plane_payload(&dst_buf->vb2_buf, 2, + inst->dst_fmt.plane_fmt[2].sizeimage); + } + + vpu_buf->ts_output = ktime_get_raw(); + wave6_vpu_handle_performance(inst, vpu_buf); + + if (vpu_buf->error) + state = VB2_BUF_STATE_ERROR; + dst_buf->sequence = inst->sequence++; + dst_buf->field = V4L2_FIELD_NONE; + if (state == VB2_BUF_STATE_ERROR) + dev_dbg(inst->dev->dev, "[%d] discard frame %d\n", inst->id, dst_buf->sequence); + v4l2_m2m_dst_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, dst_buf); + v4l2_m2m_buf_done(dst_buf, state); +} + +static void wave6_handle_display_frames(struct vpu_instance *inst, + struct dec_output_info *info) +{ + int i; + + for (i = 0; i < info->disp_frame_num; i++) + wave6_handle_display_frame(inst, + info->disp_frame_addr[i], + VB2_BUF_STATE_DONE); +} + +static void wave6_handle_discard_frames(struct vpu_instance *inst, + struct dec_output_info *info) +{ + int i; + + for (i = 0; i < info->release_disp_frame_num; i++) + wave6_handle_display_frame(inst, + info->release_disp_frame_addr[i], + VB2_BUF_STATE_ERROR); +} + +static void wave6_handle_last_frame(struct vpu_instance *inst, + struct vb2_v4l2_buffer *dst_buf) +{ + if (!dst_buf) { + dst_buf = v4l2_m2m_dst_buf_remove(inst->v4l2_fh.m2m_ctx); + if (!dst_buf) { + inst->next_buf_last = true; + return; + } + } + + if (inst->dst_fmt.num_planes == 1) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + } else if (inst->dst_fmt.num_planes == 2) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + } else if (inst->dst_fmt.num_planes == 3) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + vb2_set_plane_payload(&dst_buf->vb2_buf, 2, 0); + } + + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + dst_buf->field = V4L2_FIELD_NONE; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + if (inst->state != VPU_INST_STATE_INIT_SEQ) { + dev_dbg(inst->dev->dev, "[%d] eos\n", inst->id); + inst->eos = true; + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, false); + } +} + +static void wave6_vpu_dec_retry_one_frame(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *src_buf; + struct vpu_buffer *vpu_buf; + + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); + if (!src_buf) + return; + + vpu_buf = wave6_to_vpu_buf(src_buf); + vpu_buf->consumed = false; +} + +static void wave6_vpu_dec_handle_source_change(struct vpu_instance *inst, + struct dec_initial_info *info) +{ + static const struct v4l2_event vpu_event_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + dev_dbg(inst->dev->dev, "pic size %dx%d profile %d, min_fb_cnt : %d | min_disp_cnt : %d\n", + info->pic_width, info->pic_height, + info->profile, info->min_frame_buffer_count, info->frame_buf_delay); + + wave6_vpu_dec_retry_one_frame(inst); + wave6_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, NULL); + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); + + inst->crop.left = info->pic_crop_rect.left; + inst->crop.top = info->pic_crop_rect.top; + inst->crop.width = info->pic_crop_rect.right - inst->crop.left; + inst->crop.height = info->pic_crop_rect.bottom - inst->crop.top; + + wave6_update_v4l2_ctrls(inst, info); + wave6_update_color_info(inst, info); + wave6_update_pix_fmt(&inst->src_fmt, info->pic_width, info->pic_height); + wave6_update_pix_fmt_cap(&inst->dst_fmt, + info->pic_width, info->pic_height, + true); + + trace_source_change(inst, info); + + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_src_ch); +} + +static void wave6_vpu_dec_handle_decoding_warn_error(struct vpu_instance *inst, + struct dec_output_info *info) +{ + if (info->warn_info) + dev_dbg(inst->dev->dev, "[%d] decoding %d warning 0x%x\n", + inst->id, inst->processed_buf_num, info->warn_info); + + if (info->error_reason) + dev_err(inst->dev->dev, "[%d] decoding %d error 0x%x\n", + inst->id, inst->processed_buf_num, info->error_reason); +} + +static void wave6_vpu_dec_finish_decode(struct vpu_instance *inst, bool error) +{ + struct dec_output_info info; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + + ret = wave6_vpu_dec_get_output_info(inst, &info); + if (ret) + goto finish_decode; + + trace_dec_done(inst, &info); + + dev_dbg(inst->dev->dev, "dec %d dis %d noti_flag %d stream_end %d\n", + info.frame_decoded, info.frame_display, + info.notification_flags, info.stream_end); + + if (info.notification_flags & DEC_NOTI_FLAG_NO_FB) { + wave6_vpu_dec_retry_one_frame(inst); + goto finish_decode; + } + + if (info.notification_flags & DEC_NOTI_FLAG_SEQ_CHANGE) { + struct dec_initial_info initial_info = {0}; + + v4l2_m2m_mark_stopped(m2m_ctx); + + if (info.frame_display) + wave6_handle_display_frames(inst, &info); + + if (info.release_disp_frame_num) + wave6_handle_discard_frames(inst, &info); + + wave6_vpu_dec_give_command(inst, DEC_GET_SEQ_INFO, &initial_info); + wave6_vpu_dec_handle_source_change(inst, &initial_info); + + wave6_handle_last_frame(inst, NULL); + + goto finish_decode; + } + + wave6_vpu_dec_handle_decoding_warn_error(inst, &info); + + if (info.frame_decoded) + wave6_handle_decoded_frame(inst, &info); + else + wave6_handle_skipped_frame(inst); + + if (info.frame_display) + wave6_handle_display_frames(inst, &info); + + if (info.release_disp_frame_num) + wave6_handle_discard_frames(inst, &info); + + if (info.stream_end && !inst->eos) + wave6_handle_last_frame(inst, NULL); + +finish_decode: + wave6_vpu_finish_job(inst); +} + +static int wave6_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card)); + strscpy(cap->bus_info, "platform:" VPU_DEC_DRV_NAME, sizeof(cap->bus_info)); + + return 0; +} + +static int wave6_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize) +{ + const struct vpu_format *vpu_fmt; + + if (fsize->index) + return -EINVAL; + + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, VPU_FMT_TYPE_CODEC); + if (!vpu_fmt) { + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, VPU_FMT_TYPE_RAW); + if (!vpu_fmt) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = vpu_fmt->min_width; + fsize->stepwise.max_width = vpu_fmt->max_width; + fsize->stepwise.step_width = W6_DEC_PIC_SIZE_STEP; + fsize->stepwise.min_height = vpu_fmt->min_height; + fsize->stepwise.max_height = vpu_fmt->max_height; + fsize->stepwise.step_height = W6_DEC_PIC_SIZE_STEP; + + return 0; +} + +static int wave6_vpu_dec_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + const struct vpu_format *vpu_fmt; + + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, VPU_FMT_TYPE_RAW); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave6_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct vpu_format *vpu_fmt; + int width, height; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + if (!V4L2_TYPE_IS_CAPTURE(f->type)) + return -EINVAL; + + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, VPU_FMT_TYPE_RAW); + if (!vpu_fmt) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + pix_mp->pixelformat = inst->dst_fmt.pixelformat; + pix_mp->num_planes = inst->dst_fmt.num_planes; + } else { + width = clamp(pix_mp->width, + vpu_fmt->min_width, + round_up(inst->src_fmt.width, W6_DEC_BUF_ALIGNMENT)); + height = clamp(pix_mp->height, + vpu_fmt->min_height, inst->src_fmt.height); + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; + pix_mp->num_planes = vpu_fmt->num_planes; + } + + if (inst->state >= VPU_INST_STATE_INIT_SEQ) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + } + + wave6_update_pix_fmt_cap(pix_mp, width, height, false); + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_dec_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i, ret; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + ret = wave6_vpu_dec_try_fmt_cap(file, fh, f); + if (ret) + return ret; + + inst->dst_fmt.width = pix_mp->width; + inst->dst_fmt.height = pix_mp->height; + inst->dst_fmt.pixelformat = pix_mp->pixelformat; + inst->dst_fmt.field = pix_mp->field; + inst->dst_fmt.flags = pix_mp->flags; + inst->dst_fmt.num_planes = pix_mp->num_planes; + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + inst->dst_fmt.plane_fmt[i].bytesperline = pix_mp->plane_fmt[i].bytesperline; + inst->dst_fmt.plane_fmt[i].sizeimage = pix_mp->plane_fmt[i].sizeimage; + } + + if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + } else { + inst->cbcr_interleave = false; + inst->nv21 = false; + } + + return 0; +} + +static int wave6_vpu_dec_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i; + + pix_mp->width = inst->dst_fmt.width; + pix_mp->height = inst->dst_fmt.height; + pix_mp->pixelformat = inst->dst_fmt.pixelformat; + pix_mp->field = inst->dst_fmt.field; + pix_mp->flags = inst->dst_fmt.flags; + pix_mp->num_planes = inst->dst_fmt.num_planes; + for (i = 0; i < pix_mp->num_planes; i++) { + pix_mp->plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline; + pix_mp->plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage; + } + + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index %d\n", __func__, f->index); + + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, VPU_FMT_TYPE_CODEC); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int wave6_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct vpu_format *vpu_fmt; + int width, height; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + if (!V4L2_TYPE_IS_OUTPUT(f->type)) + return -EINVAL; + + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, VPU_FMT_TYPE_CODEC); + if (!vpu_fmt) { + width = inst->src_fmt.width; + height = inst->src_fmt.height; + pix_mp->pixelformat = inst->src_fmt.pixelformat; + pix_mp->num_planes = inst->src_fmt.num_planes; + } else { + width = pix_mp->width; + height = pix_mp->height; + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; + pix_mp->num_planes = vpu_fmt->num_planes; + } + + wave6_update_pix_fmt(pix_mp, width, height); + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane in_pix_mp = f->fmt.pix_mp; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i, ret; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + ret = wave6_vpu_dec_try_fmt_out(file, fh, f); + if (ret) + return ret; + + pix_mp->colorspace = in_pix_mp.colorspace; + pix_mp->ycbcr_enc = in_pix_mp.ycbcr_enc; + pix_mp->quantization = in_pix_mp.quantization; + pix_mp->xfer_func = in_pix_mp.xfer_func; + + inst->src_fmt.width = pix_mp->width; + inst->src_fmt.height = pix_mp->height; + inst->src_fmt.pixelformat = pix_mp->pixelformat; + inst->src_fmt.field = pix_mp->field; + inst->src_fmt.flags = pix_mp->flags; + inst->src_fmt.num_planes = pix_mp->num_planes; + for (i = 0; i < inst->src_fmt.num_planes; i++) { + inst->src_fmt.plane_fmt[i].bytesperline = pix_mp->plane_fmt[i].bytesperline; + inst->src_fmt.plane_fmt[i].sizeimage = pix_mp->plane_fmt[i].sizeimage; + } + + inst->colorspace = pix_mp->colorspace; + inst->ycbcr_enc = pix_mp->ycbcr_enc; + inst->quantization = pix_mp->quantization; + inst->xfer_func = pix_mp->xfer_func; + + wave6_update_pix_fmt_cap(&inst->dst_fmt, + pix_mp->width, pix_mp->height, + true); + + return 0; +} + +static int wave6_vpu_dec_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i; + + pix_mp->width = inst->src_fmt.width; + pix_mp->height = inst->src_fmt.height; + pix_mp->pixelformat = inst->src_fmt.pixelformat; + pix_mp->field = inst->src_fmt.field; + pix_mp->flags = inst->src_fmt.flags; + pix_mp->num_planes = inst->src_fmt.num_planes; + for (i = 0; i < pix_mp->num_planes; i++) { + pix_mp->plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline; + pix_mp->plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage; + } + + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_dec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type %d target %d\n", + __func__, s->type, s->target); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + break; + case V4L2_SEL_TGT_COMPOSE_PADDED: + case V4L2_SEL_TGT_COMPOSE: + s->r.left = 0; + s->r.top = 0; + if (inst->scaler_info.enable) { + s->r.width = inst->scaler_info.width; + s->r.height = inst->scaler_info.height; + } else if (inst->crop.width && inst->crop.height) { + s->r = inst->crop; + } else { + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + } + break; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + if (inst->crop.width && inst->crop.height) + s->r = inst->crop; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave6_vpu_dec_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + int step = 4; + int scale_width, scale_height; + int min_scale_width, min_scale_height; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + if (!(s->flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE))) + s->flags |= V4L2_SEL_FLAG_LE; + + scale_width = clamp(s->r.width, W6_MIN_DEC_PIC_WIDTH, + round_up(inst->src_fmt.width, W6_DEC_BUF_ALIGNMENT)); + scale_height = clamp(s->r.height, W6_MIN_DEC_PIC_HEIGHT, + inst->src_fmt.height); + if (s->flags & V4L2_SEL_FLAG_GE) { + scale_width = round_up(scale_width, step); + scale_height = round_up(scale_height, step); + } + if (s->flags & V4L2_SEL_FLAG_LE) { + scale_width = round_down(scale_width, step); + scale_height = round_down(scale_height, step); + } + + if (scale_width < inst->src_fmt.width || + scale_height < inst->src_fmt.height) + inst->scaler_info.enable = true; + + if (inst->scaler_info.enable) { + min_scale_width = ALIGN((inst->src_fmt.width / 8), step); + min_scale_height = ALIGN((inst->src_fmt.height / 8), step); + + if (scale_width < W6_MIN_DEC_PIC_WIDTH) + scale_width = W6_MIN_DEC_PIC_WIDTH; + if (scale_width < min_scale_width) + scale_width = min_scale_width; + if (scale_height < W6_MIN_DEC_PIC_HEIGHT) + scale_height = W6_MIN_DEC_PIC_HEIGHT; + if (scale_height < min_scale_height) + scale_height = min_scale_height; + + inst->scaler_info.width = scale_width; + inst->scaler_info.height = scale_height; + } + + s->r.left = 0; + s->r.top = 0; + s->r.width = scale_width; + s->r.height = scale_height; + + return 0; +} + +static int wave6_vpu_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + int ret; + + dev_dbg(inst->dev->dev, "%s: cmd %d\n", __func__, dc->cmd); + + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); + if (ret) + return ret; + + switch (dc->cmd) { + case V4L2_DEC_CMD_STOP: + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true); + v4l2_m2m_try_schedule(inst->v4l2_fh.m2m_ctx); + break; + case V4L2_DEC_CMD_START: + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ioctl_ops wave6_vpu_dec_ioctl_ops = { + .vidioc_querycap = wave6_vpu_dec_querycap, + .vidioc_enum_framesizes = wave6_vpu_dec_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = wave6_vpu_dec_enum_fmt_cap, + .vidioc_s_fmt_vid_cap_mplane = wave6_vpu_dec_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = wave6_vpu_dec_g_fmt_cap, + .vidioc_try_fmt_vid_cap_mplane = wave6_vpu_dec_try_fmt_cap, + + .vidioc_enum_fmt_vid_out = wave6_vpu_dec_enum_fmt_out, + .vidioc_s_fmt_vid_out_mplane = wave6_vpu_dec_s_fmt_out, + .vidioc_g_fmt_vid_out_mplane = wave6_vpu_dec_g_fmt_out, + .vidioc_try_fmt_vid_out_mplane = wave6_vpu_dec_try_fmt_out, + + .vidioc_g_selection = wave6_vpu_dec_g_selection, + .vidioc_s_selection = wave6_vpu_dec_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, + .vidioc_decoder_cmd = wave6_vpu_dec_decoder_cmd, + + .vidioc_subscribe_event = wave6_vpu_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int wave6_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_instance *inst = wave6_ctrl_to_vpu_inst(ctrl); + + trace_s_ctrl(inst, ctrl); + + dev_dbg(inst->dev->dev, "%s: name %s value %d\n", + __func__, ctrl->name, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: + inst->disp_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops wave6_vpu_dec_ctrl_ops = { + .s_ctrl = wave6_vpu_dec_s_ctrl, +}; + +static void wave6_set_dec_openparam(struct dec_open_param *open_param, + struct vpu_instance *inst) +{ + open_param->inst_buffer.temp_base = inst->dev->temp_vbuf.daddr; + open_param->inst_buffer.temp_size = inst->dev->temp_vbuf.size; + open_param->bs_mode = BS_MODE_PIC_END; + open_param->stream_endian = VPU_STREAM_ENDIAN; + open_param->frame_endian = VPU_FRAME_ENDIAN; + open_param->disp_mode = inst->disp_mode; +} + +static int wave6_vpu_dec_create_instance(struct vpu_instance *inst) +{ + int ret; + struct dec_open_param open_param; + + memset(&open_param, 0, sizeof(struct dec_open_param)); + + wave6_vpu_activate(inst->dev); + ret = pm_runtime_resume_and_get(inst->dev->dev); + if (ret) { + dev_err(inst->dev->dev, "runtime_resume failed %d\n", ret); + return ret; + } + + inst->std = wave6_to_codec_std(inst->type, inst->src_fmt.pixelformat); + if (inst->std == STD_UNKNOWN) { + dev_err(inst->dev->dev, "unsupported pixelformat: %.4s\n", + (char *)&inst->src_fmt.pixelformat); + ret = -EINVAL; + goto error_pm; + } + + wave6_set_dec_openparam(&open_param, inst); + + ret = wave6_vpu_dec_open(inst, &open_param); + if (ret) { + dev_err(inst->dev->dev, "failed create instance : %d\n", ret); + goto error_pm; + } + + dev_dbg(inst->dev->dev, "[%d] decoder\n", inst->id); + + wave6_vpu_create_dbgfs_file(inst); + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_OPEN); + inst->v4l2_fh.m2m_ctx->ignore_cap_streaming = true; + v4l2_m2m_set_dst_buffered(inst->v4l2_fh.m2m_ctx, true); + + return 0; + +error_pm: + pm_runtime_put_sync(inst->dev->dev); + + return ret; +} + +static int wave6_vpu_dec_prepare_fb(struct vpu_instance *inst) +{ + int ret; + unsigned int i; + unsigned int fb_num; + unsigned int mv_num; + unsigned int fb_stride; + unsigned int fb_height; + unsigned int luma_size; + unsigned int chroma_size; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + fb_num = p_dec_info->initial_info.min_frame_buffer_count; + mv_num = p_dec_info->initial_info.req_mv_buffer_count; + + fb_stride = ALIGN(inst->src_fmt.width, W6_FBC_BUF_ALIGNMENT); + fb_height = ALIGN(inst->src_fmt.height, W6_FBC_BUF_ALIGNMENT); + + luma_size = fb_stride * fb_height; + chroma_size = ALIGN(fb_stride / 2, W6_FBC_BUF_ALIGNMENT) * fb_height; + + for (i = 0; i < fb_num; i++) { + struct frame_buffer *frame = &inst->frame_buf[i]; + struct vpu_buf *vframe = &inst->frame_vbuf[i]; + + vframe->size = luma_size + chroma_size; + ret = wave6_alloc_dma(inst->dev->dev, vframe); + if (ret) { + dev_err(inst->dev->dev, "alloc FBC buffer fail : %zu\n", + vframe->size); + goto error; + } + + frame->buf_y = vframe->daddr; + frame->buf_cb = vframe->daddr + luma_size; + frame->buf_cr = (dma_addr_t)-1; + frame->width = inst->src_fmt.width; + frame->stride = fb_stride; + frame->height = fb_height; + frame->map_type = COMPRESSED_FRAME_MAP; + } + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_Y_TBL, fb_num); + if (ret) + goto error; + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_C_TBL, fb_num); + if (ret) + goto error; + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_MV_COL, mv_num); + if (ret) + goto error; + + ret = wave6_vpu_dec_register_frame_buffer_ex(inst, fb_num, fb_stride, + fb_height, + COMPRESSED_FRAME_MAP); + if (ret) { + dev_err(inst->dev->dev, "register frame buffer fail %d\n", ret); + goto error; + } + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_PIC_RUN); + + return 0; + +error: + wave6_vpu_dec_release_fb(inst); + return ret; +} + +static int wave6_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane inst_format = + (V4L2_TYPE_IS_OUTPUT(q->type)) ? inst->src_fmt : inst->dst_fmt; + unsigned int i; + + dev_dbg(inst->dev->dev, "%s: num_buffers %d num_planes %d type %d\n", + __func__, *num_buffers, *num_planes, q->type); + + if (*num_planes) { + if (inst_format.num_planes != *num_planes) + return -EINVAL; + + for (i = 0; i < *num_planes; i++) { + if (sizes[i] < inst_format.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = inst_format.num_planes; + for (i = 0; i < *num_planes; i++) { + sizes[i] = inst_format.plane_fmt[i].sizeimage; + dev_dbg(inst->dev->dev, "size[%d] : %d\n", i, sizes[i]); + } + + if (V4L2_TYPE_IS_CAPTURE(q->type)) { + struct v4l2_ctrl *ctrl; + unsigned int min_disp_cnt = 0; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + min_disp_cnt = v4l2_ctrl_g_ctrl(ctrl); + + *num_buffers = max(*num_buffers, min_disp_cnt); + + if (*num_buffers > WAVE6_MAX_FBS) + *num_buffers = min_disp_cnt; + } + } + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + inst->state == VPU_INST_STATE_SEEK) { + wave6_vpu_pause(inst->dev->dev, 0); + wave6_vpu_dec_destroy_instance(inst); + wave6_vpu_pause(inst->dev->dev, 1); + } + + return 0; +} + +static int wave6_vpu_dec_seek_header(struct vpu_instance *inst) +{ + struct dec_initial_info initial_info; + int ret; + + memset(&initial_info, 0, sizeof(struct dec_initial_info)); + + ret = wave6_vpu_dec_issue_seq_init(inst); + if (ret) { + dev_err(inst->dev->dev, "failed wave6_vpu_dec_issue_seq_init %d\n", ret); + return ret; + } + + ret = wave6_vpu_wait_interrupt(inst, W6_VPU_TIMEOUT); + if (ret) { + dev_err(inst->dev->dev, "failed to call vpu_wait_interrupt()\n"); + return ret; + } + + ret = wave6_vpu_dec_complete_seq_init(inst, &initial_info); + if (ret) { + dev_err(inst->dev->dev, "vpu_dec_complete_seq_init: %d, reason : 0x%x\n", + ret, initial_info.err_reason); + if ((initial_info.err_reason & WAVE6_SYSERR_NOT_SUPPORT) || + (initial_info.err_reason & WAVE6_SYSERR_NOT_SUPPORT_PROFILE)) { + ret = -EINVAL; + } else if ((initial_info.err_reason & HEVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND) || + (initial_info.err_reason & AVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND)) { + wave6_handle_skipped_frame(inst); + ret = 0; + } + } else { + wave6_vpu_dec_handle_source_change(inst, &initial_info); + inst->v4l2_fh.m2m_ctx->ignore_cap_streaming = false; + v4l2_m2m_set_dst_buffered(inst->v4l2_fh.m2m_ctx, false); + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx))) + wave6_handle_last_frame(inst, NULL); + } + + return ret; +} + +static void wave6_vpu_dec_buf_queue_src(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(inst->dev->dev, "type %4d index %4d size[0] %4ld size[1] : %4ld | size[2] : %4ld\n", + vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + vbuf->sequence = inst->queued_src_buf_num++; + vpu_buf->ts_input = ktime_get_raw(); + + v4l2_m2m_buf_queue(inst->v4l2_fh.m2m_ctx, vbuf); +} + +static void wave6_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(inst->dev->dev, "type %4d index %4d size[0] %4ld size[1] : %4ld | size[2] : %4ld\n", + vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + inst->queued_dst_buf_num++; + if (inst->next_buf_last) { + wave6_handle_last_frame(inst, vbuf); + inst->next_buf_last = false; + } else { + v4l2_m2m_buf_queue(inst->v4l2_fh.m2m_ctx, vbuf); + } +} + +static void wave6_vpu_dec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); + + vpu_buf->consumed = false; + vpu_buf->used = false; + vpu_buf->error = false; + if (V4L2_TYPE_IS_OUTPUT(vb->type)) + wave6_vpu_dec_buf_queue_src(vb); + else + wave6_vpu_dec_buf_queue_dst(vb); +} + +static int wave6_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane *fmt; + int ret = 0; + + trace_start_streaming(inst, q->type); + + wave6_vpu_pause(inst->dev->dev, 0); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + fmt = &inst->src_fmt; + if (inst->state == VPU_INST_STATE_NONE) { + ret = wave6_vpu_dec_create_instance(inst); + if (ret) + goto exit; + } + + if (inst->state == VPU_INST_STATE_SEEK) + wave6_vpu_set_instance_state(inst, inst->state_in_seek); + } else { + fmt = &inst->dst_fmt; + if (inst->state == VPU_INST_STATE_INIT_SEQ) { + ret = wave6_vpu_dec_prepare_fb(inst); + if (ret) + goto exit; + } + } + +exit: + wave6_vpu_pause(inst->dev->dev, 1); + if (ret) + wave6_vpu_return_buffers(inst, q->type, VB2_BUF_STATE_QUEUED); + + dev_dbg(inst->dev->dev, "[%d] %s %c%c%c%c %dx%d, %d buffers, ret = %d\n", + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture", + fmt->pixelformat, + fmt->pixelformat >> 8, + fmt->pixelformat >> 16, + fmt->pixelformat >> 24, + fmt->width, fmt->height, vb2_get_num_buffers(q), ret); + + return ret; +} + +static void wave6_vpu_dec_stop_streaming(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + trace_stop_streaming(inst, q->type); + + dev_dbg(inst->dev->dev, "[%d] %s, input %d, decode %d error %d\n", + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture", + inst->queued_src_buf_num, inst->processed_buf_num, inst->error_buf_num); + + if (inst->state == VPU_INST_STATE_NONE) + goto exit; + + wave6_vpu_pause(inst->dev->dev, 0); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + wave6_vpu_reset_performance(inst); + inst->queued_src_buf_num = 0; + inst->processed_buf_num = 0; + inst->error_buf_num = 0; + inst->state_in_seek = inst->state; + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, false); + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_SEEK); + inst->sequence = 0; + } else { + if (v4l2_m2m_has_stopped(m2m_ctx)) + v4l2_m2m_clear_state(m2m_ctx); + + inst->eos = false; + inst->queued_dst_buf_num = 0; + inst->sequence = 0; + wave6_vpu_dec_flush_instance(inst); + } + + wave6_vpu_pause(inst->dev->dev, 1); + +exit: + wave6_vpu_return_buffers(inst, q->type, VB2_BUF_STATE_ERROR); +} + +static int wave6_vpu_dec_buf_init(struct vb2_buffer *vb) +{ + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct dec_initial_info initial_info = {0}; + int i; + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) + return 0; + + wave6_vpu_dec_give_command(inst, DEC_GET_SEQ_INFO, &initial_info); + if (initial_info.chroma_format_idc != YUV400) + return 0; + + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + void *vaddr = vb2_plane_vaddr(vb, i); + + if (vaddr) + memset(vaddr, 0x80, vb2_plane_size(vb, i)); + } + + return 0; +} + +static const struct vb2_ops wave6_vpu_dec_vb2_ops = { + .queue_setup = wave6_vpu_dec_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = wave6_vpu_dec_buf_queue, + .start_streaming = wave6_vpu_dec_start_streaming, + .stop_streaming = wave6_vpu_dec_stop_streaming, + .buf_init = wave6_vpu_dec_buf_init, +}; + +static void wave6_set_default_format(struct v4l2_pix_format_mplane *src_fmt, + struct v4l2_pix_format_mplane *dst_fmt) +{ + const struct vpu_format *vpu_fmt; + + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_CODEC); + if (vpu_fmt) { + src_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; + src_fmt->num_planes = vpu_fmt->num_planes; + wave6_update_pix_fmt(src_fmt, + W6_DEF_DEC_PIC_WIDTH, W6_DEF_DEC_PIC_HEIGHT); + } + + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_RAW); + if (vpu_fmt) { + dst_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; + dst_fmt->num_planes = vpu_fmt->num_planes; + wave6_update_pix_fmt_cap(dst_fmt, + W6_DEF_DEC_PIC_WIDTH, W6_DEF_DEC_PIC_HEIGHT, + true); + } +} + +static int wave6_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct vpu_instance *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->ops = &wave6_vpu_dec_vb2_ops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->buf_struct_size = sizeof(struct vpu_buffer); + src_vq->min_queued_buffers = 1; + src_vq->drv_priv = inst; + src_vq->lock = &inst->dev->dev_lock; + src_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->ops = &wave6_vpu_dec_vb2_ops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->buf_struct_size = sizeof(struct vpu_buffer); + dst_vq->min_queued_buffers = 1; + dst_vq->drv_priv = inst; + dst_vq->lock = &inst->dev->dev_lock; + dst_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +static const struct vpu_instance_ops wave6_vpu_dec_inst_ops = { + .start_process = wave6_vpu_dec_start_decode, + .finish_process = wave6_vpu_dec_finish_decode, +}; + +static int wave6_vpu_open_dec(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct vpu_core_device *dev = video_drvdata(filp); + struct vpu_instance *inst = NULL; + int ret; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->dev = dev; + inst->type = VPU_INST_TYPE_DEC; + inst->ops = &wave6_vpu_dec_inst_ops; + + v4l2_fh_init(&inst->v4l2_fh, vdev); + filp->private_data = &inst->v4l2_fh; + v4l2_fh_add(&inst->v4l2_fh); + + inst->v4l2_fh.m2m_ctx = + v4l2_m2m_ctx_init(dev->m2m_dev, inst, wave6_vpu_dec_queue_init); + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); + goto free_inst; + } + + v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10); + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, &wave6_vpu_dec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1); + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, &wave6_vpu_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY, + 0, 0, 1, 0); + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, &wave6_vpu_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std_menu(&inst->v4l2_ctrl_hdl, &wave6_vpu_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + v4l2_ctrl_new_std_menu(&inst->v4l2_ctrl_hdl, &wave6_vpu_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + + if (inst->v4l2_ctrl_hdl.error) { + ret = -ENODEV; + goto err_m2m_release; + } + + inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl; + v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl); + + wave6_set_default_format(&inst->src_fmt, &inst->dst_fmt); + inst->colorspace = V4L2_COLORSPACE_DEFAULT; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->quantization = V4L2_QUANTIZATION_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; + +err_m2m_release: + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); +free_inst: + kfree(inst); + return ret; +} + +static int wave6_vpu_dec_release(struct file *filp) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(filp->private_data); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + + mutex_lock(&inst->dev->dev_lock); + if (inst->state != VPU_INST_STATE_NONE) { + wave6_vpu_pause(inst->dev->dev, 0); + wave6_vpu_dec_destroy_instance(inst); + wave6_vpu_pause(inst->dev->dev, 1); + } + mutex_unlock(&inst->dev->dev_lock); + + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl); + v4l2_fh_del(&inst->v4l2_fh); + v4l2_fh_exit(&inst->v4l2_fh); + kfree(inst); + + return 0; +} + +static const struct v4l2_file_operations wave6_vpu_dec_fops = { + .owner = THIS_MODULE, + .open = wave6_vpu_open_dec, + .release = wave6_vpu_dec_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +int wave6_vpu_dec_register_device(struct vpu_core_device *dev) +{ + struct video_device *vdev_dec; + int ret; + + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL); + if (!vdev_dec) + return -ENOMEM; + + dev->video_dev_dec = vdev_dec; + + strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec->name)); + vdev_dec->fops = &wave6_vpu_dec_fops; + vdev_dec->ioctl_ops = &wave6_vpu_dec_ioctl_ops; + vdev_dec->release = video_device_release_empty; + vdev_dec->v4l2_dev = &dev->v4l2_dev; + vdev_dec->vfl_dir = VFL_DIR_M2M; + vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vdev_dec->lock = &dev->dev_lock; + video_set_drvdata(vdev_dec, dev); + + ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + return 0; +} + +void wave6_vpu_dec_unregister_device(struct vpu_core_device *dev) +{ + video_unregister_device(dev->video_dev_dec); +} diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c new file mode 100644 index 000000000000..715b031a8254 --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c @@ -0,0 +1,2699 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - v4l2 stateful encoder interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#include +#include "wave6-vpu-core.h" +#include "wave6-vpu-dbg.h" +#include "wave6-trace.h" + +#define VPU_ENC_DEV_NAME "C&M Wave6 VPU encoder" +#define VPU_ENC_DRV_NAME "wave6-enc" + +static const struct vpu_format wave6_vpu_enc_fmt_list[2][23] = { + [VPU_FMT_TYPE_CODEC] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + }, + [VPU_FMT_TYPE_RAW] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUYV, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV24, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV24, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV42, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 3, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 2, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 2, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 3, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 2, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 2, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_RGB24, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_P010, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_ARGB32, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_XRGB32, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_RGBA32, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_RGBX32, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_ARGB2101010, + .max_width = W6_MAX_ENC_PIC_WIDTH, + .min_width = W6_MIN_ENC_PIC_WIDTH, + .max_height = W6_MAX_ENC_PIC_HEIGHT, + .min_height = W6_MIN_ENC_PIC_HEIGHT, + .num_planes = 1, + }, + } +}; + +static const struct vpu_format *wave6_find_vpu_fmt(unsigned int v4l2_pix_fmt, + enum vpu_fmt_type type) +{ + unsigned int index; + + for (index = 0; index < ARRAY_SIZE(wave6_vpu_enc_fmt_list[type]); index++) { + if (wave6_vpu_enc_fmt_list[type][index].v4l2_pix_fmt == v4l2_pix_fmt) + return &wave6_vpu_enc_fmt_list[type][index]; + } + + return NULL; +} + +static const struct vpu_format *wave6_find_vpu_fmt_by_idx(unsigned int idx, + enum vpu_fmt_type type) +{ + if (idx >= ARRAY_SIZE(wave6_vpu_enc_fmt_list[type])) + return NULL; + + if (!wave6_vpu_enc_fmt_list[type][idx].v4l2_pix_fmt) + return NULL; + + return &wave6_vpu_enc_fmt_list[type][idx]; +} + +static u32 wave6_cpb_size_msec(u32 cpb_size_kb, u32 bitrate) +{ + u64 cpb_size_bit; + u64 cpb_size_msec; + + cpb_size_bit = (u64)cpb_size_kb * 1000 * BITS_PER_BYTE; + cpb_size_msec = (cpb_size_bit * 1000) / bitrate; + + if (cpb_size_msec < 10 || cpb_size_msec > 100000) + cpb_size_msec = 10000; + + return cpb_size_msec; +} + +static void wave6_vpu_enc_release_fb(struct vpu_instance *inst) +{ + int i; + + for (i = 0; i < WAVE6_MAX_FBS; i++) { + wave6_free_dma(&inst->frame_vbuf[i]); + memset(&inst->frame_buf[i], 0, sizeof(struct frame_buffer)); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_FBC_Y_TBL][i]); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_FBC_C_TBL][i]); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_MV_COL][i]); + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_SUB_SAMPLE][i]); + } +} + +static void wave6_vpu_enc_destroy_instance(struct vpu_instance *inst) +{ + u32 fail_res; + int ret; + + wave6_vpu_remove_dbgfs_file(inst); + + ret = wave6_vpu_enc_close(inst, &fail_res); + if (ret) { + dev_err(inst->dev->dev, "failed destroy instance: %d (%d)\n", + ret, fail_res); + } + + wave6_vpu_enc_release_fb(inst); + wave6_free_dma(&inst->ar_vbuf); + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_NONE); + + if (!pm_runtime_suspended(inst->dev->dev)) + pm_runtime_put_sync(inst->dev->dev); +} + +static struct vb2_v4l2_buffer *wave6_get_valid_src_buf(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *vb2_v4l2_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vpu_buffer *vpu_buf = NULL; + + v4l2_m2m_for_each_src_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + vb2_v4l2_buf = &v4l2_m2m_buf->vb; + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); + + if (!vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "no consumed src idx : %d\n", + vb2_v4l2_buf->vb2_buf.index); + return vb2_v4l2_buf; + } + } + + return NULL; +} + +static struct vb2_v4l2_buffer *wave6_get_valid_dst_buf(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *vb2_v4l2_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vpu_buffer *vpu_buf; + + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + vb2_v4l2_buf = &v4l2_m2m_buf->vb; + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); + + if (!vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "no consumed dst idx : %d\n", + vb2_v4l2_buf->vb2_buf.index); + return vb2_v4l2_buf; + } + } + + return NULL; +} + +static void wave6_set_csc(struct vpu_instance *inst, struct enc_param *pic_param) +{ + bool is_10bit = false; + + if (!(inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGB24) && + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB32) && + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_XRGB32) && + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32) && + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32) && + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB2101010)) + return; + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB2101010) + is_10bit = true; + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32) + pic_param->csc.format_order = 8; + + if (inst->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT || + inst->ycbcr_enc == V4L2_YCBCR_ENC_601) { + if (inst->quantization == V4L2_QUANTIZATION_FULL_RANGE) { + /* + * Y 0.299(R) 0.587(G) 0.114(B) + * Cb -0.16874(R) -0.33126(G) 0.5(B) + * Cr 0.5(R) -0.41869(G) -0.08131(B) + */ + pic_param->csc.coef_ry = 0x099; + pic_param->csc.coef_gy = 0x12d; + pic_param->csc.coef_by = 0x03a; + pic_param->csc.coef_rcb = 0xffffffaa; + pic_param->csc.coef_gcb = 0xffffff56; + pic_param->csc.coef_bcb = 0x100; + pic_param->csc.coef_rcr = 0x100; + pic_param->csc.coef_gcr = 0xffffff2a; + pic_param->csc.coef_bcr = 0xffffffd6; + pic_param->csc.offset_y = 0x0; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } else { + /* + * Y 0.258(R) 0.504(G) 0.098(B) + * Cb -0.1484(R) -0.2891(G) 0.4375(B) + * Cr 0.4375(R) -0.3672(G) -0.0703(B) + */ + pic_param->csc.coef_ry = 0x084; + pic_param->csc.coef_gy = 0x102; + pic_param->csc.coef_by = 0x032; + pic_param->csc.coef_rcb = 0xffffffb4; + pic_param->csc.coef_gcb = 0xffffff6c; + pic_param->csc.coef_bcb = 0x0e0; + pic_param->csc.coef_rcr = 0x0e0; + pic_param->csc.coef_gcr = 0xffffff44; + pic_param->csc.coef_bcr = 0xffffffdc; + pic_param->csc.offset_y = (is_10bit) ? 0x40 : 0x10; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_709) { + if (inst->quantization == V4L2_QUANTIZATION_FULL_RANGE) { + /* + * Y 0.2126(R) 0.7152(G) 0.0722(B) + * Cb -0.11457(R) -0.38543(G) 0.5(B) + * Cr 0.5(R) -0.45415(G) -0.04585(B) + */ + pic_param->csc.coef_ry = 0x06d; + pic_param->csc.coef_gy = 0x16e; + pic_param->csc.coef_by = 0x025; + pic_param->csc.coef_rcb = 0xffffffc5; + pic_param->csc.coef_gcb = 0xffffff3b; + pic_param->csc.coef_bcb = 0x100; + pic_param->csc.coef_rcr = 0x100; + pic_param->csc.coef_gcr = 0xffffff17; + pic_param->csc.coef_bcr = 0xffffffe9; + pic_param->csc.offset_y = 0x0; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } else { + pic_param->csc.coef_ry = 0x05e; + pic_param->csc.coef_gy = 0x13b; + pic_param->csc.coef_by = 0x020; + pic_param->csc.coef_rcb = 0xffffffcc; + pic_param->csc.coef_gcb = 0xffffff53; + pic_param->csc.coef_bcb = 0x0e1; + pic_param->csc.coef_rcr = 0x0e1; + pic_param->csc.coef_gcr = 0xffffff34; + pic_param->csc.coef_bcr = 0xffffffeb; + pic_param->csc.offset_y = (is_10bit) ? 0x40 : 0x10; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_BT2020) { + if (inst->quantization == V4L2_QUANTIZATION_FULL_RANGE) { + /* + * Y 0.2627(R) 0.678(G) 0.0593(B) + * Cb -0.13963(R) -0.36037(G) 0.5(B) + * Cr 0.5(R) -0.45979(G) -0.04021(B) + */ + pic_param->csc.coef_ry = 0x087; + pic_param->csc.coef_gy = 0x15b; + pic_param->csc.coef_by = 0x01e; + pic_param->csc.coef_rcb = 0xffffffb9; + pic_param->csc.coef_gcb = 0xffffff47; + pic_param->csc.coef_bcb = 0x100; + pic_param->csc.coef_rcr = 0x100; + pic_param->csc.coef_gcr = 0xffffff15; + pic_param->csc.coef_bcr = 0xffffffeb; + pic_param->csc.offset_y = 0x0; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } else { + pic_param->csc.coef_ry = 0x074; + pic_param->csc.coef_gy = 0x12a; + pic_param->csc.coef_by = 0x01a; + pic_param->csc.coef_rcb = 0xffffffc1; + pic_param->csc.coef_gcb = 0xffffff5e; + pic_param->csc.coef_bcb = 0x0e1; + pic_param->csc.coef_rcr = 0x0e1; + pic_param->csc.coef_gcr = 0xffffff31; + pic_param->csc.coef_bcr = 0xffffffee; + pic_param->csc.offset_y = (is_10bit) ? 0x40 : 0x10; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_SMPTE240M) { + if (inst->quantization == V4L2_QUANTIZATION_FULL_RANGE) { + /* + * Y 0.2122(R) 0.7013(G) 0.0865(B) + * Cb -0.1161(R) -0.3839(G) 0.5(B) + * Cr 0.5(R) -0.4451(G) -0.0549(B) + */ + pic_param->csc.coef_ry = 0x06d; + pic_param->csc.coef_gy = 0x167; + pic_param->csc.coef_by = 0x02c; + pic_param->csc.coef_rcb = 0xffffffc5; + pic_param->csc.coef_gcb = 0xffffff3b; + pic_param->csc.coef_bcb = 0x100; + pic_param->csc.coef_rcr = 0x100; + pic_param->csc.coef_gcr = 0xffffff1c; + pic_param->csc.coef_bcr = 0xffffffe4; + pic_param->csc.offset_y = 0x0; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } else { + pic_param->csc.coef_ry = 0x05d; + pic_param->csc.coef_gy = 0x134; + pic_param->csc.coef_by = 0x026; + pic_param->csc.coef_rcb = 0xffffffcc; + pic_param->csc.coef_gcb = 0xffffff53; + pic_param->csc.coef_bcb = 0x0e1; + pic_param->csc.coef_rcr = 0x0e1; + pic_param->csc.coef_gcr = 0xffffff38; + pic_param->csc.coef_bcr = 0xffffffe7; + pic_param->csc.offset_y = (is_10bit) ? 0x40 : 0x10; + pic_param->csc.offset_cb = (is_10bit) ? 0x200 : 0x80; + pic_param->csc.offset_cr = (is_10bit) ? 0x200 : 0x80; + } + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_XV601) { + if (inst->quantization == V4L2_QUANTIZATION_LIM_RANGE) { + /* + * Y 0.2558(R) 0.5021(G) 0.0975(B) + * Cb -0.1476(R) -0.2899(G) 0.4375(B) + * Cr 0.4375(R) -0.3664(G) -0.0711(B) + */ + pic_param->csc.coef_ry = 0x083; + pic_param->csc.coef_gy = 0x101; + pic_param->csc.coef_by = 0x032; + pic_param->csc.coef_rcb = 0xffffffb4; + pic_param->csc.coef_gcb = 0xffffff6c; + pic_param->csc.coef_bcb = 0x0e0; + pic_param->csc.coef_rcr = 0x0e0; + pic_param->csc.coef_gcr = 0xffffff44; + pic_param->csc.coef_bcr = 0xffffffdc; + pic_param->csc.offset_y = (is_10bit) ? 0x40 : 0x10; + pic_param->csc.offset_cb = 0x0; + pic_param->csc.offset_cr = 0x0; + } + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_XV709) { + if (inst->quantization == V4L2_QUANTIZATION_LIM_RANGE) { + /* + * Y 0.1819(R) 0.6118(G) 0.0618(B) + * Cb -0.1003(R) -0.3372(G) 0.4375(B) + * Cr 0.4375(R) -0.3974(G) -0.0401(B) + */ + pic_param->csc.coef_ry = 0x05d; + pic_param->csc.coef_gy = 0x139; + pic_param->csc.coef_by = 0x020; + pic_param->csc.coef_rcb = 0xffffffcd; + pic_param->csc.coef_gcb = 0xffffff53; + pic_param->csc.coef_bcb = 0x0e0; + pic_param->csc.coef_rcr = 0x0e0; + pic_param->csc.coef_gcr = 0xffffff35; + pic_param->csc.coef_bcr = 0xffffffeb; + pic_param->csc.offset_y = (is_10bit) ? 0x40 : 0x10; + pic_param->csc.offset_cb = 0x0; + pic_param->csc.offset_cr = 0x0; + } + } +} + +static void wave6_update_crop_info(struct vpu_instance *inst, + u32 left, u32 top, u32 width, u32 height) +{ + u32 enc_pic_width, enc_pic_height; + + inst->crop.left = left; + inst->crop.top = top; + inst->crop.width = width; + inst->crop.height = height; + + inst->codec_rect.left = round_down(left, W6_ENC_CROP_X_POS_STEP); + inst->codec_rect.top = round_down(top, W6_ENC_CROP_Y_POS_STEP); + + enc_pic_width = width + left - inst->codec_rect.left; + inst->codec_rect.width = round_up(enc_pic_width, W6_ENC_PIC_SIZE_STEP); + + enc_pic_height = height + top - inst->codec_rect.top; + inst->codec_rect.height = round_up(enc_pic_height, W6_ENC_PIC_SIZE_STEP); +} + +static int wave6_allocate_aux_buffer(struct vpu_instance *inst, + enum aux_buffer_type type, + int num) +{ + struct aux_buffer buf[WAVE6_MAX_FBS]; + struct aux_buffer_info buf_info; + struct enc_aux_buffer_size_info size_info; + unsigned int size; + int i, ret; + + memset(buf, 0, sizeof(buf)); + + size_info.width = inst->codec_rect.width; + size_info.height = inst->codec_rect.height; + size_info.type = type; + size_info.mirror_direction = inst->enc_ctrls.mirror_direction; + size_info.rotation_angle = inst->enc_ctrls.rot_angle; + + ret = wave6_vpu_enc_get_aux_buffer_size(inst, size_info, &size); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Get size fail (type %d)\n", __func__, type); + return ret; + } + + for (i = 0; i < num; i++) { + inst->aux_vbuf[type][i].size = size; + ret = wave6_alloc_dma(inst->dev->dev, &inst->aux_vbuf[type][i]); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Alloc fail (type %d)\n", __func__, type); + return ret; + } + + buf[i].index = i; + buf[i].addr = inst->aux_vbuf[type][i].daddr; + buf[i].size = inst->aux_vbuf[type][i].size; + } + + buf_info.type = type; + buf_info.num = num; + buf_info.buf_array = buf; + + ret = wave6_vpu_enc_register_aux_buffer(inst, buf_info); + if (ret) { + dev_dbg(inst->dev->dev, "%s: Register fail (type %d)\n", __func__, type); + return ret; + } + + return 0; +} + +static void wave6_update_frame_buf_addr(struct vpu_instance *inst, + struct frame_buffer *frame_buf) +{ + const struct v4l2_format_info *fmt_info; + u32 stride = inst->src_fmt.plane_fmt[0].bytesperline; + u32 offset; + + fmt_info = v4l2_format_info(inst->src_fmt.pixelformat); + if (!fmt_info) + return; + + offset = inst->codec_rect.top * stride + inst->codec_rect.left * fmt_info->bpp[0]; + frame_buf->buf_y += offset; + + stride = DIV_ROUND_UP(stride, fmt_info->bpp[0]) * fmt_info->bpp[1]; + offset = inst->codec_rect.top * stride / fmt_info->vdiv / fmt_info->hdiv + + inst->codec_rect.left * fmt_info->bpp[1] / fmt_info->hdiv; + frame_buf->buf_cb += offset; + frame_buf->buf_cr += offset; +} + +static int wave6_update_seq_param(struct vpu_instance *inst) +{ + struct enc_initial_info initial_info; + bool changed = false; + int ret; + + ret = wave6_vpu_enc_issue_seq_change(inst, &changed); + if (ret) { + dev_err(inst->dev->dev, "seq change fail %d\n", ret); + return ret; + } + + if (!changed) + return 0; + + ret = wave6_vpu_wait_interrupt(inst, W6_VPU_TIMEOUT); + if (ret) { + dev_err(inst->dev->dev, "seq change timeout\n"); + return ret; + } + + ret = wave6_vpu_enc_complete_seq_init(inst, &initial_info); + if (ret) { + dev_err(inst->dev->dev, "seq change error\n"); + return ret; + } + + return 0; +} + +static int wave6_vpu_enc_start_encode(struct vpu_instance *inst) +{ + int ret = -EINVAL; + struct vb2_v4l2_buffer *src_buf = NULL; + struct vb2_v4l2_buffer *dst_buf = NULL; + struct vpu_buffer *src_vbuf = NULL; + struct vpu_buffer *dst_vbuf = NULL; + struct frame_buffer frame_buf; + struct enc_param pic_param; + u32 stride = inst->src_fmt.plane_fmt[0].bytesperline; + u32 luma_size = (stride * inst->src_fmt.height); + u32 chroma_size; + u32 fail_res; + + memset(&pic_param, 0, sizeof(struct enc_param)); + memset(&frame_buf, 0, sizeof(struct frame_buffer)); + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420M) + chroma_size = ((stride / 2) * (inst->src_fmt.height / 2)); + else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422P || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) + chroma_size = ((stride) * (inst->src_fmt.height / 2)); + else + chroma_size = 0; + + ret = wave6_update_seq_param(inst); + if (ret) + goto exit; + + src_buf = wave6_get_valid_src_buf(inst); + dst_buf = wave6_get_valid_dst_buf(inst); + + if (!dst_buf) { + dev_dbg(inst->dev->dev, "no valid dst buf\n"); + goto exit; + } + + dst_vbuf = wave6_to_vpu_buf(dst_buf); + pic_param.pic_stream_buffer_addr = wave6_get_dma_addr(dst_buf, 0); + pic_param.pic_stream_buffer_size = vb2_plane_size(&dst_buf->vb2_buf, 0); + if (!src_buf) { + dev_dbg(inst->dev->dev, "no valid src buf\n"); + if (inst->state == VPU_INST_STATE_STOP) + pic_param.src_end = true; + else + goto exit; + } else { + src_vbuf = wave6_to_vpu_buf(src_buf); + if (inst->src_fmt.num_planes == 1) { + frame_buf.buf_y = wave6_get_dma_addr(src_buf, 0); + frame_buf.buf_cb = frame_buf.buf_y + luma_size; + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } else if (inst->src_fmt.num_planes == 2) { + frame_buf.buf_y = wave6_get_dma_addr(src_buf, 0); + frame_buf.buf_cb = wave6_get_dma_addr(src_buf, 1); + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } else if (inst->src_fmt.num_planes == 3) { + frame_buf.buf_y = wave6_get_dma_addr(src_buf, 0); + frame_buf.buf_cb = wave6_get_dma_addr(src_buf, 1); + frame_buf.buf_cr = wave6_get_dma_addr(src_buf, 2); + } + wave6_update_frame_buf_addr(inst, &frame_buf); + frame_buf.stride = stride; + pic_param.src_idx = src_buf->vb2_buf.index; + if (src_vbuf->force_key_frame || inst->error_recovery) { + pic_param.force_pic_type_enable = true; + pic_param.force_pic_type = ENC_FORCE_PIC_TYPE_IDR; + inst->error_recovery = false; + } + if (src_vbuf->force_frame_qp) { + pic_param.force_pic_qp_enable = true; + pic_param.force_pic_qp_i = src_vbuf->force_i_frame_qp; + pic_param.force_pic_qp_p = src_vbuf->force_p_frame_qp; + pic_param.force_pic_qp_b = src_vbuf->force_b_frame_qp; + } + src_vbuf->ts_start = ktime_get_raw(); + } + + pic_param.source_frame = &frame_buf; + wave6_set_csc(inst, &pic_param); + + if (src_vbuf) + src_vbuf->consumed = true; + if (dst_vbuf) { + dst_vbuf->consumed = true; + dst_vbuf->used = true; + } + + trace_enc_pic(inst, &pic_param); + + ret = wave6_vpu_enc_start_one_frame(inst, &pic_param, &fail_res); + if (ret) { + dev_err(inst->dev->dev, "[%d] %s: fail %d\n", inst->id, __func__, ret); + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_STOP); + + dst_buf = v4l2_m2m_dst_buf_remove(inst->v4l2_fh.m2m_ctx); + if (dst_buf) { + dst_buf->sequence = inst->sequence; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } + + src_buf = v4l2_m2m_src_buf_remove(inst->v4l2_fh.m2m_ctx); + if (src_buf) { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + inst->sequence++; + inst->processed_buf_num++; + inst->error_buf_num++; + } + } else { + dev_dbg(inst->dev->dev, "%s: success\n", __func__); + } + +exit: + return ret; +} + +static void wave6_handle_encoded_frame(struct vpu_instance *inst, + struct enc_output_info *info) +{ + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct vpu_buffer *vpu_buf; + struct vpu_buffer *dst_vpu_buf; + enum vb2_buffer_state state; + + state = info->encoding_success ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + src_buf = v4l2_m2m_src_buf_remove_by_idx(inst->v4l2_fh.m2m_ctx, + info->enc_src_idx); + if (!src_buf) { + dev_err(inst->dev->dev, "[%d] encoder can't find src buffer\n", inst->id); + return; + } + + vpu_buf = wave6_to_vpu_buf(src_buf); + if (!vpu_buf || !vpu_buf->consumed) { + dev_err(inst->dev->dev, "[%d] src buffer is not consumed\n", inst->id); + return; + } + + dst_buf = wave6_get_dst_buf_by_addr(inst, info->bitstream_buffer); + if (!dst_buf) { + dev_err(inst->dev->dev, "[%d] encoder can't find dst buffer\n", inst->id); + return; + } + + dst_vpu_buf = wave6_to_vpu_buf(dst_buf); + + dst_vpu_buf->average_qp = info->avg_ctu_qp; + dst_vpu_buf->ts_input = vpu_buf->ts_input; + dst_vpu_buf->ts_start = vpu_buf->ts_start; + dst_vpu_buf->ts_finish = ktime_get_raw(); + dst_vpu_buf->hw_time = wave6_vpu_cycle_to_ns(inst->dev, info->cycle.frame_cycle); + dst_vpu_buf->ts_output = ktime_get_raw(); + wave6_vpu_handle_performance(inst, dst_vpu_buf); + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_done(src_buf, state); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, info->bitstream_size); + dst_buf->sequence = inst->sequence++; + dst_buf->field = V4L2_FIELD_NONE; + if (info->pic_type == PIC_TYPE_I) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else if (info->pic_type == PIC_TYPE_P) + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + else if (info->pic_type == PIC_TYPE_B) + dst_buf->flags |= V4L2_BUF_FLAG_BFRAME; + + v4l2_m2m_dst_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, dst_buf); + if (state == VB2_BUF_STATE_ERROR) { + dev_dbg(inst->dev->dev, "[%d] error frame %d\n", inst->id, inst->sequence); + inst->error_recovery = true; + inst->error_buf_num++; + } + v4l2_m2m_buf_done(dst_buf, state); + inst->processed_buf_num++; +} + +static void wave6_handle_last_frame(struct vpu_instance *inst, + dma_addr_t addr) +{ + struct vb2_v4l2_buffer *dst_buf; + + dst_buf = wave6_get_dst_buf_by_addr(inst, addr); + if (!dst_buf) + return; + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + dst_buf->field = V4L2_FIELD_NONE; + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_dst_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, dst_buf); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_PIC_RUN); + + dev_dbg(inst->dev->dev, "[%d] eos\n", inst->id); + inst->eos = true; + + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, false); +} + +static void wave6_vpu_enc_finish_encode(struct vpu_instance *inst, bool error) +{ + int ret; + struct enc_output_info info; + + if (error) { + vb2_queue_error(v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx)); + vb2_queue_error(v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx)); + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_STOP); + inst->eos = true; + + goto finish_encode; + } + + ret = wave6_vpu_enc_get_output_info(inst, &info); + if (ret) { + dev_dbg(inst->dev->dev, "vpu_enc_get_output_info fail %d reason: %d | info : %d\n", + ret, info.error_reason, info.warn_info); + goto finish_encode; + } + + trace_enc_done(inst, &info); + + if (info.enc_src_idx >= 0 && info.recon_frame_index >= 0) + wave6_handle_encoded_frame(inst, &info); + else if (info.recon_frame_index == RECON_IDX_FLAG_ENC_END) + wave6_handle_last_frame(inst, info.bitstream_buffer); + +finish_encode: + wave6_vpu_finish_job(inst); +} + +static int wave6_vpu_enc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card)); + strscpy(cap->bus_info, "platform:" VPU_ENC_DRV_NAME, sizeof(cap->bus_info)); + + return 0; +} + +static int wave6_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize) +{ + const struct vpu_format *vpu_fmt; + + if (fsize->index) + return -EINVAL; + + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, VPU_FMT_TYPE_CODEC); + if (!vpu_fmt) { + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, VPU_FMT_TYPE_RAW); + if (!vpu_fmt) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = vpu_fmt->min_width; + fsize->stepwise.max_width = vpu_fmt->max_width; + fsize->stepwise.step_width = W6_ENC_PIC_SIZE_STEP; + fsize->stepwise.min_height = vpu_fmt->min_height; + fsize->stepwise.max_height = vpu_fmt->max_height; + fsize->stepwise.step_height = W6_ENC_PIC_SIZE_STEP; + + return 0; +} + +static int wave6_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "index : %d\n", f->index); + + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, VPU_FMT_TYPE_CODEC); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave6_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct vpu_format *vpu_fmt; + int width, height; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + if (!V4L2_TYPE_IS_CAPTURE(f->type)) + return -EINVAL; + + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, VPU_FMT_TYPE_CODEC); + if (!vpu_fmt) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + pix_mp->pixelformat = inst->dst_fmt.pixelformat; + pix_mp->num_planes = inst->dst_fmt.num_planes; + } else { + width = pix_mp->width; + height = pix_mp->height; + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; + pix_mp->num_planes = vpu_fmt->num_planes; + } + + wave6_update_pix_fmt(pix_mp, width, height); + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_enc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i, ret; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + ret = wave6_vpu_enc_try_fmt_cap(file, fh, f); + if (ret) + return ret; + + inst->std = wave6_to_codec_std(inst->type, pix_mp->pixelformat); + if (inst->std == STD_UNKNOWN) { + dev_err(inst->dev->dev, "unsupported pixelformat: %.4s\n", + (char *)&pix_mp->pixelformat); + return -EINVAL; + } + + inst->dst_fmt.width = pix_mp->width; + inst->dst_fmt.height = pix_mp->height; + inst->dst_fmt.pixelformat = pix_mp->pixelformat; + inst->dst_fmt.field = pix_mp->field; + inst->dst_fmt.flags = pix_mp->flags; + inst->dst_fmt.num_planes = pix_mp->num_planes; + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + inst->dst_fmt.plane_fmt[i].bytesperline = pix_mp->plane_fmt[i].bytesperline; + inst->dst_fmt.plane_fmt[i].sizeimage = pix_mp->plane_fmt[i].sizeimage; + } + + return 0; +} + +static int wave6_vpu_enc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i; + + pix_mp->width = inst->dst_fmt.width; + pix_mp->height = inst->dst_fmt.height; + pix_mp->pixelformat = inst->dst_fmt.pixelformat; + pix_mp->field = inst->dst_fmt.field; + pix_mp->flags = inst->dst_fmt.flags; + pix_mp->num_planes = inst->dst_fmt.num_planes; + for (i = 0; i < pix_mp->num_planes; i++) { + pix_mp->plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline; + pix_mp->plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage; + } + + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index %d\n", __func__, f->index); + + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, VPU_FMT_TYPE_RAW); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave6_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct vpu_format *vpu_fmt; + int width, height; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + if (!V4L2_TYPE_IS_OUTPUT(f->type)) + return -EINVAL; + + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, VPU_FMT_TYPE_RAW); + if (!vpu_fmt) { + width = inst->src_fmt.width; + height = inst->src_fmt.height; + pix_mp->pixelformat = inst->src_fmt.pixelformat; + pix_mp->num_planes = inst->src_fmt.num_planes; + } else { + width = clamp(pix_mp->width, + vpu_fmt->min_width, vpu_fmt->max_width); + height = clamp(pix_mp->height, + vpu_fmt->min_height, vpu_fmt->max_height); + + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; + pix_mp->num_planes = vpu_fmt->num_planes; + } + + wave6_update_pix_fmt(pix_mp, width, height); + + if (pix_mp->ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_BT2020; + if (pix_mp->ycbcr_enc == V4L2_YCBCR_ENC_XV601 || + pix_mp->ycbcr_enc == V4L2_YCBCR_ENC_XV709) { + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) + pix_mp->quantization = V4L2_QUANTIZATION_LIM_RANGE; + } + + return 0; +} + +static int wave6_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i, ret; + + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d colorspace %d\n", + __func__, pix_mp->pixelformat, pix_mp->width, pix_mp->height, + pix_mp->num_planes, pix_mp->colorspace); + + ret = wave6_vpu_enc_try_fmt_out(file, fh, f); + if (ret) + return ret; + + inst->src_fmt.width = pix_mp->width; + inst->src_fmt.height = pix_mp->height; + inst->src_fmt.pixelformat = pix_mp->pixelformat; + inst->src_fmt.field = pix_mp->field; + inst->src_fmt.flags = pix_mp->flags; + inst->src_fmt.num_planes = pix_mp->num_planes; + for (i = 0; i < inst->src_fmt.num_planes; i++) { + inst->src_fmt.plane_fmt[i].bytesperline = pix_mp->plane_fmt[i].bytesperline; + inst->src_fmt.plane_fmt[i].sizeimage = pix_mp->plane_fmt[i].sizeimage; + } + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV24 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGB24 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV24 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_P010 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_XRGB32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB2101010) { + inst->cbcr_interleave = true; + inst->nv21 = false; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV42 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + } else { + inst->cbcr_interleave = false; + inst->nv21 = false; + } + + inst->colorspace = pix_mp->colorspace; + inst->ycbcr_enc = pix_mp->ycbcr_enc; + inst->quantization = pix_mp->quantization; + inst->xfer_func = pix_mp->xfer_func; + + wave6_update_pix_fmt(&inst->dst_fmt, pix_mp->width, pix_mp->height); + wave6_update_crop_info(inst, 0, 0, pix_mp->width, pix_mp->height); + + return 0; +} + +static int wave6_vpu_enc_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i; + + dev_dbg(inst->dev->dev, "\n"); + + pix_mp->width = inst->src_fmt.width; + pix_mp->height = inst->src_fmt.height; + pix_mp->pixelformat = inst->src_fmt.pixelformat; + pix_mp->field = inst->src_fmt.field; + pix_mp->flags = inst->src_fmt.flags; + pix_mp->num_planes = inst->src_fmt.num_planes; + for (i = 0; i < pix_mp->num_planes; i++) { + pix_mp->plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline; + pix_mp->plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage; + } + + pix_mp->colorspace = inst->colorspace; + pix_mp->ycbcr_enc = inst->ycbcr_enc; + pix_mp->quantization = inst->quantization; + pix_mp->xfer_func = inst->xfer_func; + + return 0; +} + +static int wave6_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type %d target %d\n", + __func__, s->type, s->target); + + if (!V4L2_TYPE_IS_OUTPUT(s->type)) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + break; + case V4L2_SEL_TGT_CROP: + s->r = inst->crop; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave6_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + u32 max_crop_w, max_crop_h; + + if (!V4L2_TYPE_IS_OUTPUT(s->type)) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (!(s->flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE))) + s->flags |= V4L2_SEL_FLAG_LE; + + if (s->flags & V4L2_SEL_FLAG_GE) { + s->r.left = round_up(s->r.left, W6_ENC_CROP_STEP); + s->r.top = round_up(s->r.top, W6_ENC_CROP_STEP); + s->r.width = round_up(s->r.width, W6_ENC_CROP_STEP); + s->r.height = round_up(s->r.height, W6_ENC_CROP_STEP); + } + if (s->flags & V4L2_SEL_FLAG_LE) { + s->r.left = round_down(s->r.left, W6_ENC_CROP_STEP); + s->r.top = round_down(s->r.top, W6_ENC_CROP_STEP); + s->r.width = round_down(s->r.width, W6_ENC_CROP_STEP); + s->r.height = round_down(s->r.height, W6_ENC_CROP_STEP); + } + + max_crop_w = inst->src_fmt.width - s->r.left; + max_crop_h = inst->src_fmt.height - s->r.top; + + if (!s->r.width || !s->r.height) + return 0; + if (max_crop_w < W6_MIN_ENC_PIC_WIDTH) + return 0; + if (max_crop_h < W6_MIN_ENC_PIC_HEIGHT) + return 0; + + s->r.width = clamp(s->r.width, W6_MIN_ENC_PIC_WIDTH, max_crop_w); + s->r.height = clamp(s->r.height, W6_MIN_ENC_PIC_HEIGHT, max_crop_h); + + wave6_update_pix_fmt(&inst->dst_fmt, s->r.width, s->r.height); + wave6_update_crop_info(inst, s->r.left, s->r.top, s->r.width, s->r.height); + + dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_CROP %dx%dx%dx%d\n", + s->r.left, s->r.top, s->r.width, s->r.height); + + return 0; +} + +static int wave6_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + int ret; + + dev_dbg(inst->dev->dev, "%s: cmd %d\n", __func__, ec->cmd); + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); + if (ret) + return ret; + + if (!wave6_vpu_both_queues_are_streaming(inst)) + return 0; + + switch (ec->cmd) { + case V4L2_ENC_CMD_STOP: + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_STOP); + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true); + v4l2_m2m_try_schedule(inst->v4l2_fh.m2m_ctx); + break; + case V4L2_ENC_CMD_START: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave6_vpu_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type %d\n", __func__, a->type); + + if (!V4L2_TYPE_IS_OUTPUT(a->type)) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe.numerator = 1; + a->parm.output.timeperframe.denominator = inst->frame_rate; + + dev_dbg(inst->dev->dev, "%s: numerator : %d | denominator : %d\n", + __func__, + a->parm.output.timeperframe.numerator, + a->parm.output.timeperframe.denominator); + + return 0; +} + +static int wave6_vpu_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type %d\n", __func__, a->type); + + if (!V4L2_TYPE_IS_OUTPUT(a->type)) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + if (a->parm.output.timeperframe.denominator && a->parm.output.timeperframe.numerator) { + inst->frame_rate = a->parm.output.timeperframe.denominator / + a->parm.output.timeperframe.numerator; + } else { + a->parm.output.timeperframe.numerator = 1; + a->parm.output.timeperframe.denominator = inst->frame_rate; + } + + dev_dbg(inst->dev->dev, "%s: numerator : %d | denominator : %d\n", + __func__, + a->parm.output.timeperframe.numerator, + a->parm.output.timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops wave6_vpu_enc_ioctl_ops = { + .vidioc_querycap = wave6_vpu_enc_querycap, + .vidioc_enum_framesizes = wave6_vpu_enc_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = wave6_vpu_enc_enum_fmt_cap, + .vidioc_s_fmt_vid_cap_mplane = wave6_vpu_enc_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = wave6_vpu_enc_g_fmt_cap, + .vidioc_try_fmt_vid_cap_mplane = wave6_vpu_enc_try_fmt_cap, + + .vidioc_enum_fmt_vid_out = wave6_vpu_enc_enum_fmt_out, + .vidioc_s_fmt_vid_out_mplane = wave6_vpu_enc_s_fmt_out, + .vidioc_g_fmt_vid_out_mplane = wave6_vpu_enc_g_fmt_out, + .vidioc_try_fmt_vid_out_mplane = wave6_vpu_enc_try_fmt_out, + + .vidioc_g_selection = wave6_vpu_enc_g_selection, + .vidioc_s_selection = wave6_vpu_enc_s_selection, + + .vidioc_g_parm = wave6_vpu_enc_g_parm, + .vidioc_s_parm = wave6_vpu_enc_s_parm, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = wave6_vpu_enc_encoder_cmd, + + .vidioc_subscribe_event = wave6_vpu_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int wave6_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_instance *inst = wave6_ctrl_to_vpu_inst(ctrl); + struct enc_controls *p = &inst->enc_ctrls; + + trace_s_ctrl(inst, ctrl); + + dev_dbg(inst->dev->dev, "%s: name %s value %d\n", + __func__, ctrl->name, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + p->mirror_direction |= (ctrl->val << 1); + break; + case V4L2_CID_VFLIP: + p->mirror_direction |= ctrl->val; + break; + case V4L2_CID_ROTATE: + p->rot_angle = ctrl->val; + break; + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + p->gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + p->slice_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + p->slice_max_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + p->bitrate_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + p->bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + p->frame_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + p->mb_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + p->force_key_frame = true; + break; + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: + p->prepend_spspps_to_idr = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE: + break; + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: + p->intra_refresh_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: + p->frame_skip_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + p->hevc.profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + p->hevc.level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + p->hevc.min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + p->hevc.max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + p->hevc.i_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: + p->hevc.p_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: + p->hevc.b_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + p->hevc.loop_filter_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: + p->hevc.lf_beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: + p->hevc.lf_tc_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + p->hevc.refresh_type = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: + p->hevc.refresh_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: + p->hevc.const_intra_pred = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: + p->hevc.strong_smoothing = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: + p->hevc.tmv_prediction = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + p->h264.profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + p->h264.level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + p->h264.min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + p->h264.max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + p->h264.i_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + p->h264.p_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + p->h264.b_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + p->h264.loop_filter_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + p->h264.loop_filter_beta = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + p->h264.loop_filter_alpha = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + p->h264._8x8_transform = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + p->h264.constrained_intra_prediction = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + p->h264.chroma_qp_index_offset = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + p->h264.entropy_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + p->h264.i_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: + p->h264.vui_sar_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: + p->h264.vui_sar_idc = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: + p->h264.vui_ext_sar_width = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: + p->h264.vui_ext_sar_height = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + p->h264.cpb_size = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops wave6_vpu_enc_ctrl_ops = { + .s_ctrl = wave6_vpu_enc_s_ctrl, +}; + +static u32 to_video_full_range_flag(enum v4l2_quantization quantization) +{ + switch (quantization) { + case V4L2_QUANTIZATION_FULL_RANGE: + return 1; + case V4L2_QUANTIZATION_LIM_RANGE: + default: + return 0; + } +} + +static u32 to_colour_primaries(enum v4l2_colorspace colorspace) +{ + switch (colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + return 6; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + case V4L2_COLORSPACE_JPEG: + return 1; + case V4L2_COLORSPACE_BT2020: + return 9; + case V4L2_COLORSPACE_DCI_P3: + return 11; + case V4L2_COLORSPACE_SMPTE240M: + return 7; + case V4L2_COLORSPACE_470_SYSTEM_M: + return 4; + case V4L2_COLORSPACE_470_SYSTEM_BG: + return 5; + case V4L2_COLORSPACE_RAW: + default: + return 2; + } +} + +static u32 to_transfer_characteristics(enum v4l2_colorspace colorspace, + enum v4l2_xfer_func xfer_func) +{ + if (xfer_func == V4L2_XFER_FUNC_DEFAULT) + xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(colorspace); + + switch (xfer_func) { + case V4L2_XFER_FUNC_709: + if (colorspace == V4L2_COLORSPACE_SMPTE170M) + return 6; + else if (colorspace == V4L2_COLORSPACE_BT2020) + return 14; + else + return 1; + case V4L2_XFER_FUNC_SRGB: + return 13; + case V4L2_XFER_FUNC_SMPTE240M: + return 7; + case V4L2_XFER_FUNC_NONE: + return 8; + case V4L2_XFER_FUNC_SMPTE2084: + return 16; + case V4L2_XFER_FUNC_DCI_P3: + default: + return 2; + } +} + +static u32 to_matrix_coeffs(enum v4l2_colorspace colorspace, + enum v4l2_ycbcr_encoding ycbcr_enc) +{ + if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace); + + switch (ycbcr_enc) { + case V4L2_YCBCR_ENC_601: + case V4L2_YCBCR_ENC_XV601: + if (colorspace == V4L2_COLORSPACE_SMPTE170M) + return 6; + else + return 5; + case V4L2_YCBCR_ENC_709: + case V4L2_YCBCR_ENC_XV709: + return 1; + case V4L2_YCBCR_ENC_BT2020: + return 9; + case V4L2_YCBCR_ENC_BT2020_CONST_LUM: + return 10; + case V4L2_YCBCR_ENC_SMPTE240M: + return 7; + default: + return 2; + } +} + +static void wave6_set_enc_h264_param(struct enc_codec_param *output, + struct h264_enc_controls *ctrls) +{ + switch (ctrls->profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + output->profile = H264_PROFILE_BP; + output->internal_bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + output->profile = H264_PROFILE_MP; + output->internal_bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + output->profile = H264_PROFILE_EXTENDED; + output->internal_bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + output->profile = H264_PROFILE_HP; + output->internal_bit_depth = 8; + break; + default: + break; + } + switch (ctrls->level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + output->level = 10; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + output->level = 9; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + output->level = 11; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + output->level = 12; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + output->level = 13; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + output->level = 20; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + output->level = 21; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + output->level = 22; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + output->level = 30; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + output->level = 31; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + output->level = 32; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + output->level = 40; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + output->level = 41; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + output->level = 42; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + output->level = 50; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + output->level = 51; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_2: + output->level = 52; + break; + default: + break; + } + output->qp = ctrls->i_frame_qp; + output->min_qp_i = ctrls->min_qp; + output->max_qp_i = ctrls->max_qp; + output->min_qp_p = ctrls->min_qp; + output->max_qp_p = ctrls->max_qp; + output->min_qp_b = ctrls->min_qp; + output->max_qp_b = ctrls->max_qp; + switch (ctrls->loop_filter_mode) { + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED: + output->en_dbk = 0; + output->en_lf_cross_slice_boundary = 0; + break; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: + output->en_dbk = 1; + output->en_lf_cross_slice_boundary = 1; + break; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + output->en_dbk = 1; + output->en_lf_cross_slice_boundary = 0; + break; + default: + break; + } + output->intra_period = ctrls->i_period; + output->beta_offset_div2 = ctrls->loop_filter_beta; + output->tc_offset_div2 = ctrls->loop_filter_alpha; + if (output->profile >= H264_PROFILE_HP) + output->en_transform8x8 = ctrls->_8x8_transform; + output->en_constrained_intra_pred = ctrls->constrained_intra_prediction; + output->cb_qp_offset = ctrls->chroma_qp_index_offset; + output->cr_qp_offset = ctrls->chroma_qp_index_offset; + if (output->profile >= H264_PROFILE_MP) + output->en_cabac = ctrls->entropy_mode; + output->en_auto_level_adjusting = DEFAULT_EN_AUTO_LEVEL_ADJUSTING; +} + +static void wave6_set_enc_hevc_param(struct enc_codec_param *output, + struct hevc_enc_controls *ctrls) +{ + switch (ctrls->profile) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + output->profile = HEVC_PROFILE_MAIN; + output->internal_bit_depth = 8; + break; + default: + break; + } + switch (ctrls->level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + output->level = 10 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + output->level = 20 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + output->level = 21 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + output->level = 30 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + output->level = 31 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + output->level = 40 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + output->level = 41 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + output->level = 50 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + output->level = 51 * 3; + break; + default: + break; + } + output->qp = ctrls->i_frame_qp; + output->min_qp_i = ctrls->min_qp; + output->max_qp_i = ctrls->max_qp; + output->min_qp_p = ctrls->min_qp; + output->max_qp_p = ctrls->max_qp; + output->min_qp_b = ctrls->min_qp; + output->max_qp_b = ctrls->max_qp; + switch (ctrls->loop_filter_mode) { + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED: + output->en_dbk = 0; + output->en_sao = 0; + output->en_lf_cross_slice_boundary = 0; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED: + output->en_dbk = 1; + output->en_sao = 1; + output->en_lf_cross_slice_boundary = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + output->en_dbk = 1; + output->en_sao = 1; + output->en_lf_cross_slice_boundary = 0; + break; + default: + break; + } + switch (ctrls->refresh_type) { + case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE: + output->decoding_refresh_type = DEC_REFRESH_TYPE_NON_IRAP; + break; + case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR: + output->decoding_refresh_type = DEC_REFRESH_TYPE_IDR; + break; + default: + break; + } + output->intra_period = ctrls->refresh_period; + if (output->idr_period) { + output->decoding_refresh_type = DEC_REFRESH_TYPE_IDR; + output->intra_period = output->idr_period; + output->idr_period = 0; + } + output->beta_offset_div2 = ctrls->lf_beta_offset_div2; + output->tc_offset_div2 = ctrls->lf_tc_offset_div2; + output->en_constrained_intra_pred = ctrls->const_intra_pred; + output->en_strong_intra_smoothing = ctrls->strong_smoothing; + output->en_temporal_mvp = ctrls->tmv_prediction; + output->num_ticks_poc_diff_one = DEFAULT_NUM_TICKS_POC_DIFF; + output->en_auto_level_adjusting = DEFAULT_EN_AUTO_LEVEL_ADJUSTING; + output->en_intra_trans_skip = DEFAULT_EN_INTRA_TRANS_SKIP; + output->en_me_center = DEFAULT_EN_ME_CENTER; + output->intra_4x4 = DEFAULT_INTRA_4X4; +} + +static void wave6_set_enc_open_param(struct enc_open_param *open_param, + struct vpu_instance *inst) +{ + struct enc_controls *ctrls = &inst->enc_ctrls; + struct enc_codec_param *output = &open_param->codec_param; + u32 ctu_size = (inst->std == W_AVC_ENC) ? 16 : 64; + u32 num_ctu_row = ALIGN(inst->src_fmt.height, ctu_size) / ctu_size; + + open_param->source_endian = VPU_SOURCE_ENDIAN; + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { + open_param->src_format = FORMAT_420; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422P || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16M || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61M) { + open_param->src_format = FORMAT_422; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV24 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV42) { + open_param->src_format = FORMAT_YUV444_24BIT; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV24) { + open_param->src_format = FORMAT_YUV444_24BIT_PACKED; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUYV) { + open_param->src_format = FORMAT_YUYV; + open_param->packed_format = PACKED_YUYV; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGB24) { + open_param->src_format = FORMAT_RGB_24BIT_PACKED; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_P010) { + open_param->src_format = FORMAT_420_P10_16BIT_MSB; + open_param->source_endian = VDI_128BIT_LE_BYTE_SWAP; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_XRGB32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32) { + open_param->src_format = FORMAT_RGB_32BIT_PACKED; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB2101010) { + open_param->src_format = FORMAT_RGB_P10_32BIT_PACKED; + open_param->source_endian = VDI_128BIT_LE_WORD_BYTE_SWAP; + } + open_param->line_buf_int_en = true; + open_param->stream_endian = VPU_STREAM_ENDIAN; + open_param->inst_buffer.temp_base = inst->dev->temp_vbuf.daddr; + open_param->inst_buffer.temp_size = inst->dev->temp_vbuf.size; + open_param->inst_buffer.ar_base = inst->ar_vbuf.daddr; + open_param->pic_width = inst->codec_rect.width; + open_param->pic_height = inst->codec_rect.height; + + output->custom_map_endian = VPU_USER_DATA_ENDIAN; + output->gop_preset_idx = PRESET_IDX_IPP_SINGLE; + output->temp_layer_cnt = DEFAULT_TEMP_LAYER_CNT; + output->rc_initial_level = DEFAULT_RC_INITIAL_LEVEL; + output->pic_rc_max_dqp = DEFAULT_PIC_RC_MAX_DQP; + output->rc_initial_qp = DEFAULT_RC_INITIAL_QP; + output->en_adaptive_round = DEFAULT_EN_ADAPTIVE_ROUND; + output->q_round_inter = DEFAULT_Q_ROUND_INTER; + output->q_round_intra = DEFAULT_Q_ROUND_INTRA; + + output->frame_rate = inst->frame_rate; + output->idr_period = ctrls->gop_size; + output->rc_mode = ctrls->bitrate_mode; + output->rc_update_speed = (ctrls->bitrate_mode) ? DEFAULT_RC_UPDATE_SPEED_CBR : + DEFAULT_RC_UPDATE_SPEED_VBR; + output->en_rate_control = ctrls->frame_rc_enable; + output->en_cu_level_rate_control = ctrls->mb_rc_enable; + output->max_intra_pic_bit = inst->dst_fmt.plane_fmt[0].sizeimage * 8; + output->max_inter_pic_bit = inst->dst_fmt.plane_fmt[0].sizeimage * 8; + output->bitrate = ctrls->bitrate; + output->cpb_size = wave6_cpb_size_msec(ctrls->h264.cpb_size, ctrls->bitrate); + output->slice_mode = ctrls->slice_mode; + output->slice_arg = ctrls->slice_max_mb; + output->forced_idr_header = ctrls->prepend_spspps_to_idr; + output->en_vbv_overflow_drop_frame = (ctrls->frame_skip_mode) ? 1 : 0; + if (ctrls->intra_refresh_period) { + output->intra_refresh_mode = INTRA_REFRESH_ROW; + if (ctrls->intra_refresh_period < num_ctu_row) { + output->intra_refresh_arg = (num_ctu_row + ctrls->intra_refresh_period - 1) + / ctrls->intra_refresh_period; + } else { + output->intra_refresh_arg = 1; + } + } + output->sar.enable = ctrls->h264.vui_sar_enable; + output->sar.idc = ctrls->h264.vui_sar_idc; + if (output->sar.idc == V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED) + output->sar.idc = H264_VUI_SAR_IDC_EXTENDED; + output->sar.width = ctrls->h264.vui_ext_sar_width; + output->sar.height = ctrls->h264.vui_ext_sar_height; + output->color.video_signal_type_present = DEFAULT_VUI_VIDEO_SIGNAL_TYPE_PRESENT_FLAG; + output->color.color_range = to_video_full_range_flag(inst->quantization); + output->color.color_description_present = DEFAULT_VUI_COLOR_DESCRIPTION_PRESENT_FLAG; + output->color.color_primaries = to_colour_primaries(inst->colorspace); + output->color.transfer_characteristics = to_transfer_characteristics(inst->colorspace, + inst->xfer_func); + output->color.matrix_coefficients = to_matrix_coeffs(inst->colorspace, inst->ycbcr_enc); + output->conf_win.left = inst->crop.left - inst->codec_rect.left; + output->conf_win.top = inst->crop.top - inst->codec_rect.top; + output->conf_win.right = inst->codec_rect.width + - inst->crop.width - output->conf_win.left; + output->conf_win.bottom = inst->codec_rect.height + - inst->crop.height - output->conf_win.top; + + switch (inst->std) { + case W_AVC_ENC: + wave6_set_enc_h264_param(output, &ctrls->h264); + break; + case W_HEVC_ENC: + wave6_set_enc_hevc_param(output, &ctrls->hevc); + break; + default: + break; + } +} + +static int wave6_vpu_enc_create_instance(struct vpu_instance *inst) +{ + int ret; + struct enc_open_param open_param; + + memset(&open_param, 0, sizeof(struct enc_open_param)); + + wave6_vpu_activate(inst->dev); + ret = pm_runtime_resume_and_get(inst->dev->dev); + if (ret) { + dev_err(inst->dev->dev, "runtime_resume failed %d\n", ret); + return ret; + } + + inst->ar_vbuf.size = ALIGN(WAVE6_ARBUF_SIZE, 4096); + ret = wave6_alloc_dma(inst->dev->dev, &inst->ar_vbuf); + if (ret) { + dev_err(inst->dev->dev, "alloc ar of size %zu failed\n", + inst->ar_vbuf.size); + goto error_pm; + } + + wave6_set_enc_open_param(&open_param, inst); + + ret = wave6_vpu_enc_open(inst, &open_param); + if (ret) { + dev_err(inst->dev->dev, "failed create instance : %d\n", ret); + goto error_open; + } + + dev_dbg(inst->dev->dev, "[%d] encoder\n", inst->id); + wave6_vpu_create_dbgfs_file(inst); + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_OPEN); + + return 0; + +error_open: + wave6_free_dma(&inst->ar_vbuf); +error_pm: + pm_runtime_put_sync(inst->dev->dev); + return ret; +} + +static int wave6_vpu_enc_initialize_instance(struct vpu_instance *inst) +{ + int ret; + struct enc_initial_info initial_info; + struct v4l2_ctrl *ctrl; + + if (inst->enc_ctrls.mirror_direction) { + wave6_vpu_enc_give_command(inst, ENABLE_MIRRORING, NULL); + wave6_vpu_enc_give_command(inst, SET_MIRROR_DIRECTION, + &inst->enc_ctrls.mirror_direction); + } + if (inst->enc_ctrls.rot_angle) { + wave6_vpu_enc_give_command(inst, ENABLE_ROTATION, NULL); + wave6_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, + &inst->enc_ctrls.rot_angle); + } + + ret = wave6_vpu_enc_issue_seq_init(inst); + if (ret) { + dev_err(inst->dev->dev, "seq init fail %d\n", ret); + return ret; + } + + ret = wave6_vpu_wait_interrupt(inst, W6_VPU_TIMEOUT); + if (ret) { + dev_err(inst->dev->dev, "seq init timeout\n"); + return ret; + } + + ret = wave6_vpu_enc_complete_seq_init(inst, &initial_info); + if (ret) { + dev_err(inst->dev->dev, "seq init error\n"); + return ret; + } + + dev_dbg(inst->dev->dev, "min_fb_cnt : %d | min_src_cnt : %d\n", + initial_info.min_frame_buffer_count, + initial_info.min_src_frame_count); + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, initial_info.min_src_frame_count); + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); + + return 0; +} + +static int wave6_vpu_enc_prepare_fb(struct vpu_instance *inst) +{ + int ret; + unsigned int i; + unsigned int fb_num; + unsigned int mv_num; + unsigned int fb_stride; + unsigned int fb_height; + unsigned int luma_size; + unsigned int chroma_size; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + fb_num = p_enc_info->initial_info.min_frame_buffer_count; + mv_num = p_enc_info->initial_info.req_mv_buffer_count; + + fb_stride = ALIGN(inst->codec_rect.width, W6_FBC_BUF_ALIGNMENT); + fb_height = ALIGN(inst->codec_rect.height, W6_FBC_BUF_ALIGNMENT); + + luma_size = fb_stride * fb_height; + chroma_size = ALIGN(fb_stride / 2, W6_FBC_BUF_ALIGNMENT) * fb_height; + + for (i = 0; i < fb_num; i++) { + struct frame_buffer *frame = &inst->frame_buf[i]; + struct vpu_buf *vframe = &inst->frame_vbuf[i]; + + vframe->size = luma_size + chroma_size; + ret = wave6_alloc_dma(inst->dev->dev, vframe); + if (ret) { + dev_err(inst->dev->dev, "alloc FBC buffer fail : %zu\n", + vframe->size); + goto error; + } + + frame->buf_y = vframe->daddr; + frame->buf_cb = vframe->daddr + luma_size; + frame->buf_cr = (dma_addr_t)-1; + frame->stride = fb_stride; + frame->height = fb_height; + frame->map_type = COMPRESSED_FRAME_MAP; + } + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_Y_TBL, fb_num); + if (ret) + goto error; + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_C_TBL, fb_num); + if (ret) + goto error; + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_MV_COL, mv_num); + if (ret) + goto error; + + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_SUB_SAMPLE, fb_num); + if (ret) + goto error; + + ret = wave6_vpu_enc_register_frame_buffer_ex(inst, fb_num, fb_stride, + fb_height, + COMPRESSED_FRAME_MAP); + if (ret) { + dev_err(inst->dev->dev, "register frame buffer fail %d\n", ret); + goto error; + } + + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_PIC_RUN); + + return 0; + +error: + wave6_vpu_enc_release_fb(inst); + return ret; +} + +static int wave6_vpu_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane inst_format = + (V4L2_TYPE_IS_OUTPUT(q->type)) ? inst->src_fmt : inst->dst_fmt; + unsigned int i; + + dev_dbg(inst->dev->dev, "%s: num_buffers %d num_planes %d type %d\n", + __func__, *num_buffers, *num_planes, q->type); + + if (*num_planes) { + if (inst_format.num_planes != *num_planes) + return -EINVAL; + + for (i = 0; i < *num_planes; i++) { + if (sizes[i] < inst_format.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = inst_format.num_planes; + for (i = 0; i < *num_planes; i++) { + sizes[i] = inst_format.plane_fmt[i].sizeimage; + dev_dbg(inst->dev->dev, "size[%d] : %d\n", i, sizes[i]); + } + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + struct v4l2_ctrl *ctrl; + unsigned int min_src_frame_count = 0; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); + if (ctrl) + min_src_frame_count = v4l2_ctrl_g_ctrl(ctrl); + + *num_buffers = max(*num_buffers, min_src_frame_count); + } + } + + return 0; +} + +static void wave6_vpu_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); + + dev_dbg(inst->dev->dev, "type %4d index %4d size[0] %4ld size[1] : %4ld | size[2] : %4ld\n", + vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) { + vbuf->sequence = inst->queued_src_buf_num++; + + vpu_buf->ts_input = ktime_get_raw(); + vpu_buf->force_key_frame = inst->enc_ctrls.force_key_frame; + inst->enc_ctrls.force_key_frame = false; + vpu_buf->force_frame_qp = (!inst->enc_ctrls.frame_rc_enable) ? true : false; + if (vpu_buf->force_frame_qp) { + if (inst->std == W_AVC_ENC) { + vpu_buf->force_i_frame_qp = inst->enc_ctrls.h264.i_frame_qp; + vpu_buf->force_p_frame_qp = inst->enc_ctrls.h264.p_frame_qp; + vpu_buf->force_b_frame_qp = inst->enc_ctrls.h264.b_frame_qp; + } else if (inst->std == W_HEVC_ENC) { + vpu_buf->force_i_frame_qp = inst->enc_ctrls.hevc.i_frame_qp; + vpu_buf->force_p_frame_qp = inst->enc_ctrls.hevc.p_frame_qp; + vpu_buf->force_b_frame_qp = inst->enc_ctrls.hevc.b_frame_qp; + } + } + } else { + inst->queued_dst_buf_num++; + } + + vpu_buf->consumed = false; + vpu_buf->used = false; + v4l2_m2m_buf_queue(inst->v4l2_fh.m2m_ctx, vbuf); +} + +static void wave6_vpu_enc_buf_finish(struct vb2_buffer *vb) +{ + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); + struct v4l2_ctrl *ctrl; + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) + return; + + ctrl = v4l2_ctrl_find(inst->v4l2_fh.ctrl_handler, V4L2_CID_MPEG_VIDEO_AVERAGE_QP); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, vpu_buf->average_qp); +} + +static int wave6_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane *fmt; + struct vb2_queue *vq_peer; + int ret = 0; + + trace_start_streaming(inst, q->type); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + fmt = &inst->src_fmt; + vq_peer = v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx); + } else { + fmt = &inst->dst_fmt; + vq_peer = v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx); + } + + dev_dbg(inst->dev->dev, "[%d] %s %c%c%c%c %dx%d, %d buffers\n", + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture", + fmt->pixelformat, + fmt->pixelformat >> 8, + fmt->pixelformat >> 16, + fmt->pixelformat >> 24, + fmt->width, fmt->height, vb2_get_num_buffers(q)); + + if (!vb2_is_streaming(vq_peer)) + return 0; + + wave6_vpu_pause(inst->dev->dev, 0); + + if (inst->state == VPU_INST_STATE_NONE) { + ret = wave6_vpu_enc_create_instance(inst); + if (ret) + goto exit; + } + + if (inst->state == VPU_INST_STATE_OPEN) { + ret = wave6_vpu_enc_initialize_instance(inst); + if (ret) { + wave6_vpu_enc_destroy_instance(inst); + goto exit; + } + } + + if (inst->state == VPU_INST_STATE_INIT_SEQ) { + ret = wave6_vpu_enc_prepare_fb(inst); + if (ret) { + wave6_vpu_enc_destroy_instance(inst); + goto exit; + } + } + +exit: + wave6_vpu_pause(inst->dev->dev, 1); + if (ret) + wave6_vpu_return_buffers(inst, q->type, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void wave6_vpu_enc_stop_streaming(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct vb2_queue *vq_peer; + + trace_stop_streaming(inst, q->type); + + dev_dbg(inst->dev->dev, "[%d] %s, input %d, decode %d\n", + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture", + inst->queued_src_buf_num, inst->sequence); + + if (inst->state == VPU_INST_STATE_NONE) + goto exit; + + if (wave6_vpu_both_queues_are_streaming(inst)) + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_STOP); + + wave6_vpu_pause(inst->dev->dev, 0); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + wave6_vpu_reset_performance(inst); + inst->queued_src_buf_num = 0; + inst->processed_buf_num = 0; + inst->error_buf_num = 0; + inst->sequence = 0; + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, false); + } else { + inst->eos = false; + inst->queued_dst_buf_num = 0; + } + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vq_peer = v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx); + else + vq_peer = v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx); + + if (!vb2_is_streaming(vq_peer) && inst->state != VPU_INST_STATE_NONE) + wave6_vpu_enc_destroy_instance(inst); + + wave6_vpu_pause(inst->dev->dev, 1); + +exit: + wave6_vpu_return_buffers(inst, q->type, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops wave6_vpu_enc_vb2_ops = { + .queue_setup = wave6_vpu_enc_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = wave6_vpu_enc_buf_queue, + .buf_finish = wave6_vpu_enc_buf_finish, + .start_streaming = wave6_vpu_enc_start_streaming, + .stop_streaming = wave6_vpu_enc_stop_streaming, +}; + +static void wave6_set_default_format(struct v4l2_pix_format_mplane *src_fmt, + struct v4l2_pix_format_mplane *dst_fmt) +{ + const struct vpu_format *vpu_fmt; + + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_RAW); + if (vpu_fmt) { + src_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; + src_fmt->num_planes = vpu_fmt->num_planes; + wave6_update_pix_fmt(src_fmt, + W6_DEF_ENC_PIC_WIDTH, W6_DEF_ENC_PIC_HEIGHT); + } + + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_CODEC); + if (vpu_fmt) { + dst_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; + dst_fmt->num_planes = vpu_fmt->num_planes; + wave6_update_pix_fmt(dst_fmt, + W6_DEF_ENC_PIC_WIDTH, W6_DEF_ENC_PIC_HEIGHT); + } +} + +static int wave6_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct vpu_instance *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->ops = &wave6_vpu_enc_vb2_ops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->buf_struct_size = sizeof(struct vpu_buffer); + src_vq->drv_priv = inst; + src_vq->lock = &inst->dev->dev_lock; + src_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->ops = &wave6_vpu_enc_vb2_ops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->buf_struct_size = sizeof(struct vpu_buffer); + dst_vq->drv_priv = inst; + dst_vq->lock = &inst->dev->dev_lock; + dst_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +static const struct vpu_instance_ops wave6_vpu_enc_inst_ops = { + .start_process = wave6_vpu_enc_start_encode, + .finish_process = wave6_vpu_enc_finish_encode, +}; + +static int wave6_vpu_open_enc(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct vpu_core_device *dev = video_drvdata(filp); + struct vpu_instance *inst = NULL; + struct v4l2_ctrl_handler *v4l2_ctrl_hdl; + int ret; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl; + + inst->dev = dev; + inst->type = VPU_INST_TYPE_ENC; + inst->ops = &wave6_vpu_enc_inst_ops; + + v4l2_fh_init(&inst->v4l2_fh, vdev); + filp->private_data = &inst->v4l2_fh; + v4l2_fh_add(&inst->v4l2_fh); + + inst->v4l2_fh.m2m_ctx = + v4l2_m2m_ctx_init(dev->m2m_dev, inst, wave6_vpu_enc_queue_init); + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); + goto free_inst; + } + + v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + 0, 51, 1, 8); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + 0, 51, 1, 51); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + 0, 51, 1, 30); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, + 0, 51, 1, 30); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, + 0, 51, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2, + -6, 6, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE, + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, + BIT(V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA), + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD, + 0, 2047, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION, + 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 0, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_2, 0, + V4L2_MPEG_VIDEO_H264_LEVEL_5_0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 8); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 51, 1, 30); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + 0, 51, 1, 30); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + 0, 51, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, + -12, 12, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + 0, 2047, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, 0, 1, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED, 0, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH, + 0, 0xFFFF, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT, + 0, 0xFFFF, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_ROTATE, + 0, 270, 90, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 0, 18750000, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 1, 240000000, 1, 2097152); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, 2047, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + 0, 0x3FFFF, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR, + 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE, + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC, + BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM), + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD, + 0, 2160, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE, + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, + BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT), + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, NULL, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0); + + if (v4l2_ctrl_hdl->error) { + ret = -ENODEV; + goto err_m2m_release; + } + + inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl; + v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); + + wave6_set_default_format(&inst->src_fmt, &inst->dst_fmt); + wave6_update_crop_info(inst, 0, 0, inst->dst_fmt.width, inst->dst_fmt.height); + inst->colorspace = V4L2_COLORSPACE_DEFAULT; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->quantization = V4L2_QUANTIZATION_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; + inst->frame_rate = 30; + + return 0; + +err_m2m_release: + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); +free_inst: + kfree(inst); + return ret; +} + +static int wave6_vpu_enc_release(struct file *filp) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(filp->private_data); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + + mutex_lock(&inst->dev->dev_lock); + if (inst->state != VPU_INST_STATE_NONE) { + wave6_vpu_pause(inst->dev->dev, 0); + wave6_vpu_enc_destroy_instance(inst); + wave6_vpu_pause(inst->dev->dev, 1); + } + mutex_unlock(&inst->dev->dev_lock); + + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl); + v4l2_fh_del(&inst->v4l2_fh); + v4l2_fh_exit(&inst->v4l2_fh); + kfree(inst); + + return 0; +} + +static const struct v4l2_file_operations wave6_vpu_enc_fops = { + .owner = THIS_MODULE, + .open = wave6_vpu_open_enc, + .release = wave6_vpu_enc_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +int wave6_vpu_enc_register_device(struct vpu_core_device *dev) +{ + struct video_device *vdev_enc; + int ret; + + vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_enc), GFP_KERNEL); + if (!vdev_enc) + return -ENOMEM; + + dev->video_dev_enc = vdev_enc; + + strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc->name)); + vdev_enc->fops = &wave6_vpu_enc_fops; + vdev_enc->ioctl_ops = &wave6_vpu_enc_ioctl_ops; + vdev_enc->release = video_device_release_empty; + vdev_enc->v4l2_dev = &dev->v4l2_dev; + vdev_enc->vfl_dir = VFL_DIR_M2M; + vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vdev_enc->lock = &dev->dev_lock; + video_set_drvdata(vdev_enc, dev); + + ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + return 0; +} + +void wave6_vpu_enc_unregister_device(struct vpu_core_device *dev) +{ + video_unregister_device(dev->video_dev_enc); +} diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c new file mode 100644 index 000000000000..cea501b13539 --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - v4l2 driver helper interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#include +#include +#include "wave6-vpu.h" +#include "wave6-vpu-core.h" +#include "wave6-vpu-dbg.h" +#include "wave6-trace.h" + +void wave6_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, + unsigned int width, + unsigned int height) +{ + const struct v4l2_format_info *fmt_info; + unsigned int stride_y; + int i; + + pix_mp->width = width; + pix_mp->height = height; + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + + fmt_info = v4l2_format_info(pix_mp->pixelformat); + if (!fmt_info) { + pix_mp->plane_fmt[0].bytesperline = 0; + if (!pix_mp->plane_fmt[0].sizeimage) + pix_mp->plane_fmt[0].sizeimage = width * height; + + return; + } + + stride_y = width * fmt_info->bpp[0]; + if (pix_mp->plane_fmt[0].bytesperline <= W6_MAX_PIC_STRIDE) + stride_y = max(stride_y, pix_mp->plane_fmt[0].bytesperline); + stride_y = round_up(stride_y, W6_PIC_STRIDE_ALIGNMENT); + pix_mp->plane_fmt[0].bytesperline = stride_y; + pix_mp->plane_fmt[0].sizeimage = stride_y * height; + + stride_y = DIV_ROUND_UP(stride_y, fmt_info->bpp[0]); + + for (i = 1; i < fmt_info->comp_planes; i++) { + unsigned int stride_c, sizeimage_c; + + stride_c = DIV_ROUND_UP(stride_y, fmt_info->hdiv) * + fmt_info->bpp[i]; + sizeimage_c = stride_c * DIV_ROUND_UP(height, fmt_info->vdiv); + + if (fmt_info->mem_planes == 1) { + pix_mp->plane_fmt[0].sizeimage += sizeimage_c; + } else { + pix_mp->plane_fmt[i].bytesperline = stride_c; + pix_mp->plane_fmt[i].sizeimage = sizeimage_c; + } + } +} + +dma_addr_t wave6_get_dma_addr(struct vb2_v4l2_buffer *buf, unsigned int plane_no) +{ + return vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, plane_no) + + buf->planes[plane_no].data_offset; +} + +struct vb2_v4l2_buffer *wave6_get_dst_buf_by_addr(struct vpu_instance *inst, + dma_addr_t addr) +{ + struct vb2_v4l2_buffer *vb2_v4l2_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vb2_v4l2_buffer *dst_buf = NULL; + + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + vb2_v4l2_buf = &v4l2_m2m_buf->vb; + if (addr == wave6_get_dma_addr(vb2_v4l2_buf, 0)) { + dst_buf = vb2_v4l2_buf; + break; + } + } + + return dst_buf; +} + +enum codec_std wave6_to_codec_std(enum vpu_instance_type type, unsigned int v4l2_pix_fmt) +{ + enum codec_std std = STD_UNKNOWN; + + if (v4l2_pix_fmt == V4L2_PIX_FMT_H264) + std = (type == VPU_INST_TYPE_DEC) ? W_AVC_DEC : W_AVC_ENC; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_HEVC) + std = (type == VPU_INST_TYPE_DEC) ? W_HEVC_DEC : W_HEVC_ENC; + + return std; +} + +const char *wave6_vpu_instance_state_name(u32 state) +{ + switch (state) { + case VPU_INST_STATE_NONE: return "none"; + case VPU_INST_STATE_OPEN: return "open"; + case VPU_INST_STATE_INIT_SEQ: return "init_seq"; + case VPU_INST_STATE_PIC_RUN: return "pic_run"; + case VPU_INST_STATE_SEEK: return "seek"; + case VPU_INST_STATE_STOP: return "stop"; + } + return "unknown"; +} + +void wave6_vpu_set_instance_state(struct vpu_instance *inst, u32 state) +{ + trace_set_state(inst, state); + + dev_dbg(inst->dev->dev, "[%d] %s -> %s\n", + inst->id, + wave6_vpu_instance_state_name(inst->state), + wave6_vpu_instance_state_name(state)); + + inst->state = state; + if (state == VPU_INST_STATE_PIC_RUN && !inst->performance.ts_first) + inst->performance.ts_first = ktime_get_raw(); +} + +u64 wave6_vpu_cycle_to_ns(struct vpu_core_device *core, u64 cycle) +{ + struct wave6_vpu_device *vpu = dev_get_drvdata(core->dev->parent); + unsigned long rate; + + if (!vpu || !vpu->ops) + return 0; + + rate = call_vop(vpu, get_clk_rate); + + return (cycle * NSEC_PER_SEC) / rate; +} + +int wave6_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout) +{ + int ret; + + ret = wait_for_completion_timeout(&inst->dev->irq_done, + msecs_to_jiffies(timeout)); + if (!ret) + return -ETIMEDOUT; + + reinit_completion(&inst->dev->irq_done); + + return 0; +} + +int wave6_vpu_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct vpu_instance *inst = wave6_to_vpu_inst(fh); + bool is_decoder = (inst->type == VPU_INST_TYPE_DEC) ? true : false; + + dev_dbg(inst->dev->dev, "%s: [%s] type: %d id: %d | flags: %d\n", + __func__, is_decoder ? "decoder" : "encoder", sub->type, + sub->id, sub->flags); + + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + if (is_decoder) + return v4l2_src_change_event_subscribe(fh, sub); + return -EINVAL; + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +void wave6_vpu_return_buffers(struct vpu_instance *inst, + unsigned int type, enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *buf; + int i; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + while ((buf = v4l2_m2m_src_buf_remove(inst->v4l2_fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, state); + } else { + while ((buf = v4l2_m2m_dst_buf_remove(inst->v4l2_fh.m2m_ctx))) { + for (i = 0; i < inst->dst_fmt.num_planes; i++) + vb2_set_plane_payload(&buf->vb2_buf, i, 0); + v4l2_m2m_buf_done(buf, state); + } + } +} + +u32 wave6_vpu_get_consumed_fb_num(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *vb2_v4l2_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vpu_buffer *vpu_buf; + u32 num = 0; + + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + vb2_v4l2_buf = &v4l2_m2m_buf->vb; + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); + if (vpu_buf->consumed) + num++; + } + + return num; +} + +u32 wave6_vpu_get_used_fb_num(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *vb2_v4l2_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vpu_buffer *vpu_buf; + u32 num = 0; + + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + vb2_v4l2_buf = &v4l2_m2m_buf->vb; + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); + if (vpu_buf->used) + num++; + } + + return num; +} + +static bool wave6_vpu_check_fb_available(struct vpu_instance *inst) +{ + struct vb2_v4l2_buffer *vb2_v4l2_buf; + struct v4l2_m2m_buffer *v4l2_m2m_buf; + struct vpu_buffer *vpu_buf; + + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, v4l2_m2m_buf) { + vb2_v4l2_buf = &v4l2_m2m_buf->vb; + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); + + if (!vpu_buf->used) + return true; + } + + return false; +} + +static int wave6_vpu_job_ready(void *priv) +{ + struct vpu_instance *inst = priv; + + dev_dbg(inst->dev->dev, "[%d]%s: state %d\n", + inst->id, __func__, inst->state); + + if (inst->type == VPU_INST_TYPE_DEC && inst->state == VPU_INST_STATE_OPEN) + return 1; + if (inst->state < VPU_INST_STATE_PIC_RUN) + return 0; + if (inst->state == VPU_INST_STATE_STOP && inst->eos) + return 0; + if (!wave6_vpu_check_fb_available(inst)) + return 0; + + return 1; +} + +static void wave6_vpu_device_run_timeout(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct vpu_core_device *dev = container_of(dwork, struct vpu_core_device, task_timer); + struct vpu_instance *inst = v4l2_m2m_get_curr_priv(dev->m2m_dev); + struct vb2_v4l2_buffer *src_buf = NULL; + struct vb2_v4l2_buffer *dst_buf = NULL; + + if (!inst) + return; + + dev_err(inst->dev->dev, "[%d] sequence %d timeout\n", inst->id, inst->sequence); + src_buf = v4l2_m2m_src_buf_remove(inst->v4l2_fh.m2m_ctx); + if (src_buf) { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + if (inst->type == VPU_INST_TYPE_DEC) + inst->processed_buf_num++; + inst->error_buf_num++; + } + + dst_buf = v4l2_m2m_dst_buf_remove(inst->v4l2_fh.m2m_ctx); + if (dst_buf) + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + + vb2_queue_error(v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx)); + vb2_queue_error(v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx)); + + v4l2_m2m_job_finish(inst->dev->m2m_dev, inst->v4l2_fh.m2m_ctx); +} + +static void wave6_vpu_device_run(void *priv) +{ + struct vpu_instance *inst = priv; + int ret; + + dev_dbg(inst->dev->dev, "[%d]%s: state %d\n", + inst->id, __func__, inst->state); + + ret = inst->ops->start_process(inst); + if (!ret) + schedule_delayed_work(&inst->dev->task_timer, msecs_to_jiffies(W6_VPU_TIMEOUT)); + else + v4l2_m2m_job_finish(inst->dev->m2m_dev, inst->v4l2_fh.m2m_ctx); +} + +void wave6_vpu_finish_job(struct vpu_instance *inst) +{ + cancel_delayed_work(&inst->dev->task_timer); + v4l2_m2m_job_finish(inst->dev->m2m_dev, inst->v4l2_fh.m2m_ctx); +} + +void wave6_vpu_handle_performance(struct vpu_instance *inst, struct vpu_buffer *vpu_buf) +{ + s64 latency, time_spent; + + if (!inst || !vpu_buf) + return; + + inst->performance.ts_last = vpu_buf->ts_output; + + latency = vpu_buf->ts_output - vpu_buf->ts_input; + time_spent = vpu_buf->ts_finish - vpu_buf->ts_start; + + if (!inst->performance.latency_first) + inst->performance.latency_first = latency; + inst->performance.latency_max = max_t(s64, latency, inst->performance.latency_max); + + if (!inst->performance.min_process_time) + inst->performance.min_process_time = time_spent; + else if (inst->performance.min_process_time > time_spent) + inst->performance.min_process_time = time_spent; + + if (inst->performance.max_process_time < time_spent) + inst->performance.max_process_time = time_spent; + + inst->performance.total_sw_time += time_spent; + inst->performance.total_hw_time += vpu_buf->hw_time; +} + +void wave6_vpu_reset_performance(struct vpu_instance *inst) +{ + if (!inst) + return; + + if (inst->processed_buf_num) { + s64 tmp; + s64 fps_act, fps_sw, fps_hw; + struct vpu_performance_info *perf = &inst->performance; + + tmp = MSEC_PER_SEC * inst->processed_buf_num; + fps_act = DIV_ROUND_CLOSEST(tmp, (perf->ts_last - perf->ts_first) / NSEC_PER_MSEC); + fps_sw = DIV_ROUND_CLOSEST(tmp, perf->total_sw_time / NSEC_PER_MSEC); + fps_hw = DIV_ROUND_CLOSEST(tmp, perf->total_hw_time / NSEC_PER_MSEC); + dev_dbg(inst->dev->dev, + "[%d] fps actual: %lld, sw: %lld, hw: %lld, latency(ms) %llu.%06llu\n", + inst->id, fps_act, fps_sw, fps_hw, + perf->latency_first / NSEC_PER_MSEC, + perf->latency_first % NSEC_PER_MSEC); + } + + memset(&inst->performance, 0, sizeof(inst->performance)); +} + +static const struct v4l2_m2m_ops wave6_vpu_m2m_ops = { + .device_run = wave6_vpu_device_run, + .job_ready = wave6_vpu_job_ready, +}; + +int wave6_vpu_init_m2m_dev(struct vpu_core_device *dev) +{ + dev->m2m_dev = v4l2_m2m_init(&wave6_vpu_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + dev_err(dev->dev, "v4l2_m2m_init fail: %ld\n", PTR_ERR(dev->m2m_dev)); + return PTR_ERR(dev->m2m_dev); + } + + INIT_DELAYED_WORK(&dev->task_timer, wave6_vpu_device_run_timeout); + + return 0; +} + +void wave6_vpu_release_m2m_dev(struct vpu_core_device *dev) +{ + v4l2_m2m_release(dev->m2m_dev); +} From patchwork Tue Apr 22 09:31:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nas Chung X-Patchwork-Id: 883352 Received: from SEVP216CU002.outbound.protection.outlook.com (mail-koreacentralazon11022082.outbound.protection.outlook.com [40.107.43.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 3AD51201270; Tue, 22 Apr 2025 09:31:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.43.82 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314313; cv=fail; b=m75qNgK+Vwo8EQtxBjrZ3eUjlg9b0AkyHnYM0XesoIX9Pf4fjy6xZngaFqQGbawAcy6U1BFxGMYh3bTzifCspoduA0ftVr6BDdHsrlvFoOtOTTD7whVEMCc72GkmsK4dbjUi4vqWQzkRXwwSVey/yEU5gPugpU2U+Jp9Xxd5tAY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745314313; c=relaxed/simple; bh=VluaPHL9UX9LqMPQMC0/Jh2vKC2+SpztPMQr3w/jmc4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=FdMVTPM+Ek/GC8nAPOctydjKJlQ74RbFhL6GtmDL9DQ3W9yarVMFcsSKqEglFDF41nd8uqWjZXMgvMUdURlGnl3IE+9CsSamURyFE3fc3FdH84QaxymGIKKhFpH5Vi4TQKBgj+1OjAKZA+H7c/q/axeKKP7c3ZoV43L89ZyL65k= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com; spf=fail smtp.mailfrom=chipsnmedia.com; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b=E523KKnc; arc=fail smtp.client-ip=40.107.43.82 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=chipsnmedia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chipsnmedia.com header.i=@chipsnmedia.com header.b="E523KKnc" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Qh4yJCBWek5FfSWgyyVVy4FjLAbHfVAOJxyrVx7SyCl78Z4ko8jOEl0ZkA+sgtyVj/uC9VnhF8DkklYWIyf2bnbPzUVSJn/BSp7ZMxhA47oBM/zSt1y+6/25sBXqC2J2H3ZfwoNRM6NlHXgD+QHf126fpnhiN1KRwhZRJe5hwe5N6P+hHBv95aqHEdhZpMq1uivkmh9Kw631jwy+bXU2lmAna7cx18vmCINhbor7NlEuAlGWgzUZIgcdfh2tQmOuDBx2FiiDaUFsYB/Y0dCMh3AEWrK6AZhpSNHqB06/l6VyvVYhS7LzaVD6knnwXpk92G2Sm/4AZ/NFLVlyyKrUJw== 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=J27edSFdIELhJiatcHJmPGjPZyF2yRGGhn+qGuee2B8=; b=G9N4CGif3s23Qag1+h1RW5FNjtm5Mp3KFJEmUJ4xAUw30t6Px7+OyFP1QTOTUhZiVUUoohIWp9dRPcuxTN61oF69VKLm4hmkxaXenI61s166k7AFKKdmD+3XtTydD2maR92R4D5d2BomLS5E4cRcJNDdaEcXc3LKWCJI60MNY8OE2Dgcv+gMbUOIo6+wDdDJvVOdaBEO/hdm5R0qlm12Fe1xMrHyhXBW4KIb+Lwmgpq3ZBMAJKMeR875zEwjAvguRuono7/6CayOEfwcBl8gQFSiY1k0wg5RtwoIMyYNAfhpbDJSV2HbrioHclVu4QdS/HHW1y5E+1tz44dxmk963A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=chipsnmedia.com; dmarc=pass action=none header.from=chipsnmedia.com; dkim=pass header.d=chipsnmedia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chipsnmedia.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=J27edSFdIELhJiatcHJmPGjPZyF2yRGGhn+qGuee2B8=; b=E523KKncpoPczbHaCHX18p07TMdEm4P3QK5ZTHvdbQFxfhWFUyGd+PDWOE+wBsnpTJRIO/qs6zwP4N3T1VwX5Nkg4pMg7GMFatxuAGPJtrjCQvbG0eVsy1newADxHhFDLT+q91LvqEUh2t9+yifC5Ud3+98mYS5B0goeI5UqeMQ= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=chipsnmedia.com; Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) by SE2P216MB1521.KORP216.PROD.OUTLOOK.COM (2603:1096:101:1e::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8655.34; Tue, 22 Apr 2025 09:31:39 +0000 Received: from SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07]) by SL2P216MB1246.KORP216.PROD.OUTLOOK.COM ([fe80::9e3d:ee20:8cc7:3c07%3]) with mapi id 15.20.8655.033; Tue, 22 Apr 2025 09:31:39 +0000 From: Nas Chung To: mchehab@kernel.org, hverkuil@xs4all.nl, sebastian.fricke@collabora.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-imx@nxp.com, marex@denx.de, jackson.lee@chipsnmedia.com, lafley.kim@chipsnmedia.com, Nas Chung , Ming Qian Subject: [PATCH v2 8/8] media: chips-media: wave6: Add Wave6 core driver Date: Tue, 22 Apr 2025 18:31:19 +0900 Message-Id: <20250422093119.595-9-nas.chung@chipsnmedia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250422093119.595-1-nas.chung@chipsnmedia.com> References: <20250422093119.595-1-nas.chung@chipsnmedia.com> X-ClientProxiedBy: SE2P216CA0120.KORP216.PROD.OUTLOOK.COM (2603:1096:101:2c9::7) To SL2P216MB1246.KORP216.PROD.OUTLOOK.COM (2603:1096:101:a::9) Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SL2P216MB1246:EE_|SE2P216MB1521:EE_ X-MS-Office365-Filtering-Correlation-Id: fe2a3e2c-ddfc-4694-a323-08dd81807bf7 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|7416014|52116014|376014|1800799024|366016|38350700014; X-Microsoft-Antispam-Message-Info: n4KSr2Gc1VQ4fKF+40J6p/1LBoWyu4khtN14ZgLkNt76PYSCcPvvJP7OZc0fnbRnN/hoP8518ZAF0T+TVEzdyc04i88r5kWxNAyhmFCog5fHDQ2PzWyeg7U5YeiTOvVQBjmsktkXCgkAfL9/cyzYN1yF4bP9zO55WKxtJylDeCjur+GQcG41FAEHG2ctNUxOO6FyX58z5xJsRXmYJfr2sSCsgDfY4YRQ+sPRcI5O31SBgO7IhcUJAhVWB9BBrcD/Iq26oRB0ZLjV3LxMqyxfsTECA0h9oWtBrsuYWDpnBTiVojHZYKGw6dqcILBTk7zgxk1O8LK+HhKB2Z7bdrsaSABmGsaB0Rp2pt8sTOCSSHXaTxjEl8hgSn1nJ8YOh3UKxIwIXwSfDnOuhoLYfFNRUc1YJCDgmPU0Jrzg5NzHibZDE5ME+PNT4NUEJgtnB+QS/mC6uf4+AssMmcjFuG19jNeLMAiMs0QW5cEt0DCsnoxUOJIqpsBluhgZF6a+Skx7jfhsPuhNuRB7gK4Qy48emULZsZ8SGQJeJ7k6NYbIFSNYGPdtWNkdKhiNzHTL37Bq2no1lP7l6XkEKZseOtjfp68KXwRVWu4czt4rw6N6OdzAnDQqCXdOmaptHlly2dzzCH+rY8X7pm6GAjYxDULHZLRKwkBgZd2Ocd/8GK7F9fW142lAI36hNjMRlVERmmj2f3yiW8TC3DUpBb+uLVDtIJhsdKJxjfar0FfpD8JOC8JpuyctIaZ9yWMPxQuf1t90+s2E+3IameHCgzatsbvmk5UutVT6fpyIGglZ+4LM8puZJwg30zz+mdx1WDU6nIkIizUKbz7hmOgkOvrTKBR/AibtDtOfJaNUXthIi/lP5/N11jYOmbWFwLU/WP0aOB8hw9xTOuOxc3vQagvrtcAtJVtVkjNLzul36s81DHIfI+sKam6hKwoNr79e5xJOPJ0dY1GfTMoNtGt6fex4nUqBZhcpOtCfGx3kMJLcPebKhj45xInLewB4iiEl4MUSDQjNDa3y/2nSAPSqUwAT37kmGQs8e6slzblFflvJg5IEMUiON5WMQUFh1FLn8NIKM3S8HiDO7L4E2sx7ykAiv4b9j0dK/OujXZmVLblDjABTecrksOrWKgzAVsR5PZpHRUOHvYAVHzHA0Kb7ftZTpgufamDTYpe1ogpXFxuCygw9Yova5NQbmCHBdaAPomcvbRFIc8juGVYLl88ofUi3gpjUczzd/EtjAmMnmJDCyStTmwFzVIMSnAYfKlaVV82zqlQytVQXvJ/749YqR9WhdQlV+sQsxVn9VKdU4HtuWD4ORP+UqOuNSnI3a+rXKv34cYKXYDx4eye9fVoi7gH5QeZ6QGAneQO3CymKBUe7FOuAE5NajSRFRRMGqGT8Zkwus9J/5bhW2kK9k13KYK6XyW2LSQ071vmJtW8I5M2afN9T3GVCGm327Mi8ztxSls0ffehR3uL88Y91LsoRXI9AL76SLg== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SL2P216MB1246.KORP216.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230040)(7416014)(52116014)(376014)(1800799024)(366016)(38350700014); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: +GxXOWpEmNm1nK73O1OKo2zc9Jxw2EBc7xWdVH0bqRTgBR787rIdI6z++TnJ9tiliQivWxQLObPMj1j6DIA/zwY8J6LizOnFZXoszYCHrkv9WmBfCoaLV0BancQW1IjE0csFcqKQ3qk6N/i/wWyQ4nmMGmO3k+X0FQEhXbrfumKQfhuuf8NRRuceDhYuRi5M+jsbCJwzl74TfX27mC8bKmkLhkN9+vu5YCX+aba/umuJqTZz0GZ2ZNCSksjPDm/onFzUHwQAGuFGCR3QLyowohqNctVEOSc6AicjwZETLQoEoJy1C2ZWY9F8mjl7IszEo11s5k0OgAa5VfcbpuK2JmIGyXS1GYQ2wxHc1Oli4VGFv48SEzRQ/s+tzIldBZN1/Jb49Ns4fvoqHKAycxXhaF6MBSasVLDIG1ZIEndUsptDM/Y+5xTACrVfQkeF9X0FqK3sg0O38MfpwR8GowqjKefKZ7lkv/NW9NLOLralxgdOBLQDbycC9aAHIggHk2RzOej5r6f8cHzZJRtroHU/NjACH5rLVe0Wvg18VdCnpbQAn1Kf3q2CFbS3ncxFPaC/+9IcgPiGQjZyqFDCoScvkfK64HA7W8+xzBam6d7ILJ/3dePvfApQ6JSJpDMYs3TM630R7G1TmhxdYNgwUqiXztLdjIuB9XLHpGLCrA9NPyo2ddlv9bkGh2y+LFQOVFxvAfDmgFt+g6DVQQMeM0u8GtEY8l6IuLjxdR88dijtL6uyMth/9ZWLpGSDM02EG8nf54lPqc4s6xn37AidSkSNOoOLk/v6XsnqDQuL/7uDUd1UX7Mm/iCVcIRzKxCwJzRPyXKPBN1RqZnOqHZItg0bD7fbdb+ayXSecZgKqNk6k3lEImC8W94YXYWOR9Kkg5/KxQV/TwiYxlOo4qjxMIix9qbNEW7OYSi3PXzkE/LhQyxUikbNVeTdXczn3w9YVjK6OeeKEUojKWY3hmqmK47Ac2x4OmsaM5aL1xouG2xyle0GiNTn/U0/p6JTian1Xw0oD+c2CpnG8EsZ70ubXwukXHdiXVY2rB/hmzBmyl/jw9/drVMfHv9ECV/lgAAMJGHixu5pUS1WyBosaTkwa6P7lkFsscDWP9AZCP5jsFkJuMQ66rYw8Niibj1nSWzUQOtuC5WJ01WNsjNfbIBAuD+4qnrX7i5EyUzAsgzNIaTCjk2Cw1IsZP0+xQzNoGKH7gUqKmwpj8eRrFQWaMxqJAR0kqLJA701bCopqD2weMJTLOaHsgwKbxh3M2dx7iyXCDpHawTSEm8T3lcFYPJ9oiCsW9dluwJj07Hu6+J2c9XzPCmip8wUCQfiYum77q5udabzkglTSx84MME2yVE5P6Wl/ABB/7kEswbodzu3HQZh2J8+RqOkqA1BbAo3mprrSRYdx1A2DAOhOhIALxjKS38ewl/GKlFDklZ2eVm9LLxCLgxk/6HPPWYDKO02gcXky6y3OjpT2pQuptFYCJznoqDHMOw+FhVczQXEuMqijRNFQBzzd09Sfg5nQpliLQ44CLUclLRLG7pXzTeZ3awgkcPy9dlj2wH3m7mDmMMGemA6OMIiyIT+mX9TllzaRx7Irp076iMqCKSCwqSPkAtyB5li5Q== X-OriginatorOrg: chipsnmedia.com X-MS-Exchange-CrossTenant-Network-Message-Id: fe2a3e2c-ddfc-4694-a323-08dd81807bf7 X-MS-Exchange-CrossTenant-AuthSource: SL2P216MB1246.KORP216.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Apr 2025 09:31:39.3823 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 4d70c8e9-142b-4389-b7f2-fa8a3c68c467 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: wDbei8Aqp2I8XDIeY3TrTyJzAzu33PLUjktDOam0P12sdou4pGvPDAQ8Afrp5oaMMk7iLH+jR1aRsG/gzszv6H0mXloqbx6h1ysyq8CP1ts= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SE2P216MB1521 This adds the core driver for the Chips&Media Wave6 video codec IP. The core region provides the encoding and decoding capabilities of the VPU and depends on the control region for firmware and shared resource management. This driver configure the v4l2 m2m video device and handles communication with the Wave6 hardware to perform video processing tasks. Signed-off-by: Nas Chung Tested-by: Ming Qian --- .../media/platform/chips-media/wave6/Kconfig | 2 +- .../media/platform/chips-media/wave6/Makefile | 13 + .../chips-media/wave6/wave6-vpu-core.c | 461 ++++++++++++++++++ .../chips-media/wave6/wave6-vpu-core.h | 105 ++++ 4 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-core.c create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-core.h diff --git a/drivers/media/platform/chips-media/wave6/Kconfig b/drivers/media/platform/chips-media/wave6/Kconfig index 9ca428f9d20c..14ea908e3cd4 100644 --- a/drivers/media/platform/chips-media/wave6/Kconfig +++ b/drivers/media/platform/chips-media/wave6/Kconfig @@ -13,7 +13,7 @@ config VIDEO_WAVE6_VPU The codec driver provides encoding and decoding capabilities for H.264, HEVC, and other video formats. To compile this driver as modules, choose M here: the - modules will be called wave6. + modules will be called wave6 and wave6-core. config VIDEO_WAVE6_VPU_SUPPORT_FOLLOWER bool "Support Wave6 VPU follower" diff --git a/drivers/media/platform/chips-media/wave6/Makefile b/drivers/media/platform/chips-media/wave6/Makefile index 7bbc8615c5b9..b99f8bec1b9a 100644 --- a/drivers/media/platform/chips-media/wave6/Makefile +++ b/drivers/media/platform/chips-media/wave6/Makefile @@ -1,7 +1,20 @@ # SPDX-License-Identifier: GPL-2.0 +# tell define_trace.h where to find the trace header +CFLAGS_wave6-vpu-core.o := -I$(src) + wave6-objs += wave6-vpu.o obj-$(CONFIG_VIDEO_WAVE6_VPU) += wave6.o +wave6-core-objs += wave6-vpu-core.o \ + wave6-vpu-v4l2.o \ + wave6-vpu-dbg.o \ + wave6-vdi.o \ + wave6-vpuapi.o \ + wave6-vpu-dec.o \ + wave6-vpu-enc.o \ + wave6-hw.o +obj-$(CONFIG_VIDEO_WAVE6_VPU) += wave6-core.o + wave6-ctrl-objs += wave6-vpu-ctrl.o obj-$(CONFIG_VIDEO_WAVE6_VPU_CTRL) += wave6-ctrl.o diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-core.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-core.c new file mode 100644 index 000000000000..572c2e55067d --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-core.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - wave6 core driver + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wave6-vpu-core.h" +#include "wave6-regdefine.h" +#include "wave6-vpuconfig.h" +#include "wave6-hw.h" +#include "wave6-vpu.h" +#include "wave6-vpu-dbg.h" + +#define CREATE_TRACE_POINTS +#include "wave6-trace.h" + +#define VPU_CORE_PLATFORM_DEVICE_NAME "wave6-vpu-core" +#define WAVE6_VPU_DEBUGFS_DIR "wave6" + +#define WAVE6_IS_ENC BIT(0) +#define WAVE6_IS_DEC BIT(1) + +struct wave6_match_data { + int codec_types; + u32 compatible_fw_version; +}; + +static const struct wave6_match_data wave633c_data = { + .codec_types = WAVE6_IS_ENC | WAVE6_IS_DEC, + .compatible_fw_version = 0x2010000, +}; + +static irqreturn_t wave6_vpu_irq(int irq, void *dev_id) +{ + struct vpu_core_device *dev = dev_id; + u32 irq_status; + + if (wave6_vdi_readl(dev, W6_VPU_VPU_INT_STS)) { + irq_status = wave6_vdi_readl(dev, W6_VPU_VINT_REASON); + + wave6_vdi_writel(dev, W6_VPU_VINT_REASON_CLR, irq_status); + wave6_vdi_writel(dev, W6_VPU_VINT_CLEAR, 0x1); + + trace_irq(dev, irq_status); + + kfifo_in(&dev->irq_status, &irq_status, sizeof(int)); + + return IRQ_WAKE_THREAD; + } + + return IRQ_HANDLED; +} + +static irqreturn_t wave6_vpu_irq_thread(int irq, void *dev_id) +{ + struct vpu_core_device *core = dev_id; + struct wave6_vpu_device *vpu = dev_get_drvdata(core->dev->parent); + struct vpu_instance *inst; + int irq_status, ret; + + while (kfifo_len(&core->irq_status)) { + bool error = false; + + ret = kfifo_out(&core->irq_status, &irq_status, sizeof(int)); + if (!ret) + break; + + if (irq_status & BIT(W6_INT_BIT_REQ_WORK_BUF)) { + call_void_vop(vpu, req_work_buffer, &core->entity); + continue; + } + + if ((irq_status & BIT(W6_INT_BIT_INIT_SEQ)) || + (irq_status & BIT(W6_INT_BIT_ENC_SET_PARAM))) { + complete(&core->irq_done); + continue; + } + + if (irq_status & BIT(W6_INT_BIT_BSBUF_ERROR)) + error = true; + + inst = v4l2_m2m_get_curr_priv(core->m2m_dev); + if (inst) + inst->ops->finish_process(inst, error); + } + + return IRQ_HANDLED; +} + +static u32 wave6_vpu_read_reg(struct device *dev, u32 addr) +{ + struct vpu_core_device *vpu_dev = dev_get_drvdata(dev); + + return wave6_vdi_readl(vpu_dev, addr); +} + +static void wave6_vpu_write_reg(struct device *dev, u32 addr, u32 data) +{ + struct vpu_core_device *vpu_dev = dev_get_drvdata(dev); + + wave6_vdi_writel(vpu_dev, addr, data); +} + +static void wave6_vpu_on_boot(struct device *dev) +{ + struct vpu_core_device *vpu_dev = dev_get_drvdata(dev); + u32 product_code; + u32 version; + u32 revision; + u32 hw_version; + int ret; + + mutex_lock(&vpu_dev->hw_lock); + + product_code = wave6_vdi_readl(vpu_dev, W6_VPU_RET_PRODUCT_VERSION); + + wave6_vpu_enable_interrupt(vpu_dev); + ret = wave6_vpu_get_version(vpu_dev, &version, &revision); + if (ret) { + dev_err(dev, "wave6_vpu_get_version fail\n"); + goto unlock; + } + + hw_version = wave6_vdi_readl(vpu_dev, W6_RET_CONF_REVISION); + + if (vpu_dev->product_code != product_code || + vpu_dev->fw_version != version || + vpu_dev->fw_revision != revision || + vpu_dev->hw_version != hw_version) { + vpu_dev->product_code = product_code; + vpu_dev->fw_version = version; + vpu_dev->fw_revision = revision; + vpu_dev->hw_version = hw_version; + dev_info(dev, "product: %08x, fw_version : %d.%d.%d(r%d), hw_version : 0x%x\n", + vpu_dev->product_code, + (version >> 24) & 0xFF, + (version >> 16) & 0xFF, + (version >> 0) & 0xFFFF, + revision, + vpu_dev->hw_version); + } + + if (vpu_dev->res->compatible_fw_version > version) + dev_err(dev, "compatible firmware version is v%d.%d.%d or higher, but only v%d.%d.%d\n", + (vpu_dev->res->compatible_fw_version >> 24) & 0xFF, + (vpu_dev->res->compatible_fw_version >> 16) & 0xFF, + vpu_dev->res->compatible_fw_version & 0xFFFF, + (version >> 24) & 0xFF, + (version >> 16) & 0xFF, + version & 0xFFFF); + +unlock: + mutex_unlock(&vpu_dev->hw_lock); +} + +void wave6_vpu_pause(struct device *dev, int resume) +{ + struct vpu_core_device *vpu_dev = dev_get_drvdata(dev); + + mutex_lock(&vpu_dev->pause_lock); + + if (resume) { + vpu_dev->pause_request--; + if (!vpu_dev->pause_request) + v4l2_m2m_resume(vpu_dev->m2m_dev); + } else { + if (!vpu_dev->pause_request) + v4l2_m2m_suspend(vpu_dev->m2m_dev); + vpu_dev->pause_request++; + } + mutex_unlock(&vpu_dev->pause_lock); +} + +void wave6_vpu_activate(struct vpu_core_device *dev) +{ + dev->entity.active = true; +} + +static void wave6_vpu_wait_activated(struct vpu_core_device *dev) +{ + if (dev->entity.active) + wave6_vpu_check_state(dev); +} + +static int wave6_vpu_core_register_device(struct vpu_core_device *core) +{ + struct wave6_vpu_device *vpu = dev_get_drvdata(core->dev->parent); + + if (!vpu || !vpu->ops) + return -EPROBE_DEFER; + + core->entity.dev = core->dev; + core->entity.read_reg = wave6_vpu_read_reg; + core->entity.write_reg = wave6_vpu_write_reg; + core->entity.on_boot = wave6_vpu_on_boot; + core->entity.pause = wave6_vpu_pause; + + return call_vop(vpu, reg_core, &core->entity); +} + +static void wave6_vpu_core_unregister_device(struct vpu_core_device *core) +{ + struct wave6_vpu_device *vpu = dev_get_drvdata(core->dev->parent); + + if (!vpu || !vpu->ops) + return; + + call_void_vop(vpu, unreg_core, &core->entity); +} + +static int wave6_vpu_core_probe(struct platform_device *pdev) +{ + int ret; + struct vpu_core_device *dev; + const struct wave6_match_data *match_data; + + match_data = device_get_match_data(&pdev->dev); + if (!match_data) { + dev_err(&pdev->dev, "missing match_data\n"); + return -EINVAL; + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret < 0) { + dev_err(&pdev->dev, "dma_set_mask_and_coherent failed: %d\n", ret); + return ret; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + mutex_init(&dev->dev_lock); + mutex_init(&dev->hw_lock); + mutex_init(&dev->pause_lock); + init_completion(&dev->irq_done); + dev_set_drvdata(&pdev->dev, dev); + dev->dev = &pdev->dev; + dev->res = match_data; + + dev->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->reg_base)) + return PTR_ERR(dev->reg_base); + + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks); + if (ret < 0) { + dev_warn(&pdev->dev, "unable to get clocks: %d\n", ret); + ret = 0; + } + dev->num_clks = ret; + + ret = wave6_vpu_core_register_device(dev); + if (ret < 0) { + dev_err(&pdev->dev, "register vpu core fail, ret = %d\n", ret); + return ret; + } + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "v4l2_device_register fail: %d\n", ret); + goto err_register_core; + } + + ret = wave6_vpu_init_m2m_dev(dev); + if (ret) + goto err_v4l2_unregister; + + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + ret = -ENXIO; + goto err_m2m_dev_release; + } + + ret = kfifo_alloc(&dev->irq_status, 16 * sizeof(int), GFP_KERNEL); + if (ret) { + dev_err(&pdev->dev, "failed to allocate fifo\n"); + goto err_m2m_dev_release; + } + + ret = devm_request_threaded_irq(&pdev->dev, dev->irq, wave6_vpu_irq, + wave6_vpu_irq_thread, 0, "vpu_irq", dev); + if (ret) { + dev_err(&pdev->dev, "fail to register interrupt handler: %d\n", ret); + goto err_kfifo_free; + } + + dev->temp_vbuf.size = ALIGN(WAVE6_TEMPBUF_SIZE, 4096); + ret = wave6_alloc_dma(dev->dev, &dev->temp_vbuf); + if (ret) { + dev_err(&pdev->dev, "alloc temp of size %zu failed\n", + dev->temp_vbuf.size); + goto err_kfifo_free; + } + + dev->debugfs = debugfs_lookup(WAVE6_VPU_DEBUGFS_DIR, NULL); + if (IS_ERR_OR_NULL(dev->debugfs)) + dev->debugfs = debugfs_create_dir(WAVE6_VPU_DEBUGFS_DIR, NULL); + + pm_runtime_enable(&pdev->dev); + + if (dev->res->codec_types & WAVE6_IS_DEC) { + ret = wave6_vpu_dec_register_device(dev); + if (ret) { + dev_err(&pdev->dev, "wave6_vpu_dec_register_device fail: %d\n", ret); + goto err_temp_vbuf_free; + } + } + if (dev->res->codec_types & WAVE6_IS_ENC) { + ret = wave6_vpu_enc_register_device(dev); + if (ret) { + dev_err(&pdev->dev, "wave6_vpu_enc_register_device fail: %d\n", ret); + goto err_dec_unreg; + } + } + + dev_dbg(&pdev->dev, "Added wave6 driver with caps %s %s\n", + dev->res->codec_types & WAVE6_IS_ENC ? "'ENCODE'" : "", + dev->res->codec_types & WAVE6_IS_DEC ? "'DECODE'" : ""); + + return 0; + +err_dec_unreg: + if (dev->res->codec_types & WAVE6_IS_DEC) + wave6_vpu_dec_unregister_device(dev); +err_temp_vbuf_free: + wave6_free_dma(&dev->temp_vbuf); +err_kfifo_free: + kfifo_free(&dev->irq_status); +err_m2m_dev_release: + wave6_vpu_release_m2m_dev(dev); +err_v4l2_unregister: + v4l2_device_unregister(&dev->v4l2_dev); +err_register_core: + wave6_vpu_core_unregister_device(dev); + mutex_destroy(&dev->dev_lock); + mutex_destroy(&dev->hw_lock); + mutex_destroy(&dev->pause_lock); + + return ret; +} + +static void wave6_vpu_core_remove(struct platform_device *pdev) +{ + struct vpu_core_device *dev = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + wave6_vpu_enc_unregister_device(dev); + wave6_vpu_dec_unregister_device(dev); + wave6_free_dma(&dev->temp_vbuf); + kfifo_free(&dev->irq_status); + wave6_vpu_release_m2m_dev(dev); + v4l2_device_unregister(&dev->v4l2_dev); + wave6_vpu_core_unregister_device(dev); + mutex_destroy(&dev->dev_lock); + mutex_destroy(&dev->hw_lock); + mutex_destroy(&dev->pause_lock); +} + +static int __maybe_unused wave6_vpu_core_runtime_suspend(struct device *dev) +{ + struct vpu_core_device *core = dev_get_drvdata(dev); + struct wave6_vpu_device *vpu = dev_get_drvdata(dev->parent); + + if (!core || !vpu || !vpu->ops) + return -ENODEV; + + call_void_vop(vpu, put_vpu, &core->entity); + if (core->num_clks) + clk_bulk_disable_unprepare(core->num_clks, core->clks); + + return 0; +} + +static int __maybe_unused wave6_vpu_core_runtime_resume(struct device *dev) +{ + struct vpu_core_device *core = dev_get_drvdata(dev); + struct wave6_vpu_device *vpu = dev_get_drvdata(dev->parent); + int ret = 0; + + if (!core || !vpu || !vpu->ops) + return -ENODEV; + + if (core->num_clks) { + ret = clk_bulk_prepare_enable(core->num_clks, core->clks); + if (ret) { + dev_err(dev, "failed to enable clocks: %d\n", ret); + return ret; + } + } + + ret = call_vop(vpu, get_vpu, &core->entity); + if (!ret) + wave6_vpu_wait_activated(core); + else if (core->num_clks) + clk_bulk_disable_unprepare(core->num_clks, core->clks); + + return ret; +} + +static int __maybe_unused wave6_vpu_core_suspend(struct device *dev) +{ + int ret; + + wave6_vpu_pause(dev, 0); + + ret = pm_runtime_force_suspend(dev); + if (ret) + wave6_vpu_pause(dev, 1); + + return ret; +} + +static int __maybe_unused wave6_vpu_core_resume(struct device *dev) +{ + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + wave6_vpu_pause(dev, 1); + return 0; +} + +static const struct dev_pm_ops wave6_vpu_core_pm_ops = { + SET_RUNTIME_PM_OPS(wave6_vpu_core_runtime_suspend, + wave6_vpu_core_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(wave6_vpu_core_suspend, + wave6_vpu_core_resume) +}; + +static const struct of_device_id wave6_vpu_core_ids[] = { + { .compatible = "nxp,imx95-vpu-core", .data = &wave633c_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, wave6_vpu_core_ids); + +static struct platform_driver wave6_vpu_core_driver = { + .driver = { + .name = VPU_CORE_PLATFORM_DEVICE_NAME, + .of_match_table = wave6_vpu_core_ids, + .pm = &wave6_vpu_core_pm_ops, + }, + .probe = wave6_vpu_core_probe, + .remove = wave6_vpu_core_remove, +}; + +module_platform_driver(wave6_vpu_core_driver); +MODULE_DESCRIPTION("chips&media Wave6 VPU CORE V4L2 driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-core.h b/drivers/media/platform/chips-media/wave6/wave6-vpu-core.h new file mode 100644 index 000000000000..0e5e305bf0af --- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-core.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave6 series multi-standard codec IP - wave6 core driver + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#ifndef __WAVE6_VPU_CORE_H__ +#define __WAVE6_VPU_CORE_H__ + +#include +#include +#include +#include +#include +#include +#include "wave6-vpuconfig.h" +#include "wave6-vpuapi.h" + +struct vpu_buffer { + struct v4l2_m2m_buffer v4l2_m2m_buf; + bool consumed; + bool used; + bool error; + bool force_key_frame; + bool force_frame_qp; + u32 force_i_frame_qp; + u32 force_p_frame_qp; + u32 force_b_frame_qp; + ktime_t ts_input; + ktime_t ts_start; + ktime_t ts_finish; + ktime_t ts_output; + u64 hw_time; + u32 average_qp; +}; + +enum vpu_fmt_type { + VPU_FMT_TYPE_CODEC = 0, + VPU_FMT_TYPE_RAW = 1 +}; + +struct vpu_format { + unsigned int v4l2_pix_fmt; + unsigned int max_width; + unsigned int min_width; + unsigned int max_height; + unsigned int min_height; + unsigned int num_planes; +}; + +static inline struct vpu_instance *wave6_to_vpu_inst(struct v4l2_fh *vfh) +{ + return container_of(vfh, struct vpu_instance, v4l2_fh); +} + +static inline struct vpu_instance *wave6_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl) +{ + return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl); +} + +static inline struct vpu_buffer *wave6_to_vpu_buf(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct vpu_buffer, v4l2_m2m_buf.vb); +} + +static inline bool wave6_vpu_both_queues_are_streaming(struct vpu_instance *inst) +{ + struct vb2_queue *vq_cap = v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx); + struct vb2_queue *vq_out = v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx); + + return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out); +} + +u32 wave6_vpu_get_consumed_fb_num(struct vpu_instance *inst); +u32 wave6_vpu_get_used_fb_num(struct vpu_instance *inst); +void wave6_vpu_pause(struct device *dev, int resume); +void wave6_vpu_activate(struct vpu_core_device *dev); +void wave6_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, + unsigned int width, + unsigned int height); +struct vb2_v4l2_buffer *wave6_get_dst_buf_by_addr(struct vpu_instance *inst, + dma_addr_t addr); +dma_addr_t wave6_get_dma_addr(struct vb2_v4l2_buffer *buf, + unsigned int plane_no); +enum codec_std wave6_to_codec_std(enum vpu_instance_type type, unsigned int v4l2_pix_fmt); +const char *wave6_vpu_instance_state_name(u32 state); +void wave6_vpu_set_instance_state(struct vpu_instance *inst, u32 state); +u64 wave6_vpu_cycle_to_ns(struct vpu_core_device *core, u64 cycle); +int wave6_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout); +int wave6_vpu_dec_register_device(struct vpu_core_device *dev); +void wave6_vpu_dec_unregister_device(struct vpu_core_device *dev); +int wave6_vpu_enc_register_device(struct vpu_core_device *dev); +void wave6_vpu_enc_unregister_device(struct vpu_core_device *dev); +void wave6_vpu_finish_job(struct vpu_instance *inst); +void wave6_vpu_handle_performance(struct vpu_instance *inst, struct vpu_buffer *vpu_buf); +void wave6_vpu_reset_performance(struct vpu_instance *inst); +int wave6_vpu_init_m2m_dev(struct vpu_core_device *dev); +void wave6_vpu_release_m2m_dev(struct vpu_core_device *dev); +int wave6_vpu_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub); +void wave6_vpu_return_buffers(struct vpu_instance *inst, + unsigned int type, enum vb2_buffer_state state); + +#endif /* __WAVE6_VPU_CORE_H__ */